| /////////////////////////////////////////////////////////////////////////////// |
| // // |
| // DxilContainerReflection.cpp // |
| // Copyright (C) Microsoft Corporation. All rights reserved. // |
| // This file is distributed under the University of Illinois Open Source // |
| // License. See LICENSE.TXT for details. // |
| // // |
| // Provides support for reading DXIL container structures. // |
| // // |
| /////////////////////////////////////////////////////////////////////////////// |
| |
| #include "dxc/DXIL/DxilCounters.h" |
| #include "dxc/DXIL/DxilFunctionProps.h" |
| #include "dxc/DXIL/DxilInstructions.h" |
| #include "dxc/DXIL/DxilModule.h" |
| #include "dxc/DXIL/DxilOperations.h" |
| #include "dxc/DXIL/DxilPDB.h" |
| #include "dxc/DXIL/DxilShaderModel.h" |
| #include "dxc/DXIL/DxilUtil.h" |
| #include "dxc/DxilContainer/DxilContainer.h" |
| #include "dxc/HLSL/HLMatrixType.h" |
| #include "dxc/Support/FileIOHelper.h" |
| #include "dxc/Support/Global.h" |
| #include "dxc/Support/Unicode.h" |
| #include "dxc/Support/WinIncludes.h" |
| #include "dxc/Support/dxcapi.impl.h" |
| #include "dxc/Support/microcom.h" |
| #include "llvm/ADT/STLExtras.h" |
| #include "llvm/Bitcode/ReaderWriter.h" |
| #include "llvm/IR/InstIterator.h" |
| #include "llvm/IR/LLVMContext.h" |
| #include "llvm/IR/Operator.h" |
| |
| #include "llvm/ADT/SetVector.h" |
| #include <unordered_set> |
| |
| #include "dxc/dxcapi.h" |
| |
| #include "dxc/Support/D3DReflection.h" |
| #ifdef _WIN32 |
| #include "d3d11shader.h" // for compatibility |
| #else |
| // Dummy D3D11 struct to allow nix-dead code to compile |
| struct D3D11_SHADER_INPUT_BIND_DESC { |
| int dummy; |
| }; |
| #include "dxc/WinAdapter.h" |
| #endif |
| |
| #include "dxc/DxilContainer/DxilRuntimeReflection.h" |
| |
| // Remove this workaround once newer version of d3dcommon.h can be compiled |
| // against |
| #define ADD_16_64_BIT_TYPES |
| #define ADD_SVC_BIT_FIELD |
| |
| const GUID IID_ID3D11ShaderReflection_43 = { |
| 0x0a233719, |
| 0x3960, |
| 0x4578, |
| {0x9d, 0x7c, 0x20, 0x3b, 0x8b, 0x1d, 0x9c, 0xc1}}; |
| const GUID IID_ID3D11ShaderReflection_47 = { |
| 0x8d536ca1, |
| 0x0cca, |
| 0x4956, |
| {0xa8, 0x37, 0x78, 0x69, 0x63, 0x75, 0x55, 0x84}}; |
| |
| using namespace llvm; |
| using namespace hlsl; |
| using namespace hlsl::DXIL; |
| |
| class DxilContainerReflection : public IDxcContainerReflection { |
| private: |
| DXC_MICROCOM_TM_REF_FIELDS() |
| CComPtr<IDxcBlob> m_container; |
| const DxilContainerHeader *m_pHeader = nullptr; |
| uint32_t m_headerLen = 0; |
| bool IsLoaded() const { return m_pHeader != nullptr; } |
| |
| public: |
| DXC_MICROCOM_TM_ADDREF_RELEASE_IMPL() |
| DXC_MICROCOM_TM_CTOR(DxilContainerReflection) |
| HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, |
| void **ppvObject) override { |
| return DoBasicQueryInterface<IDxcContainerReflection>(this, iid, ppvObject); |
| } |
| |
| HRESULT STDMETHODCALLTYPE Load(IDxcBlob *pContainer) override; |
| HRESULT STDMETHODCALLTYPE GetPartCount(UINT32 *pResult) override; |
| HRESULT STDMETHODCALLTYPE GetPartKind(UINT32 idx, UINT32 *pResult) override; |
| HRESULT STDMETHODCALLTYPE GetPartContent(UINT32 idx, |
| IDxcBlob **ppResult) override; |
| HRESULT STDMETHODCALLTYPE FindFirstPartKind(UINT32 kind, |
| UINT32 *pResult) override; |
| HRESULT STDMETHODCALLTYPE GetPartReflection(UINT32 idx, REFIID iid, |
| void **ppvObject) override; |
| }; |
| |
| class CShaderReflectionConstantBuffer; |
| class CShaderReflectionType; |
| |
| enum class PublicAPI { D3D12 = 0, D3D11_47 = 1, D3D11_43 = 2, Invalid }; |
| |
| #ifdef ADD_16_64_BIT_TYPES |
| // Disable warning about value not being valid in enum |
| #pragma warning(disable : 4063) |
| #define D3D_SVT_INT16 ((D3D_SHADER_VARIABLE_TYPE)58) |
| #define D3D_SVT_UINT16 ((D3D_SHADER_VARIABLE_TYPE)59) |
| #define D3D_SVT_FLOAT16 ((D3D_SHADER_VARIABLE_TYPE)60) |
| #define D3D_SVT_INT64 ((D3D_SHADER_VARIABLE_TYPE)61) |
| #define D3D_SVT_UINT64 ((D3D_SHADER_VARIABLE_TYPE)62) |
| #endif // ADD_16_64_BIT_TYPES |
| |
| #ifdef ADD_SVC_BIT_FIELD |
| // Disable warning about value not being valid in enum |
| #pragma warning(disable : 4063) |
| // FIXME: remove the define once D3D_SVC_BIT_FIELD added into |
| // D3D_SHADER_VARIABLE_CLASS. |
| #define D3D_SVC_BIT_FIELD \ |
| ((D3D_SHADER_VARIABLE_CLASS)(D3D_SVC_INTERFACE_POINTER + 1)) |
| #endif |
| |
| class DxilModuleReflection { |
| public: |
| hlsl::RDAT::DxilRuntimeData m_RDAT; |
| LLVMContext Context; |
| std::unique_ptr<Module> m_pModule; // Must come after LLVMContext, otherwise |
| // unique_ptr will over-delete. |
| DxilModule *m_pDxilModule = nullptr; |
| bool m_bUsageInMetadata = false; |
| std::vector<std::unique_ptr<CShaderReflectionConstantBuffer>> m_CBs; |
| std::vector<D3D12_SHADER_INPUT_BIND_DESC> m_Resources; |
| std::vector<std::unique_ptr<CShaderReflectionType>> m_Types; |
| |
| // Key strings owned by CShaderReflectionConstantBuffer objects |
| std::map<StringRef, UINT> m_CBsByName; |
| // Due to the possibility of overlapping names between CB and other resources, |
| // m_StructuredBufferCBsByName is the index into m_CBs corresponding to |
| // StructuredBuffer resources, separately from CB resources. |
| std::map<StringRef, UINT> m_StructuredBufferCBsByName; |
| |
| void CreateReflectionObjects(); |
| void CreateReflectionObjectForResource(DxilResourceBase *R); |
| |
| HRESULT LoadRDAT(const DxilPartHeader *pPart); |
| HRESULT LoadProgramHeader(const DxilProgramHeader *pProgramHeader); |
| |
| // Common code |
| ID3D12ShaderReflectionConstantBuffer *_GetConstantBufferByIndex(UINT Index); |
| ID3D12ShaderReflectionConstantBuffer *_GetConstantBufferByName(LPCSTR Name); |
| |
| HRESULT _GetResourceBindingDesc(UINT ResourceIndex, |
| D3D12_SHADER_INPUT_BIND_DESC *pDesc, |
| PublicAPI api = PublicAPI::D3D12); |
| |
| ID3D12ShaderReflectionVariable *_GetVariableByName(LPCSTR Name); |
| |
| HRESULT _GetResourceBindingDescByName(LPCSTR Name, |
| D3D12_SHADER_INPUT_BIND_DESC *pDesc, |
| PublicAPI api = PublicAPI::D3D12); |
| }; |
| |
| class DxilShaderReflection : public DxilModuleReflection, |
| public ID3D12ShaderReflection { |
| private: |
| DXC_MICROCOM_TM_REF_FIELDS() |
| std::vector<D3D12_SIGNATURE_PARAMETER_DESC> m_InputSignature; |
| std::vector<D3D12_SIGNATURE_PARAMETER_DESC> m_OutputSignature; |
| std::vector<D3D12_SIGNATURE_PARAMETER_DESC> m_PatchConstantSignature; |
| std::vector<std::unique_ptr<char[]>> m_UpperCaseNames; |
| D3D12_SHADER_DESC m_Desc = {}; |
| |
| void SetCBufferUsage(); |
| void CreateReflectionObjectsForSignature( |
| const DxilSignature &Sig, |
| std::vector<D3D12_SIGNATURE_PARAMETER_DESC> &Descs); |
| LPCSTR CreateUpperCase(LPCSTR pValue); |
| void MarkUsedSignatureElements(); |
| void InitDesc(); |
| |
| public: |
| PublicAPI m_PublicAPI; |
| void SetPublicAPI(PublicAPI value) { m_PublicAPI = value; } |
| static PublicAPI IIDToAPI(REFIID iid) { |
| PublicAPI api = PublicAPI::Invalid; |
| if (IsEqualIID(__uuidof(ID3D12ShaderReflection), iid)) |
| api = PublicAPI::D3D12; |
| else if (IsEqualIID(IID_ID3D11ShaderReflection_43, iid)) |
| api = PublicAPI::D3D11_43; |
| else if (IsEqualIID(IID_ID3D11ShaderReflection_47, iid)) |
| api = PublicAPI::D3D11_47; |
| return api; |
| } |
| DXC_MICROCOM_TM_ADDREF_RELEASE_IMPL() |
| DXC_MICROCOM_TM_CTOR(DxilShaderReflection) |
| HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, |
| void **ppvObject) noexcept override { |
| HRESULT hr = E_NOINTERFACE; |
| |
| // There is non-standard handling of QueryInterface: |
| // - although everything uses the same vtable as ID3D12ShaderReflection, |
| // there are differences in behavior depending on the API version, and |
| // there are 3 of these - it's not just d3d11 vs d3d12. |
| // - when the object is created the API version is fixed |
| // - from that point on, this object can only be QI'd for the matching API |
| // version. |
| PublicAPI api = IIDToAPI(iid); |
| if (api == m_PublicAPI) { |
| *ppvObject = static_cast<ID3D12ShaderReflection *>(this); |
| this->AddRef(); |
| hr = S_OK; |
| } else if (IsEqualIID(__uuidof(IUnknown), iid)) { |
| *ppvObject = static_cast<IUnknown *>(this); |
| this->AddRef(); |
| hr = S_OK; |
| } |
| return hr; |
| } |
| |
| HRESULT Load(const DxilProgramHeader *pProgramHeader, |
| const DxilPartHeader *pRDATPart); |
| |
| // ID3D12ShaderReflection |
| STDMETHODIMP GetDesc(D3D12_SHADER_DESC *pDesc) noexcept override; |
| |
| STDMETHODIMP_(ID3D12ShaderReflectionConstantBuffer *) |
| GetConstantBufferByIndex(UINT Index) noexcept override; |
| STDMETHODIMP_(ID3D12ShaderReflectionConstantBuffer *) |
| GetConstantBufferByName(LPCSTR Name) noexcept override; |
| |
| STDMETHODIMP |
| GetResourceBindingDesc(UINT ResourceIndex, |
| D3D12_SHADER_INPUT_BIND_DESC *pDesc) noexcept override; |
| |
| STDMETHODIMP GetInputParameterDesc( |
| UINT ParameterIndex, |
| D3D12_SIGNATURE_PARAMETER_DESC *pDesc) noexcept override; |
| STDMETHODIMP GetOutputParameterDesc( |
| UINT ParameterIndex, |
| D3D12_SIGNATURE_PARAMETER_DESC *pDesc) noexcept override; |
| STDMETHODIMP GetPatchConstantParameterDesc( |
| UINT ParameterIndex, |
| D3D12_SIGNATURE_PARAMETER_DESC *pDesc) noexcept override; |
| |
| STDMETHODIMP_(ID3D12ShaderReflectionVariable *) |
| GetVariableByName(LPCSTR Name) noexcept override; |
| |
| STDMETHODIMP GetResourceBindingDescByName( |
| LPCSTR Name, D3D12_SHADER_INPUT_BIND_DESC *pDesc) noexcept override; |
| |
| STDMETHODIMP_(UINT) GetMovInstructionCount(THIS) noexcept override; |
| STDMETHODIMP_(UINT) GetMovcInstructionCount(THIS) noexcept override; |
| STDMETHODIMP_(UINT) GetConversionInstructionCount(THIS) noexcept override; |
| STDMETHODIMP_(UINT) GetBitwiseInstructionCount(THIS) noexcept override; |
| |
| STDMETHODIMP_(D3D_PRIMITIVE) GetGSInputPrimitive(THIS) noexcept override; |
| STDMETHODIMP_(BOOL) IsSampleFrequencyShader(THIS) noexcept override; |
| |
| STDMETHODIMP_(UINT) GetNumInterfaceSlots(THIS) noexcept override; |
| STDMETHODIMP |
| GetMinFeatureLevel(D3D_FEATURE_LEVEL *pLevel) noexcept override; |
| |
| STDMETHODIMP_(UINT) |
| GetThreadGroupSize(UINT *pSizeX, UINT *pSizeY, |
| UINT *pSizeZ) noexcept override; |
| |
| STDMETHODIMP_(UINT64) GetRequiresFlags(THIS) noexcept override; |
| }; |
| |
| class CFunctionReflection; |
| class DxilLibraryReflection : public DxilModuleReflection, |
| public ID3D12LibraryReflection { |
| private: |
| DXC_MICROCOM_TM_REF_FIELDS() |
| |
| // Storage, and function by name: |
| typedef DenseMap<StringRef, std::unique_ptr<CFunctionReflection>> FunctionMap; |
| typedef DenseMap<const Function *, CFunctionReflection *> FunctionsByPtr; |
| FunctionMap m_FunctionMap; |
| FunctionsByPtr m_FunctionsByPtr; |
| // Enable indexing into functions in deterministic order: |
| std::vector<CFunctionReflection *> m_FunctionVector; |
| |
| void AddResourceDependencies(); |
| void SetCBufferUsage(); |
| |
| public: |
| DXC_MICROCOM_TM_ADDREF_RELEASE_IMPL() |
| DXC_MICROCOM_TM_CTOR(DxilLibraryReflection) |
| HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, |
| void **ppvObject) noexcept override { |
| return DoBasicQueryInterface<ID3D12LibraryReflection>(this, iid, ppvObject); |
| } |
| |
| HRESULT Load(const DxilProgramHeader *pProgramHeader, |
| const DxilPartHeader *pRDATPart); |
| |
| // ID3D12LibraryReflection |
| STDMETHOD(GetDesc)(D3D12_LIBRARY_DESC *pDesc) override; |
| |
| STDMETHOD_(ID3D12FunctionReflection *, GetFunctionByIndex) |
| (INT FunctionIndex) override; |
| }; |
| |
| namespace hlsl { |
| |
| HRESULT CreateDxilShaderReflection(const DxilProgramHeader *pProgramHeader, |
| const DxilPartHeader *pRDATPart, REFIID iid, |
| void **ppvObject) { |
| if (!ppvObject) |
| return E_INVALIDARG; |
| PublicAPI api = DxilShaderReflection::IIDToAPI(iid); |
| if (api == PublicAPI::Invalid) { |
| if (IsEqualIID(__uuidof(IUnknown), iid)) |
| api = PublicAPI::D3D12; |
| else |
| return E_NOINTERFACE; |
| } |
| CComPtr<DxilShaderReflection> pReflection = |
| DxilShaderReflection::Alloc(DxcGetThreadMallocNoRef()); |
| IFROOM(pReflection.p); |
| pReflection->SetPublicAPI(api); |
| // pRDATPart to be used for transition. |
| IFR(pReflection->Load(pProgramHeader, pRDATPart)); |
| IFR(pReflection.p->QueryInterface(iid, ppvObject)); |
| return S_OK; |
| } |
| |
| HRESULT CreateDxilLibraryReflection(const DxilProgramHeader *pProgramHeader, |
| const DxilPartHeader *pRDATPart, REFIID iid, |
| void **ppvObject) { |
| if (!ppvObject) |
| return E_INVALIDARG; |
| if (!IsEqualIID(__uuidof(ID3D12LibraryReflection), iid) && |
| !IsEqualIID(__uuidof(IUnknown), iid)) |
| return E_NOINTERFACE; |
| CComPtr<DxilLibraryReflection> pReflection = |
| DxilLibraryReflection::Alloc(DxcGetThreadMallocNoRef()); |
| IFROOM(pReflection.p); |
| // pRDATPart used for resource usage per-function. |
| IFR(pReflection->Load(pProgramHeader, pRDATPart)); |
| IFR(pReflection.p->QueryInterface(iid, ppvObject)); |
| return S_OK; |
| } |
| |
| HRESULT CreateDxilShaderOrLibraryReflectionFromProgramHeader( |
| const DxilProgramHeader *pProgramHeader, const DxilPartHeader *pRDATPart, |
| REFIID iid, void **ppvObject) { |
| // Detect whether library, or if unrecognized program version. |
| DXIL::ShaderKind SK = GetVersionShaderType(pProgramHeader->ProgramVersion); |
| if (!(SK < DXIL::ShaderKind::Invalid)) |
| return E_INVALIDARG; |
| bool bIsLibrary = DXIL::ShaderKind::Library == SK; |
| |
| if (bIsLibrary) { |
| IFR(hlsl::CreateDxilLibraryReflection(pProgramHeader, pRDATPart, iid, |
| ppvObject)); |
| } else { |
| IFR(hlsl::CreateDxilShaderReflection(pProgramHeader, pRDATPart, iid, |
| ppvObject)); |
| } |
| return S_OK; |
| } |
| |
| bool IsValidReflectionModulePart(DxilFourCC fourCC) { |
| return fourCC == DFCC_DXIL || fourCC == DFCC_ShaderDebugInfoDXIL || |
| fourCC == DFCC_ShaderStatistics; |
| } |
| |
| HRESULT CreateDxilShaderOrLibraryReflectionFromModulePart( |
| const DxilPartHeader *pModulePart, const DxilPartHeader *pRDATPart, |
| REFIID iid, void **ppvObject) { |
| if (!pModulePart) |
| return E_INVALIDARG; |
| |
| if (!IsValidReflectionModulePart((DxilFourCC)pModulePart->PartFourCC)) |
| return E_INVALIDARG; |
| |
| const DxilProgramHeader *pProgramHeader = |
| reinterpret_cast<const DxilProgramHeader *>(GetDxilPartData(pModulePart)); |
| if (!IsValidDxilProgramHeader(pProgramHeader, pModulePart->PartSize)) |
| return E_INVALIDARG; |
| |
| // If bitcode is too small, it's probably been stripped, and we cannot create |
| // reflection with it. |
| if (pModulePart->PartSize - pProgramHeader->BitcodeHeader.BitcodeOffset < 4) |
| return DXC_E_MISSING_PART; |
| |
| return CreateDxilShaderOrLibraryReflectionFromProgramHeader( |
| pProgramHeader, pRDATPart, iid, ppvObject); |
| } |
| |
| } // namespace hlsl |
| |
| HRESULT DxilContainerReflection::Load(IDxcBlob *pContainer) { |
| |
| if (pContainer == nullptr) { |
| m_container.Release(); |
| m_pHeader = nullptr; |
| m_headerLen = 0; |
| return S_OK; |
| } |
| |
| CComPtr<IDxcBlob> pPDBContainer; |
| try { |
| DxcThreadMalloc DxcMalloc(m_pMalloc); |
| CComPtr<IStream> pStream; |
| IFR(hlsl::CreateReadOnlyBlobStream(pContainer, &pStream)); |
| if (SUCCEEDED(hlsl::pdb::LoadDataFromStream(m_pMalloc, pStream, |
| &pPDBContainer))) { |
| pContainer = pPDBContainer; |
| } |
| } |
| CATCH_CPP_RETURN_HRESULT(); |
| |
| uint32_t bufLen = pContainer->GetBufferSize(); |
| const DxilContainerHeader *pHeader = |
| IsDxilContainerLike(pContainer->GetBufferPointer(), bufLen); |
| if (pHeader == nullptr) { |
| return E_INVALIDARG; |
| } |
| if (!IsValidDxilContainer(pHeader, bufLen)) { |
| return E_INVALIDARG; |
| } |
| |
| m_container = pContainer; |
| m_headerLen = bufLen; |
| m_pHeader = pHeader; |
| |
| return S_OK; |
| } |
| |
| HRESULT DxilContainerReflection::GetPartCount(UINT32 *pResult) { |
| if (pResult == nullptr) |
| return E_POINTER; |
| if (!IsLoaded()) |
| return E_NOT_VALID_STATE; |
| *pResult = m_pHeader->PartCount; |
| return S_OK; |
| } |
| |
| HRESULT DxilContainerReflection::GetPartKind(UINT32 idx, UINT32 *pResult) { |
| if (pResult == nullptr) |
| return E_POINTER; |
| if (!IsLoaded()) |
| return E_NOT_VALID_STATE; |
| if (idx >= m_pHeader->PartCount) |
| return E_BOUNDS; |
| const DxilPartHeader *pPart = GetDxilContainerPart(m_pHeader, idx); |
| *pResult = pPart->PartFourCC; |
| return S_OK; |
| } |
| |
| HRESULT DxilContainerReflection::GetPartContent(UINT32 idx, |
| IDxcBlob **ppResult) { |
| if (ppResult == nullptr) |
| return E_POINTER; |
| *ppResult = nullptr; |
| if (!IsLoaded()) |
| return E_NOT_VALID_STATE; |
| if (idx >= m_pHeader->PartCount) |
| return E_BOUNDS; |
| const DxilPartHeader *pPart = GetDxilContainerPart(m_pHeader, idx); |
| const char *pData = GetDxilPartData(pPart); |
| uint32_t offset = |
| (uint32_t)(pData - |
| (char *)m_container |
| ->GetBufferPointer()); // Offset from the beginning. |
| uint32_t length = pPart->PartSize; |
| DxcThreadMalloc TM(m_pMalloc); |
| return DxcCreateBlobFromBlob(m_container, offset, length, ppResult); |
| } |
| |
| HRESULT DxilContainerReflection::FindFirstPartKind(UINT32 kind, |
| UINT32 *pResult) { |
| if (pResult == nullptr) |
| return E_POINTER; |
| *pResult = 0; |
| if (!IsLoaded()) |
| return E_NOT_VALID_STATE; |
| DxilPartIterator it = |
| std::find_if(begin(m_pHeader), end(m_pHeader), DxilPartIsType(kind)); |
| if (it == end(m_pHeader)) |
| return HRESULT_FROM_WIN32(ERROR_NOT_FOUND); |
| *pResult = it.index; |
| return S_OK; |
| } |
| |
| HRESULT DxilContainerReflection::GetPartReflection(UINT32 idx, REFIID iid, |
| void **ppvObject) { |
| if (ppvObject == nullptr) |
| return E_POINTER; |
| *ppvObject = nullptr; |
| if (!IsLoaded()) |
| return E_NOT_VALID_STATE; |
| if (idx >= m_pHeader->PartCount) |
| return E_BOUNDS; |
| const DxilPartHeader *pPart = GetDxilContainerPart(m_pHeader, idx); |
| if (!hlsl::IsValidReflectionModulePart((hlsl::DxilFourCC)pPart->PartFourCC)) |
| return E_NOTIMPL; |
| |
| // Use DFCC_ShaderStatistics for reflection instead of DXIL part, until switch |
| // to using RDAT for reflection instead of module. |
| const DxilPartHeader *pRDATPart = nullptr; |
| for (idx = 0; idx < m_pHeader->PartCount; ++idx) { |
| const DxilPartHeader *pPartTest = GetDxilContainerPart(m_pHeader, idx); |
| if (pPartTest->PartFourCC == DFCC_RuntimeData) { |
| pRDATPart = pPartTest; |
| } |
| if (pPart->PartFourCC != DFCC_ShaderStatistics) { |
| if (pPartTest->PartFourCC == DFCC_ShaderStatistics) { |
| const DxilProgramHeader *pProgramHeaderTest = |
| reinterpret_cast<const DxilProgramHeader *>( |
| GetDxilPartData(pPartTest)); |
| if (IsValidDxilProgramHeader(pProgramHeaderTest, pPartTest->PartSize)) { |
| pPart = pPartTest; |
| continue; |
| } |
| } |
| } |
| } |
| |
| DxcThreadMalloc TM(m_pMalloc); |
| HRESULT hr = S_OK; |
| |
| IFC(hlsl::CreateDxilShaderOrLibraryReflectionFromModulePart(pPart, pRDATPart, |
| iid, ppvObject)); |
| |
| Cleanup: |
| return hr; |
| } |
| |
| void hlsl::CreateDxcContainerReflection(IDxcContainerReflection **ppResult) { |
| CComPtr<DxilContainerReflection> pReflection = |
| DxilContainerReflection::Alloc(DxcGetThreadMallocNoRef()); |
| *ppResult = pReflection.Detach(); |
| if (*ppResult == nullptr) |
| throw std::bad_alloc(); |
| } |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| // DxilShaderReflection implementation - helper objects. // |
| |
| class CShaderReflectionType; |
| class CShaderReflectionVariable; |
| class CShaderReflectionConstantBuffer; |
| class CShaderReflection; |
| struct D3D11_INTERNALSHADER_RESOURCE_DEF; |
| class CShaderReflectionType final : public ID3D12ShaderReflectionType { |
| friend class CShaderReflectionConstantBuffer; |
| |
| protected: |
| D3D12_SHADER_TYPE_DESC m_Desc; |
| UINT m_SizeInCBuffer; |
| std::string m_Name; |
| std::vector<StringRef> m_MemberNames; |
| std::vector<CShaderReflectionType *> m_MemberTypes; |
| CShaderReflectionType *m_pSubType; |
| CShaderReflectionType *m_pBaseClass; |
| std::vector<CShaderReflectionType *> m_Interfaces; |
| ULONG_PTR m_Identity; |
| |
| public: |
| // Internal |
| HRESULT InitializeEmpty(); |
| HRESULT |
| Initialize(DxilModule &M, llvm::Type *type, |
| DxilFieldAnnotation &typeAnnotation, unsigned int baseOffset, |
| std::vector<std::unique_ptr<CShaderReflectionType>> &allTypes, |
| bool isCBuffer); |
| |
| // ID3D12ShaderReflectionType |
| STDMETHOD(GetDesc)(D3D12_SHADER_TYPE_DESC *pDesc); |
| |
| STDMETHOD_(ID3D12ShaderReflectionType *, GetMemberTypeByIndex)(UINT Index); |
| STDMETHOD_(ID3D12ShaderReflectionType *, GetMemberTypeByName)(LPCSTR Name); |
| STDMETHOD_(LPCSTR, GetMemberTypeName)(UINT Index); |
| |
| STDMETHOD(IsEqual)(ID3D12ShaderReflectionType *pType); |
| STDMETHOD_(ID3D12ShaderReflectionType *, GetSubType)(); |
| STDMETHOD_(ID3D12ShaderReflectionType *, GetBaseClass)(); |
| STDMETHOD_(UINT, GetNumInterfaces)(); |
| STDMETHOD_(ID3D12ShaderReflectionType *, GetInterfaceByIndex)(UINT uIndex); |
| STDMETHOD(IsOfType)(ID3D12ShaderReflectionType *pType); |
| STDMETHOD(ImplementsInterface)(ID3D12ShaderReflectionType *pBase); |
| |
| bool CheckEqual(CShaderReflectionType *pOther) { |
| return m_Identity == pOther->m_Identity; |
| } |
| |
| UINT GetCBufferSize() { return m_SizeInCBuffer; } |
| }; |
| |
| class CShaderReflectionVariable final : public ID3D12ShaderReflectionVariable { |
| protected: |
| D3D12_SHADER_VARIABLE_DESC m_Desc; |
| CShaderReflectionType *m_pType; |
| CShaderReflectionConstantBuffer *m_pBuffer; |
| BYTE *m_pDefaultValue; |
| |
| public: |
| void Initialize(CShaderReflectionConstantBuffer *pBuffer, |
| D3D12_SHADER_VARIABLE_DESC *pDesc, |
| CShaderReflectionType *pType, BYTE *pDefaultValue); |
| |
| LPCSTR GetName() { return m_Desc.Name; } |
| |
| // ID3D12ShaderReflectionVariable |
| STDMETHOD(GetDesc)(D3D12_SHADER_VARIABLE_DESC *pDesc); |
| |
| STDMETHOD_(ID3D12ShaderReflectionType *, GetType)(); |
| STDMETHOD_(ID3D12ShaderReflectionConstantBuffer *, GetBuffer)(); |
| |
| STDMETHOD_(UINT, GetInterfaceSlot)(UINT uArrayIndex); |
| }; |
| |
| class CShaderReflectionConstantBuffer final |
| : public ID3D12ShaderReflectionConstantBuffer { |
| protected: |
| D3D12_SHADER_BUFFER_DESC m_Desc; |
| std::vector<CShaderReflectionVariable> m_Variables; |
| // For StructuredBuffer arrays, Name will have [0] appended for each dimension |
| // to match fxc behavior. |
| std::string m_ReflectionName; |
| |
| public: |
| CShaderReflectionConstantBuffer() = default; |
| CShaderReflectionConstantBuffer(CShaderReflectionConstantBuffer &&other) { |
| m_Desc = other.m_Desc; |
| std::swap(m_Variables, other.m_Variables); |
| } |
| |
| void Initialize(DxilModule &M, DxilCBuffer &CB, |
| std::vector<std::unique_ptr<CShaderReflectionType>> &allTypes, |
| bool bUsageInMetadata); |
| void InitializeStructuredBuffer( |
| DxilModule &M, DxilResource &R, |
| std::vector<std::unique_ptr<CShaderReflectionType>> &allTypes); |
| void InitializeTBuffer( |
| DxilModule &M, DxilResource &R, |
| std::vector<std::unique_ptr<CShaderReflectionType>> &allTypes, |
| bool bUsageInMetadata); |
| LPCSTR GetName() { return m_Desc.Name; } |
| |
| // ID3D12ShaderReflectionConstantBuffer |
| STDMETHOD(GetDesc)(D3D12_SHADER_BUFFER_DESC *pDesc); |
| |
| STDMETHOD_(ID3D12ShaderReflectionVariable *, GetVariableByIndex)(UINT Index); |
| STDMETHOD_(ID3D12ShaderReflectionVariable *, GetVariableByName)(LPCSTR Name); |
| }; |
| |
| // Invalid type sentinel definitions |
| class CInvalidSRType; |
| class CInvalidSRVariable; |
| class CInvalidSRConstantBuffer; |
| class CInvalidSRLibraryFunction; |
| class CInvalidSRFunctionParameter; |
| |
| class CInvalidSRType final : public ID3D12ShaderReflectionType { |
| STDMETHOD(GetDesc)(D3D12_SHADER_TYPE_DESC *pDesc) { return E_FAIL; } |
| |
| STDMETHOD_(ID3D12ShaderReflectionType *, GetMemberTypeByIndex)(UINT Index); |
| STDMETHOD_(ID3D12ShaderReflectionType *, GetMemberTypeByName)(LPCSTR Name); |
| STDMETHOD_(LPCSTR, GetMemberTypeName)(UINT Index) { return "$Invalid"; } |
| |
| STDMETHOD(IsEqual)(ID3D12ShaderReflectionType *pType) { return E_FAIL; } |
| STDMETHOD_(ID3D12ShaderReflectionType *, GetSubType)(); |
| STDMETHOD_(ID3D12ShaderReflectionType *, GetBaseClass)(); |
| STDMETHOD_(UINT, GetNumInterfaces)() { return 0; } |
| STDMETHOD_(ID3D12ShaderReflectionType *, GetInterfaceByIndex)(UINT uIndex); |
| STDMETHOD(IsOfType)(ID3D12ShaderReflectionType *pType) { return E_FAIL; } |
| STDMETHOD(ImplementsInterface)(ID3D12ShaderReflectionType *pBase) { |
| return E_FAIL; |
| } |
| }; |
| static CInvalidSRType g_InvalidSRType; |
| |
| ID3D12ShaderReflectionType *CInvalidSRType::GetMemberTypeByIndex(UINT) { |
| return &g_InvalidSRType; |
| } |
| ID3D12ShaderReflectionType *CInvalidSRType::GetMemberTypeByName(LPCSTR) { |
| return &g_InvalidSRType; |
| } |
| ID3D12ShaderReflectionType *CInvalidSRType::GetSubType() { |
| return &g_InvalidSRType; |
| } |
| ID3D12ShaderReflectionType *CInvalidSRType::GetBaseClass() { |
| return &g_InvalidSRType; |
| } |
| ID3D12ShaderReflectionType *CInvalidSRType::GetInterfaceByIndex(UINT) { |
| return &g_InvalidSRType; |
| } |
| |
| class CInvalidSRVariable final : public ID3D12ShaderReflectionVariable { |
| STDMETHOD(GetDesc)(D3D12_SHADER_VARIABLE_DESC *pDesc) { return E_FAIL; } |
| |
| STDMETHOD_(ID3D12ShaderReflectionType *, GetType)() { |
| return &g_InvalidSRType; |
| } |
| STDMETHOD_(ID3D12ShaderReflectionConstantBuffer *, GetBuffer)(); |
| |
| STDMETHOD_(UINT, GetInterfaceSlot)(UINT uIndex) { return UINT_MAX; } |
| }; |
| static CInvalidSRVariable g_InvalidSRVariable; |
| |
| class CInvalidSRConstantBuffer final |
| : public ID3D12ShaderReflectionConstantBuffer { |
| STDMETHOD(GetDesc)(D3D12_SHADER_BUFFER_DESC *pDesc) { return E_FAIL; } |
| |
| STDMETHOD_(ID3D12ShaderReflectionVariable *, GetVariableByIndex)(UINT Index) { |
| return &g_InvalidSRVariable; |
| } |
| STDMETHOD_(ID3D12ShaderReflectionVariable *, GetVariableByName)(LPCSTR Name) { |
| return &g_InvalidSRVariable; |
| } |
| }; |
| static CInvalidSRConstantBuffer g_InvalidSRConstantBuffer; |
| |
| class CInvalidFunctionParameter final |
| : public ID3D12FunctionParameterReflection { |
| STDMETHOD(GetDesc)(D3D12_PARAMETER_DESC *pDesc) { return E_FAIL; } |
| }; |
| CInvalidFunctionParameter g_InvalidFunctionParameter; |
| |
| class CInvalidFunction final : public ID3D12FunctionReflection { |
| STDMETHOD(GetDesc)(D3D12_FUNCTION_DESC *pDesc) { return E_FAIL; } |
| |
| STDMETHOD_(ID3D12ShaderReflectionConstantBuffer *, GetConstantBufferByIndex) |
| (UINT BufferIndex) { return &g_InvalidSRConstantBuffer; } |
| STDMETHOD_(ID3D12ShaderReflectionConstantBuffer *, GetConstantBufferByName) |
| (LPCSTR Name) { return &g_InvalidSRConstantBuffer; } |
| |
| STDMETHOD(GetResourceBindingDesc) |
| (UINT ResourceIndex, D3D12_SHADER_INPUT_BIND_DESC *pDesc) { return E_FAIL; } |
| |
| STDMETHOD_(ID3D12ShaderReflectionVariable *, GetVariableByName)(LPCSTR Name) { |
| return nullptr; |
| } |
| |
| STDMETHOD(GetResourceBindingDescByName) |
| (LPCSTR Name, D3D12_SHADER_INPUT_BIND_DESC *pDesc) { return E_FAIL; } |
| |
| // Use D3D_RETURN_PARAMETER_INDEX to get description of the return value. |
| STDMETHOD_(ID3D12FunctionParameterReflection *, GetFunctionParameter) |
| (INT ParameterIndex) { return &g_InvalidFunctionParameter; } |
| }; |
| CInvalidFunction g_InvalidFunction; |
| |
| void CShaderReflectionVariable::Initialize( |
| CShaderReflectionConstantBuffer *pBuffer, D3D12_SHADER_VARIABLE_DESC *pDesc, |
| CShaderReflectionType *pType, BYTE *pDefaultValue) { |
| m_pBuffer = pBuffer; |
| memcpy(&m_Desc, pDesc, sizeof(m_Desc)); |
| m_pType = pType; |
| m_pDefaultValue = pDefaultValue; |
| } |
| |
| HRESULT CShaderReflectionVariable::GetDesc(D3D12_SHADER_VARIABLE_DESC *pDesc) { |
| if (!pDesc) |
| return E_POINTER; |
| memcpy(pDesc, &m_Desc, sizeof(m_Desc)); |
| return S_OK; |
| } |
| |
| ID3D12ShaderReflectionType *CShaderReflectionVariable::GetType() { |
| return m_pType; |
| } |
| |
| ID3D12ShaderReflectionConstantBuffer *CShaderReflectionVariable::GetBuffer() { |
| return m_pBuffer; |
| } |
| |
| UINT CShaderReflectionVariable::GetInterfaceSlot(UINT uArrayIndex) { |
| return UINT_MAX; |
| } |
| |
| ID3D12ShaderReflectionConstantBuffer *CInvalidSRVariable::GetBuffer() { |
| return &g_InvalidSRConstantBuffer; |
| } |
| |
| STDMETHODIMP CShaderReflectionType::GetDesc(D3D12_SHADER_TYPE_DESC *pDesc) { |
| if (!pDesc) |
| return E_POINTER; |
| memcpy(pDesc, &m_Desc, sizeof(m_Desc)); |
| return S_OK; |
| } |
| |
| STDMETHODIMP_(ID3D12ShaderReflectionType *) |
| CShaderReflectionType::GetMemberTypeByIndex(UINT Index) { |
| if (Index >= m_MemberTypes.size()) { |
| return &g_InvalidSRType; |
| } |
| return m_MemberTypes[Index]; |
| } |
| |
| STDMETHODIMP_(LPCSTR) CShaderReflectionType::GetMemberTypeName(UINT Index) { |
| if (Index >= m_MemberTypes.size()) { |
| return nullptr; |
| } |
| return (LPCSTR)m_MemberNames[Index].bytes_begin(); |
| } |
| |
| STDMETHODIMP_(ID3D12ShaderReflectionType *) |
| CShaderReflectionType::GetMemberTypeByName(LPCSTR Name) { |
| UINT memberCount = m_Desc.Members; |
| for (UINT mm = 0; mm < memberCount; ++mm) { |
| if (m_MemberNames[mm] == Name) { |
| return m_MemberTypes[mm]; |
| } |
| } |
| return nullptr; |
| } |
| |
| STDMETHODIMP CShaderReflectionType::IsEqual(ID3D12ShaderReflectionType *pType) { |
| // TODO: implement this check, if users actually depend on it |
| return S_FALSE; |
| } |
| |
| STDMETHODIMP_(ID3D12ShaderReflectionType *) |
| CShaderReflectionType::GetSubType() { |
| // TODO: implement `class`-related features, if requested |
| return nullptr; |
| } |
| |
| STDMETHODIMP_(ID3D12ShaderReflectionType *) |
| CShaderReflectionType::GetBaseClass() { |
| // TODO: implement `class`-related features, if requested |
| return nullptr; |
| } |
| |
| STDMETHODIMP_(UINT) CShaderReflectionType::GetNumInterfaces() { |
| // HLSL interfaces have been deprecated |
| return 0; |
| } |
| |
| STDMETHODIMP_(ID3D12ShaderReflectionType *) |
| CShaderReflectionType::GetInterfaceByIndex(UINT uIndex) { |
| // HLSL interfaces have been deprecated |
| return nullptr; |
| } |
| |
| STDMETHODIMP |
| CShaderReflectionType::IsOfType(ID3D12ShaderReflectionType *pType) { |
| // TODO: implement `class`-related features, if requested |
| return S_FALSE; |
| } |
| |
| STDMETHODIMP |
| CShaderReflectionType::ImplementsInterface(ID3D12ShaderReflectionType *pBase) { |
| // HLSL interfaces have been deprecated |
| return S_FALSE; |
| } |
| |
| // Helper routine for types that don't have an obvious mapping |
| // to the existing shader reflection interface. |
| static bool |
| ProcessUnhandledObjectType(llvm::StructType *structType, |
| D3D_SHADER_VARIABLE_TYPE *outObjectType) { |
| // Don't actually make this a hard error, but instead report the problem using |
| // a suitable debug message. |
| #ifndef NDEBUG |
| OutputDebugFormatA( |
| "DxilContainerReflection.cpp: error: unhandled object type '%s'.\n", |
| structType->getName().str().c_str()); |
| #endif |
| *outObjectType = D3D_SVT_VOID; |
| return true; |
| } |
| |
| // Helper routine to try to detect if a type represents an HLSL "object" type |
| // (a texture, sampler, buffer, etc.), and to extract the coresponding shader |
| // reflection type. |
| static bool TryToDetectObjectType(llvm::StructType *structType, |
| D3D_SHADER_VARIABLE_TYPE *outObjectType) { |
| // Note: This logic is largely duplicated from `dxilutil::IsHLSLObjectType` |
| // with the addition of returning the appropriate reflection type tag. |
| // |
| // That logic looks error-prone, since it relies on string tests against |
| // type names, including cases that just test against a prefix. |
| // This code doesn't try to be any more robust. |
| |
| StringRef name = structType->getName(); |
| |
| if (name.startswith("dx.types.wave_t")) { |
| return ProcessUnhandledObjectType(structType, outObjectType); |
| } |
| |
| // Strip off some prefixes we are likely to see. |
| name = name.ltrim("class."); |
| name = name.ltrim("struct."); |
| |
| // Slice types occur as intermediates (they aren not objects) |
| if (name.endswith("_slice_type")) { |
| return false; |
| } |
| |
| // We might check for an exact name match, or a prefix match |
| #define EXACT_MATCH(NAME, TAG) \ |
| else if (name == #NAME) do { \ |
| *outObjectType = TAG; \ |
| return true; \ |
| } \ |
| while (0) |
| #define PREFIX_MATCH(NAME, TAG) \ |
| else if (name.startswith(#NAME)) do { \ |
| *outObjectType = TAG; \ |
| return true; \ |
| } \ |
| while (0) |
| |
| if (0) { |
| } |
| EXACT_MATCH(SamplerState, D3D_SVT_SAMPLER); |
| EXACT_MATCH(SamplerComparisonState, D3D_SVT_SAMPLER); |
| |
| // Note: GS output stream types are supported in the reflection interface. |
| else if (name.startswith("TriangleStream")) { |
| return ProcessUnhandledObjectType(structType, outObjectType); |
| } |
| else if (name.startswith("PointStream")) { |
| return ProcessUnhandledObjectType(structType, outObjectType); |
| } |
| else if (name.startswith("LineStream")) { |
| return ProcessUnhandledObjectType(structType, outObjectType); |
| } |
| |
| PREFIX_MATCH(AppendStructuredBuffer, D3D_SVT_APPEND_STRUCTURED_BUFFER); |
| PREFIX_MATCH(ConsumeStructuredBuffer, D3D_SVT_CONSUME_STRUCTURED_BUFFER); |
| PREFIX_MATCH(ConstantBuffer, D3D_SVT_CBUFFER); |
| |
| // Note: the `HLModule` code does this trick to avoid checking more names |
| // than it has to, but it doesn't seem 100% correct to do this. |
| // TODO: consider just listing the `RasterizerOrdered` cases explicitly, |
| // just as we do for the `RW` cases already. |
| name = name.ltrim("RasterizerOrdered"); |
| |
| if (0) { |
| } |
| EXACT_MATCH(ByteAddressBuffer, D3D_SVT_BYTEADDRESS_BUFFER); |
| EXACT_MATCH(RWByteAddressBuffer, D3D_SVT_RWBYTEADDRESS_BUFFER); |
| PREFIX_MATCH(Buffer, D3D_SVT_BUFFER); |
| PREFIX_MATCH(RWBuffer, D3D_SVT_RWBUFFER); |
| PREFIX_MATCH(StructuredBuffer, D3D_SVT_STRUCTURED_BUFFER); |
| PREFIX_MATCH(RWStructuredBuffer, D3D_SVT_RWSTRUCTURED_BUFFER); |
| PREFIX_MATCH(Texture1D, D3D_SVT_TEXTURE1D); |
| PREFIX_MATCH(RWTexture1D, D3D_SVT_RWTEXTURE1D); |
| PREFIX_MATCH(Texture1DArray, D3D_SVT_TEXTURE1DARRAY); |
| PREFIX_MATCH(RWTexture1DArray, D3D_SVT_RWTEXTURE1DARRAY); |
| PREFIX_MATCH(Texture2D, D3D_SVT_TEXTURE2D); |
| PREFIX_MATCH(RWTexture2D, D3D_SVT_RWTEXTURE2D); |
| PREFIX_MATCH(Texture2DArray, D3D_SVT_TEXTURE2DARRAY); |
| PREFIX_MATCH(RWTexture2DArray, D3D_SVT_RWTEXTURE2DARRAY); |
| PREFIX_MATCH(Texture3D, D3D_SVT_TEXTURE3D); |
| PREFIX_MATCH(RWTexture3D, D3D_SVT_RWTEXTURE3D); |
| PREFIX_MATCH(TextureCube, D3D_SVT_TEXTURECUBE); |
| PREFIX_MATCH(TextureCubeArray, D3D_SVT_TEXTURECUBEARRAY); |
| PREFIX_MATCH(Texture2DMS, D3D_SVT_TEXTURE2DMS); |
| PREFIX_MATCH(Texture2DMSArray, D3D_SVT_TEXTURE2DMSARRAY); |
| |
| #undef EXACT_MATCH |
| #undef PREFIX_MATCH |
| |
| // Default: not an object type |
| return false; |
| } |
| |
| // Helper to determine if an LLVM type represents an HLSL |
| // object type (uses the `TryToDetectObjectType()` function |
| // defined previously). |
| static bool IsObjectType(llvm::Type *inType) { |
| llvm::Type *type = inType; |
| while (type->isArrayTy()) { |
| type = type->getArrayElementType(); |
| } |
| |
| llvm::StructType *structType = dyn_cast<StructType>(type); |
| if (!structType) |
| return false; |
| |
| D3D_SHADER_VARIABLE_TYPE ignored; |
| return TryToDetectObjectType(structType, &ignored); |
| } |
| |
| HRESULT CShaderReflectionType::InitializeEmpty() { |
| ZeroMemory(&m_Desc, sizeof(m_Desc)); |
| return S_OK; |
| } |
| |
| // Returns true if type is array and/or vec with matching number of elements. |
| static bool MatchVectorOrMatrixType(llvm::Type *type, unsigned count, |
| int maxDepth) { |
| if (type->isArrayTy()) { |
| unsigned arraySize = (unsigned)type->getArrayNumElements(); |
| if (maxDepth < 1 || count < arraySize || (count % arraySize) != 0) |
| return false; |
| return MatchVectorOrMatrixType(type->getArrayElementType(), |
| count / arraySize, maxDepth - 1); |
| } else if (type->isVectorTy()) { |
| if (maxDepth < 1) |
| return false; |
| return type->getVectorNumElements() == count; |
| } |
| return count == 1; |
| } |
| |
| // Main logic for translating an LLVM type and associated |
| // annotations into a D3D shader reflection type. |
| HRESULT CShaderReflectionType::Initialize( |
| DxilModule &M, llvm::Type *inType, DxilFieldAnnotation &typeAnnotation, |
| unsigned int baseOffset, |
| std::vector<std::unique_ptr<CShaderReflectionType>> &allTypes, |
| bool isCBuffer) { |
| DXASSERT_NOMSG(inType); |
| |
| // Set a bunch of fields to default values, to avoid duplication. |
| m_Desc.Class = D3D_SVC_SCALAR; |
| m_Desc.Rows = 0; |
| m_Desc.Columns = 0; |
| m_Desc.Elements = 0; |
| m_Desc.Members = 0; |
| m_SizeInCBuffer = 0; |
| |
| // Used for calculating size later |
| unsigned cbRows = 1; |
| unsigned cbCols = 1; |
| unsigned cbCompSize = 4; // or 8 for 64-bit types. |
| unsigned cbRowStride = 16; // or 32 if 64-bit and cols > 2. |
| |
| if (isCBuffer) { |
| // Extract offset relative to parent. |
| // Note: the `baseOffset` is used in the case where the type in |
| // question is a field in a constant buffer, since then both the |
| // field and the variable store the same offset information, and |
| // we need to zero out the value in the type to avoid the user |
| // of the reflection interface seeing 2x the correct value. |
| m_Desc.Offset = typeAnnotation.GetCBufferOffset() - baseOffset; |
| } else { |
| m_Desc.Offset = baseOffset; |
| } |
| |
| // Arrays don't seem to be represented directly in the reflection |
| // data, but only as the `Elements` field being non-zero. |
| // We "unwrap" any array type here, and then proceed to look |
| // at the element type. |
| llvm::Type *type = inType; |
| |
| // Arrays can be a bit difficult, since some types are translated to arrays. |
| // Additionally, matrices have multiple potential forms, so we must pay |
| // attention to the field annotation to determine when we have reached the |
| // element type that may be a matrix or a vector. |
| |
| // There are several possible matrix encodings: |
| // High level: struct { [rows x <cols x float>] } |
| // High level struct stripped: [rows x <cols x float>] |
| // High level struct stripped, one row: <cols x float> |
| // Vector as array: [rows x [cols x float]] |
| // Vector as array, one row: [cols x float] |
| // Flattened vector: <(rows*cols) x float> |
| // Flattened vector as array: [(rows*cols) x float] |
| // And vector may use llvm vector, or be translated to array: |
| // <cols x float> <-> [cols x float] |
| // Use type annotation to determine if we have a vector or matrix first, |
| // so we can stop multiplying in array dims at the right time. |
| |
| if (typeAnnotation.HasMatrixAnnotation()) { |
| // We can extract the details from the annotation. |
| DxilMatrixAnnotation const &matrixAnnotation = |
| typeAnnotation.GetMatrixAnnotation(); |
| |
| switch (matrixAnnotation.Orientation) { |
| default: |
| #ifndef NDEBUG |
| OutputDebugStringA( |
| "DxilContainerReflection.cpp: error: unknown matrix orientation\n"); |
| #endif |
| // Note: column-major layout is the default |
| LLVM_FALLTHROUGH; // HLSL Change |
| case hlsl::MatrixOrientation::Undefined: |
| case hlsl::MatrixOrientation::ColumnMajor: |
| m_Desc.Class = D3D_SVC_MATRIX_COLUMNS; |
| break; |
| |
| case hlsl::MatrixOrientation::RowMajor: |
| m_Desc.Class = D3D_SVC_MATRIX_ROWS; |
| break; |
| } |
| |
| m_Desc.Rows = matrixAnnotation.Rows; |
| m_Desc.Columns = matrixAnnotation.Cols; |
| |
| cbRows = m_Desc.Rows; |
| cbCols = m_Desc.Columns; |
| if (m_Desc.Class == D3D_SVC_MATRIX_COLUMNS) { |
| std::swap(cbRows, cbCols); |
| } |
| } else if (unsigned cols = typeAnnotation.GetVectorSize()) { |
| // Older format lacks this size, but the type will be a vector, |
| // so that will be handled later by original code path. |
| m_Desc.Class = D3D_SVC_VECTOR; |
| m_Desc.Rows = 1; |
| m_Desc.Columns = cols; |
| |
| cbRows = m_Desc.Rows; |
| cbCols = m_Desc.Columns; |
| } |
| |
| while (type->isArrayTy()) { |
| // Already determined that this is a vector or matrix, so break if the |
| // number of remaining array and/or vector elements matches. |
| if (m_Desc.Class != D3D_SVC_SCALAR) { |
| // max depth is 1 for vector, and 2 for matrix, unless rows in storage |
| // orientation is 1. |
| if (MatchVectorOrMatrixType( |
| type, cbRows * cbCols, |
| (m_Desc.Class == D3D_SVC_VECTOR || cbRows == 1) ? 1 : 2)) |
| break; |
| } |
| |
| // Non-array types should have `Elements` be zero, so as soon as we |
| // find that we have our first real array (not a matrix), we initialize |
| // `Elements` |
| if (!m_Desc.Elements) |
| m_Desc.Elements = 1; |
| |
| // It isn't clear what is the desired behavior for multi-dimensional arrays, |
| // but for now we do the expedient thing of multiplying out all their |
| // dimensions. |
| m_Desc.Elements *= type->getArrayNumElements(); |
| type = type->getArrayElementType(); |
| } |
| |
| // Look at the annotation to try to determine the basic type of value. |
| // |
| // Note that DXIL supports some types that don't currently have equivalents |
| // in the reflection interface, so we try to muddle through here. |
| bool bMinPrec = M.GetUseMinPrecision(); |
| D3D_SHADER_VARIABLE_TYPE componentType = D3D_SVT_VOID; |
| switch (typeAnnotation.GetCompType().GetKind()) { |
| case hlsl::DXIL::ComponentType::Invalid: |
| break; |
| |
| case hlsl::DXIL::ComponentType::I1: |
| componentType = D3D_SVT_BOOL; |
| m_Name = "bool"; |
| break; |
| |
| case hlsl::DXIL::ComponentType::I16: |
| if (bMinPrec) { |
| componentType = D3D_SVT_MIN16INT; |
| m_Name = "min16int"; |
| } else { |
| componentType = D3D_SVT_INT16; |
| m_Name = "int16_t"; |
| cbCompSize = 2; |
| } |
| break; |
| |
| case hlsl::DXIL::ComponentType::U16: |
| if (bMinPrec) { |
| componentType = D3D_SVT_MIN16UINT; |
| m_Name = "min16uint"; |
| } else { |
| componentType = D3D_SVT_UINT16; |
| m_Name = "uint16_t"; |
| cbCompSize = 2; |
| } |
| break; |
| |
| case hlsl::DXIL::ComponentType::I64: |
| componentType = D3D_SVT_INT64; |
| m_Name = "int64_t"; |
| cbCompSize = 8; |
| break; |
| case hlsl::DXIL::ComponentType::I32: |
| componentType = D3D_SVT_INT; |
| m_Name = "int"; |
| break; |
| |
| case hlsl::DXIL::ComponentType::U64: |
| componentType = D3D_SVT_UINT64; |
| m_Name = "uint64_t"; |
| cbCompSize = 8; |
| break; |
| case hlsl::DXIL::ComponentType::U32: |
| componentType = D3D_SVT_UINT; |
| m_Name = "uint"; |
| break; |
| |
| case hlsl::DXIL::ComponentType::F16: |
| case hlsl::DXIL::ComponentType::SNormF16: |
| case hlsl::DXIL::ComponentType::UNormF16: |
| if (bMinPrec) { |
| componentType = D3D_SVT_MIN16FLOAT; |
| m_Name = "min16float"; |
| } else { |
| componentType = D3D_SVT_FLOAT16; |
| m_Name = "float16_t"; |
| cbCompSize = 2; |
| } |
| break; |
| |
| case hlsl::DXIL::ComponentType::F32: |
| case hlsl::DXIL::ComponentType::SNormF32: |
| case hlsl::DXIL::ComponentType::UNormF32: |
| componentType = D3D_SVT_FLOAT; |
| m_Name = "float"; |
| break; |
| |
| case hlsl::DXIL::ComponentType::F64: |
| case hlsl::DXIL::ComponentType::SNormF64: |
| case hlsl::DXIL::ComponentType::UNormF64: |
| cbCompSize = 8; |
| componentType = D3D_SVT_DOUBLE; |
| m_Name = "double"; |
| break; |
| |
| default: |
| #ifndef NDEBUG |
| OutputDebugStringA( |
| "DxilContainerReflection.cpp: error: unknown component type\n"); |
| #endif |
| break; |
| } |
| m_Desc.Type = componentType; |
| |
| if (m_Desc.Class != D3D_SVC_SCALAR) { |
| // matrix or explicit vector already handled, except for name. |
| if (m_Desc.Class == D3D_SVC_VECTOR) { |
| m_Name += std::to_string(m_Desc.Columns); |
| } else { |
| m_Name += |
| std::to_string(m_Desc.Rows) + "x" + std::to_string(m_Desc.Columns); |
| } |
| } else if (FixedVectorType *VT = dyn_cast<FixedVectorType>(type)) { |
| // We assume that LLVM vectors either represent matrices (handled above) |
| // or HLSL vectors. |
| // |
| // Note: the reflection interface encodes an N-vector as if it had 1 row |
| // and N columns. |
| m_Desc.Class = D3D_SVC_VECTOR; |
| m_Desc.Rows = 1; |
| m_Desc.Columns = VT->getNumElements(); |
| |
| m_Name += std::to_string(VT->getNumElements()); |
| |
| cbRows = m_Desc.Rows; |
| cbCols = m_Desc.Columns; |
| } else if (type->isStructTy()) { |
| // A struct type might be an ordinary user-defined `struct`, |
| // or one of the builtin in HLSL "object" types. |
| StructType *structType = cast<StructType>(type); |
| const StructLayout *structLayout = |
| isCBuffer ? nullptr |
| : M.GetModule()->getDataLayout().getStructLayout(structType); |
| |
| // We use our function to try to detect an object type |
| // based on its name. |
| if (TryToDetectObjectType(structType, &m_Desc.Type)) { |
| m_Desc.Class = D3D_SVC_OBJECT; |
| } else { |
| // Otherwise we have a struct and need to recurse on its fields. |
| m_Desc.Class = D3D_SVC_STRUCT; |
| m_Desc.Rows = 1; |
| |
| // Try to "clean" the type name for use in reflection data |
| llvm::StringRef name = structType->getName(); |
| name = |
| name.ltrim("dx.alignment.legacy."); // legacy prefix for legacy types |
| name = name.ltrim(kHostLayoutTypePrefix); |
| name = name.ltrim("struct."); |
| m_Name = name; |
| |
| // Fields may have annotations, and we need to look at these |
| // in order to decode their types properly. |
| DxilTypeSystem &typeSys = M.GetTypeSystem(); |
| DxilStructAnnotation *structAnnotation = |
| typeSys.GetStructAnnotation(structType); |
| |
| // There is no annotation for empty structs |
| unsigned int fieldCount = 0; |
| if (structAnnotation && !structAnnotation->IsEmptyBesidesResources()) |
| fieldCount = type->getStructNumElements(); |
| |
| // The DXBC reflection info computes `Columns` for a |
| // `struct` type from the fields (see below) |
| UINT columnCounter = 0; |
| |
| CShaderReflectionType *fieldReflectionType = nullptr; |
| |
| for (unsigned int ff = 0; ff < fieldCount; ++ff) { |
| DxilFieldAnnotation &fieldAnnotation = |
| structAnnotation->GetFieldAnnotation(ff); |
| llvm::Type *fieldType = structType->getStructElementType(ff); |
| |
| // Skip fields with object types, since these are not part of constant |
| // buffers, and are not allowed in other buffer types. |
| if (IsObjectType(fieldType)) { |
| continue; |
| } |
| |
| fieldReflectionType = new CShaderReflectionType(); |
| allTypes.push_back( |
| std::unique_ptr<CShaderReflectionType>(fieldReflectionType)); |
| |
| unsigned int elementOffset = |
| structLayout ? (unsigned int)structLayout->getElementOffset(ff) : 0; |
| |
| fieldReflectionType->Initialize(M, fieldType, fieldAnnotation, |
| elementOffset, allTypes, isCBuffer); |
| |
| // Treat bit fields as member inside the integer. |
| if (fieldAnnotation.HasBitFields()) |
| fieldReflectionType->m_Desc.Members = |
| fieldAnnotation.GetBitFields().size(); |
| |
| m_MemberTypes.push_back(fieldReflectionType); |
| m_MemberNames.push_back(fieldAnnotation.GetFieldName().c_str()); |
| |
| // Skip structures fields with no real contents, otherwise we expand |
| // the size of this struct by 1 when we treat a zero column size as 1. |
| if (isa<StructType>(fieldType) && |
| fieldReflectionType->m_Desc.Columns == 0) { |
| continue; |
| } |
| |
| // Effectively, we want to add one to `Columns` for every scalar |
| // nested recursively inside this `struct` type (ignoring objects, |
| // which we filtered above). We should be able to compute this as the |
| // product of the `Columns`, `Rows` and `Elements` of each field, with |
| // the caveat that some of these may be zero, but shoud be treated as |
| // one. |
| columnCounter += |
| (fieldReflectionType->m_Desc.Columns |
| ? fieldReflectionType->m_Desc.Columns |
| : 1) * |
| (fieldReflectionType->m_Desc.Rows ? fieldReflectionType->m_Desc.Rows |
| : 1) * |
| (fieldReflectionType->m_Desc.Elements |
| ? fieldReflectionType->m_Desc.Elements |
| : 1); |
| |
| if (fieldAnnotation.HasBitFields()) { |
| unsigned bitOffset = 0; |
| CShaderReflectionType *bitFieldReflectionType = nullptr; |
| for (auto &bitfieldAnnotation : fieldAnnotation.GetBitFields()) { |
| bitFieldReflectionType = new CShaderReflectionType(); |
| allTypes.push_back( |
| std::unique_ptr<CShaderReflectionType>(bitFieldReflectionType)); |
| |
| bitFieldReflectionType->Initialize(M, fieldType, fieldAnnotation, |
| elementOffset, allTypes, |
| isCBuffer); |
| bitFieldReflectionType->m_Desc.Class = D3D_SVC_BIT_FIELD; |
| |
| // Save bit size to columns. |
| bitFieldReflectionType->m_Desc.Columns = |
| bitfieldAnnotation.GetBitFieldWidth(); |
| // Save bit offset to Offset. |
| bitFieldReflectionType->m_Desc.Offset = bitOffset; |
| bitOffset += bitfieldAnnotation.GetBitFieldWidth(); |
| |
| fieldReflectionType->m_MemberTypes.push_back( |
| bitFieldReflectionType); |
| fieldReflectionType->m_MemberNames.push_back( |
| bitfieldAnnotation.GetFieldName().c_str()); |
| } |
| } |
| } |
| |
| m_Desc.Columns = columnCounter; |
| |
| if (fieldReflectionType) { |
| // Set our size based on the last fields offset + size: |
| m_SizeInCBuffer = fieldReflectionType->m_Desc.Offset + |
| fieldReflectionType->m_SizeInCBuffer; |
| if (m_Desc.Elements > 1) { |
| unsigned alignedSize = ((m_SizeInCBuffer + 15) & ~0xF); |
| m_SizeInCBuffer += (m_Desc.Elements - 1) * alignedSize; |
| } |
| } |
| |
| // Because we might have skipped fields during enumeration, |
| // the `Members` count in the description might not be the same |
| // as the field count of the original LLVM type. |
| m_Desc.Members = m_MemberTypes.size(); |
| } |
| } else if (type->isPointerTy()) { |
| #ifndef NDEBUG |
| OutputDebugStringA( |
| "DxilContainerReflection.cpp: error: cannot reflect pointer type\n"); |
| #endif |
| } else if (type->isVoidTy()) { |
| // Name for `void` wasn't handle in the component-type `switch` above |
| m_Name = "void"; |
| m_Desc.Class = D3D_SVC_SCALAR; |
| m_Desc.Rows = 1; |
| m_Desc.Columns = 1; |
| } else { |
| // Assume we have a scalar at this point. |
| m_Desc.Class = D3D_SVC_SCALAR; |
| m_Desc.Rows = 1; |
| m_Desc.Columns = 1; |
| |
| // Special-case naming |
| switch (m_Desc.Type) { |
| default: |
| break; |
| |
| case D3D_SVT_UINT: |
| // Scalar `uint` gets reflected as `dword`, while vectors/matrices use |
| // `uint`... |
| m_Name = "dword"; |
| break; |
| } |
| |
| cbRows = 1; |
| cbCols = 1; |
| } |
| // TODO: are there other cases to be handled? |
| |
| // Compute our cbuffer size for member reflection |
| switch (m_Desc.Class) { |
| case D3D_SVC_SCALAR: |
| case D3D_SVC_MATRIX_COLUMNS: |
| case D3D_SVC_MATRIX_ROWS: |
| case D3D_SVC_VECTOR: |
| if (m_Desc.Elements > 1) |
| cbRows = cbRows * m_Desc.Elements; |
| if (cbCompSize > 4 && cbCols > 2) |
| cbRowStride = 32; |
| m_SizeInCBuffer = cbRowStride * (cbRows - 1) + cbCompSize * cbCols; |
| break; |
| } |
| |
| m_Desc.Name = m_Name.c_str(); |
| |
| return S_OK; |
| } |
| |
| void CShaderReflectionConstantBuffer::Initialize( |
| DxilModule &M, DxilCBuffer &CB, |
| std::vector<std::unique_ptr<CShaderReflectionType>> &allTypes, |
| bool bUsageInMetadata) { |
| ZeroMemory(&m_Desc, sizeof(m_Desc)); |
| m_ReflectionName = CB.GetGlobalName(); |
| m_Desc.Name = m_ReflectionName.c_str(); |
| m_Desc.Size = CB.GetSize(); |
| m_Desc.Size = |
| (m_Desc.Size + 0x0f) & ~(0x0f); // Round up to 16 bytes for reflection. |
| m_Desc.Type = D3D_CT_CBUFFER; |
| m_Desc.uFlags = 0; |
| // For ConstantBuffer<> buf[2], the array size is in Resource binding count |
| // part. |
| Type *Ty = |
| dxilutil::StripArrayTypes(CB.GetHLSLType()->getPointerElementType()); |
| |
| DxilTypeSystem &typeSys = M.GetTypeSystem(); |
| StructType *ST = cast<StructType>(Ty); |
| DxilStructAnnotation *annotation = |
| typeSys.GetStructAnnotation(cast<StructType>(ST)); |
| // Dxil from dxbc doesn't have annotation. |
| if (!annotation) |
| return; |
| |
| m_Desc.Variables = ST->getNumContainedTypes(); |
| |
| if (CB.GetRangeSize() > 1) { |
| DXASSERT(m_Desc.Variables == 1, "otherwise, assumption is wrong"); |
| } |
| |
| // If only one member, it's used if it's here. |
| bool bAllUsed = ST->getNumContainedTypes() < 2; |
| bAllUsed |= !bUsageInMetadata; // Will update in SetCBufferUsage. |
| |
| for (unsigned i = 0; i < ST->getNumContainedTypes(); ++i) { |
| DxilFieldAnnotation &fieldAnnotation = annotation->GetFieldAnnotation(i); |
| |
| D3D12_SHADER_VARIABLE_DESC VarDesc; |
| ZeroMemory(&VarDesc, sizeof(VarDesc)); |
| VarDesc.uFlags = |
| (bAllUsed || fieldAnnotation.IsCBVarUsed()) ? D3D_SVF_USED : 0; |
| CShaderReflectionVariable Var; |
| // Create reflection type. |
| CShaderReflectionType *pVarType = new CShaderReflectionType(); |
| allTypes.push_back(std::unique_ptr<CShaderReflectionType>(pVarType)); |
| pVarType->Initialize(M, ST->getContainedType(i), fieldAnnotation, |
| fieldAnnotation.GetCBufferOffset(), allTypes, true); |
| |
| // Replicate fxc bug, where Elements == 1 for inner struct of CB array, |
| // instead of 0. |
| if (CB.GetRangeSize() > 1) { |
| DXASSERT(pVarType->m_Desc.Elements == 0, |
| "otherwise, assumption is wrong"); |
| pVarType->m_Desc.Elements = 1; |
| } else if (CB.GetHLSLType()->getPointerElementType()->isArrayTy() && |
| CB.GetRangeSize() == 1) { |
| // Set elements to 1 for size 1 array. |
| pVarType->m_Desc.Elements = 1; |
| } |
| |
| BYTE *pDefaultValue = nullptr; |
| |
| VarDesc.Name = fieldAnnotation.GetFieldName().c_str(); |
| VarDesc.StartOffset = fieldAnnotation.GetCBufferOffset(); |
| VarDesc.Size = pVarType->GetCBufferSize(); |
| Var.Initialize(this, &VarDesc, pVarType, pDefaultValue); |
| m_Variables.push_back(Var); |
| } |
| } |
| |
| static unsigned CalcResTypeSize(DxilModule &M, DxilResource &R) { |
| UNREFERENCED_PARAMETER(M); |
| Type *Ty = R.GetHLSLType()->getPointerElementType(); |
| if (R.IsStructuredBuffer()) { |
| Ty = dxilutil::StripArrayTypes(Ty); |
| } |
| return M.GetModule()->getDataLayout().getTypeAllocSize(Ty); |
| } |
| |
| void CShaderReflectionConstantBuffer::InitializeStructuredBuffer( |
| DxilModule &M, DxilResource &R, |
| std::vector<std::unique_ptr<CShaderReflectionType>> &allTypes) { |
| ZeroMemory(&m_Desc, sizeof(m_Desc)); |
| m_ReflectionName = R.GetGlobalName(); |
| m_Desc.Type = D3D11_CT_RESOURCE_BIND_INFO; |
| m_Desc.uFlags = 0; |
| m_Desc.Variables = 1; |
| |
| D3D12_SHADER_VARIABLE_DESC VarDesc; |
| ZeroMemory(&VarDesc, sizeof(VarDesc)); |
| VarDesc.Name = "$Element"; |
| VarDesc.Size = CalcResTypeSize(M, R); |
| VarDesc.StartTexture = UINT_MAX; |
| VarDesc.StartSampler = UINT_MAX; |
| VarDesc.uFlags |= D3D_SVF_USED; |
| CShaderReflectionVariable Var; |
| |
| // First type is an empty type: returned if no annotation available. |
| CShaderReflectionType *pVarType = allTypes[0].get(); |
| |
| // Create reflection type, if we have the necessary annotation info |
| |
| // Extract the `struct` that wraps element type of the buffer resource |
| Type *Ty = R.GetHLSLType()->getPointerElementType(); |
| SmallVector<unsigned, 4> arrayDims; |
| Ty = dxilutil::StripArrayTypes(Ty, &arrayDims); |
| for (unsigned i = 0; i < arrayDims.size(); ++i) { |
| m_ReflectionName += "[0]"; |
| } |
| m_Desc.Name = m_ReflectionName.c_str(); |
| StructType *ST = cast<StructType>(Ty); |
| |
| // Look up struct type annotation on the element type |
| DxilTypeSystem &typeSys = M.GetTypeSystem(); |
| DxilStructAnnotation *annotation = |
| typeSys.GetStructAnnotation(cast<StructType>(ST)); |
| |
| // Dxil from dxbc doesn't have annotation. |
| if (annotation) { |
| // Actually create the reflection type. |
| pVarType = new CShaderReflectionType(); |
| allTypes.push_back(std::unique_ptr<CShaderReflectionType>(pVarType)); |
| |
| // The user-visible element type is the first field of the wrapepr `struct` |
| Type *fieldType = ST->getElementType(0); |
| DxilFieldAnnotation &fieldAnnotation = annotation->GetFieldAnnotation(0); |
| |
| pVarType->Initialize(M, fieldType, fieldAnnotation, 0, allTypes, false); |
| } |
| |
| BYTE *pDefaultValue = nullptr; |
| Var.Initialize(this, &VarDesc, pVarType, pDefaultValue); |
| m_Variables.push_back(Var); |
| |
| m_Desc.Size = VarDesc.Size; |
| } |
| |
| void CShaderReflectionConstantBuffer::InitializeTBuffer( |
| DxilModule &M, DxilResource &R, |
| std::vector<std::unique_ptr<CShaderReflectionType>> &allTypes, |
| bool bUsageInMetadata) { |
| ZeroMemory(&m_Desc, sizeof(m_Desc)); |
| m_ReflectionName = R.GetGlobalName(); |
| m_Desc.Type = D3D11_CT_TBUFFER; |
| m_Desc.uFlags = 0; |
| |
| Type *Ty = R.GetHLSLType()->getPointerElementType(); |
| |
| DxilTypeSystem &typeSys = M.GetTypeSystem(); |
| StructType *ST = cast<StructType>(Ty); |
| DxilStructAnnotation *annotation = |
| typeSys.GetStructAnnotation(cast<StructType>(ST)); |
| // Dxil from dxbc doesn't have annotation. |
| if (!annotation) |
| return; |
| |
| m_Desc.Name = m_ReflectionName.c_str(); |
| m_Desc.Variables = ST->getNumContainedTypes(); |
| |
| // If only one member, it's used if it's here. |
| bool bAllUsed = ST->getNumContainedTypes() < 2; |
| bAllUsed |= !bUsageInMetadata; // Will update in SetCBufferUsage. |
| |
| for (unsigned i = 0; i < ST->getNumContainedTypes(); ++i) { |
| DxilFieldAnnotation &fieldAnnotation = annotation->GetFieldAnnotation(i); |
| |
| D3D12_SHADER_VARIABLE_DESC VarDesc; |
| ZeroMemory(&VarDesc, sizeof(VarDesc)); |
| VarDesc.uFlags = |
| (bAllUsed || fieldAnnotation.IsCBVarUsed()) ? D3D_SVF_USED : 0; |
| CShaderReflectionVariable Var; |
| // Create reflection type. |
| CShaderReflectionType *pVarType = new CShaderReflectionType(); |
| allTypes.push_back(std::unique_ptr<CShaderReflectionType>(pVarType)); |
| pVarType->Initialize(M, ST->getContainedType(i), fieldAnnotation, |
| fieldAnnotation.GetCBufferOffset(), allTypes, true); |
| |
| BYTE *pDefaultValue = nullptr; |
| |
| VarDesc.Name = fieldAnnotation.GetFieldName().c_str(); |
| VarDesc.StartOffset = fieldAnnotation.GetCBufferOffset(); |
| VarDesc.Size = pVarType->GetCBufferSize(); |
| VarDesc.StartTexture = UINT_MAX; |
| VarDesc.StartSampler = UINT_MAX; |
| Var.Initialize(this, &VarDesc, pVarType, pDefaultValue); |
| m_Variables.push_back(Var); |
| |
| m_Desc.Size = std::max(m_Desc.Size, VarDesc.StartOffset + VarDesc.Size); |
| } |
| m_Desc.Size = |
| (m_Desc.Size + 0x0f) & ~(0x0f); // Round up to 16 bytes for reflection. |
| } |
| |
| HRESULT |
| CShaderReflectionConstantBuffer::GetDesc(D3D12_SHADER_BUFFER_DESC *pDesc) { |
| if (!pDesc) |
| return E_POINTER; |
| memcpy(pDesc, &m_Desc, sizeof(m_Desc)); |
| return S_OK; |
| } |
| |
| ID3D12ShaderReflectionVariable * |
| CShaderReflectionConstantBuffer::GetVariableByIndex(UINT Index) { |
| if (Index >= m_Variables.size()) { |
| return &g_InvalidSRVariable; |
| } |
| |
| return &m_Variables[Index]; |
| } |
| |
| ID3D12ShaderReflectionVariable * |
| CShaderReflectionConstantBuffer::GetVariableByName(LPCSTR Name) { |
| UINT index; |
| |
| if (NULL == Name) { |
| return &g_InvalidSRVariable; |
| } |
| |
| for (index = 0; index < m_Variables.size(); ++index) { |
| if (0 == strcmp(m_Variables[index].GetName(), Name)) { |
| return &m_Variables[index]; |
| } |
| } |
| |
| return &g_InvalidSRVariable; |
| } |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| // DxilShaderReflection implementation. // |
| |
| static DxilResource *DxilResourceFromBase(DxilResourceBase *RB) { |
| DxilResourceBase::Class C = RB->GetClass(); |
| if (C == DXIL::ResourceClass::UAV || C == DXIL::ResourceClass::SRV) |
| return (DxilResource *)RB; |
| return nullptr; |
| } |
| |
| static D3D_SHADER_INPUT_TYPE ResourceToShaderInputType(DxilResourceBase *RB) { |
| DxilResource *R = DxilResourceFromBase(RB); |
| bool isUAV = RB->GetClass() == DxilResourceBase::Class::UAV; |
| switch (RB->GetKind()) { |
| case DxilResource::Kind::CBuffer: |
| return D3D_SIT_CBUFFER; |
| case DxilResource::Kind::Sampler: |
| return D3D_SIT_SAMPLER; |
| case DxilResource::Kind::RawBuffer: |
| return isUAV ? D3D_SIT_UAV_RWBYTEADDRESS : D3D_SIT_BYTEADDRESS; |
| case DxilResource::Kind::StructuredBuffer: { |
| if (!isUAV) |
| return D3D_SIT_STRUCTURED; |
| // TODO: D3D_SIT_UAV_CONSUME_STRUCTURED, D3D_SIT_UAV_APPEND_STRUCTURED? |
| if (R->HasCounter()) |
| return D3D_SIT_UAV_RWSTRUCTURED_WITH_COUNTER; |
| return D3D_SIT_UAV_RWSTRUCTURED; |
| } |
| case DxilResource::Kind::TBuffer: |
| return D3D_SIT_TBUFFER; |
| case DxilResource::Kind::TypedBuffer: |
| case DxilResource::Kind::Texture1D: |
| case DxilResource::Kind::Texture1DArray: |
| case DxilResource::Kind::Texture2D: |
| case DxilResource::Kind::Texture2DArray: |
| case DxilResource::Kind::Texture2DMS: |
| case DxilResource::Kind::Texture2DMSArray: |
| case DxilResource::Kind::Texture3D: |
| case DxilResource::Kind::TextureCube: |
| case DxilResource::Kind::TextureCubeArray: |
| return isUAV ? D3D_SIT_UAV_RWTYPED : D3D_SIT_TEXTURE; |
| case DxilResource::Kind::RTAccelerationStructure: |
| return (D3D_SHADER_INPUT_TYPE)(D3D_SIT_UAV_RWSTRUCTURED_WITH_COUNTER + |
| 1); // D3D_SIT_RTACCELERATIONSTRUCTURE |
| case DxilResource::Kind::FeedbackTexture2D: |
| case DxilResource::Kind::FeedbackTexture2DArray: |
| return (D3D_SHADER_INPUT_TYPE)(D3D_SIT_UAV_RWSTRUCTURED_WITH_COUNTER + |
| 2); // D3D_SIT_UAV_FEEDBACKTEXTURE |
| default: |
| return (D3D_SHADER_INPUT_TYPE)-1; |
| } |
| } |
| |
| static D3D_RESOURCE_RETURN_TYPE ResourceToReturnType(DxilResourceBase *RB) { |
| DxilResource *R = DxilResourceFromBase(RB); |
| if (R != nullptr && !R->IsTBuffer()) { |
| CompType CT = R->GetCompType(); |
| if (CT.GetKind() == CompType::Kind::F64) |
| return D3D_RETURN_TYPE_DOUBLE; |
| if (CT.IsUNorm()) |
| return D3D_RETURN_TYPE_UNORM; |
| if (CT.IsSNorm()) |
| return D3D_RETURN_TYPE_SNORM; |
| if (CT.IsSIntTy()) |
| return D3D_RETURN_TYPE_SINT; |
| if (CT.IsUIntTy()) |
| return D3D_RETURN_TYPE_UINT; |
| if (CT.IsFloatTy()) |
| return D3D_RETURN_TYPE_FLOAT; |
| |
| // D3D_RETURN_TYPE_CONTINUED: Return type is a multiple-dword type, such as |
| // a double or uint64, and the component is continued from the previous |
| // component that was declared. The first component represents the lower |
| // bits. |
| return D3D_RETURN_TYPE_MIXED; |
| } |
| |
| return (D3D_RESOURCE_RETURN_TYPE)0; |
| } |
| |
| static D3D_SRV_DIMENSION ResourceToDimension(DxilResourceBase *RB) { |
| switch (RB->GetKind()) { |
| case DxilResource::Kind::StructuredBuffer: |
| case DxilResource::Kind::TypedBuffer: |
| return D3D_SRV_DIMENSION_BUFFER; |
| case DxilResource::Kind::TBuffer: |
| return D3D_SRV_DIMENSION_UNKNOWN; // Fxc returns this |
| case DxilResource::Kind::Texture1D: |
| return D3D_SRV_DIMENSION_TEXTURE1D; |
| case DxilResource::Kind::Texture1DArray: |
| return D3D_SRV_DIMENSION_TEXTURE1DARRAY; |
| case DxilResource::Kind::Texture2D: |
| case DxilResource::Kind::FeedbackTexture2D: |
| return D3D_SRV_DIMENSION_TEXTURE2D; |
| case DxilResource::Kind::Texture2DArray: |
| case DxilResource::Kind::FeedbackTexture2DArray: |
| return D3D_SRV_DIMENSION_TEXTURE2DARRAY; |
| case DxilResource::Kind::Texture2DMS: |
| return D3D_SRV_DIMENSION_TEXTURE2DMS; |
| case DxilResource::Kind::Texture2DMSArray: |
| return D3D_SRV_DIMENSION_TEXTURE2DMSARRAY; |
| case DxilResource::Kind::Texture3D: |
| return D3D_SRV_DIMENSION_TEXTURE3D; |
| case DxilResource::Kind::TextureCube: |
| return D3D_SRV_DIMENSION_TEXTURECUBE; |
| case DxilResource::Kind::TextureCubeArray: |
| return D3D_SRV_DIMENSION_TEXTURECUBEARRAY; |
| case DxilResource::Kind::RawBuffer: |
| return D3D11_SRV_DIMENSION_BUFFER; // D3D11_SRV_DIMENSION_BUFFEREX? |
| default: |
| return D3D_SRV_DIMENSION_UNKNOWN; |
| } |
| } |
| |
| static UINT ResourceToFlags(DxilResourceBase *RB) { |
| if (RB->GetClass() == DXIL::ResourceClass::CBuffer) |
| return D3D_SIF_USERPACKED; |
| UINT result = 0; |
| DxilResource *R = DxilResourceFromBase(RB); |
| if (R != nullptr && |
| (R->IsAnyTexture() || R->GetKind() == DXIL::ResourceKind::TypedBuffer)) { |
| llvm::Type *RetTy = R->GetRetType(); |
| if (VectorType *VT = dyn_cast<VectorType>(RetTy)) { |
| unsigned vecSize = VT->getNumElements(); |
| switch (vecSize) { |
| case 4: |
| result |= D3D_SIF_TEXTURE_COMPONENTS; |
| break; |
| case 3: |
| result |= D3D_SIF_TEXTURE_COMPONENT_1; |
| break; |
| case 2: |
| result |= D3D_SIF_TEXTURE_COMPONENT_0; |
| break; |
| } |
| } |
| } else if (R && R->IsTBuffer()) { |
| return D3D_SIF_USERPACKED; |
| } else if (RB->GetClass() == DXIL::ResourceClass::Sampler) { |
| DxilSampler *S = static_cast<DxilSampler *>(RB); |
| if (S->GetSamplerKind() == DXIL::SamplerKind::Comparison) |
| result |= D3D_SIF_COMPARISON_SAMPLER; |
| } |
| return result; |
| } |
| |
| void DxilModuleReflection::CreateReflectionObjectForResource( |
| DxilResourceBase *RB) { |
| DxilResourceBase::Class C = RB->GetClass(); |
| DxilResource *R = |
| (C == DXIL::ResourceClass::UAV || C == DXIL::ResourceClass::SRV) |
| ? (DxilResource *)RB |
| : nullptr; |
| D3D12_SHADER_INPUT_BIND_DESC inputBind; |
| ZeroMemory(&inputBind, sizeof(inputBind)); |
| inputBind.BindCount = RB->GetRangeSize(); |
| // FXC Bug: For Unbounded range, CBuffers say bind count is UINT_MAX, but all |
| // others report 0! |
| if (RB->GetRangeSize() == UINT_MAX && C != DXIL::ResourceClass::CBuffer) |
| inputBind.BindCount = 0; |
| inputBind.BindPoint = RB->GetLowerBound(); |
| inputBind.Dimension = ResourceToDimension(RB); |
| inputBind.Name = RB->GetGlobalName().c_str(); |
| inputBind.Type = ResourceToShaderInputType(RB); |
| if (R == nullptr) { |
| inputBind.NumSamples = 0; |
| } else { |
| inputBind.NumSamples = R->GetSampleCount(); |
| if (inputBind.NumSamples == 0) { |
| if (R->IsStructuredBuffer()) { |
| inputBind.NumSamples = CalcResTypeSize(*m_pDxilModule, *R); |
| } else if (!R->IsRawBuffer() && !R->IsTBuffer() && |
| R->GetKind() != DXIL::ResourceKind::Texture2DMS && |
| R->GetKind() != DXIL::ResourceKind::Texture2DMSArray) { |
| inputBind.NumSamples = 0xFFFFFFFF; |
| } |
| } |
| } |
| inputBind.ReturnType = ResourceToReturnType(RB); |
| inputBind.Space = RB->GetSpaceID(); |
| inputBind.uFlags = ResourceToFlags(RB); |
| inputBind.uID = RB->GetID(); |
| m_Resources.push_back(inputBind); |
| } |
| |
| // Find the imm offset part from a value. |
| // It must exist unless offset is 0. |
| static unsigned GetCBOffset(Value *V) { |
| if (ConstantInt *Imm = dyn_cast<ConstantInt>(V)) |
| return Imm->getLimitedValue(); |
| else if (isa<UnaryInstruction>(V)) { |
| return 0; |
| } else if (BinaryOperator *BO = dyn_cast<BinaryOperator>(V)) { |
| switch (BO->getOpcode()) { |
| case Instruction::Add: { |
| unsigned left = GetCBOffset(BO->getOperand(0)); |
| unsigned right = GetCBOffset(BO->getOperand(1)); |
| return left + right; |
| } break; |
| case Instruction::Or: { |
| unsigned left = GetCBOffset(BO->getOperand(0)); |
| unsigned right = GetCBOffset(BO->getOperand(1)); |
| return left | right; |
| } break; |
| default: |
| return 0; |
| } |
| } else { |
| return 0; |
| } |
| } |
| |
| static unsigned GetOffsetForCBExtractValue(ExtractValueInst *EV, |
| bool bMinPrecision) { |
| DXASSERT(EV->getNumIndices() == 1, |
| "otherwise, unexpected indices/type for extractvalue"); |
| unsigned typeSize = 4; |
| unsigned bits = EV->getType()->getScalarSizeInBits(); |
| if (bits == 64) |
| typeSize = 8; |
| else if (bits == 16 && !bMinPrecision) |
| typeSize = 2; |
| return (EV->getIndices().front() * typeSize); |
| } |
| |
| static void CollectInPhiChain(PHINode *cbUser, std::vector<unsigned> &cbufUsage, |
| unsigned offset, |
| std::unordered_set<Value *> &userSet, |
| bool bMinPrecision) { |
| if (userSet.count(cbUser) > 0) |
| return; |
| |
| userSet.insert(cbUser); |
| for (User *cbU : cbUser->users()) { |
| if (ExtractValueInst *EV = dyn_cast<ExtractValueInst>(cbU)) { |
| cbufUsage.emplace_back(offset + |
| GetOffsetForCBExtractValue(EV, bMinPrecision)); |
| } else { |
| PHINode *phi = cast<PHINode>(cbU); |
| CollectInPhiChain(phi, cbufUsage, offset, userSet, bMinPrecision); |
| } |
| } |
| } |
| |
| static void CollectCBufUsage(Value *cbHandle, std::vector<unsigned> &cbufUsage, |
| bool bMinPrecision) { |
| for (User *U : cbHandle->users()) { |
| CallInst *CI = cast<CallInst>(U); |
| ConstantInt *opcodeV = |
| cast<ConstantInt>(CI->getArgOperand(DXIL::OperandIndex::kOpcodeIdx)); |
| DXIL::OpCode opcode = static_cast<DXIL::OpCode>(opcodeV->getLimitedValue()); |
| if (opcode == DXIL::OpCode::CBufferLoadLegacy) { |
| DxilInst_CBufferLoadLegacy cbload(CI); |
| Value *resIndex = cbload.get_regIndex(); |
| unsigned offset = GetCBOffset(resIndex); |
| // 16 bytes align. |
| offset <<= 4; |
| for (User *cbU : U->users()) { |
| if (ExtractValueInst *EV = dyn_cast<ExtractValueInst>(cbU)) { |
| cbufUsage.emplace_back(offset + |
| GetOffsetForCBExtractValue(EV, bMinPrecision)); |
| } else { |
| PHINode *phi = cast<PHINode>(cbU); |
| std::unordered_set<Value *> userSet; |
| CollectInPhiChain(phi, cbufUsage, offset, userSet, bMinPrecision); |
| } |
| } |
| } else if (opcode == DXIL::OpCode::CBufferLoad) { |
| DxilInst_CBufferLoad cbload(CI); |
| Value *byteOffset = cbload.get_byteOffset(); |
| unsigned offset = GetCBOffset(byteOffset); |
| cbufUsage.emplace_back(offset); |
| } else if (opcode == DXIL::OpCode::AnnotateHandle) { |
| DxilInst_AnnotateHandle annotateHandle(CI); |
| Value *annotatedHandle = annotateHandle.get_res(); |
| CollectCBufUsage(annotatedHandle, cbufUsage, bMinPrecision); |
| } else { |
| // |
| DXASSERT(0, "invalid opcode"); |
| } |
| } |
| } |
| |
| static void SetCBufVarUsage(CShaderReflectionConstantBuffer &cb, |
| std::vector<unsigned> &usage) { |
| D3D12_SHADER_BUFFER_DESC Desc; |
| if (FAILED(cb.GetDesc(&Desc))) |
| return; |
| |
| unsigned size = Desc.Variables; |
| |
| std::sort(usage.begin(), usage.end()); |
| for (unsigned i = 0; i < size; i++) { |
| ID3D12ShaderReflectionVariable *pVar = cb.GetVariableByIndex(i); |
| D3D12_SHADER_VARIABLE_DESC VarDesc; |
| if (FAILED(pVar->GetDesc(&VarDesc))) |
| continue; |
| if (!pVar) |
| continue; |
| |
| unsigned begin = VarDesc.StartOffset; |
| unsigned end = begin + VarDesc.Size; |
| auto beginIt = std::find_if(usage.begin(), usage.end(), |
| [&](unsigned v) { return v >= begin; }); |
| auto endIt = std::find_if(usage.begin(), usage.end(), |
| [&](unsigned v) { return v >= end; }); |
| |
| bool used = beginIt != endIt; |
| // Clear used. |
| if (!used) { |
| CShaderReflectionType *pVarType = |
| (CShaderReflectionType *)pVar->GetType(); |
| BYTE *pDefaultValue = nullptr; |
| |
| VarDesc.uFlags &= ~D3D_SVF_USED; |
| CShaderReflectionVariable *pCVarDesc = (CShaderReflectionVariable *)pVar; |
| pCVarDesc->Initialize(&cb, &VarDesc, pVarType, pDefaultValue); |
| } |
| } |
| } |
| |
| void DxilShaderReflection::SetCBufferUsage() { |
| hlsl::OP *hlslOP = m_pDxilModule->GetOP(); |
| LLVMContext &Ctx = m_pDxilModule->GetCtx(); |
| |
| // Indexes >= cbuffer size from DxilModule are SRV or UAV structured buffers. |
| // We only collect usage for actual cbuffers, so don't go clearing usage on |
| // other buffers. |
| unsigned cbSize = std::min(m_CBs.size(), m_pDxilModule->GetCBuffers().size()); |
| std::vector<std::vector<unsigned>> cbufUsage(cbSize); |
| |
| Function *createHandle = |
| hlslOP->GetOpFunc(DXIL::OpCode::CreateHandle, Type::getVoidTy(Ctx)); |
| |
| if (createHandle->user_empty()) { |
| createHandle->eraseFromParent(); |
| return; |
| } |
| |
| // Find all cb handles. |
| for (User *U : createHandle->users()) { |
| DxilInst_CreateHandle handle(cast<CallInst>(U)); |
| Value *resClass = handle.get_resourceClass(); |
| ConstantInt *immResClass = cast<ConstantInt>(resClass); |
| if (immResClass->getLimitedValue() == |
| (unsigned)DXIL::ResourceClass::CBuffer) { |
| ConstantInt *cbID = cast<ConstantInt>(handle.get_rangeId()); |
| CollectCBufUsage(U, cbufUsage[cbID->getLimitedValue()], |
| m_pDxilModule->GetUseMinPrecision()); |
| } |
| } |
| |
| for (unsigned i = 0; i < cbSize; i++) { |
| SetCBufVarUsage(*m_CBs[i], cbufUsage[i]); |
| } |
| } |
| |
| void DxilModuleReflection::CreateReflectionObjects() { |
| DXASSERT_NOMSG(m_pDxilModule != nullptr); |
| |
| { |
| // Add empty type for when no type info is available, instead of returning |
| // nullptr. |
| DXASSERT_NOMSG(m_Types.empty()); |
| CShaderReflectionType *pEmptyType = new CShaderReflectionType(); |
| m_Types.push_back(std::unique_ptr<CShaderReflectionType>(pEmptyType)); |
| pEmptyType->InitializeEmpty(); |
| } |
| |
| // Create constant buffers, resources and signatures. |
| for (auto &&cb : m_pDxilModule->GetCBuffers()) { |
| std::unique_ptr<CShaderReflectionConstantBuffer> rcb( |
| new CShaderReflectionConstantBuffer()); |
| rcb->Initialize(*m_pDxilModule, *(cb.get()), m_Types, m_bUsageInMetadata); |
| m_CBsByName[rcb->GetName()] = (UINT)m_CBs.size(); |
| m_CBs.emplace_back(std::move(rcb)); |
| } |
| |
| // TODO: add tbuffers into m_CBs |
| for (auto &&uav : m_pDxilModule->GetUAVs()) { |
| if (!DXIL::IsStructuredBuffer(uav->GetKind())) { |
| continue; |
| } |
| std::unique_ptr<CShaderReflectionConstantBuffer> rcb( |
| new CShaderReflectionConstantBuffer()); |
| rcb->InitializeStructuredBuffer(*m_pDxilModule, *(uav.get()), m_Types); |
| m_StructuredBufferCBsByName[rcb->GetName()] = (UINT)m_CBs.size(); |
| m_CBs.emplace_back(std::move(rcb)); |
| } |
| for (auto &&srv : m_pDxilModule->GetSRVs()) { |
| if (srv->GetKind() != DxilResource::Kind::StructuredBuffer && |
| srv->GetKind() != DxilResource::Kind::TBuffer) { |
| continue; |
| } |
| std::unique_ptr<CShaderReflectionConstantBuffer> rcb( |
| new CShaderReflectionConstantBuffer()); |
| if (srv->GetKind() == DxilResource::Kind::TBuffer) { |
| rcb->InitializeTBuffer(*m_pDxilModule, *(srv.get()), m_Types, |
| m_bUsageInMetadata); |
| m_CBsByName[rcb->GetName()] = (UINT)m_CBs.size(); |
| } else { |
| rcb->InitializeStructuredBuffer(*m_pDxilModule, *(srv.get()), m_Types); |
| m_StructuredBufferCBsByName[rcb->GetName()] = (UINT)m_CBs.size(); |
| } |
| m_CBs.emplace_back(std::move(rcb)); |
| } |
| |
| // Populate all resources. |
| for (auto &&cbRes : m_pDxilModule->GetCBuffers()) { |
| CreateReflectionObjectForResource(cbRes.get()); |
| } |
| for (auto &&samplerRes : m_pDxilModule->GetSamplers()) { |
| CreateReflectionObjectForResource(samplerRes.get()); |
| } |
| for (auto &&srvRes : m_pDxilModule->GetSRVs()) { |
| CreateReflectionObjectForResource(srvRes.get()); |
| } |
| for (auto &&uavRes : m_pDxilModule->GetUAVs()) { |
| CreateReflectionObjectForResource(uavRes.get()); |
| } |
| } |
| |
| static D3D_REGISTER_COMPONENT_TYPE |
| CompTypeToRegisterComponentType(CompType CT) { |
| switch (CT.GetKind()) { |
| case DXIL::ComponentType::F16: |
| case DXIL::ComponentType::F32: |
| return D3D_REGISTER_COMPONENT_FLOAT32; |
| case DXIL::ComponentType::I1: |
| case DXIL::ComponentType::U16: |
| case DXIL::ComponentType::U32: |
| return D3D_REGISTER_COMPONENT_UINT32; |
| case DXIL::ComponentType::I16: |
| case DXIL::ComponentType::I32: |
| return D3D_REGISTER_COMPONENT_SINT32; |
| default: |
| return D3D_REGISTER_COMPONENT_UNKNOWN; |
| } |
| } |
| |
| static D3D_MIN_PRECISION CompTypeToMinPrecision(CompType CT) { |
| switch (CT.GetKind()) { |
| case DXIL::ComponentType::F16: |
| return D3D_MIN_PRECISION_FLOAT_16; |
| case DXIL::ComponentType::I16: |
| return D3D_MIN_PRECISION_SINT_16; |
| case DXIL::ComponentType::U16: |
| return D3D_MIN_PRECISION_UINT_16; |
| default: |
| return D3D_MIN_PRECISION_DEFAULT; |
| } |
| } |
| |
| D3D_NAME SemanticToSystemValueType(const Semantic *S, |
| DXIL::TessellatorDomain domain) { |
| switch (S->GetKind()) { |
| case Semantic::Kind::ClipDistance: |
| return D3D_NAME_CLIP_DISTANCE; |
| case Semantic::Kind::Arbitrary: |
| return D3D_NAME_UNDEFINED; |
| case Semantic::Kind::VertexID: |
| return D3D_NAME_VERTEX_ID; |
| case Semantic::Kind::InstanceID: |
| return D3D_NAME_INSTANCE_ID; |
| case Semantic::Kind::Position: |
| return D3D_NAME_POSITION; |
| case Semantic::Kind::Coverage: |
| return D3D_NAME_COVERAGE; |
| case Semantic::Kind::InnerCoverage: |
| return D3D_NAME_INNER_COVERAGE; |
| case Semantic::Kind::PrimitiveID: |
| return D3D_NAME_PRIMITIVE_ID; |
| case Semantic::Kind::SampleIndex: |
| return D3D_NAME_SAMPLE_INDEX; |
| case Semantic::Kind::IsFrontFace: |
| return D3D_NAME_IS_FRONT_FACE; |
| case Semantic::Kind::RenderTargetArrayIndex: |
| return D3D_NAME_RENDER_TARGET_ARRAY_INDEX; |
| case Semantic::Kind::ViewPortArrayIndex: |
| return D3D_NAME_VIEWPORT_ARRAY_INDEX; |
| case Semantic::Kind::CullDistance: |
| return D3D_NAME_CULL_DISTANCE; |
| case Semantic::Kind::Target: |
| return D3D_NAME_TARGET; |
| case Semantic::Kind::Depth: |
| return D3D_NAME_DEPTH; |
| case Semantic::Kind::DepthLessEqual: |
| return D3D_NAME_DEPTH_LESS_EQUAL; |
| case Semantic::Kind::DepthGreaterEqual: |
| return D3D_NAME_DEPTH_GREATER_EQUAL; |
| case Semantic::Kind::StencilRef: |
| return D3D_NAME_STENCIL_REF; |
| case Semantic::Kind::TessFactor: { |
| switch (domain) { |
| case DXIL::TessellatorDomain::IsoLine: |
| return D3D_NAME_FINAL_LINE_DETAIL_TESSFACTOR; |
| case DXIL::TessellatorDomain::Tri: |
| return D3D_NAME_FINAL_TRI_EDGE_TESSFACTOR; |
| case DXIL::TessellatorDomain::Quad: |
| return D3D_NAME_FINAL_QUAD_EDGE_TESSFACTOR; |
| default: |
| return D3D_NAME_UNDEFINED; |
| } |
| case Semantic::Kind::Barycentrics: |
| return (D3D_NAME)DxilProgramSigSemantic::Barycentrics; |
| case Semantic::Kind::ShadingRate: |
| return (D3D_NAME)DxilProgramSigSemantic::ShadingRate; |
| case Semantic::Kind::CullPrimitive: |
| return (D3D_NAME)DxilProgramSigSemantic::CullPrimitive; |
| } |
| case Semantic::Kind::InsideTessFactor: |
| switch (domain) { |
| case DXIL::TessellatorDomain::Tri: |
| return D3D_NAME_FINAL_TRI_INSIDE_TESSFACTOR; |
| case DXIL::TessellatorDomain::Quad: |
| return D3D_NAME_FINAL_QUAD_INSIDE_TESSFACTOR; |
| default: |
| return D3D_NAME_UNDEFINED; |
| } |
| case Semantic::Kind::DispatchThreadID: |
| case Semantic::Kind::GroupID: |
| case Semantic::Kind::GroupIndex: |
| case Semantic::Kind::GroupThreadID: |
| case Semantic::Kind::DomainLocation: |
| case Semantic::Kind::OutputControlPointID: |
| case Semantic::Kind::GSInstanceID: |
| case Semantic::Kind::Invalid: |
| default: |
| return D3D_NAME_UNDEFINED; |
| } |
| } |
| |
| static uint8_t NegMask(uint8_t V) { |
| V ^= 0xF; |
| return V & 0xF; |
| } |
| |
| void DxilShaderReflection::CreateReflectionObjectsForSignature( |
| const DxilSignature &Sig, |
| std::vector<D3D12_SIGNATURE_PARAMETER_DESC> &Descs) { |
| for (auto &&SigElem : Sig.GetElements()) { |
| D3D12_SIGNATURE_PARAMETER_DESC Desc; |
| Desc.ComponentType = |
| CompTypeToRegisterComponentType(SigElem->GetCompType()); |
| Desc.Mask = SigElem->GetColsAsMask(); |
| Desc.MinPrecision = CompTypeToMinPrecision(SigElem->GetCompType()); |
| if (m_bUsageInMetadata) { |
| unsigned UsageMask = SigElem->GetUsageMask(); |
| if (SigElem->IsAllocated()) |
| UsageMask <<= SigElem->GetStartCol(); |
| Desc.ReadWriteMask = Sig.IsInput() ? UsageMask : NegMask(UsageMask); |
| } else { |
| Desc.ReadWriteMask = |
| Sig.IsInput() |
| ? 0 |
| : Desc.Mask; // Start with output-never-written/input-never-read. |
| } |
| Desc.Register = SigElem->GetStartRow(); |
| Desc.Stream = SigElem->GetOutputStream(); |
| Desc.SystemValueType = SemanticToSystemValueType( |
| SigElem->GetSemantic(), m_pDxilModule->GetTessellatorDomain()); |
| Desc.SemanticName = SigElem->GetName(); |
| if (!SigElem->GetSemantic()->IsArbitrary()) |
| Desc.SemanticName = CreateUpperCase(Desc.SemanticName); |
| |
| const std::vector<unsigned> &indexVec = SigElem->GetSemanticIndexVec(); |
| for (unsigned semIdx = 0; semIdx < indexVec.size(); ++semIdx) { |
| Desc.SemanticIndex = indexVec[semIdx]; |
| if (Desc.SystemValueType == D3D_NAME_FINAL_LINE_DETAIL_TESSFACTOR && |
| Desc.SemanticIndex == 1) |
| Desc.SystemValueType = D3D_NAME_FINAL_LINE_DETAIL_TESSFACTOR; |
| Descs.push_back(Desc); |
| // When indexVec.size() > 1, subsequent indices need incremented register |
| // index |
| Desc.Register += 1; |
| } |
| } |
| } |
| |
| LPCSTR DxilShaderReflection::CreateUpperCase(LPCSTR pValue) { |
| // Restricted only to [a-z] ASCII. |
| LPCSTR pCursor = pValue; |
| while (*pCursor != '\0') { |
| if ('a' <= *pCursor && *pCursor <= 'z') { |
| break; |
| } |
| ++pCursor; |
| } |
| if (*pCursor == '\0') |
| return pValue; |
| |
| std::unique_ptr<char[]> pUpperStr = |
| llvm::make_unique<char[]>(strlen(pValue) + 1); |
| char *pWrite = pUpperStr.get(); |
| pCursor = pValue; |
| for (;;) { |
| *pWrite = *pCursor; |
| if ('a' <= *pWrite && *pWrite <= 'z') { |
| *pWrite += ('A' - 'a'); |
| } |
| if (*pWrite == '\0') |
| break; |
| ++pWrite; |
| ++pCursor; |
| } |
| m_UpperCaseNames.push_back(std::move(pUpperStr)); |
| return m_UpperCaseNames.back().get(); |
| } |
| |
| HRESULT DxilModuleReflection::LoadRDAT(const DxilPartHeader *pPart) { |
| if (pPart) { |
| IFRBOOL(m_RDAT.InitFromRDAT(GetDxilPartData(pPart), pPart->PartSize), |
| DXC_E_CONTAINER_INVALID); |
| } |
| return S_OK; |
| } |
| |
| HRESULT DxilModuleReflection::LoadProgramHeader( |
| const DxilProgramHeader *pProgramHeader) { |
| try { |
| const char *pBitcode; |
| uint32_t bitcodeLength; |
| GetDxilProgramBitcode((const DxilProgramHeader *)pProgramHeader, &pBitcode, |
| &bitcodeLength); |
| std::unique_ptr<MemoryBuffer> pMemBuffer = |
| MemoryBuffer::getMemBufferCopy(StringRef(pBitcode, bitcodeLength)); |
| bool bBitcodeLoadError = false; |
| auto errorHandler = [&bBitcodeLoadError](const DiagnosticInfo &diagInfo) { |
| bBitcodeLoadError |= diagInfo.getSeverity() == DS_Error; |
| }; |
| #if 0 // We materialize eagerly, because we'll need to walk instructions to look |
| // for usage information. |
| ErrorOr<std::unique_ptr<Module>> mod = |
| getLazyBitcodeModule(std::move(pMemBuffer), Context, errorHandler); |
| #else |
| ErrorOr<std::unique_ptr<Module>> mod = |
| parseBitcodeFile(pMemBuffer->getMemBufferRef(), Context, errorHandler); |
| #endif |
| if (!mod || bBitcodeLoadError) { |
| return E_INVALIDARG; |
| } |
| std::swap(m_pModule, mod.get()); |
| m_pDxilModule = &m_pModule->GetOrCreateDxilModule(); |
| |
| unsigned ValMajor, ValMinor; |
| m_pDxilModule->GetValidatorVersion(ValMajor, ValMinor); |
| m_bUsageInMetadata = |
| hlsl::DXIL::CompareVersions(ValMajor, ValMinor, 1, 5) >= 0; |
| |
| CreateReflectionObjects(); |
| return S_OK; |
| } |
| CATCH_CPP_RETURN_HRESULT(); |
| } |
| |
| HRESULT DxilShaderReflection::Load(const DxilProgramHeader *pProgramHeader, |
| const DxilPartHeader *pRDATPart) { |
| IFR(LoadRDAT(pRDATPart)); |
| IFR(LoadProgramHeader(pProgramHeader)); |
| |
| try { |
| // Set cbuf usage. |
| if (!m_bUsageInMetadata) |
| SetCBufferUsage(); |
| |
| // Populate input/output/patch constant signatures. |
| CreateReflectionObjectsForSignature(m_pDxilModule->GetInputSignature(), |
| m_InputSignature); |
| CreateReflectionObjectsForSignature(m_pDxilModule->GetOutputSignature(), |
| m_OutputSignature); |
| CreateReflectionObjectsForSignature( |
| m_pDxilModule->GetPatchConstOrPrimSignature(), |
| m_PatchConstantSignature); |
| if (!m_bUsageInMetadata) |
| MarkUsedSignatureElements(); |
| |
| InitDesc(); |
| |
| return S_OK; |
| } |
| CATCH_CPP_RETURN_HRESULT(); |
| } |
| |
| HRESULT |
| DxilShaderReflection::GetDesc(D3D12_SHADER_DESC *pDesc) noexcept { |
| if (nullptr == pDesc) |
| return E_POINTER; |
| memcpy(pDesc, &m_Desc, sizeof(D3D12_SHADER_DESC)); |
| return S_OK; |
| } |
| |
| static bool GetUnsignedVal(Value *V, uint32_t *pValue) { |
| ConstantInt *CI = dyn_cast<ConstantInt>(V); |
| if (!CI) |
| return false; |
| uint64_t u = CI->getZExtValue(); |
| if (u > UINT32_MAX) |
| return false; |
| *pValue = (uint32_t)u; |
| return true; |
| } |
| |
| void DxilShaderReflection::MarkUsedSignatureElements() { |
| Function *F = m_pDxilModule->GetEntryFunction(); |
| if (F == nullptr) { |
| F = m_pDxilModule->GetPatchConstantFunction(); |
| } |
| DXASSERT(F != nullptr, "else module load should have failed"); |
| // For every loadInput/storeOutput, update the corresponding ReadWriteMask. |
| // F is a pointer to a Function instance |
| unsigned elementCount = m_InputSignature.size() + m_OutputSignature.size() + |
| m_PatchConstantSignature.size(); |
| unsigned markedElementCount = 0; |
| for (inst_iterator I = inst_begin(F), E = inst_end(F); I != E; ++I) { |
| DxilInst_LoadInput LI(&*I); |
| DxilInst_StoreOutput SO(&*I); |
| DxilInst_LoadPatchConstant LPC(&*I); |
| DxilInst_StorePatchConstant SPC(&*I); |
| DxilInst_StoreVertexOutput SVO(&*I); |
| DxilInst_StorePrimitiveOutput SPO(&*I); |
| std::vector<D3D12_SIGNATURE_PARAMETER_DESC> *pDescs = nullptr; |
| const DxilSignature *pSig; |
| uint32_t col, row, sigId; |
| if (LI) { |
| if (!GetUnsignedVal(LI.get_inputSigId(), &sigId)) |
| continue; |
| if (!GetUnsignedVal(LI.get_colIndex(), &col)) |
| continue; |
| GetUnsignedVal(LI.get_rowIndex(), &row); |
| pDescs = &m_InputSignature; |
| pSig = &m_pDxilModule->GetInputSignature(); |
| } else if (SO) { |
| if (!GetUnsignedVal(SO.get_outputSigId(), &sigId)) |
| continue; |
| if (!GetUnsignedVal(SO.get_colIndex(), &col)) |
| continue; |
| GetUnsignedVal(SO.get_rowIndex(), &row); |
| pDescs = &m_OutputSignature; |
| pSig = &m_pDxilModule->GetOutputSignature(); |
| } else if (SPC) { |
| if (!GetUnsignedVal(SPC.get_outputSigID(), &sigId)) |
| continue; |
| if (!GetUnsignedVal(SPC.get_col(), &col)) |
| continue; |
| GetUnsignedVal(SPC.get_row(), &row); |
| pDescs = &m_PatchConstantSignature; |
| pSig = &m_pDxilModule->GetPatchConstOrPrimSignature(); |
| } else if (LPC) { |
| if (!GetUnsignedVal(LPC.get_inputSigId(), &sigId)) |
| continue; |
| if (!GetUnsignedVal(LPC.get_col(), &col)) |
| continue; |
| GetUnsignedVal(LPC.get_row(), &row); |
| pDescs = &m_PatchConstantSignature; |
| pSig = &m_pDxilModule->GetPatchConstOrPrimSignature(); |
| } else if (SVO) { |
| if (!GetUnsignedVal(SVO.get_outputSigId(), &sigId)) |
| continue; |
| if (!GetUnsignedVal(SVO.get_colIndex(), &col)) |
| continue; |
| GetUnsignedVal(SVO.get_rowIndex(), &row); |
| pSig = &m_pDxilModule->GetOutputSignature(); |
| } else if (SPO) { |
| if (!GetUnsignedVal(SPO.get_outputSigId(), &sigId)) |
| continue; |
| if (!GetUnsignedVal(SPO.get_colIndex(), &col)) |
| continue; |
| GetUnsignedVal(SPO.get_rowIndex(), &row); |
| pSig = &m_pDxilModule->GetPatchConstOrPrimSignature(); |
| } else { |
| continue; |
| } |
| |
| if (sigId >= pDescs->size()) |
| continue; |
| |
| D3D12_SIGNATURE_PARAMETER_DESC *pDesc = &(*pDescs)[sigId]; |
| // Consider being more fine-grained about masks. |
| // We report sometimes-read on input as always-read. |
| unsigned UsedMask = pSig->IsInput() ? pDesc->Mask : NegMask(pDesc->Mask); |
| if (pDesc->ReadWriteMask == UsedMask) |
| continue; |
| pDesc->ReadWriteMask = UsedMask; |
| ++markedElementCount; |
| if (markedElementCount == elementCount) |
| return; |
| } |
| } |
| |
| void DxilShaderReflection::InitDesc() { |
| D3D12_SHADER_DESC *pDesc = &m_Desc; |
| |
| const DxilModule &M = *m_pDxilModule; |
| const ShaderModel *pSM = M.GetShaderModel(); |
| |
| pDesc->Version = |
| EncodeVersion(pSM->GetKind(), pSM->GetMajor(), pSM->GetMinor()); |
| |
| Module *pModule = M.GetModule(); |
| if (NamedMDNode *pIdentMD = pModule->getNamedMetadata("llvm.ident")) { |
| if (pIdentMD->getNumOperands()) { |
| if (MDNode *pMDList = pIdentMD->getOperand(0)) { |
| if (pMDList->getNumOperands()) { |
| if (MDString *pMDString = |
| dyn_cast_or_null<MDString>(pMDList->getOperand(0))) { |
| pDesc->Creator = pMDString->getString().data(); |
| } |
| } |
| } |
| } |
| } |
| |
| // Unset: UINT Flags; // Shader |
| // compilation/parse flags |
| |
| pDesc->ConstantBuffers = m_CBs.size(); |
| pDesc->BoundResources = m_Resources.size(); |
| pDesc->InputParameters = m_InputSignature.size(); |
| pDesc->OutputParameters = m_OutputSignature.size(); |
| pDesc->PatchConstantParameters = m_PatchConstantSignature.size(); |
| |
| pDesc->GSOutputTopology = |
| (D3D_PRIMITIVE_TOPOLOGY)M.GetStreamPrimitiveTopology(); |
| pDesc->GSMaxOutputVertexCount = M.GetMaxVertexCount(); |
| |
| if (pSM->IsHS()) |
| pDesc->InputPrimitive = |
| (D3D_PRIMITIVE)(D3D_PRIMITIVE_1_CONTROL_POINT_PATCH + |
| M.GetInputControlPointCount() - 1); |
| else |
| pDesc->InputPrimitive = (D3D_PRIMITIVE)M.GetInputPrimitive(); |
| |
| pDesc->cGSInstanceCount = M.GetGSInstanceCount(); |
| |
| if (pSM->IsHS()) |
| pDesc->cControlPoints = M.GetOutputControlPointCount(); |
| else if (pSM->IsDS()) |
| pDesc->cControlPoints = M.GetInputControlPointCount(); |
| |
| pDesc->HSOutputPrimitive = |
| (D3D_TESSELLATOR_OUTPUT_PRIMITIVE)M.GetTessellatorOutputPrimitive(); |
| pDesc->HSPartitioning = |
| (D3D_TESSELLATOR_PARTITIONING)M.GetTessellatorPartitioning(); |
| pDesc->TessellatorDomain = (D3D_TESSELLATOR_DOMAIN)M.GetTessellatorDomain(); |
| |
| // Instruction counts only roughly track some fxc counters |
| DxilCounters counters = {}; |
| m_pDxilModule->LoadDxilCounters(counters); |
| |
| // UINT InstructionCount; // Num llvm instructions in all functions |
| // UINT TempArrayCount; // Number of bytes used in arrays (alloca + static |
| // global) |
| // UINT DynamicFlowControlCount; // Number of branches with more than one |
| // successor for now |
| // UINT ArrayInstructionCount; // number of load/store on arrays for now |
| pDesc->InstructionCount = counters.insts; |
| pDesc->TempArrayCount = counters.AllArrayBytes(); |
| pDesc->DynamicFlowControlCount = counters.branches; |
| pDesc->ArrayInstructionCount = counters.AllArrayAccesses(); |
| |
| // UINT FloatInstructionCount; // Number of floating point arithmetic |
| // instructions used |
| // UINT IntInstructionCount; // Number of signed integer arithmetic |
| // instructions used |
| // UINT UintInstructionCount; // Number of unsigned integer arithmetic |
| // instructions used |
| pDesc->FloatInstructionCount = counters.floats; |
| pDesc->IntInstructionCount = counters.ints; |
| pDesc->UintInstructionCount = counters.uints; |
| |
| // UINT TextureNormalInstructions; // Number of non-categorized texture |
| // instructions |
| // UINT TextureLoadInstructions; // Number of texture load |
| // instructions |
| // UINT TextureCompInstructions; // Number of texture |
| // comparison instructions |
| // UINT TextureBiasInstructions; // Number of |
| // texture bias instructions |
| // UINT TextureGradientInstructions; // Number of |
| // texture gradient instructions |
| pDesc->TextureNormalInstructions = counters.tex_norm; |
| pDesc->TextureLoadInstructions = counters.tex_load; |
| pDesc->TextureCompInstructions = counters.tex_cmp; |
| pDesc->TextureBiasInstructions = counters.tex_bias; |
| pDesc->TextureGradientInstructions = counters.tex_grad; |
| |
| // UINT CutInstructionCount; // Number of cut instructions used |
| // UINT EmitInstructionCount; // Number of emit instructions used |
| pDesc->CutInstructionCount = counters.gs_cut; |
| pDesc->EmitInstructionCount = counters.gs_emit; |
| |
| // UINT cBarrierInstructions; // Number of barrier instructions in a |
| // compute shader |
| // UINT cInterlockedInstructions; // Number of |
| // interlocked instructions |
| // UINT cTextureStoreInstructions; // Number of |
| // texture writes |
| pDesc->cBarrierInstructions = counters.barrier; |
| pDesc->cInterlockedInstructions = counters.atomic; |
| pDesc->cTextureStoreInstructions = counters.tex_store; |
| |
| // Unset: UINT TempRegisterCount; // Don't know how to map this for SSA (not |
| // going to do reg allocation here) |
| // Unset: UINT DefCount; // Not sure what to map this to |
| // Unset: UINT DclCount; // Number of declarations (input + output) |
| // TODO: map to used input + output signature rows? |
| // Unset: UINT StaticFlowControlCount; // Number of static flow control |
| // instructions used This used to map to flow control using special |
| // int/bool constant registers in DX9. |
| // Unset: UINT MacroInstructionCount; // Number of macro instructions used |
| // Macro instructions are a <= DX9 concept. |
| } |
| |
| ID3D12ShaderReflectionConstantBuffer * |
| DxilShaderReflection::GetConstantBufferByIndex(UINT Index) noexcept { |
| return DxilModuleReflection::_GetConstantBufferByIndex(Index); |
| } |
| ID3D12ShaderReflectionConstantBuffer * |
| DxilModuleReflection::_GetConstantBufferByIndex(UINT Index) { |
| if (Index >= m_CBs.size()) { |
| return &g_InvalidSRConstantBuffer; |
| } |
| return m_CBs[Index].get(); |
| } |
| |
| ID3D12ShaderReflectionConstantBuffer * |
| DxilShaderReflection::GetConstantBufferByName(LPCSTR Name) noexcept { |
| return DxilModuleReflection::_GetConstantBufferByName(Name); |
| } |
| ID3D12ShaderReflectionConstantBuffer * |
| DxilModuleReflection::_GetConstantBufferByName(LPCSTR Name) { |
| if (!Name) { |
| return &g_InvalidSRConstantBuffer; |
| } |
| |
| size_t index = m_CBs.size(); |
| auto it = m_CBsByName.find(Name); |
| if (it != m_CBsByName.end()) { |
| index = it->second; |
| } else { |
| it = m_StructuredBufferCBsByName.find(Name); |
| if (it != m_StructuredBufferCBsByName.end()) { |
| index = it->second; |
| } |
| } |
| if (index < m_CBs.size()) { |
| return m_CBs[index].get(); |
| } |
| |
| return &g_InvalidSRConstantBuffer; |
| } |
| |
| HRESULT DxilShaderReflection::GetResourceBindingDesc( |
| UINT ResourceIndex, D3D12_SHADER_INPUT_BIND_DESC *pDesc) noexcept { |
| return DxilModuleReflection::_GetResourceBindingDesc(ResourceIndex, pDesc, |
| m_PublicAPI); |
| } |
| HRESULT DxilModuleReflection::_GetResourceBindingDesc( |
| UINT ResourceIndex, D3D12_SHADER_INPUT_BIND_DESC *pDesc, PublicAPI api) { |
| IFRBOOL(pDesc != nullptr, E_INVALIDARG); |
| IFRBOOL(ResourceIndex < m_Resources.size(), E_INVALIDARG); |
| if (api != PublicAPI::D3D12) { |
| memcpy(pDesc, &m_Resources[ResourceIndex], |
| sizeof(D3D11_SHADER_INPUT_BIND_DESC)); |
| } else { |
| *pDesc = m_Resources[ResourceIndex]; |
| } |
| return S_OK; |
| } |
| |
| HRESULT DxilShaderReflection::GetInputParameterDesc( |
| UINT ParameterIndex, D3D12_SIGNATURE_PARAMETER_DESC *pDesc) noexcept { |
| IFRBOOL(pDesc != nullptr, E_INVALIDARG); |
| IFRBOOL(ParameterIndex < m_InputSignature.size(), E_INVALIDARG); |
| if (m_PublicAPI != PublicAPI::D3D11_43) |
| *pDesc = m_InputSignature[ParameterIndex]; |
| else |
| memcpy(pDesc, &m_InputSignature[ParameterIndex], |
| // D3D11_43 does not have MinPrecison. |
| offsetof(D3D12_SIGNATURE_PARAMETER_DESC, Stream) + |
| sizeof(D3D12_SIGNATURE_PARAMETER_DESC::Stream)); |
| |
| return S_OK; |
| } |
| |
| HRESULT DxilShaderReflection::GetOutputParameterDesc( |
| UINT ParameterIndex, D3D12_SIGNATURE_PARAMETER_DESC *pDesc) noexcept { |
| IFRBOOL(pDesc != nullptr, E_INVALIDARG); |
| IFRBOOL(ParameterIndex < m_OutputSignature.size(), E_INVALIDARG); |
| if (m_PublicAPI != PublicAPI::D3D11_43) |
| *pDesc = m_OutputSignature[ParameterIndex]; |
| else |
| memcpy(pDesc, &m_OutputSignature[ParameterIndex], |
| // D3D11_43 does not have MinPrecison. |
| offsetof(D3D12_SIGNATURE_PARAMETER_DESC, Stream) + |
| sizeof(D3D12_SIGNATURE_PARAMETER_DESC::Stream)); |
| |
| return S_OK; |
| } |
| |
| HRESULT |
| DxilShaderReflection::GetPatchConstantParameterDesc( |
| UINT ParameterIndex, D3D12_SIGNATURE_PARAMETER_DESC *pDesc) noexcept { |
| IFRBOOL(pDesc != nullptr, E_INVALIDARG); |
| IFRBOOL(ParameterIndex < m_PatchConstantSignature.size(), E_INVALIDARG); |
| if (m_PublicAPI != PublicAPI::D3D11_43) |
| *pDesc = m_PatchConstantSignature[ParameterIndex]; |
| else |
| memcpy(pDesc, &m_PatchConstantSignature[ParameterIndex], |
| // D3D11_43 does not have MinPrecison. |
| offsetof(D3D12_SIGNATURE_PARAMETER_DESC, Stream) + |
| sizeof(D3D12_SIGNATURE_PARAMETER_DESC::Stream)); |
| |
| return S_OK; |
| } |
| |
| ID3D12ShaderReflectionVariable * |
| DxilShaderReflection::GetVariableByName(LPCSTR Name) noexcept { |
| return DxilModuleReflection::_GetVariableByName(Name); |
| } |
| ID3D12ShaderReflectionVariable * |
| DxilModuleReflection::_GetVariableByName(LPCSTR Name) { |
| if (Name != nullptr) { |
| // Iterate through all cbuffers to find the variable. |
| for (UINT i = 0; i < m_CBs.size(); i++) { |
| ID3D12ShaderReflectionVariable *pVar = m_CBs[i]->GetVariableByName(Name); |
| if (pVar != &g_InvalidSRVariable) { |
| return pVar; |
| } |
| } |
| } |
| |
| return &g_InvalidSRVariable; |
| } |
| |
| HRESULT DxilShaderReflection::GetResourceBindingDescByName( |
| LPCSTR Name, D3D12_SHADER_INPUT_BIND_DESC *pDesc) noexcept { |
| return DxilModuleReflection::_GetResourceBindingDescByName(Name, pDesc, |
| m_PublicAPI); |
| } |
| HRESULT DxilModuleReflection::_GetResourceBindingDescByName( |
| LPCSTR Name, D3D12_SHADER_INPUT_BIND_DESC *pDesc, PublicAPI api) { |
| IFRBOOL(Name != nullptr, E_INVALIDARG); |
| |
| for (UINT i = 0; i < m_Resources.size(); i++) { |
| if (strcmp(m_Resources[i].Name, Name) == 0) { |
| if (api != PublicAPI::D3D12) { |
| memcpy(pDesc, &m_Resources[i], sizeof(D3D11_SHADER_INPUT_BIND_DESC)); |
| } else { |
| *pDesc = m_Resources[i]; |
| } |
| return S_OK; |
| } |
| } |
| |
| return HRESULT_FROM_WIN32(ERROR_NOT_FOUND); |
| } |
| |
| UINT DxilShaderReflection::GetMovInstructionCount() noexcept { return 0; } |
| UINT DxilShaderReflection::GetMovcInstructionCount() noexcept { return 0; } |
| UINT DxilShaderReflection::GetConversionInstructionCount() noexcept { |
| return 0; |
| } |
| UINT DxilShaderReflection::GetBitwiseInstructionCount() noexcept { return 0; } |
| |
| D3D_PRIMITIVE DxilShaderReflection::GetGSInputPrimitive() noexcept { |
| if (!m_pDxilModule->GetShaderModel()->IsGS()) |
| return D3D_PRIMITIVE::D3D10_PRIMITIVE_UNDEFINED; |
| return (D3D_PRIMITIVE)m_pDxilModule->GetInputPrimitive(); |
| } |
| |
| BOOL DxilShaderReflection::IsSampleFrequencyShader() noexcept { |
| // TODO: determine correct value |
| return FALSE; |
| } |
| |
| UINT DxilShaderReflection::GetNumInterfaceSlots() noexcept { return 0; } |
| |
| HRESULT |
| DxilShaderReflection::GetMinFeatureLevel(D3D_FEATURE_LEVEL *pLevel) noexcept { |
| IFR(AssignToOut(D3D_FEATURE_LEVEL_12_0, pLevel)); |
| return S_OK; |
| } |
| |
| UINT DxilShaderReflection::GetThreadGroupSize(UINT *pSizeX, UINT *pSizeY, |
| UINT *pSizeZ) noexcept { |
| if (!m_pDxilModule->GetShaderModel()->IsCS() && |
| !m_pDxilModule->GetShaderModel()->IsMS() && |
| !m_pDxilModule->GetShaderModel()->IsAS()) { |
| AssignToOutOpt((UINT)0, pSizeX); |
| AssignToOutOpt((UINT)0, pSizeY); |
| AssignToOutOpt((UINT)0, pSizeZ); |
| return 0; |
| } |
| unsigned x = m_pDxilModule->GetNumThreads(0); |
| unsigned y = m_pDxilModule->GetNumThreads(1); |
| unsigned z = m_pDxilModule->GetNumThreads(2); |
| AssignToOutOpt(x, pSizeX); |
| AssignToOutOpt(y, pSizeY); |
| AssignToOutOpt(z, pSizeZ); |
| return x * y * z; |
| } |
| |
| UINT64 DxilShaderReflection::GetRequiresFlags() noexcept { |
| UINT64 result = m_pDxilModule->m_ShaderFlags.GetFeatureInfo(); |
| // FeatureInfo flags are identical, with the exception of a collision between: |
| // SHADER_FEATURE_COMPUTE_SHADERS_PLUS_RAW_AND_STRUCTURED_BUFFERS_VIA_SHADER_4_X |
| // and D3D_SHADER_REQUIRES_EARLY_DEPTH_STENCIL |
| // We keep track of the flag elsewhere, so use that instead. |
| result &= ~(UINT64)D3D_SHADER_REQUIRES_EARLY_DEPTH_STENCIL; |
| if (m_pDxilModule->m_ShaderFlags.GetForceEarlyDepthStencil()) |
| result |= D3D_SHADER_REQUIRES_EARLY_DEPTH_STENCIL; |
| return result; |
| } |
| |
| // ID3D12FunctionReflection |
| |
| class CFunctionReflection final : public ID3D12FunctionReflection { |
| protected: |
| DxilLibraryReflection *m_pLibraryReflection = nullptr; |
| const Function *m_pFunction; |
| const DxilFunctionProps *m_pProps; // nullptr if non-shader library function |
| // or patch constant function |
| std::string m_Name; |
| typedef SmallSetVector<UINT32, 8> ResourceUseSet; |
| ResourceUseSet m_UsedResources; |
| ResourceUseSet m_UsedCBs; |
| UINT64 m_FeatureFlags; |
| |
| public: |
| void Initialize(DxilLibraryReflection *pLibraryReflection, |
| Function *pFunction) { |
| DXASSERT_NOMSG(pLibraryReflection); |
| DXASSERT_NOMSG(pFunction); |
| m_pLibraryReflection = pLibraryReflection; |
| m_pFunction = pFunction; |
| |
| const DxilModule &M = *m_pLibraryReflection->m_pDxilModule; |
| m_Name = m_pFunction->getName().str(); |
| m_pProps = nullptr; |
| if (M.HasDxilFunctionProps(m_pFunction)) { |
| m_pProps = &M.GetDxilFunctionProps(m_pFunction); |
| } |
| } |
| void AddResourceReference(UINT resIndex) { m_UsedResources.insert(resIndex); } |
| void AddCBReference(UINT cbIndex) { m_UsedCBs.insert(cbIndex); } |
| void SetFeatureFlags(UINT64 flags) { m_FeatureFlags = flags; } |
| |
| // ID3D12FunctionReflection |
| STDMETHOD(GetDesc)(D3D12_FUNCTION_DESC *pDesc); |
| |
| // BufferIndex relative to used constant buffers here |
| STDMETHOD_(ID3D12ShaderReflectionConstantBuffer *, GetConstantBufferByIndex) |
| (UINT BufferIndex); |
| STDMETHOD_(ID3D12ShaderReflectionConstantBuffer *, GetConstantBufferByName) |
| (LPCSTR Name); |
| |
| STDMETHOD(GetResourceBindingDesc) |
| (UINT ResourceIndex, D3D12_SHADER_INPUT_BIND_DESC *pDesc); |
| |
| STDMETHOD_(ID3D12ShaderReflectionVariable *, GetVariableByName)(LPCSTR Name); |
| |
| STDMETHOD(GetResourceBindingDescByName) |
| (LPCSTR Name, D3D12_SHADER_INPUT_BIND_DESC *pDesc); |
| |
| // Use D3D_RETURN_PARAMETER_INDEX to get description of the return value. |
| STDMETHOD_(ID3D12FunctionParameterReflection *, GetFunctionParameter) |
| (INT ParameterIndex) { return &g_InvalidFunctionParameter; } |
| }; |
| |
| HRESULT CFunctionReflection::GetDesc(D3D12_FUNCTION_DESC *pDesc) { |
| DXASSERT_NOMSG(m_pLibraryReflection); |
| IFR(ZeroMemoryToOut(pDesc)); |
| |
| const ShaderModel *pSM = |
| m_pLibraryReflection->m_pDxilModule->GetShaderModel(); |
| DXIL::ShaderKind kind = DXIL::ShaderKind::Library; |
| if (m_pProps) { |
| kind = m_pProps->shaderKind; |
| } |
| pDesc->Version = EncodeVersion(kind, pSM->GetMajor(), pSM->GetMinor()); |
| |
| // Unset: LPCSTR Creator; // Creator string |
| // Unset: UINT Flags; // Shader compilation/parse flags |
| |
| pDesc->ConstantBuffers = (UINT)m_UsedCBs.size(); |
| pDesc->BoundResources = (UINT)m_UsedResources.size(); |
| |
| // Unset: UINT InstructionCount; // Number of emitted instructions |
| // Unset: UINT TempRegisterCount; // Number of temporary registers used |
| // Unset: UINT TempArrayCount; // Number of temporary arrays used |
| // Unset: UINT DefCount; // Number of constant defines |
| // Unset: UINT DclCount; // Number of declarations (input + output) |
| // Unset: UINT |
| // TextureNormalInstructions; // Number of non-categorized texture |
| // instructions |
| // Unset: UINT TextureLoadInstructions; // Number of texture load |
| // instructions |
| // Unset: UINT TextureCompInstructions; // Number of texture comparison |
| // instructions |
| |
| // Unset: UINT TextureBiasInstructions;// Number of texture bias instructions |
| // Unset: UINT TextureGradientInstructions; // Number of texture gradient |
| // instructions |
| // Unset: UINT FloatInstructionCount; // Number of floating point arithmetic |
| // instructions used |
| // Unset: UINT IntInstructionCount; // Number of signed integer arithmetic |
| // instructions used |
| // Unset: UINT UintInstructionCount; // Number of unsigned integer |
| // arithmetic instructions used |
| // Unset: UINT StaticFlowControlCount; // Number of static flow control |
| // instructions used |
| // Unset: UINT DynamicFlowControlCount; // Number of dynamic flow control |
| // instructions used |
| // Unset: UINT MacroInstructionCount; // Number of macro instructions used |
| // Unset: UINT ArrayInstructionCount; // Number of array instructions used |
| // Unset: UINT MovInstructionCount; // Number of mov instructions used |
| // Unset: UINT MovcInstructionCount; // Number of movc instructions used |
| // Unset: UINT ConversionInstructionCount; // Number of type conversion |
| // instructions used |
| // Unset: UINT BitwiseInstructionCount; // Number of bitwise arithmetic |
| // instructions used |
| // Unset: D3D_FEATURE_LEVEL MinFeatureLevel; // Min target of the function |
| // byte code |
| |
| pDesc->RequiredFeatureFlags = |
| m_FeatureFlags & ~(UINT64)D3D_SHADER_REQUIRES_EARLY_DEPTH_STENCIL; |
| // Also Mask off function-level derivatives flag. |
| pDesc->RequiredFeatureFlags &= ~DXIL::OptFeatureInfo_UsesDerivatives; |
| if (kind == DXIL::ShaderKind::Pixel && m_pProps && |
| m_pProps->ShaderProps.PS.EarlyDepthStencil) { |
| pDesc->RequiredFeatureFlags |= D3D_SHADER_REQUIRES_EARLY_DEPTH_STENCIL; |
| } |
| |
| pDesc->Name = m_Name.c_str(); |
| |
| // Unset: INT FunctionParameterCount; // Number of logical parameters in the |
| // function signature (not including return) |
| // Unset: BOOL HasReturn; // TRUE, if function returns a value, false - it is |
| // a subroutine |
| // Unset: BOOL Has10Level9VertexShader; // TRUE, if there is a 10L9 VS blob |
| // Unset: BOOL Has10Level9PixelShader; // TRUE, if there is a 10L9 PS blob |
| return S_OK; |
| } |
| |
| // BufferIndex is relative to used constant buffers here |
| ID3D12ShaderReflectionConstantBuffer * |
| CFunctionReflection::GetConstantBufferByIndex(UINT BufferIndex) { |
| DXASSERT_NOMSG(m_pLibraryReflection); |
| if (BufferIndex >= m_UsedCBs.size()) |
| return &g_InvalidSRConstantBuffer; |
| return m_pLibraryReflection->_GetConstantBufferByIndex( |
| m_UsedCBs[BufferIndex]); |
| } |
| |
| ID3D12ShaderReflectionConstantBuffer * |
| CFunctionReflection::GetConstantBufferByName(LPCSTR Name) { |
| DXASSERT_NOMSG(m_pLibraryReflection); |
| return m_pLibraryReflection->_GetConstantBufferByName(Name); |
| } |
| |
| HRESULT CFunctionReflection::GetResourceBindingDesc( |
| UINT ResourceIndex, D3D12_SHADER_INPUT_BIND_DESC *pDesc) { |
| DXASSERT_NOMSG(m_pLibraryReflection); |
| if (ResourceIndex >= m_UsedResources.size()) |
| return E_INVALIDARG; |
| return m_pLibraryReflection->_GetResourceBindingDesc( |
| m_UsedResources[ResourceIndex], pDesc); |
| } |
| |
| ID3D12ShaderReflectionVariable * |
| CFunctionReflection::GetVariableByName(LPCSTR Name) { |
| DXASSERT_NOMSG(m_pLibraryReflection); |
| return m_pLibraryReflection->_GetVariableByName(Name); |
| } |
| |
| HRESULT CFunctionReflection::GetResourceBindingDescByName( |
| LPCSTR Name, D3D12_SHADER_INPUT_BIND_DESC *pDesc) { |
| DXASSERT_NOMSG(m_pLibraryReflection); |
| return m_pLibraryReflection->_GetResourceBindingDescByName(Name, pDesc); |
| } |
| |
| // DxilLibraryReflection |
| |
| void DxilLibraryReflection::AddResourceDependencies() { |
| auto functionTable = m_RDAT.GetFunctionTable(); |
| m_FunctionVector.clear(); |
| m_FunctionVector.reserve(functionTable.Count()); |
| std::map<StringRef, CFunctionReflection *> orderedMap; |
| |
| auto resourceTable = m_RDAT.GetResourceTable(); |
| unsigned SamplersStart = 0; |
| unsigned SRVsStart = 0; |
| unsigned UAVsStart = 0; |
| DXIL::ResourceClass prevClass = DXIL::ResourceClass::CBuffer; |
| for (unsigned i = 0; i < resourceTable.Count(); i++) { |
| auto resource = resourceTable[i]; |
| if (prevClass != resource.getClass()) { |
| prevClass = resource.getClass(); |
| switch (prevClass) { |
| case DXIL::ResourceClass::Sampler: |
| SamplersStart = i; |
| LLVM_FALLTHROUGH; |
| case DXIL::ResourceClass::SRV: |
| SRVsStart = i; |
| LLVM_FALLTHROUGH; |
| case DXIL::ResourceClass::UAV: |
| UAVsStart = i; |
| break; |
| } |
| } |
| } |
| |
| IFTBOOL(resourceTable.Count() == m_Resources.size(), |
| DXC_E_INCORRECT_DXIL_METADATA); |
| |
| for (unsigned iFunc = 0; iFunc < functionTable.Count(); ++iFunc) { |
| auto FR = functionTable[iFunc]; |
| auto &func = m_FunctionMap[FR.getName()]; |
| DXASSERT(!func.get(), "otherwise duplicate named functions"); |
| Function *F = m_pModule->getFunction(FR.getName()); |
| func.reset(new CFunctionReflection()); |
| func->Initialize(this, F); |
| m_FunctionsByPtr[F] = func.get(); |
| orderedMap[FR.getName()] = func.get(); |
| |
| func->SetFeatureFlags(FR.GetFeatureFlags()); |
| |
| for (unsigned iRes = 0; iRes < FR.getResources().Count(); ++iRes) { |
| auto RR = FR.getResources()[iRes]; |
| unsigned id = RR.getID(); |
| switch (RR.getClass()) { |
| case DXIL::ResourceClass::CBuffer: |
| func->AddResourceReference(id); |
| func->AddCBReference(id); |
| break; |
| case DXIL::ResourceClass::Sampler: |
| func->AddResourceReference(SamplersStart + id); |
| break; |
| case DXIL::ResourceClass::SRV: |
| func->AddResourceReference(SRVsStart + id); |
| if (DXIL::IsStructuredBuffer(RR.getKind())) { |
| auto it = m_StructuredBufferCBsByName.find(RR.getName()); |
| if (it != m_StructuredBufferCBsByName.end()) |
| func->AddCBReference(it->second); |
| } else if (RR.getKind() == DXIL::ResourceKind::TBuffer) { |
| auto it = m_CBsByName.find(RR.getName()); |
| if (it != m_CBsByName.end()) |
| func->AddCBReference(it->second); |
| } |
| break; |
| case DXIL::ResourceClass::UAV: |
| func->AddResourceReference(UAVsStart + id); |
| if (DXIL::IsStructuredBuffer(RR.getKind())) { |
| auto it = m_StructuredBufferCBsByName.find(RR.getName()); |
| if (it != m_StructuredBufferCBsByName.end()) |
| func->AddCBReference(it->second); |
| } |
| break; |
| default: |
| DXASSERT(false, "Unrecognized ResourceClass in RDAT"); |
| } |
| } |
| } |
| |
| for (auto &it : orderedMap) { |
| m_FunctionVector.push_back(it.second); |
| } |
| } |
| |
| static void CollectCBufUsageForLib(Value *V, std::vector<unsigned> &cbufUsage, |
| bool bMinPrecision) { |
| for (auto user : V->users()) { |
| Value *V = user; |
| if (auto *CI = dyn_cast<CallInst>(V)) { |
| if (hlsl::OP::IsDxilOpFuncCallInst( |
| CI, hlsl::OP::OpCode::CreateHandleForLib)) { |
| CollectCBufUsage(CI, cbufUsage, bMinPrecision); |
| } |
| } else if (isa<GEPOperator>(V) || isa<LoadInst>(V)) { |
| CollectCBufUsageForLib(user, cbufUsage, bMinPrecision); |
| } |
| } |
| } |
| |
| void DxilLibraryReflection::SetCBufferUsage() { |
| unsigned cbSize = std::min(m_CBs.size(), m_pDxilModule->GetCBuffers().size()); |
| |
| for (unsigned i = 0; i < cbSize; i++) { |
| std::vector<unsigned> cbufUsage; |
| CollectCBufUsageForLib(m_pDxilModule->GetCBuffer(i).GetGlobalSymbol(), |
| cbufUsage, m_pDxilModule->GetUseMinPrecision()); |
| SetCBufVarUsage(*m_CBs[i], cbufUsage); |
| } |
| } |
| |
| // ID3D12LibraryReflection |
| |
| HRESULT DxilLibraryReflection::Load(const DxilProgramHeader *pProgramHeader, |
| const DxilPartHeader *pRDATPart) { |
| IFR(LoadRDAT(pRDATPart)); |
| IFR(LoadProgramHeader(pProgramHeader)); |
| |
| try { |
| AddResourceDependencies(); |
| if (!m_bUsageInMetadata) |
| SetCBufferUsage(); |
| return S_OK; |
| } |
| CATCH_CPP_RETURN_HRESULT(); |
| } |
| |
| HRESULT DxilLibraryReflection::GetDesc(D3D12_LIBRARY_DESC *pDesc) { |
| IFR(ZeroMemoryToOut(pDesc)); |
| // Unset: LPCSTR Creator; // The name of the originator of the |
| // library. Unset: UINT Flags; // Compilation flags. UINT |
| // FunctionCount; // Number of functions exported from the library. |
| pDesc->FunctionCount = (UINT)m_FunctionVector.size(); |
| return S_OK; |
| } |
| |
| ID3D12FunctionReflection * |
| DxilLibraryReflection::GetFunctionByIndex(INT FunctionIndex) { |
| if ((UINT)FunctionIndex >= m_FunctionVector.size()) |
| return &g_InvalidFunction; |
| return m_FunctionVector[FunctionIndex]; |
| } |