You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1374 lines
50 KiB
C++
1374 lines
50 KiB
C++
/*
|
|
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
|
* its licensors.
|
|
*
|
|
* For complete copyright and license terms please see the LICENSE at the root of this
|
|
* distribution (the "License"). All use of this software is governed by the License,
|
|
* or, if provided, by the license below or the license accompanying this file. Do not
|
|
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
*
|
|
*/
|
|
|
|
// Description : Implements the shader related functions
|
|
|
|
#include "RenderDll_precompiled.h"
|
|
#include "GLShader.hpp"
|
|
#include "MetalDevice.hpp"
|
|
#include "GLExtensions.hpp"
|
|
|
|
// Confetti BEGIN: Igor Lobanchikov
|
|
#include "hlslcc.hpp"
|
|
#include "hlslcc_bin.hpp"
|
|
// Confetti End: Igor Lobanchikov
|
|
|
|
namespace NCryMetal
|
|
{
|
|
SSource::SSource(const char* pData, uint32 uDataSize)
|
|
: m_uDataSize(uDataSize)
|
|
, m_pData(pData)
|
|
{
|
|
}
|
|
|
|
SSource::~SSource()
|
|
{
|
|
}
|
|
|
|
struct SDXBCInfo
|
|
{
|
|
uint32 m_uMajorVersion, m_uMinorVersion;
|
|
};
|
|
|
|
struct SDXBCParseContext
|
|
: SDXBCInputBuffer
|
|
{
|
|
SDXBCInfo* m_pInfo;
|
|
|
|
SDXBCParseContext(const uint8* pBegin, const uint8* pEnd, SDXBCInfo* pInfo)
|
|
: SDXBCInputBuffer(pBegin, pEnd)
|
|
, m_pInfo(pInfo)
|
|
{
|
|
}
|
|
|
|
SDXBCParseContext(const SDXBCParseContext& kOther, uint32 uOffset)
|
|
: SDXBCInputBuffer(kOther.m_pBegin + uOffset, kOther.m_pEnd)
|
|
, m_pInfo(kOther.m_pInfo)
|
|
{
|
|
m_pIter = m_pBegin;
|
|
}
|
|
|
|
bool ReadString(char* pBuffer, uint32 uBufSize)
|
|
{
|
|
uint32 uSize((uint32)strlen(alias_cast<const char*>(m_pIter)) + 1);
|
|
if (uSize > uBufSize)
|
|
{
|
|
return false;
|
|
}
|
|
return Read(pBuffer, uSize);
|
|
}
|
|
};
|
|
|
|
bool DXBCRetrieveInfo(SDXBCParseContext& kContext)
|
|
{
|
|
uint32 uNumChunks;
|
|
DXBCReadUint32(kContext, uNumChunks);
|
|
|
|
uint32 uChunk;
|
|
for (uChunk = 0; uChunk < uNumChunks; ++uChunk)
|
|
{
|
|
uint32 uChunkBegin;
|
|
DXBCReadUint32(kContext, uChunkBegin);
|
|
|
|
SDXBCParseContext kChunkHeaderContext(kContext, uChunkBegin);
|
|
|
|
uint32 uChunkFourCC, uChunkSize;
|
|
DXBCReadUint32(kChunkHeaderContext, uChunkFourCC);
|
|
DXBCReadUint32(kChunkHeaderContext, uChunkSize);
|
|
|
|
if (uChunkFourCC == FOURCC_SHDR || uChunkFourCC == FOURCC_SHEX)
|
|
{
|
|
uint32 uEncodedInfo;
|
|
DXBCReadUint32(kChunkHeaderContext, uEncodedInfo);
|
|
|
|
kContext.m_pInfo->m_uMajorVersion = (uEncodedInfo >> 4) & 0xF;
|
|
kContext.m_pInfo->m_uMinorVersion = uEncodedInfo & 0xF;
|
|
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
SShaderSource::SShaderSource()
|
|
{
|
|
}
|
|
|
|
SShaderSource::~SShaderSource()
|
|
{
|
|
delete [] m_pData;
|
|
}
|
|
|
|
void SShaderSource::SetData(const char* pData, uint32 uDataSize)
|
|
{
|
|
delete [] m_pData;
|
|
if (uDataSize > 0)
|
|
{
|
|
char* pBuffer = new char[uDataSize];
|
|
memcpy(pBuffer, pData, uDataSize);
|
|
m_pData = pBuffer;
|
|
}
|
|
else
|
|
{
|
|
m_pData = NULL;
|
|
}
|
|
m_uDataSize = uDataSize;
|
|
}
|
|
|
|
|
|
SGLShader::SGLShader()
|
|
// Confetti BEGIN: Igor Lobanchikov
|
|
: m_pFunction(0)
|
|
// Confetti End: Igor Lobanchikov
|
|
{
|
|
}
|
|
|
|
SGLShader::~SGLShader()
|
|
{
|
|
// Confetti BEGIN: Igor Lobanchikov
|
|
if (m_pFunction)
|
|
{
|
|
[m_pFunction release];
|
|
}
|
|
// Confetti End: Igor Lobanchikov
|
|
}
|
|
|
|
SShader::SShader()
|
|
{
|
|
}
|
|
|
|
SShader::~SShader()
|
|
{
|
|
// All pipelines with this shader bound must be removed from the cache and deleted
|
|
TPipelines::const_iterator kPipelineIter(m_kBoundPipelines.begin());
|
|
const TPipelines::const_iterator kPipelineEnd(m_kBoundPipelines.end());
|
|
while (kPipelineIter != kPipelineEnd)
|
|
{
|
|
(*kPipelineIter)->m_pContext->RemovePipeline(*kPipelineIter, this);
|
|
++kPipelineIter;
|
|
}
|
|
}
|
|
|
|
void SShader::AttachPipeline(SPipeline* pPipeline)
|
|
{
|
|
TPipelines::const_iterator kFound(std::find(m_kBoundPipelines.begin(), m_kBoundPipelines.end(), pPipeline));
|
|
if (kFound == m_kBoundPipelines.end())
|
|
{
|
|
m_kBoundPipelines.push_back(pPipeline);
|
|
}
|
|
}
|
|
|
|
void SShader::DetachPipeline(SPipeline* pPipeline)
|
|
{
|
|
TPipelines::iterator kFound(std::find(m_kBoundPipelines.begin(), m_kBoundPipelines.end(), pPipeline));
|
|
if (kFound != m_kBoundPipelines.end())
|
|
{
|
|
m_kBoundPipelines.erase(kFound);
|
|
}
|
|
else
|
|
{
|
|
DXGL_ERROR("Could not find the pipeline to be detached from the shader");
|
|
}
|
|
}
|
|
|
|
SPipeline::SPipeline(const SPipelineConfiguration& kConfiguration, CContext* pContext)
|
|
: m_kConfiguration(kConfiguration)
|
|
, m_pContext(pContext)
|
|
// Confetti BEGIN: Igor Lobanchikov
|
|
, m_PipelineState(0)
|
|
// Confetti End: Igor Lobanchikov
|
|
{
|
|
}
|
|
|
|
SPipeline::~SPipeline()
|
|
{
|
|
// Confetti BEGIN: Igor Lobanchikov
|
|
if (m_PipelineState)
|
|
{
|
|
[m_PipelineState release];
|
|
}
|
|
// Confetti End: Igor Lobanchikov
|
|
}
|
|
|
|
// Confetti BEGIN: Igor Lobanchikov
|
|
SInputLayout::SInputLayout()
|
|
: m_pVertexDescriptor([[MTLVertexDescriptor alloc] init])
|
|
{
|
|
;
|
|
}
|
|
|
|
SInputLayout::~SInputLayout()
|
|
{
|
|
[(MTLVertexDescriptor*)m_pVertexDescriptor release];
|
|
}
|
|
SInputLayout::SInputLayout(const SInputLayout& other)
|
|
{
|
|
m_pVertexDescriptor = other.m_pVertexDescriptor;
|
|
[(MTLVertexDescriptor*)m_pVertexDescriptor retain];
|
|
}
|
|
|
|
SInputLayout& SInputLayout::operator=(const SInputLayout& other)
|
|
{
|
|
[(MTLVertexDescriptor*)m_pVertexDescriptor release];
|
|
m_pVertexDescriptor = other.m_pVertexDescriptor;
|
|
[(MTLVertexDescriptor*)m_pVertexDescriptor retain];
|
|
|
|
return *this;
|
|
}
|
|
// Confetti End: Igor Lobanchikov
|
|
|
|
bool InitializeShaderReflectionVariable(SShaderReflectionVariable* pVariable, SDXBCParseContext* pContext)
|
|
{
|
|
uint32 uNamePos;
|
|
if (!DXBCReadUint32(*pContext, uNamePos) ||
|
|
!SDXBCParseContext(*pContext, uNamePos).ReadString(pVariable->m_acName, DXGL_ARRAY_SIZE(pVariable->m_acName)))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
uint32 uPosType;
|
|
memset(&pVariable->m_kDesc, 0, sizeof(D3D11_SHADER_VARIABLE_DESC));
|
|
pVariable->m_kDesc.Name = pVariable->m_acName;
|
|
if (!DXBCReadUint32(*pContext, pVariable->m_kDesc.StartOffset) ||
|
|
!DXBCReadUint32(*pContext, pVariable->m_kDesc.Size) ||
|
|
!DXBCReadUint32(*pContext, pVariable->m_kDesc.uFlags) ||
|
|
!DXBCReadUint32(*pContext, uPosType))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
{
|
|
SDXBCParseContext kTypeContext(*pContext);
|
|
memset(&pVariable->m_kType, 0, sizeof(D3D11_SHADER_TYPE_DESC));
|
|
|
|
if (!kTypeContext.SeekAbs(uPosType) ||
|
|
!DXBCReadUint16(kTypeContext, pVariable->m_kType.Class) ||
|
|
!DXBCReadUint16(kTypeContext, pVariable->m_kType.Type) ||
|
|
!DXBCReadUint16(kTypeContext, pVariable->m_kType.Rows) ||
|
|
!DXBCReadUint16(kTypeContext, pVariable->m_kType.Columns) ||
|
|
!DXBCReadUint16(kTypeContext, pVariable->m_kType.Elements) ||
|
|
!DXBCReadUint16(kTypeContext, pVariable->m_kType.Members) ||
|
|
!DXBCReadUint32(kTypeContext, pVariable->m_kType.Offset))
|
|
{
|
|
return false;
|
|
}
|
|
pVariable->m_kType.Name = NULL;
|
|
}
|
|
|
|
uint32 uPosDefaultValue;
|
|
if (!DXBCReadUint32(*pContext, uPosDefaultValue))
|
|
{
|
|
return false;
|
|
}
|
|
if (uPosDefaultValue != 0)
|
|
{
|
|
pVariable->m_kDefaultValue.resize(pVariable->m_kDesc.Size);
|
|
if (!pContext->Read(pVariable->m_kDefaultValue.data(), pVariable->m_kDesc.Size))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pVariable->m_kDesc.DefaultValue = NULL;
|
|
}
|
|
|
|
if (pContext->m_pInfo->m_uMajorVersion >= 5)
|
|
{
|
|
if (!DXBCReadUint32(*pContext, pVariable->m_kDesc.StartTexture) ||
|
|
!DXBCReadUint32(*pContext, pVariable->m_kDesc.TextureSize) ||
|
|
!DXBCReadUint32(*pContext, pVariable->m_kDesc.StartSampler) ||
|
|
!DXBCReadUint32(*pContext, pVariable->m_kDesc.SamplerSize))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool InitializeShaderReflectionConstBuffer(SShaderReflectionConstBuffer* pConstBuffer, SDXBCParseContext* pContext)
|
|
{
|
|
uint32 uNamePos;
|
|
if (!DXBCReadUint32(*pContext, uNamePos) ||
|
|
!SDXBCParseContext(*pContext, uNamePos).ReadString(pConstBuffer->m_acName, DXGL_ARRAY_SIZE(pConstBuffer->m_acName)))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
memset(&pConstBuffer->m_kDesc, 0, sizeof(pConstBuffer->m_kDesc));
|
|
pConstBuffer->m_kDesc.Name = pConstBuffer->m_acName;
|
|
|
|
uint32 uNumVariables, uPosVariables;
|
|
if (!DXBCReadUint32(*pContext, uNumVariables) ||
|
|
!DXBCReadUint32(*pContext, uPosVariables))
|
|
{
|
|
return false;
|
|
}
|
|
pConstBuffer->m_kDesc.Variables = uNumVariables;
|
|
|
|
SDXBCParseContext kVariablesContext(*pContext);
|
|
if (!kVariablesContext.SeekAbs(uPosVariables))
|
|
{
|
|
return false;
|
|
}
|
|
uint32 uVariable;
|
|
pConstBuffer->m_kVariables.resize(pConstBuffer->m_kDesc.Variables);
|
|
for (uVariable = 0; uVariable < pConstBuffer->m_kDesc.Variables; ++uVariable)
|
|
{
|
|
if (!InitializeShaderReflectionVariable(&pConstBuffer->m_kVariables.at(uVariable), &kVariablesContext))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (!DXBCReadUint32(*pContext, pConstBuffer->m_kDesc.Size) ||
|
|
!DXBCReadUint32(*pContext, pConstBuffer->m_kDesc.uFlags) ||
|
|
!DXBCReadUint32(*pContext, pConstBuffer->m_kDesc.Type))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool InitializeShaderReflectionResource(SShaderReflectionResource* pParameter, SDXBCParseContext* pContext)
|
|
{
|
|
uint32 uNamePos;
|
|
if (!DXBCReadUint32(*pContext, uNamePos) ||
|
|
!SDXBCParseContext(*pContext, uNamePos).ReadString(pParameter->m_acName, DXGL_ARRAY_SIZE(pParameter->m_acName)))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
memset(&pParameter->m_kDesc, 0, sizeof(pParameter->m_kDesc));
|
|
|
|
pParameter->m_kDesc.Name = pParameter->m_acName;
|
|
if (!DXBCReadUint32(*pContext, pParameter->m_kDesc.Type) ||
|
|
!DXBCReadUint32(*pContext, pParameter->m_kDesc.ReturnType) ||
|
|
!DXBCReadUint32(*pContext, pParameter->m_kDesc.Dimension) ||
|
|
!DXBCReadUint32(*pContext, pParameter->m_kDesc.NumSamples) ||
|
|
!DXBCReadUint32(*pContext, pParameter->m_kDesc.BindPoint) ||
|
|
!DXBCReadUint32(*pContext, pParameter->m_kDesc.BindCount) ||
|
|
!DXBCReadUint32(*pContext, pParameter->m_kDesc.uFlags))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool InitializeShaderReflectionParameters(SShaderReflection::TParameters& kParameters, SDXBCParseContext* pContext, bool extended = false)
|
|
{
|
|
uint32 uNumElements, uVersion;
|
|
if (!DXBCReadUint32(*pContext, uNumElements) ||
|
|
!DXBCReadUint32(*pContext, uVersion))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (uNumElements > 0)
|
|
{
|
|
uint32 uPrevNumElements(kParameters.size());
|
|
kParameters.resize(kParameters.size() + uNumElements);
|
|
|
|
uint32 uElement;
|
|
for (uElement = uPrevNumElements; uElement < kParameters.size(); ++uElement)
|
|
{
|
|
SShaderReflectionParameter& kParameter(kParameters.at(uElement));
|
|
|
|
memset(&kParameter.m_kDesc, 0, sizeof(kParameter.m_kDesc));
|
|
|
|
// Only for extended parameters fxc adds two extra pieces of information, the stream index and the minimum precision.
|
|
if (extended && !DXBCReadUint32(*pContext, kParameter.m_kDesc.Stream))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
uint32 uSemNamePosition;
|
|
if (!DXBCReadUint32(*pContext, uSemNamePosition) ||
|
|
!SDXBCParseContext(*pContext, uSemNamePosition).ReadString(kParameter.m_acSemanticName, DXGL_ARRAY_SIZE(kParameter.m_acSemanticName)))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
kParameter.m_kDesc.SemanticName = kParameter.m_acSemanticName;
|
|
if (!DXBCReadUint32(*pContext, kParameter.m_kDesc.SemanticIndex) ||
|
|
!DXBCReadUint32(*pContext, kParameter.m_kDesc.SystemValueType) ||
|
|
!DXBCReadUint32(*pContext, kParameter.m_kDesc.ComponentType) ||
|
|
!DXBCReadUint32(*pContext, kParameter.m_kDesc.Register) ||
|
|
!DXBCReadUint8(*pContext, kParameter.m_kDesc.Mask) ||
|
|
!DXBCReadUint8(*pContext, kParameter.m_kDesc.ReadWriteMask) ||
|
|
!pContext->SeekRel(sizeof(uint16_t)) || // These two bytes are part of the mask but are not used, so we skip them.
|
|
(extended && !DXBCReadUint32(*pContext, kParameter.m_kDesc.MinPrecision))) // Precision available only for extended parameters.
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool InitializeShaderReflection(SShaderReflection* pReflection, const void* pvData)
|
|
{
|
|
enum
|
|
{
|
|
SIZE_POSITION = 6
|
|
};
|
|
|
|
uint32 uSize(alias_cast<const uint32*>(pvData)[SIZE_POSITION]);
|
|
|
|
|
|
|
|
enum
|
|
{
|
|
CHUNKS_POSITION = sizeof(uint32) * 7
|
|
};
|
|
SDXBCInfo kInfo;
|
|
const uint8* puData(alias_cast<const uint8*>(pvData));
|
|
SDXBCParseContext kContext(puData, puData + uSize, &kInfo);
|
|
|
|
if (!kContext.SeekAbs(CHUNKS_POSITION))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
{
|
|
SDXBCParseContext kParseContext(kContext);
|
|
if (!DXBCRetrieveInfo(kParseContext))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
uint32 uNumChunks;
|
|
if (!DXBCReadUint32(kContext, uNumChunks))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
uint32 uChunk;
|
|
for (uChunk = 0; uChunk < uNumChunks; ++uChunk)
|
|
{
|
|
uint32 uChunkBegin;
|
|
if (!DXBCReadUint32(kContext, uChunkBegin))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
SDXBCParseContext kChunkHeaderContext(kContext, uChunkBegin);
|
|
|
|
uint32 uChunkFourCC, uChunkSize;
|
|
if (!DXBCReadUint32(kChunkHeaderContext, uChunkFourCC) ||
|
|
!DXBCReadUint32(kChunkHeaderContext, uChunkSize))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
SDXBCParseContext kChunkContext(kChunkHeaderContext.m_pIter, kChunkHeaderContext.m_pEnd, &kInfo);
|
|
switch (uChunkFourCC)
|
|
{
|
|
case FOURCC_RDEF:
|
|
{
|
|
uint32 uNumConstBuffers, uPosConstBuffers, uNumResources, uPosResources;
|
|
if (!DXBCReadUint32(kChunkContext, uNumConstBuffers) ||
|
|
!DXBCReadUint32(kChunkContext, uPosConstBuffers) ||
|
|
!DXBCReadUint32(kChunkContext, uNumResources) ||
|
|
!DXBCReadUint32(kChunkContext, uPosResources))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
SDXBCParseContext kConstBufferContext(kChunkContext);
|
|
if (!kConstBufferContext.SeekAbs(uPosConstBuffers))
|
|
{
|
|
return false;
|
|
}
|
|
uint32 uConstBuffer;
|
|
uint32 uPrevNumConstBuffers(pReflection->m_kConstantBuffers.size());
|
|
pReflection->m_kConstantBuffers.resize(uNumConstBuffers);
|
|
for (uConstBuffer = uPrevNumConstBuffers; uConstBuffer < uNumConstBuffers; ++uConstBuffer)
|
|
{
|
|
if (!InitializeShaderReflectionConstBuffer(&pReflection->m_kConstantBuffers.at(uConstBuffer), &kConstBufferContext))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
SDXBCParseContext kResourceContext(kChunkContext);
|
|
if (!kResourceContext.SeekAbs(uPosResources))
|
|
{
|
|
return false;
|
|
}
|
|
uint32 uResource;
|
|
uint32 uPrevNumResources(pReflection->m_kResources.size());
|
|
pReflection->m_kResources.resize(uNumResources);
|
|
for (uResource = uPrevNumResources; uResource < uNumResources; ++uResource)
|
|
{
|
|
if (!InitializeShaderReflectionResource(&pReflection->m_kResources.at(uResource), &kResourceContext))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case FOURCC_ISGN:
|
|
case FOURCC_ISG1:
|
|
if (!InitializeShaderReflectionParameters(pReflection->m_kInputs, &kChunkContext, uChunkFourCC == FOURCC_ISG1))
|
|
{
|
|
return false;
|
|
}
|
|
break;
|
|
case FOURCC_OSGN:
|
|
case FOURCC_OSG1:
|
|
if (!InitializeShaderReflectionParameters(pReflection->m_kOutputs, &kChunkContext, uChunkFourCC == FOURCC_OSG1))
|
|
{
|
|
return false;
|
|
}
|
|
break;
|
|
case FOURCC_PCSG:
|
|
if (!InitializeShaderReflectionParameters(pReflection->m_kPatchConstants, &kChunkContext))
|
|
{
|
|
return false;
|
|
}
|
|
break;
|
|
case FOURCC_GLSL:
|
|
if (!DXBCReadUint32(kChunkContext, pReflection->m_uSamplerMapSize) ||
|
|
!DXBCReadUint32(kChunkContext, pReflection->m_uImportsSize) ||
|
|
!DXBCReadUint32(kChunkContext, pReflection->m_uExportsSize) ||
|
|
!DXBCReadUint32(kChunkContext, pReflection->m_uInputHash) ||
|
|
!DXBCReadUint32(kChunkContext, pReflection->m_uUAVBindingAreaSize) ||
|
|
!DXBCReadUint32(kChunkContext, pReflection->m_uThread_x) ||
|
|
!DXBCReadUint32(kChunkContext, pReflection->m_uThread_y) ||
|
|
!DXBCReadUint32(kChunkContext, pReflection->m_uThread_z))
|
|
{
|
|
return false;
|
|
}
|
|
pReflection->m_uSamplerMapOffset = (uint32)(kChunkContext.m_pIter - kContext.m_pBegin);
|
|
pReflection->m_uImportsOffset =
|
|
pReflection->m_uSamplerMapOffset +
|
|
pReflection->m_uSamplerMapSize * 2 * 4; // Each sampler is {uint32 uTexture; uint32 uSampler;}
|
|
pReflection->m_uExportsOffset =
|
|
pReflection->m_uImportsOffset +
|
|
pReflection->m_uImportsSize * 3 * 4; // Each import is {uint32 uType; uint32 uID; uint32 uValue;}
|
|
pReflection->m_uUAVBindingAreaOffset =
|
|
pReflection->m_uExportsOffset +
|
|
pReflection->m_uExportsSize * 3 * 4; // Each export is {uint32 uType; uint32 uID; uint32 uValue;}
|
|
pReflection->m_uGLSLSourceOffset =
|
|
pReflection->m_uUAVBindingAreaOffset +
|
|
pReflection->m_uUAVBindingAreaSize * 2 * 4; // Each export is {uint32 uResource; uint32 uValue;}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
SDXBCParseContext kConstUAVAreaContext(kContext);
|
|
if (!kConstUAVAreaContext.SeekAbs(pReflection->m_uUAVBindingAreaOffset))
|
|
{
|
|
return false;
|
|
}
|
|
for (uint uResource = 0; uResource < pReflection->m_uUAVBindingAreaSize; uResource++)
|
|
{
|
|
uint32 uResourceIndex;
|
|
if (!DXBCReadUint32(kConstUAVAreaContext, uResourceIndex))
|
|
{
|
|
return false;
|
|
}
|
|
if (!DXBCReadUint32(kConstUAVAreaContext, pReflection->m_kResources.at(uResourceIndex).m_kDesc.UAVBindingArea))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
memset(&pReflection->m_kDesc, 0, sizeof(D3D11_SHADER_DESC));
|
|
pReflection->m_kDesc.ConstantBuffers = pReflection->m_kConstantBuffers.size();
|
|
pReflection->m_kDesc.BoundResources = pReflection->m_kResources.size();
|
|
pReflection->m_kDesc.InputParameters = pReflection->m_kInputs.size();
|
|
pReflection->m_kDesc.OutputParameters = pReflection->m_kOutputs.size();
|
|
pReflection->m_kDesc.PatchConstantParameters = pReflection->m_kPatchConstants.size();
|
|
|
|
return true;
|
|
}
|
|
|
|
MTLLanguageVersion GetMetalLanguage()
|
|
{
|
|
#if defined(AZ_COMPILER_CLANG) && AZ_COMPILER_CLANG >= 9 //@available was added in Xcode 9
|
|
#if defined(__MAC_10_13) || defined(__IPHONE_11_0) || defined(__TVOS_11_0)
|
|
if(@available(macOS 10.13, iOS 11.0, tvOS 11.0, *))
|
|
{
|
|
return MTLLanguageVersion2_0;
|
|
}
|
|
#endif
|
|
|
|
#if defined(__MAC_10_12) || defined(__IPHONE_10_0) || defined(__TVOS_10_0)
|
|
if (@available(macOS 10.12, iOS 10.0, tvOS 10.0, *))
|
|
{
|
|
return MTLLanguageVersion1_2;
|
|
}
|
|
#endif
|
|
|
|
#if defined(__MAC_10_11) || defined(__IPHONE_9_0) || defined(__TVOS_9_0)
|
|
if(@available(macOS 10.11, iOS 9.0, tvOS 9.0, *))
|
|
{
|
|
|
|
return MTLLanguageVersion1_1;
|
|
}
|
|
#endif
|
|
#else // Xcode 8.
|
|
|
|
// NSFoundationVersionNumber10_10_4 is to check for iOS 10.0+ and macOS 10.12+
|
|
if (floor(NSFoundationVersionNumber) < NSFoundationVersionNumber10_10_4)
|
|
{
|
|
return MTLLanguageVersion1_1;
|
|
}
|
|
else
|
|
{
|
|
return MTLLanguageVersion1_2;
|
|
}
|
|
|
|
#endif
|
|
return MTLLanguageVersion1_2;
|
|
}
|
|
|
|
bool CompileShader(SShader* pShader, SSource* aSourceVersions, SGLShader* pGLShader, id<MTLDevice> mtlDevice)
|
|
{
|
|
|
|
NSMutableString* source = [[NSMutableString alloc] initWithBytes:aSourceVersions->m_pData
|
|
length:(aSourceVersions->m_uDataSize - 1)
|
|
encoding:NSASCIIStringEncoding];
|
|
|
|
MTLCompileOptions* options = [MTLCompileOptions alloc];
|
|
options.fastMathEnabled = CRenderer::CV_r_MetalShadersFastMath ? YES : NO;
|
|
|
|
MTLLanguageVersion metalLang = GetMetalLanguage();
|
|
if(metalLang == MTLLanguageVersion1_1)
|
|
{
|
|
//Our cross compiler doesnt have support for different mtllanguageversions. In order to ensure
|
|
//that the game runs on ios 9.0 we simply have language specific macros in the metal shader
|
|
//and inserting the define language MTLLanguageVersion1_1 to ensure correct code is used
|
|
[source insertString:@"#define MTLLanguage1_1\n" atIndex:0];
|
|
}
|
|
options.languageVersion = metalLang;
|
|
NSError* error = nil;
|
|
|
|
id<MTLLibrary> lib = [mtlDevice newLibraryWithSource:source
|
|
options:options
|
|
error:&error];
|
|
|
|
|
|
LOG_METAL_SHADER_SOURCE(@ "%@", source);
|
|
|
|
if (error)
|
|
{
|
|
// Error code 4 is warning, but sometimes a 3 (compile error) is returned on warnings only.
|
|
// The documentation indicates that if the lib is nil there is a compile error, otherwise anything
|
|
// in the error is really a warning. Therefore, we check the lib instead of the error code
|
|
if (!lib)
|
|
{
|
|
LOG_METAL_SHADER_ERRORS(@ "%@", error);
|
|
assert(!"HLSLcc compiler should generate valid code.");
|
|
}
|
|
else
|
|
{
|
|
LOG_METAL_SHADER_WARNINGS(@ "%@", error);
|
|
}
|
|
|
|
// Igor: error string is an autorelease object.
|
|
error = 0;
|
|
}
|
|
|
|
if (lib)
|
|
{
|
|
pGLShader->m_pFunction = [lib newFunctionWithName:@ "metalMain"];
|
|
[lib release];
|
|
}
|
|
|
|
[options release];
|
|
[source release];
|
|
|
|
if (!pGLShader->m_pFunction)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (pGLShader->m_pFunction.functionType == MTLFunctionTypeVertex)
|
|
{
|
|
LOG_METAL_SHADER_REFLECTION_VALIDATION(@ "%@", pGLShader->m_pFunction.vertexAttributes);
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
// Confetti BEGIN: Igor Lobanchikov
|
|
void LogMTLArgument(MTLArgument* arg)
|
|
{
|
|
#if DXMETAL_LOG_SHADER_REFLECTION_VALIDATION
|
|
NSLog(@ "Name: %@", arg.name);
|
|
|
|
NSLog(@ "\tActive: %@", arg.active ? @ "true" : @ "false");
|
|
|
|
switch (arg.type)
|
|
{
|
|
case MTLArgumentTypeBuffer:
|
|
NSLog(@ "\tType: %@", @ "MTLArgumentTypeBuffer");
|
|
break;
|
|
|
|
case MTLArgumentTypeThreadgroupMemory:
|
|
NSLog(@ "\tType: %@", @ "MTLArgumentTypeThreadgroupMemory");
|
|
break;
|
|
|
|
case MTLArgumentTypeTexture:
|
|
NSLog(@ "\tType: %@", @ "MTLArgumentTypeTexture");
|
|
break;
|
|
|
|
case MTLArgumentTypeSampler:
|
|
NSLog(@ "\tType: %@", @ "MTLArgumentTypeSampler");
|
|
break;
|
|
}
|
|
|
|
NSLog(@ "\tIndex: %lu", arg.index);
|
|
|
|
switch (arg.access)
|
|
{
|
|
case MTLArgumentAccessReadOnly:
|
|
NSLog(@ "\tAccess: %@", @ "MTLArgumentAccessReadOnly");
|
|
break;
|
|
case MTLArgumentAccessReadWrite:
|
|
NSLog(@ "\tAccess: %@", @ "MTLArgumentAccessReadWrite");
|
|
break;
|
|
case MTLArgumentAccessWriteOnly:
|
|
NSLog(@ "\tAccess: %@", @ "MTLArgumentAccessWriteOnly");
|
|
break;
|
|
}
|
|
|
|
if (arg.type == MTLArgumentTypeBuffer)
|
|
{
|
|
NSLog(@ "StructType: %@", arg.bufferStructType);
|
|
}
|
|
#endif // DXMETAL_LOG_SHADER_REFLECTION_VALIDATION
|
|
}
|
|
|
|
MTLArgumentType DX11SITToMetal(D3D_SHADER_INPUT_TYPE dx11Type)
|
|
{
|
|
switch (dx11Type)
|
|
{
|
|
case D3D_SIT_CBUFFER:
|
|
case D3D_SIT_TBUFFER:
|
|
return MTLArgumentTypeBuffer;
|
|
|
|
case D3D_SIT_TEXTURE:
|
|
return MTLArgumentTypeTexture;
|
|
|
|
case D3D_SIT_SAMPLER:
|
|
return MTLArgumentTypeSampler;
|
|
|
|
case D3D_SIT_STRUCTURED:
|
|
return MTLArgumentTypeBuffer;
|
|
case D3D_SIT_UAV_RWTYPED:
|
|
return MTLArgumentTypeBuffer;
|
|
case D3D_SIT_UAV_RWSTRUCTURED:
|
|
return MTLArgumentTypeBuffer;
|
|
default:
|
|
assert(!"Not supported for this platform yet");
|
|
return (MTLArgumentType)99;
|
|
}
|
|
|
|
//MTLArgumentTypeThreadgroupMemory= 1,
|
|
}
|
|
|
|
bool ValidateBindPoint(MTLArgument* arg, SShaderReflection& cryReflection)
|
|
{
|
|
SShaderReflection::TResources kResources = cryReflection.m_kResources;
|
|
|
|
char acName[DXGL_MAX_REFLECT_STRING_LENGTH];
|
|
strncpy(acName, [arg.name UTF8String], DXGL_MAX_REFLECT_STRING_LENGTH - 1);
|
|
acName[DXGL_MAX_REFLECT_STRING_LENGTH - 1] = 0;
|
|
|
|
if (strstr(acName, "vertexBuffer.") == acName)
|
|
{
|
|
LOG_METAL_SHADER_REFLECTION_VALIDATION(@ "Resource %@ is attached via input assembler.", arg.name);
|
|
return true;
|
|
}
|
|
|
|
size_t strArrLen = kResources.size();
|
|
|
|
size_t i = 0;
|
|
for (; i < strArrLen; ++i)
|
|
{
|
|
uint32 offset = 0;
|
|
if (kResources[i].m_kDesc.Type == D3D_SIT_UAV_RWTYPED || kResources[i].m_kDesc.Type == D3D_SIT_UAV_RWSTRUCTURED)
|
|
{
|
|
// UAV resources are after the constants buffers so set the offset appropriately
|
|
offset = SBufferStateStageCache::s_MaxConstantBuffersPerStage;
|
|
}
|
|
if (strstr(acName, kResources[i].m_acName) == acName)
|
|
{
|
|
if (((kResources[i].m_kDesc.BindPoint + offset) == arg.index && DX11SITToMetal(kResources[i].m_kDesc.Type) == arg.type))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (i == strArrLen)
|
|
{
|
|
LOG_METAL_SHADER_ERRORS(@ "Resource \"%@\" or similar was not found in DX11 reflection or bind points do not match.", arg.name);
|
|
assert(!"HLSLcc code must pass validator");
|
|
return false;
|
|
}
|
|
|
|
LOG_METAL_SHADER_REFLECTION_VALIDATION(@ "Resource %s is detected as \"%@\" in the metal shader.", kResources[i].m_acName, arg.name);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool ValidateBufferResource(MTLArgument* arg, SShaderReflection& cryReflection)
|
|
{
|
|
assert(arg.type == MTLArgumentTypeBuffer);
|
|
|
|
char acName[DXGL_MAX_REFLECT_STRING_LENGTH];
|
|
strncpy(acName, [arg.name UTF8String], DXGL_MAX_REFLECT_STRING_LENGTH - 1);
|
|
acName[DXGL_MAX_REFLECT_STRING_LENGTH - 1] = 0;
|
|
|
|
if (strstr(acName, "vertexBuffer.") == acName)
|
|
{
|
|
LOG_METAL_SHADER_REFLECTION_VALIDATION(@ "Resource %@ is attached via input assembler.", arg.name);
|
|
return true;
|
|
}
|
|
|
|
SShaderReflection::TConstantBuffers& kConstantBuffers = cryReflection.m_kConstantBuffers;
|
|
|
|
size_t strArrLen = kConstantBuffers.size();
|
|
|
|
size_t i = 0;
|
|
for (; i < strArrLen; ++i)
|
|
{
|
|
if (strstr(acName, kConstantBuffers[i].m_acName) == acName)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (i == strArrLen)
|
|
{
|
|
LOG_METAL_SHADER_ERRORS(@ "Buffer \"%@\" description or similar was not found in DX11 reflection.", arg.name);
|
|
assert(!"HLSLcc code must pass validator");
|
|
return false;
|
|
}
|
|
|
|
SShaderReflectionConstBuffer::TVariables& kDX11Vars = kConstantBuffers[i].m_kVariables;
|
|
size_t dx11VarNum = kDX11Vars.size();
|
|
|
|
LOG_METAL_SHADER_REFLECTION_VALIDATION(@ "Buffer %s is detected as \"%@\" in the metal shader.", kConstantBuffers[i].m_acName, arg.name);
|
|
LOG_METAL_SHADER_REFLECTION_VALIDATION(@ "%@", arg.bufferStructType);
|
|
|
|
for (MTLStructMember* var in arg.bufferStructType.members)
|
|
{
|
|
if ([var.name rangeOfString:@ "offsetDummy"].location != NSNotFound)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
char acVarName[DXGL_MAX_REFLECT_STRING_LENGTH];
|
|
strncpy(acVarName, [var.name UTF8String], DXGL_MAX_REFLECT_STRING_LENGTH - 1);
|
|
acVarName[DXGL_MAX_REFLECT_STRING_LENGTH - 1] = 0;
|
|
|
|
size_t j = 0;
|
|
int varOffset = var.offset;
|
|
for (; j < dx11VarNum; ++j)
|
|
{
|
|
if ((strstr(acVarName, kDX11Vars[j].m_acName) == acVarName) && (varOffset == kDX11Vars[j].m_kDesc.StartOffset) ||
|
|
(strstr(kDX11Vars[j].m_acName, "$Element") == kDX11Vars[j].m_acName))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (j == dx11VarNum)
|
|
{
|
|
LOG_METAL_SHADER_ERRORS(@ "Variable \"%@::%@\" or similar was not found in DX11 reflection or offsets in buffers do not match", arg.name, var.name);
|
|
assert(!"HLSLcc code must pass validator");
|
|
return false;
|
|
}
|
|
|
|
LOG_METAL_SHADER_REFLECTION_VALIDATION(@ "Variable %s::%s is detected as \"%@::%@\" in the metal shader.", kConstantBuffers[i].m_acName, kDX11Vars[j].m_acName, arg.name, var.name);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
bool ValidateReflection(EShaderType shaderType, NSArray* metalReflection, SShaderReflection& cryReflection)
|
|
{
|
|
LOG_METAL_SHADER_REFLECTION_VALIDATION(shaderType == eST_Vertex ? @ "Vertex Shader inputs validation" :
|
|
(shaderType == eST_Fragment ? @ "Fragment Shader inputs validation" : @ "Compute Shader inputs validation"));
|
|
|
|
bool res = true;
|
|
|
|
for (MTLArgument* arg in metalReflection)
|
|
{
|
|
if (!arg.active)
|
|
{
|
|
continue;
|
|
}
|
|
if (arg.access == MTLArgumentAccessWriteOnly)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (!ValidateBindPoint(arg, cryReflection))
|
|
{
|
|
res = false;
|
|
LOG_METAL_SHADER_REFLECTION_VALIDATION(@ "%@", arg);
|
|
LogMTLArgument(arg);
|
|
}
|
|
|
|
if (shaderType != eST_Compute)
|
|
{
|
|
switch (arg.type)
|
|
{
|
|
case MTLArgumentTypeBuffer:
|
|
{
|
|
if (!ValidateBufferResource(arg, cryReflection))
|
|
{
|
|
res = false;
|
|
LogMTLArgument(arg);
|
|
}
|
|
break;
|
|
}
|
|
case MTLArgumentTypeTexture:
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return res;
|
|
}
|
|
// Confetti End: Igor Lobanchikov
|
|
|
|
MTLVertexDescriptor* CleanUnusedInputs(MTLVertexDescriptor* pIn, const TShaderReflection& kReflection)
|
|
{
|
|
MTLVertexDescriptor* pOut = nil;
|
|
|
|
bool bBufferUsed[DXMETAL_MAX_ENTRIES_BUFFER_ARG_TABLE];
|
|
memset(bBufferUsed, 0, sizeof(bBufferUsed));
|
|
|
|
// TODO: Igor: figure out why input layout can come with vertex buffer being used without any inputs attached.
|
|
// Note: disable the following line and metal pipeline will explaine what's wrong.
|
|
pOut = [pIn copyWithZone:nil];
|
|
|
|
// Igor: For unused inputs clean input attribute record
|
|
// Igor: For used inputs mark input buffer as used
|
|
TShaderReflection::TParameters::const_iterator kInputSemanticIter(kReflection.m_kInputs.begin());
|
|
const TShaderReflection::TParameters::const_iterator kInputSemanticEnd(kReflection.m_kInputs.end());
|
|
while (kInputSemanticIter != kInputSemanticEnd)
|
|
{
|
|
if (kInputSemanticIter->m_kDesc.ReadWriteMask)
|
|
{
|
|
bBufferUsed[pIn.attributes[kInputSemanticIter->m_kDesc.Register].bufferIndex] = true;
|
|
}
|
|
else
|
|
{
|
|
if (!pOut)
|
|
{
|
|
pOut = [pIn copyWithZone:nil];
|
|
}
|
|
|
|
[pOut.attributes setObject : 0 atIndexedSubscript: kInputSemanticIter->m_kDesc.Register];
|
|
}
|
|
|
|
++kInputSemanticIter;
|
|
}
|
|
|
|
|
|
// pOut is not nil if had to patch at least single input
|
|
if (pOut)
|
|
{
|
|
const int iAttrCount = 31;
|
|
for (int i = 0; i < iAttrCount; ++i)
|
|
{
|
|
uint32 VBIndex = pOut.attributes[i].bufferIndex;
|
|
if ((pOut.attributes[i].format != MTLVertexFormatInvalid) && (!bBufferUsed[VBIndex]))
|
|
{
|
|
[pOut.attributes setObject : 0 atIndexedSubscript: i];
|
|
}
|
|
}
|
|
|
|
const int bufCount = DXMETAL_MAX_ENTRIES_BUFFER_ARG_TABLE;
|
|
for (int VBIndex = 0; VBIndex < bufCount; ++VBIndex)
|
|
{
|
|
if (!bBufferUsed[VBIndex])
|
|
{
|
|
[pOut.layouts setObject : 0 atIndexedSubscript: VBIndex];
|
|
}
|
|
}
|
|
|
|
return pOut;
|
|
}
|
|
else
|
|
{
|
|
// Igor: the caller is responsible for releasing the result
|
|
[pIn retain];
|
|
return pIn;
|
|
}
|
|
}
|
|
|
|
bool CompilePipeline(SPipeline* pPipeline, CDevice* pDevice)
|
|
{
|
|
id<MTLDevice> mtlDevice = pDevice->GetMetalDevice();
|
|
if (pPipeline->m_kConfiguration.m_apShaders[eST_Compute] != NULL)
|
|
{
|
|
MTLComputePipelineDescriptor* desc = [[MTLComputePipelineDescriptor alloc] init];
|
|
desc.computeFunction = pPipeline->m_kConfiguration.m_apShaders[eST_Compute]->m_kGLShader.m_pFunction;
|
|
|
|
NSError* error = 0;
|
|
MTLComputePipelineReflection* ref;
|
|
pPipeline->m_ComputePipelineState = [mtlDevice newComputePipelineStateWithDescriptor:desc options : MTLPipelineOptionBufferTypeInfo reflection : &ref error : &error];
|
|
|
|
if (!pPipeline->m_ComputePipelineState)
|
|
{
|
|
LOG_METAL_PIPELINE_ERRORS(@ "Error generation compute pipeline object: %@", error);
|
|
LOG_METAL_PIPELINE_ERRORS(@ "Descriptor: %@", desc);
|
|
assert(!"Compute pipeline object shouldn't fail to be created.");
|
|
return false;
|
|
}
|
|
|
|
bool res = ValidateReflection(eST_Compute, ref.arguments, pPipeline->m_kConfiguration.m_apShaders[eST_Compute]->m_kReflection);
|
|
|
|
assert(res || !"compute shader must pass validation");
|
|
|
|
[desc release];
|
|
|
|
}
|
|
|
|
if (pPipeline->m_kConfiguration.m_apShaders[eST_Vertex] != NULL)
|
|
{
|
|
MTLRenderPipelineDescriptor* desc = [[MTLRenderPipelineDescriptor alloc] init];
|
|
|
|
for (int i = 0; i < SAttachmentConfiguration::s_ColorAttachmentDescCount; ++i)
|
|
{
|
|
desc.colorAttachments[i].pixelFormat = pPipeline->m_kConfiguration.m_AttachmentConfiguration.colorAttachments[i].pixelFormat;
|
|
|
|
desc.colorAttachments[i].writeMask = pPipeline->m_kConfiguration.m_AttachmentConfiguration.colorAttachments[i].writeMask;
|
|
desc.colorAttachments[i].blendingEnabled = pPipeline->m_kConfiguration.m_AttachmentConfiguration.colorAttachments[i].blendingEnabled;
|
|
desc.colorAttachments[i].alphaBlendOperation = pPipeline->m_kConfiguration.m_AttachmentConfiguration.colorAttachments[i].alphaBlendOperation;
|
|
desc.colorAttachments[i].rgbBlendOperation = pPipeline->m_kConfiguration.m_AttachmentConfiguration.colorAttachments[i].rgbBlendOperation;
|
|
desc.colorAttachments[i].destinationAlphaBlendFactor = pPipeline->m_kConfiguration.m_AttachmentConfiguration.colorAttachments[i].destinationAlphaBlendFactor;
|
|
desc.colorAttachments[i].destinationAlphaBlendFactor = pPipeline->m_kConfiguration.m_AttachmentConfiguration.colorAttachments[i].destinationAlphaBlendFactor;
|
|
desc.colorAttachments[i].destinationRGBBlendFactor = pPipeline->m_kConfiguration.m_AttachmentConfiguration.colorAttachments[i].destinationRGBBlendFactor;
|
|
desc.colorAttachments[i].sourceAlphaBlendFactor = pPipeline->m_kConfiguration.m_AttachmentConfiguration.colorAttachments[i].sourceAlphaBlendFactor;
|
|
desc.colorAttachments[i].sourceRGBBlendFactor = pPipeline->m_kConfiguration.m_AttachmentConfiguration.colorAttachments[i].sourceRGBBlendFactor;
|
|
}
|
|
desc.depthAttachmentPixelFormat = pPipeline->m_kConfiguration.m_AttachmentConfiguration.depthAttachmentPixelFormat;
|
|
desc.stencilAttachmentPixelFormat = pPipeline->m_kConfiguration.m_AttachmentConfiguration.stencilAttachmentPixelFormat;
|
|
|
|
MTLVertexDescriptor* pCleanedDesctiptor = CleanUnusedInputs((MTLVertexDescriptor*)pPipeline->m_kConfiguration.m_pVertexDescriptor,
|
|
pPipeline->m_kConfiguration.m_apShaders[eST_Vertex]->m_kReflection);
|
|
|
|
// set the bound shader state settings
|
|
desc.vertexDescriptor = pCleanedDesctiptor;
|
|
desc.vertexFunction = pPipeline->m_kConfiguration.m_apShaders[eST_Vertex]->m_kGLShader.m_pFunction;
|
|
desc.fragmentFunction = pPipeline->m_kConfiguration.m_apShaders[eST_Fragment] ? pPipeline->m_kConfiguration.m_apShaders[eST_Fragment]->m_kGLShader.m_pFunction : 0;
|
|
|
|
[pCleanedDesctiptor release];
|
|
|
|
NSError* error = 0;
|
|
MTLRenderPipelineReflection* ref;
|
|
pPipeline->m_PipelineState = [mtlDevice newRenderPipelineStateWithDescriptor:desc options : MTLPipelineOptionBufferTypeInfo reflection : &ref error : &error];
|
|
|
|
if (!pPipeline->m_PipelineState)
|
|
{
|
|
LOG_METAL_PIPELINE_ERRORS(@ "Error generation pipeline object: %@", error);
|
|
LOG_METAL_PIPELINE_ERRORS(@ "Descriptor: %@", desc);
|
|
assert(!"Pipeline object shouldn't fail to be created.");
|
|
return false;
|
|
}
|
|
|
|
bool res = ValidateReflection(eST_Vertex, ref.vertexArguments, pPipeline->m_kConfiguration.m_apShaders[eST_Vertex]->m_kReflection);
|
|
res &= ValidateReflection(eST_Fragment, ref.fragmentArguments, pPipeline->m_kConfiguration.m_apShaders[eST_Fragment]->m_kReflection);
|
|
|
|
assert(res || !"shader must pass validation");
|
|
|
|
[desc release];
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool InitializeShader(SShader* pShader, const void* pvSourceData, size_t uSourceSize, id<MTLDevice> mtlDevice)
|
|
{
|
|
pShader->m_kSource.SetData(reinterpret_cast<const char*>(pvSourceData), uSourceSize);
|
|
|
|
uint32 uGLSLOffset;
|
|
if (!InitializeShaderReflection(&pShader->m_kReflection, pShader->m_kSource.m_pData) ||
|
|
(uGLSLOffset = pShader->m_kReflection.m_uGLSLSourceOffset) >= (uint32)uSourceSize)
|
|
{
|
|
DXGL_ERROR("Could not retrieve shader reflection data");
|
|
return false;
|
|
}
|
|
|
|
SSource kGLSLSource(pShader->m_kSource.m_pData + uGLSLOffset, pShader->m_kSource.m_uDataSize - uGLSLOffset);
|
|
if (!CompileShader(pShader, &kGLSLSource, &pShader->m_kGLShader, mtlDevice))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
SPipelineConfiguration::SPipelineConfiguration()
|
|
// Confetti BEGIN: Igor Lobanchikov
|
|
: m_pVertexDescriptor([[MTLVertexDescriptor alloc] init])
|
|
// Confetti End: Igor Lobanchikov
|
|
{
|
|
memset(m_apShaders, 0, sizeof(m_apShaders));
|
|
}
|
|
|
|
// Confetti BEGIN: Igor Lobanchikov
|
|
SPipelineConfiguration::~SPipelineConfiguration()
|
|
{
|
|
[(MTLVertexDescriptor*)m_pVertexDescriptor release];
|
|
}
|
|
|
|
SPipelineConfiguration::SPipelineConfiguration(const SPipelineConfiguration& other)
|
|
{
|
|
m_pVertexDescriptor = other.m_pVertexDescriptor;
|
|
[(MTLVertexDescriptor*)m_pVertexDescriptor retain];
|
|
|
|
memcpy(&m_apShaders, &other.m_apShaders, sizeof(m_apShaders));
|
|
memcpy(&m_AttachmentConfiguration, &other.m_AttachmentConfiguration, sizeof(m_AttachmentConfiguration));
|
|
}
|
|
|
|
SPipelineConfiguration& SPipelineConfiguration::operator=(const SPipelineConfiguration& other)
|
|
{
|
|
[(MTLVertexDescriptor*)m_pVertexDescriptor release];
|
|
m_pVertexDescriptor = other.m_pVertexDescriptor;
|
|
[(MTLVertexDescriptor*)m_pVertexDescriptor retain];
|
|
|
|
memcpy(&m_apShaders, &other.m_apShaders, sizeof(m_apShaders));
|
|
memcpy(&m_AttachmentConfiguration, &other.m_AttachmentConfiguration, sizeof(m_AttachmentConfiguration));
|
|
|
|
return *this;
|
|
}
|
|
|
|
void SPipelineConfiguration::SetVertexDescriptor(void* vertexDescriptor)
|
|
{
|
|
[(MTLVertexDescriptor*)m_pVertexDescriptor release];
|
|
|
|
m_pVertexDescriptor = [(MTLVertexDescriptor*)vertexDescriptor copyWithZone:nil];
|
|
}
|
|
// Confetti End: Igor Lobanchikov
|
|
|
|
// Confetti BEGIN: Igor Lobanchikov
|
|
bool GetMetalVertexFormatSize(const SGIFormatInfo* pFormatInfo, uint32& size)
|
|
{
|
|
switch (pFormatInfo->m_pTexture->m_eMetalVertexFormat)
|
|
{
|
|
case MTLVertexFormatInvalid:
|
|
return false;
|
|
|
|
case MTLVertexFormatUChar2:
|
|
case MTLVertexFormatChar2:
|
|
case MTLVertexFormatUChar2Normalized:
|
|
case MTLVertexFormatChar2Normalized:
|
|
size = 2;
|
|
break;
|
|
|
|
case MTLVertexFormatUChar3:
|
|
case MTLVertexFormatChar3:
|
|
case MTLVertexFormatUChar3Normalized:
|
|
case MTLVertexFormatChar3Normalized:
|
|
size = 3;
|
|
break;
|
|
|
|
case MTLVertexFormatUChar4:
|
|
case MTLVertexFormatChar4:
|
|
case MTLVertexFormatUChar4Normalized:
|
|
case MTLVertexFormatChar4Normalized:
|
|
case MTLVertexFormatUShort2:
|
|
case MTLVertexFormatShort2:
|
|
case MTLVertexFormatUShort2Normalized:
|
|
case MTLVertexFormatShort2Normalized:
|
|
case MTLVertexFormatHalf2:
|
|
case MTLVertexFormatFloat:
|
|
case MTLVertexFormatInt:
|
|
case MTLVertexFormatUInt:
|
|
case MTLVertexFormatInt1010102Normalized:
|
|
case MTLVertexFormatUInt1010102Normalized:
|
|
size = 4;
|
|
break;
|
|
|
|
case MTLVertexFormatUShort3:
|
|
case MTLVertexFormatShort3:
|
|
case MTLVertexFormatUShort3Normalized:
|
|
case MTLVertexFormatShort3Normalized:
|
|
case MTLVertexFormatHalf3:
|
|
size = 6;
|
|
break;
|
|
|
|
case MTLVertexFormatUShort4:
|
|
case MTLVertexFormatShort4:
|
|
case MTLVertexFormatUShort4Normalized:
|
|
case MTLVertexFormatShort4Normalized:
|
|
case MTLVertexFormatHalf4:
|
|
case MTLVertexFormatFloat2:
|
|
case MTLVertexFormatInt2:
|
|
case MTLVertexFormatUInt2:
|
|
size = 8;
|
|
break;
|
|
|
|
case MTLVertexFormatFloat3:
|
|
case MTLVertexFormatInt3:
|
|
case MTLVertexFormatUInt3:
|
|
size = 12;
|
|
break;
|
|
|
|
case MTLVertexFormatFloat4:
|
|
case MTLVertexFormatInt4:
|
|
case MTLVertexFormatUInt4:
|
|
size = 16;
|
|
break;
|
|
|
|
default:
|
|
assert(!"Unknown metal vertex format");
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
// Confetti End: Igor Lobanchikov
|
|
|
|
|
|
bool PushInputLayoutAttribute(
|
|
SInputLayout& kLayout,
|
|
uint32 auSlotOffsets[D3D11_IA_VERTEX_INPUT_RESOURCE_SLOT_COUNT],
|
|
const D3D11_INPUT_ELEMENT_DESC& kDesc,
|
|
uint32 uIndex)
|
|
{
|
|
// The format must have texture and uncompressed layout information in order to determine
|
|
// the input layout
|
|
NCryMetal::EGIFormat eGIFormat(GetGIFormat(kDesc.Format));
|
|
const NCryMetal::SGIFormatInfo* pFormatInfo(NULL);
|
|
if (eGIFormat == eGIF_NUM || (pFormatInfo = (GetGIFormatInfo(eGIFormat))) == NULL ||
|
|
pFormatInfo->m_pTexture == NULL || pFormatInfo->m_pUncompressed == NULL)
|
|
{
|
|
DXGL_ERROR("Invalid DXGI format for vertex attribute");
|
|
return false;
|
|
}
|
|
|
|
uint32& uSlotOffset(auSlotOffsets[kDesc.InputSlot]);
|
|
uint32 uSize;
|
|
|
|
// Confetti BEGIN: Igor Lobanchikov
|
|
if (pFormatInfo->m_pTexture->m_eMetalVertexFormat == MTLVertexFormatInvalid)
|
|
{
|
|
DXGL_ERROR("Invalid data type for vertex attribute");
|
|
return false;
|
|
}
|
|
|
|
GetMetalVertexFormatSize(pFormatInfo, uSize);
|
|
|
|
assert(uSize == pFormatInfo->m_pTexture->m_uNumBlockBytes);
|
|
|
|
uint32 VBIndex = (DXMETAL_MAX_ENTRIES_BUFFER_ARG_TABLE - 1) - kDesc.InputSlot;
|
|
MTLVertexAttributeDescriptor* MetalDesc = ((MTLVertexDescriptor*)kLayout.m_pVertexDescriptor).attributes[uIndex];
|
|
MetalDesc.bufferIndex = VBIndex;
|
|
MetalDesc.offset = (kDesc.AlignedByteOffset == D3D11_APPEND_ALIGNED_ELEMENT) ? uSize : kDesc.AlignedByteOffset;
|
|
|
|
MetalDesc.format = pFormatInfo->m_pTexture->m_eMetalVertexFormat;
|
|
|
|
uSlotOffset = max(uSlotOffset, (uint32)MetalDesc.offset + uSize);
|
|
|
|
// Igor: We calculate stride here assuming that the vertex structure won't have any additional padding at the end
|
|
// which is not 100% true, since DX11 api allows to change vertex stride at vertex buffer bind time.
|
|
// We override this padding later at vertex buffer bind time anyway. Just keep it here as the reference.
|
|
uint32 stride = ((uSlotOffset + 3) / 4) * 4;
|
|
((MTLVertexDescriptor*)kLayout.m_pVertexDescriptor).layouts[VBIndex].stride = stride;
|
|
// Confetti End: Igor Lobanchikov
|
|
|
|
|
|
return true;
|
|
}
|
|
|
|
SInputLayoutPtr CreateInputLayout(const D3D11_INPUT_ELEMENT_DESC* pInputElementDescs, uint32 uNumElements, const TShaderReflection& kReflection)
|
|
{
|
|
SInputLayoutPtr spLayout(new SInputLayout());
|
|
uint32 auSlotOffsets[D3D11_IA_VERTEX_INPUT_RESOURCE_SLOT_COUNT];
|
|
memset(auSlotOffsets, 0, sizeof(auSlotOffsets));
|
|
|
|
int attrMatchedNum = 0;
|
|
int reflectionAttrNum = 0;
|
|
|
|
//Calculate the number of attributes in the vertex shader that dont start with 'SV_'.
|
|
TShaderReflection::TParameters::const_iterator kInputSemanticIter(kReflection.m_kInputs.begin());
|
|
const TShaderReflection::TParameters::const_iterator kInputSemanticEnd(kReflection.m_kInputs.end());
|
|
while (kInputSemanticIter != kInputSemanticEnd)
|
|
{
|
|
if(strstr(kInputSemanticIter->m_kDesc.SemanticName, "SV_") == 0)
|
|
{
|
|
reflectionAttrNum++;
|
|
}
|
|
++kInputSemanticIter;
|
|
}
|
|
|
|
for (uint32 uElement = 0; uElement < uNumElements; ++uElement)
|
|
{
|
|
const D3D11_INPUT_ELEMENT_DESC& kDesc(pInputElementDescs[uElement]);
|
|
|
|
// Confetti BEGIN: Igor Lobanchikov
|
|
{
|
|
uint32 VBIndex = (DXMETAL_MAX_ENTRIES_BUFFER_ARG_TABLE - 1) - kDesc.InputSlot;
|
|
|
|
// TODO: do check here so that all values for the same buffer has the same parameters
|
|
|
|
// Igor: set layout here
|
|
// Don't set stride here. Set it when the actual buffer is bound, since in dx11 this is the place wherer we know the stride
|
|
// Initial auto-calculated stride value will be set once the input layout is complete
|
|
//spLayout->layouts[VBIndex].stride = kDesk.;
|
|
((MTLVertexDescriptor*)spLayout->m_pVertexDescriptor).layouts[VBIndex].stepFunction = (kDesc.InputSlotClass == D3D11_INPUT_PER_VERTEX_DATA) ? MTLVertexStepFunctionPerVertex : MTLVertexStepFunctionPerInstance;
|
|
((MTLVertexDescriptor*)spLayout->m_pVertexDescriptor).layouts[VBIndex].stepRate = (kDesc.InputSlotClass == D3D11_INPUT_PER_VERTEX_DATA) ? 1 : kDesc.InstanceDataStepRate;
|
|
}
|
|
// Confetti End: Igor Lobanchikov
|
|
|
|
|
|
// Find a match within the input signatures of the shader reflection data
|
|
TShaderReflection::TParameters::const_iterator kInputSemanticIter(kReflection.m_kInputs.begin());
|
|
const TShaderReflection::TParameters::const_iterator kInputSemanticEnd(kReflection.m_kInputs.end());
|
|
while (kInputSemanticIter != kInputSemanticEnd)
|
|
{
|
|
if (kInputSemanticIter->m_kDesc.SemanticIndex == kDesc.SemanticIndex &&
|
|
strcmp(kInputSemanticIter->m_kDesc.SemanticName, kDesc.SemanticName) == 0)
|
|
{
|
|
if (!PushInputLayoutAttribute(*spLayout, auSlotOffsets, kDesc, kInputSemanticIter->m_kDesc.Register))
|
|
{
|
|
AZ_Assert(false, "Failed to Push Input Layout");
|
|
return NULL;
|
|
}
|
|
attrMatchedNum++;
|
|
break;
|
|
}
|
|
++kInputSemanticIter;
|
|
}
|
|
}
|
|
|
|
if( attrMatchedNum != reflectionAttrNum)
|
|
{
|
|
AZ_Assert(false, "Shader input attributes count doesn not match with vertex format attributes count");
|
|
return NULL;
|
|
}
|
|
|
|
return spLayout;
|
|
}
|
|
}
|