/////////////////////////////////////////////////////////////////////////////// // // // DxilRootSignature.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 manipulating root signature structures. // // // /////////////////////////////////////////////////////////////////////////////// #include "dxc/HLSL/DxilConstants.h" #include "dxc/HLSL/DxilRootSignature.h" #include "dxc/HLSL/DxilPipelineStateValidation.h" #include "dxc/Support/Global.h" #include "dxc/Support/WinIncludes.h" #include "dxc/Support/FileIOHelper.h" #include "dxc/dxcapi.h" #include "llvm/Support/raw_ostream.h" #include "llvm/IR/DiagnosticPrinter.h" #include #include #include #include #include using namespace llvm; using std::string; namespace hlsl { DEFINE_ENUM_FLAG_OPERATORS(DxilRootSignatureFlags) DEFINE_ENUM_FLAG_OPERATORS(DxilRootDescriptorFlags) DEFINE_ENUM_FLAG_OPERATORS(DxilDescriptorRangeType) DEFINE_ENUM_FLAG_OPERATORS(DxilDescriptorRangeFlags) // Execute (error) and throw. #define EAT(x) { (x); throw ::hlsl::Exception(E_FAIL); } ////////////////////////////////////////////////////////////////////////////// // Root signature handler. RootSignatureHandle::RootSignatureHandle(RootSignatureHandle&& other) { m_pDesc = nullptr; m_pSerialized = nullptr; std::swap(m_pDesc, other.m_pDesc); std::swap(m_pSerialized, other.m_pSerialized); } void RootSignatureHandle::Assign(const DxilVersionedRootSignatureDesc *pDesc, IDxcBlob *pSerialized) { Clear(); m_pDesc = pDesc; m_pSerialized = pSerialized; if (m_pSerialized) m_pSerialized->AddRef(); } void RootSignatureHandle::Clear() { hlsl::DeleteRootSignature(m_pDesc); m_pDesc = nullptr; if (m_pSerialized != nullptr) { m_pSerialized->Release(); m_pSerialized = nullptr; } } const uint8_t *RootSignatureHandle::GetSerializedBytes() const { DXASSERT_NOMSG(m_pSerialized != nullptr); return (uint8_t *)m_pSerialized->GetBufferPointer(); } unsigned RootSignatureHandle::GetSerializedSize() const { DXASSERT_NOMSG(m_pSerialized != nullptr); return m_pSerialized->GetBufferSize(); } void RootSignatureHandle::EnsureSerializedAvailable() { DXASSERT_NOMSG(!IsEmpty()); if (m_pSerialized == nullptr) { CComPtr pResult; hlsl::SerializeRootSignature(m_pDesc, &pResult, nullptr, false); IFTBOOL(pResult != nullptr, E_FAIL); m_pSerialized = pResult.Detach(); } } void RootSignatureHandle::Deserialize() { DXASSERT_NOMSG(m_pSerialized && !m_pDesc); DeserializeRootSignature((uint8_t*)m_pSerialized->GetBufferPointer(), (uint32_t)m_pSerialized->GetBufferSize(), &m_pDesc); } void RootSignatureHandle::LoadSerialized(const uint8_t *pData, unsigned length) { DXASSERT_NOMSG(IsEmpty()); IDxcBlobEncoding *pCreated; IFT(DxcCreateBlobWithEncodingOnHeapCopy(pData, length, CP_UTF8, &pCreated)); m_pSerialized = pCreated; } ////////////////////////////////////////////////////////////////////////////// // Simple serializer. class SimpleSerializer { struct Segment { void *pData; unsigned cbSize; bool bOwner; unsigned Offset; Segment *pNext; }; public: SimpleSerializer(); ~SimpleSerializer(); HRESULT AddBlock(void *pData, unsigned cbSize, unsigned *pOffset); HRESULT ReserveBlock(void **ppData, unsigned cbSize, unsigned *pOffset); HRESULT Compact(_Out_writes_bytes_(cbSize) char *pData, unsigned cbSize); unsigned GetSize(); protected: unsigned m_cbSegments; Segment *m_pSegment; Segment **m_ppSegment; }; SimpleSerializer::SimpleSerializer() { m_cbSegments = 0; m_pSegment = nullptr; m_ppSegment = &m_pSegment; } SimpleSerializer::~SimpleSerializer() { while (m_pSegment) { Segment *pSegment = m_pSegment; m_pSegment = pSegment->pNext; if (pSegment->bOwner) { delete pSegment->pData; } delete pSegment; } } HRESULT SimpleSerializer::AddBlock(void *pData, unsigned cbSize, unsigned *pOffset) { Segment *pSegment = nullptr; IFRBOOL(!(cbSize != 0 && pData == nullptr), E_FAIL); IFROOM(pSegment = new (std::nothrow) Segment); pSegment->pData = pData; m_cbSegments = (m_cbSegments + 3) & ~3; pSegment->Offset = m_cbSegments; pSegment->cbSize = cbSize; pSegment->bOwner = false; pSegment->pNext = nullptr; m_cbSegments += pSegment->cbSize; *m_ppSegment = pSegment; m_ppSegment = &pSegment->pNext; if (pOffset != nullptr) { *pOffset = pSegment->Offset; } return S_OK; } HRESULT SimpleSerializer::ReserveBlock(void **ppData, unsigned cbSize, unsigned *pOffset) { HRESULT hr = S_OK; Segment *pSegment = nullptr; void *pClonedData = nullptr; IFCOOM(pSegment = new (std::nothrow) Segment); pSegment->pData = nullptr; IFCOOM(pClonedData = new (std::nothrow) char[cbSize]); pSegment->pData = pClonedData; m_cbSegments = (m_cbSegments + 3) & ~3; pSegment->Offset = m_cbSegments; pSegment->cbSize = cbSize; pSegment->bOwner = true; pSegment->pNext = nullptr; m_cbSegments += pSegment->cbSize; *m_ppSegment = pSegment; m_ppSegment = &pSegment->pNext; *ppData = pClonedData; if (pOffset) { *pOffset = pSegment->Offset; } Cleanup: if (FAILED(hr)) { delete[] pClonedData; delete pSegment; } return hr; } HRESULT SimpleSerializer::Compact(_Out_writes_bytes_(cbSize) char *pData, unsigned cbSize) { unsigned cb = GetSize(); IFRBOOL(cb <= cbSize, E_FAIL); DXASSERT_NOMSG(cb <= UINT32_MAX / 2); char *p = (char *)pData; cb = 0; for (Segment *pSegment = m_pSegment; pSegment; pSegment = pSegment->pNext) { unsigned cbAlign = ((cb + 3) & ~3) - cb; _Analysis_assume_(p + cbAlign <= pData + cbSize); memset(p, 0xab, cbAlign); p += cbAlign; cb += cbAlign; _Analysis_assume_(p + pSegment->cbSize <= pData + cbSize); memcpy(p, pSegment->pData, pSegment->cbSize); p += pSegment->cbSize; cb += pSegment->cbSize; } // Trailing zeros _Analysis_assume_(p + cbSize - cb <= pData + cbSize); memset(p, 0xab, cbSize - cb); return S_OK; } unsigned SimpleSerializer::GetSize() { // Round up to 4==sizeof(unsigned). return ((m_cbSegments + 3) >> 2) * 4; } ////////////////////////////////////////////////////////////////////////////// // Interval helper. template class CIntervalCollection { private: std::set m_set; public: const T* FindIntersectingInterval(const T &I) { auto it = m_set.find(I); if (it != m_set.end()) return &*it; return nullptr; } void Insert(const T& value) { auto result = m_set.insert(value); UNREFERENCED_PARAMETER(result); #if DBG DXASSERT(result.second, "otherwise interval collides with existing in collection"); #endif } }; ////////////////////////////////////////////////////////////////////////////// // Verifier classes. class DescriptorTableVerifier { public: void Verify(const DxilDescriptorRange1 *pRanges, unsigned NumRanges, unsigned iRTS, DiagnosticPrinter &DiagPrinter); }; class StaticSamplerVerifier { public: void Verify(const DxilStaticSamplerDesc *pDesc, DiagnosticPrinter &DiagPrinter); }; class RootSignatureVerifier { public: RootSignatureVerifier(); ~RootSignatureVerifier(); void AllowReservedRegisterSpace(bool bAllow); // Call this before calling VerifyShader, as it accumulates root signature state. void VerifyRootSignature(const DxilVersionedRootSignatureDesc *pRootSignature, DiagnosticPrinter &DiagPrinter); void VerifyShader(DxilShaderVisibility VisType, const void *pPSVData, uint32_t PSVSize, DiagnosticPrinter &DiagPrinter); typedef enum NODE_TYPE { DESCRIPTOR_TABLE_ENTRY, ROOT_DESCRIPTOR, ROOT_CONSTANT, STATIC_SAMPLER } NODE_TYPE; private: static const unsigned kMinVisType = (unsigned)DxilShaderVisibility::All; static const unsigned kMaxVisType = (unsigned)DxilShaderVisibility::Pixel; static const unsigned kMinDescType = (unsigned)DxilDescriptorRangeType::SRV; static const unsigned kMaxDescType = (unsigned)DxilDescriptorRangeType::Sampler; struct RegisterRange { NODE_TYPE nt; unsigned space; unsigned lb; // inclusive lower bound unsigned ub; // inclusive upper bound unsigned iRP; unsigned iDTS; // Sort by space, then lower bound. bool operator<(const RegisterRange& other) const { return space < other.space || (space == other.space && ub < other.lb); } // Like a regular -1,0,1 comparison, but 0 indicates overlap. int overlap(const RegisterRange& other) const { if (space < other.space) return -1; if (space > other.space) return 1; if (ub < other.lb) return -1; if (lb > other.ub) return 1; return 0; } // Check containment. bool contains(const RegisterRange& other) const { return (space == other.space) && (lb <= other.lb && other.ub <= ub); } }; typedef CIntervalCollection RegisterRanges; void AddRegisterRange(unsigned iRTS, NODE_TYPE nt, unsigned iDTS, DxilDescriptorRangeType DescType, DxilShaderVisibility VisType, unsigned NumRegisters, unsigned BaseRegister, unsigned RegisterSpace, DiagnosticPrinter &DiagPrinter); const RegisterRange *FindCoveringInterval(DxilDescriptorRangeType RangeType, DxilShaderVisibility VisType, unsigned Num, unsigned LB, unsigned Space); RegisterRanges & GetRanges(DxilShaderVisibility VisType, DxilDescriptorRangeType DescType) { return RangeKinds[(unsigned)VisType][(unsigned)DescType]; } RegisterRanges RangeKinds[kMaxVisType + 1][kMaxDescType + 1]; bool m_bAllowReservedRegisterSpace; DxilRootSignatureFlags m_RootSignatureFlags; }; void DescriptorTableVerifier::Verify(const DxilDescriptorRange1 *pRanges, uint32_t NumRanges, uint32_t iRP, DiagnosticPrinter &DiagPrinter) { bool bHasSamplers = false; bool bHasResources = false; uint64_t iAppendStartSlot = 0; for (unsigned iDTS = 0; iDTS < NumRanges; iDTS++) { const DxilDescriptorRange1 *pRange = &pRanges[iDTS]; switch (pRange->RangeType) { case DxilDescriptorRangeType::SRV: case DxilDescriptorRangeType::UAV: case DxilDescriptorRangeType::CBV: bHasResources = true; break; case DxilDescriptorRangeType::Sampler: bHasSamplers = true; break; default: EAT(DiagPrinter << "Unsupported RangeType value " << (uint32_t)pRange->RangeType << " (descriptor table slot [" << iDTS << "], root parameter [" << iRP << "]).\n"); } // Samplers cannot be mixed with other resources. if (bHasResources && bHasSamplers) { EAT(DiagPrinter << "Samplers cannot be mixed with other " << "resource types in a descriptor table (root " << "parameter [" << iRP << "]).\n"); } // NumDescriptors is not 0. if (pRange->NumDescriptors == 0) { EAT(DiagPrinter << "NumDescriptors cannot be 0 (descriptor " << "table slot [" << iDTS << "], root parameter [" << iRP << "]).\n"); } // Range start. uint64_t iStartSlot = iAppendStartSlot; if (pRange->OffsetInDescriptorsFromTableStart != DxilDescriptorRangeOffsetAppend) { iStartSlot = pRange->OffsetInDescriptorsFromTableStart; } if (iStartSlot > UINT_MAX) { EAT(DiagPrinter << "Cannot append range with implicit lower " << "bound after an unbounded range (descriptor " << "table slot [" << iDTS << "], root parameter [" << iRP << "]).\n"); } // Descriptor range and shader register range overlow. if (pRange->NumDescriptors != UINT_MAX) { // Bounded range. uint64_t ub1 = (uint64_t)pRange->BaseShaderRegister + (uint64_t)pRange->NumDescriptors - 1ull; if (ub1 > UINT_MAX) { EAT(DiagPrinter << "Overflow for shader register range: " << "BaseShaderRegister=" << pRange->BaseShaderRegister << ", NumDescriptor=" << pRange->NumDescriptors << "; (descriptor table slot [" << iDTS << "], root parameter [" << iRP << "]).\n"); } uint64_t ub2 = (uint64_t)iStartSlot + (uint64_t)pRange->NumDescriptors - 1ull; if (ub2 > UINT_MAX) { EAT(DiagPrinter << "Overflow for descriptor range (descriptor " << "table slot [" << iDTS << "], root parameter [" << iRP << "])\n"); } iAppendStartSlot = iStartSlot + (uint64_t)pRange->NumDescriptors; } else { // Unbounded range. iAppendStartSlot = 1ull + (uint64_t)UINT_MAX; } } } RootSignatureVerifier::RootSignatureVerifier() { m_RootSignatureFlags = DxilRootSignatureFlags::None; m_bAllowReservedRegisterSpace = false; } RootSignatureVerifier::~RootSignatureVerifier() {} void RootSignatureVerifier::AllowReservedRegisterSpace(bool bAllow) { m_bAllowReservedRegisterSpace = bAllow; } const char* RangeTypeString(DxilDescriptorRangeType rt) { static const char *RangeType[] = {"SRV", "UAV", "CBV", "SAMPLER"}; return (rt <= DxilDescriptorRangeType::Sampler) ? RangeType[(unsigned)rt] : "unknown"; } const char *VisTypeString(DxilShaderVisibility vis) { static const char *Vis[] = {"ALL", "VERTEX", "HULL", "DOMAIN", "GEOMETRY", "PIXEL"}; unsigned idx = (unsigned)vis; return vis <= DxilShaderVisibility::Pixel ? Vis[idx] : "unknown"; } static bool IsDxilShaderVisibility(DxilShaderVisibility v) { return v <= DxilShaderVisibility::Pixel; } void RootSignatureVerifier::AddRegisterRange(unsigned iRP, NODE_TYPE nt, unsigned iDTS, DxilDescriptorRangeType DescType, DxilShaderVisibility VisType, unsigned NumRegisters, unsigned BaseRegister, unsigned RegisterSpace, DiagnosticPrinter &DiagPrinter) { RegisterRange interval; interval.space = RegisterSpace; interval.lb = BaseRegister; interval.ub = (NumRegisters != UINT_MAX) ? BaseRegister + NumRegisters - 1 : UINT_MAX; interval.nt = nt; interval.iDTS = iDTS; interval.iRP = iRP; if (!m_bAllowReservedRegisterSpace && (RegisterSpace >= DxilSystemReservedRegisterSpaceValuesStart) && (RegisterSpace <= DxilSystemReservedRegisterSpaceValuesEnd)) { if (nt == DESCRIPTOR_TABLE_ENTRY) { EAT(DiagPrinter << "Root parameter [" << iRP << "] descriptor table entry [" << iDTS << "] specifies RegisterSpace=" << std::hex << RegisterSpace << ", which is invalid since RegisterSpace values in the range " << "[" << std::hex << DxilSystemReservedRegisterSpaceValuesStart << "," << std::hex << DxilSystemReservedRegisterSpaceValuesEnd << "] are reserved for system use.\n"); } else { EAT(DiagPrinter << "Root parameter [" << iRP << "] specifies RegisterSpace=" << std::hex << RegisterSpace << ", which is invalid since RegisterSpace values in the range " << "[" << std::hex << DxilSystemReservedRegisterSpaceValuesStart << "," << std::hex << DxilSystemReservedRegisterSpaceValuesEnd << "] are reserved for system use.\n"); } } const RegisterRange *pNode = nullptr; DxilShaderVisibility NodeVis = VisType; if (VisType == DxilShaderVisibility::All) { // Check for overlap with each visibility type. for (unsigned iVT = kMinVisType; iVT <= kMaxVisType; iVT++) { pNode = GetRanges((DxilShaderVisibility)iVT, DescType).FindIntersectingInterval(interval); if (pNode != nullptr) break; } } else { // Check for overlap with the same visibility. pNode = GetRanges(VisType, DescType).FindIntersectingInterval(interval); // Check for overlap with ALL visibility. if (pNode == nullptr) { pNode = GetRanges(DxilShaderVisibility::All, DescType).FindIntersectingInterval(interval); NodeVis = DxilShaderVisibility::All; } } if (pNode != nullptr) { const int strSize = 132; char testString[strSize]; char nodeString[strSize]; switch (nt) { case DESCRIPTOR_TABLE_ENTRY: StringCchPrintfA(testString, strSize, "(root parameter [%u], visibility %s, descriptor table slot [%u])", iRP, VisTypeString(VisType), iDTS); break; case ROOT_DESCRIPTOR: case ROOT_CONSTANT: StringCchPrintfA(testString, strSize, "(root parameter [%u], visibility %s)", iRP, VisTypeString(VisType)); break; case STATIC_SAMPLER: StringCchPrintfA(testString, strSize, "(static sampler [%u], visibility %s)", iRP, VisTypeString(VisType)); break; default: DXASSERT_NOMSG(false); break; } switch (pNode->nt) { case DESCRIPTOR_TABLE_ENTRY: StringCchPrintfA(nodeString, strSize, "(root parameter[%u], visibility %s, descriptor table slot [%u])", pNode->iRP, VisTypeString(NodeVis), pNode->iDTS); break; case ROOT_DESCRIPTOR: case ROOT_CONSTANT: StringCchPrintfA(nodeString, strSize, "(root parameter [%u], visibility %s)", pNode->iRP, VisTypeString(NodeVis)); break; case STATIC_SAMPLER: StringCchPrintfA(nodeString, strSize, "(static sampler [%u], visibility %s)", pNode->iRP, VisTypeString(NodeVis)); break; default: DXASSERT_NOMSG(false); break; } EAT(DiagPrinter << "Shader register range of type " << RangeTypeString(DescType) << " " << testString << " overlaps with another " << "shader register range " << nodeString << ".\n"); } // Insert node. GetRanges(VisType, DescType).Insert(interval); } const RootSignatureVerifier::RegisterRange * RootSignatureVerifier::FindCoveringInterval(DxilDescriptorRangeType RangeType, DxilShaderVisibility VisType, unsigned Num, unsigned LB, unsigned Space) { RegisterRange RR; RR.space = Space; RR.lb = LB; RR.ub = LB + Num - 1; const RootSignatureVerifier::RegisterRange *pRange = GetRanges(DxilShaderVisibility::All, RangeType).FindIntersectingInterval(RR); if (!pRange && VisType != DxilShaderVisibility::All) { pRange = GetRanges(VisType, RangeType).FindIntersectingInterval(RR); } if (pRange && !pRange->contains(RR)) { pRange = nullptr; } return pRange; } static DxilDescriptorRangeType GetRangeType(DxilRootParameterType RPT) { switch (RPT) { case DxilRootParameterType::CBV: return DxilDescriptorRangeType::CBV; case DxilRootParameterType::SRV: return DxilDescriptorRangeType::SRV; case DxilRootParameterType::UAV: return DxilDescriptorRangeType::UAV; default: break; } DXASSERT_NOMSG(false); return DxilDescriptorRangeType::SRV; } void RootSignatureVerifier::VerifyRootSignature( const DxilVersionedRootSignatureDesc *pVersionedRootSignature, DiagnosticPrinter &DiagPrinter) { const DxilVersionedRootSignatureDesc *pUpconvertedRS = nullptr; // Up-convert root signature to the latest RS version. ConvertRootSignature(pVersionedRootSignature, DxilRootSignatureVersion::Version_1_1, &pUpconvertedRS); DXASSERT_NOMSG(pUpconvertedRS->Version == DxilRootSignatureVersion::Version_1_1); // Ensure this gets deleted as necessary. struct SigGuard { const DxilVersionedRootSignatureDesc *Orig, *Guard; SigGuard(const DxilVersionedRootSignatureDesc *pOrig, const DxilVersionedRootSignatureDesc *pGuard) : Orig(pOrig), Guard(pGuard) { } ~SigGuard() { if (Orig != Guard) { DeleteRootSignature(Guard); } } }; SigGuard S(pVersionedRootSignature, pUpconvertedRS); const DxilRootSignatureDesc1 *pRootSignature = &pUpconvertedRS->Desc_1_1; // Flags (assume they are bits that can be combined with OR). if ((pRootSignature->Flags & ~DxilRootSignatureFlags::ValidFlags) != DxilRootSignatureFlags::None) { EAT(DiagPrinter << "Unsupported bit-flag set (root signature flags " << std::hex << (uint32_t)pRootSignature->Flags << ").\n"); } m_RootSignatureFlags = pRootSignature->Flags; for (unsigned iRP = 0; iRP < pRootSignature->NumParameters; iRP++) { const DxilRootParameter1 *pSlot = &pRootSignature->pParameters[iRP]; // Shader visibility. DxilShaderVisibility Visibility = pSlot->ShaderVisibility; if (!IsDxilShaderVisibility(Visibility)) { EAT(DiagPrinter << "Unsupported ShaderVisibility value " << (uint32_t)Visibility << " (root parameter [" << iRP << "]).\n"); } DxilRootParameterType ParameterType = pSlot->ParameterType; switch (ParameterType) { case DxilRootParameterType::DescriptorTable: { DescriptorTableVerifier DTV; DTV.Verify(pSlot->DescriptorTable.pDescriptorRanges, pSlot->DescriptorTable.NumDescriptorRanges, iRP, DiagPrinter); for (unsigned iDTS = 0; iDTS < pSlot->DescriptorTable.NumDescriptorRanges; iDTS++) { const DxilDescriptorRange1 *pRange = &pSlot->DescriptorTable.pDescriptorRanges[iDTS]; unsigned RangeFlags = (unsigned)pRange->Flags; // Verify range flags. if (RangeFlags & ~(unsigned)DxilDescriptorRangeFlags::ValidFlags) { EAT(DiagPrinter << "Unsupported bit-flag set (descriptor range flags " << (uint32_t)pRange->Flags << ").\n"); } switch (pRange->RangeType) { case DxilDescriptorRangeType::Sampler: { if (RangeFlags & (unsigned)(DxilDescriptorRangeFlags::DataVolatile | DxilDescriptorRangeFlags::DataStatic | DxilDescriptorRangeFlags::DataStaticWhileSetAtExecute)) { EAT(DiagPrinter << "Sampler descriptor ranges can't specify DATA_* flags " << "since there is no data pointed to by samplers " << "(descriptor range flags " << (uint32_t)pRange->Flags << ").\n"); } break; } default: { unsigned NumDataFlags = 0; if (RangeFlags & (unsigned)DxilDescriptorRangeFlags::DataVolatile) { NumDataFlags++; } if (RangeFlags & (unsigned)DxilDescriptorRangeFlags::DataStatic) { NumDataFlags++; } if (RangeFlags & (unsigned)DxilDescriptorRangeFlags::DataStaticWhileSetAtExecute) { NumDataFlags++; } if (NumDataFlags > 1) { EAT(DiagPrinter << "Descriptor range flags cannot specify more than one DATA_* flag " << "at a time (descriptor range flags " << (uint32_t)pRange->Flags << ").\n"); } if ((RangeFlags & (unsigned)DxilDescriptorRangeFlags::DataStatic) && (RangeFlags & (unsigned)DxilDescriptorRangeFlags::DescriptorsVolatile)) { EAT(DiagPrinter << "Descriptor range flags cannot specify DESCRIPTORS_VOLATILE with the DATA_STATIC flag at the same time (descriptor range flags " << (uint32_t)pRange->Flags << "). " << "DATA_STATIC_WHILE_SET_AT_EXECUTE is fine to combine with DESCRIPTORS_VOLATILE, since DESCRIPTORS_VOLATILE still requires descriptors don't change during execution. \n"); } break; } } AddRegisterRange(iRP, DESCRIPTOR_TABLE_ENTRY, iDTS, pRange->RangeType, Visibility, pRange->NumDescriptors, pRange->BaseShaderRegister, pRange->RegisterSpace, DiagPrinter); } break; } case DxilRootParameterType::Constants32Bit: AddRegisterRange(iRP, ROOT_CONSTANT, (unsigned)-1, DxilDescriptorRangeType::CBV, Visibility, 1, pSlot->Constants.ShaderRegister, pSlot->Constants.RegisterSpace, DiagPrinter); break; case DxilRootParameterType::CBV: case DxilRootParameterType::SRV: case DxilRootParameterType::UAV: { // Verify root descriptor flags. unsigned Flags = (unsigned)pSlot->Descriptor.Flags; if (Flags & ~(unsigned)DxilRootDescriptorFlags::ValidFlags) { EAT(DiagPrinter << "Unsupported bit-flag set (root descriptor flags " << std::hex << Flags << ").\n"); } unsigned NumDataFlags = 0; if (Flags & (unsigned)DxilRootDescriptorFlags::DataVolatile) { NumDataFlags++; } if (Flags & (unsigned)DxilRootDescriptorFlags::DataStatic) { NumDataFlags++; } if (Flags & (unsigned)DxilRootDescriptorFlags::DataStaticWhileSetAtExecute) { NumDataFlags++; } if (NumDataFlags > 1) { EAT(DiagPrinter << "Root descriptor flags cannot specify more " << "than one DATA_* flag at a time (root " << "descriptor flags " << NumDataFlags << ").\n"); } AddRegisterRange(iRP, ROOT_DESCRIPTOR, (unsigned)-1, GetRangeType(ParameterType), Visibility, 1, pSlot->Descriptor.ShaderRegister, pSlot->Descriptor.RegisterSpace, DiagPrinter); break; } default: EAT(DiagPrinter << "Unsupported ParameterType value " << (uint32_t)ParameterType << " (root parameter " << iRP << ")\n"); } } for (unsigned iSS = 0; iSS < pRootSignature->NumStaticSamplers; iSS++) { const DxilStaticSamplerDesc *pSS = &pRootSignature->pStaticSamplers[iSS]; // Shader visibility. DxilShaderVisibility Visibility = pSS->ShaderVisibility; if (!IsDxilShaderVisibility(Visibility)) { EAT(DiagPrinter << "Unsupported ShaderVisibility value " << (uint32_t)Visibility << " (static sampler [" << iSS << "]).\n"); } StaticSamplerVerifier SSV; SSV.Verify(pSS, DiagPrinter); AddRegisterRange(iSS, STATIC_SAMPLER, (unsigned)-1, DxilDescriptorRangeType::Sampler, Visibility, 1, pSS->ShaderRegister, pSS->RegisterSpace, DiagPrinter); } } void RootSignatureVerifier::VerifyShader(DxilShaderVisibility VisType, const void *pPSVData, uint32_t PSVSize, DiagnosticPrinter &DiagPrinter) { DxilPipelineStateValidation PSV; IFTBOOL(PSV.InitFromPSV0(pPSVData, PSVSize), E_INVALIDARG); bool bShaderDeniedByRootSig = false; switch (VisType) { case DxilShaderVisibility::Vertex: if ((m_RootSignatureFlags & DxilRootSignatureFlags::DenyVertexShaderRootAccess) != DxilRootSignatureFlags::None) { bShaderDeniedByRootSig = true; } break; case DxilShaderVisibility::Hull: if ((m_RootSignatureFlags & DxilRootSignatureFlags::DenyHullShaderRootAccess) != DxilRootSignatureFlags::None) { bShaderDeniedByRootSig = true; } break; case DxilShaderVisibility::Domain: if ((m_RootSignatureFlags & DxilRootSignatureFlags::DenyDomainShaderRootAccess) != DxilRootSignatureFlags::None) { bShaderDeniedByRootSig = true; } break; case DxilShaderVisibility::Geometry: if ((m_RootSignatureFlags & DxilRootSignatureFlags::DenyGeometryShaderRootAccess) != DxilRootSignatureFlags::None) { bShaderDeniedByRootSig = true; } break; case DxilShaderVisibility::Pixel: if ((m_RootSignatureFlags & DxilRootSignatureFlags::DenyPixelShaderRootAccess) != DxilRootSignatureFlags::None) { bShaderDeniedByRootSig = true; } break; default: break; } bool bShaderHasRootBindings = false; for (unsigned iResource = 0; iResource < PSV.GetBindCount(); iResource++) { const PSVResourceBindInfo0 *pBindInfo0 = PSV.GetPSVResourceBindInfo0(iResource); DXASSERT_NOMSG(pBindInfo0); unsigned Space = pBindInfo0->Space; unsigned LB = pBindInfo0->LowerBound; unsigned UB = pBindInfo0->UpperBound; unsigned Num = (UB != UINT_MAX) ? (UB - LB + 1) : 1; PSVResourceType ResType = (PSVResourceType)pBindInfo0->ResType; switch(ResType) { case PSVResourceType::Sampler: { bShaderHasRootBindings = true; auto pCoveringRange = FindCoveringInterval(DxilDescriptorRangeType::Sampler, VisType, Num, LB, Space); if(!pCoveringRange) { EAT(DiagPrinter << "Shader sampler descriptor range (RegisterSpace=" << Space << ", NumDescriptors=" << Num << ", BaseShaderRegister=" << LB << ") is not fully bound in root signature.\n"); } break; } case PSVResourceType::SRVTyped: case PSVResourceType::SRVRaw: case PSVResourceType::SRVStructured: { bShaderHasRootBindings = true; auto pCoveringRange = FindCoveringInterval(DxilDescriptorRangeType::SRV, VisType, Num, LB, Space); if (pCoveringRange) { if(pCoveringRange->nt == ROOT_DESCRIPTOR && ResType == PSVResourceType::SRVTyped) { EAT(DiagPrinter << "A Shader is declaring a resource object as a texture using " << "a register mapped to a root descriptor SRV (RegisterSpace=" << Space << ", ShaderRegister=" << LB << "). " << "SRV or UAV root descriptors can only be Raw or Structured buffers.\n"); } } else { EAT(DiagPrinter << "Shader SRV descriptor range (RegisterSpace=" << Space << ", NumDescriptors=" << Num << ", BaseShaderRegister=" << LB << ") is not fully bound in root signature.\n"); } break; } case PSVResourceType::UAVTyped: case PSVResourceType::UAVRaw: case PSVResourceType::UAVStructured: case PSVResourceType::UAVStructuredWithCounter: { bShaderHasRootBindings = true; auto pCoveringRange = FindCoveringInterval(DxilDescriptorRangeType::UAV, VisType, Num, LB, Space); if (pCoveringRange) { if (pCoveringRange->nt == ROOT_DESCRIPTOR) { if (ResType == PSVResourceType::UAVTyped) { EAT(DiagPrinter << "A shader is declaring a typed UAV using a register mapped " << "to a root descriptor UAV (RegisterSpace=" << Space << ", ShaderRegister=" << LB << "). " << "SRV or UAV root descriptors can only be Raw or Structured buffers.\n"); } if (ResType == PSVResourceType::UAVStructuredWithCounter) { EAT(DiagPrinter << "A Shader is declaring a structured UAV with counter using " << "a register mapped to a root descriptor UAV (RegisterSpace=" << Space << ", ShaderRegister=" << LB << "). " << "SRV or UAV root descriptors can only be Raw or Structured buffers.\n"); } } } else { EAT(DiagPrinter << "Shader UAV descriptor range (RegisterSpace=" << Space << ", NumDescriptors=" << Num << ", BaseShaderRegister=" << LB << ") is not fully bound in root signature.\n"); } break; } case PSVResourceType::CBV: { bShaderHasRootBindings = true; auto pCoveringRange = FindCoveringInterval(DxilDescriptorRangeType::CBV, VisType, Num, LB, Space); if (!pCoveringRange) { EAT(DiagPrinter << "Shader CBV descriptor range (RegisterSpace=" << Space << ", NumDescriptors=" << Num << ", BaseShaderRegister=" << LB << ") is not fully bound in root signature.\n"); } break; } default: break; } } if (bShaderHasRootBindings && bShaderDeniedByRootSig) { EAT(DiagPrinter << "Shader has root bindings but root signature uses a DENY flag " << "to disallow root binding access to the shader stage.\n"); } } BOOL isNaN(const float &a) { static const unsigned exponentMask = 0x7f800000; static const unsigned mantissaMask = 0x007fffff; unsigned u = *(const unsigned *)&a; return (((u & exponentMask) == exponentMask) && (u & mantissaMask)); // NaN } static bool IsDxilTextureAddressMode(DxilTextureAddressMode v) { return DxilTextureAddressMode::Wrap <= v && v <= DxilTextureAddressMode::MirrorOnce; } static bool IsDxilComparisonFunc(DxilComparisonFunc v) { return DxilComparisonFunc::Never <= v && v <= DxilComparisonFunc::Always; } // This validation closely mirrors CCreateSamplerStateValidator's checks void StaticSamplerVerifier::Verify(const DxilStaticSamplerDesc* pDesc, DiagnosticPrinter &DiagPrinter) { if (!pDesc) { EAT(DiagPrinter << "Static sampler: A nullptr pSamplerDesc was specified.\n"); } bool bIsComparison = false; switch (pDesc->Filter) { case DxilFilter::MINIMUM_MIN_MAG_MIP_POINT: case DxilFilter::MINIMUM_MIN_MAG_POINT_MIP_LINEAR: case DxilFilter::MINIMUM_MIN_POINT_MAG_LINEAR_MIP_POINT: case DxilFilter::MINIMUM_MIN_POINT_MAG_MIP_LINEAR: case DxilFilter::MINIMUM_MIN_LINEAR_MAG_MIP_POINT: case DxilFilter::MINIMUM_MIN_LINEAR_MAG_POINT_MIP_LINEAR: case DxilFilter::MINIMUM_MIN_MAG_LINEAR_MIP_POINT: case DxilFilter::MINIMUM_MIN_MAG_MIP_LINEAR: case DxilFilter::MINIMUM_ANISOTROPIC: case DxilFilter::MAXIMUM_MIN_MAG_MIP_POINT: case DxilFilter::MAXIMUM_MIN_MAG_POINT_MIP_LINEAR: case DxilFilter::MAXIMUM_MIN_POINT_MAG_LINEAR_MIP_POINT: case DxilFilter::MAXIMUM_MIN_POINT_MAG_MIP_LINEAR: case DxilFilter::MAXIMUM_MIN_LINEAR_MAG_MIP_POINT: case DxilFilter::MAXIMUM_MIN_LINEAR_MAG_POINT_MIP_LINEAR: case DxilFilter::MAXIMUM_MIN_MAG_LINEAR_MIP_POINT: case DxilFilter::MAXIMUM_MIN_MAG_MIP_LINEAR: case DxilFilter::MAXIMUM_ANISOTROPIC: break; case DxilFilter::MIN_MAG_MIP_POINT: case DxilFilter::MIN_MAG_POINT_MIP_LINEAR: case DxilFilter::MIN_POINT_MAG_LINEAR_MIP_POINT: case DxilFilter::MIN_POINT_MAG_MIP_LINEAR: case DxilFilter::MIN_LINEAR_MAG_MIP_POINT: case DxilFilter::MIN_LINEAR_MAG_POINT_MIP_LINEAR: case DxilFilter::MIN_MAG_LINEAR_MIP_POINT: case DxilFilter::MIN_MAG_MIP_LINEAR: case DxilFilter::ANISOTROPIC: break; case DxilFilter::COMPARISON_MIN_MAG_MIP_POINT: case DxilFilter::COMPARISON_MIN_MAG_POINT_MIP_LINEAR: case DxilFilter::COMPARISON_MIN_POINT_MAG_LINEAR_MIP_POINT: case DxilFilter::COMPARISON_MIN_POINT_MAG_MIP_LINEAR: case DxilFilter::COMPARISON_MIN_LINEAR_MAG_MIP_POINT: case DxilFilter::COMPARISON_MIN_LINEAR_MAG_POINT_MIP_LINEAR: case DxilFilter::COMPARISON_MIN_MAG_LINEAR_MIP_POINT: case DxilFilter::COMPARISON_MIN_MAG_MIP_LINEAR: case DxilFilter::COMPARISON_ANISOTROPIC: bIsComparison = true; break; default: EAT(DiagPrinter << "Static sampler: Filter unrecognized.\n"); } if (!IsDxilTextureAddressMode(pDesc->AddressU)) { EAT(DiagPrinter << "Static sampler: AddressU unrecognized.\n"); } if (!IsDxilTextureAddressMode(pDesc->AddressV)) { EAT(DiagPrinter << "Static sampler: AddressV unrecognized.\n"); } if (!IsDxilTextureAddressMode(pDesc->AddressW)) { EAT(DiagPrinter << "Static sampler: AddressW unrecognized.\n"); } if (isNaN(pDesc->MipLODBias) || (pDesc->MipLODBias < DxilMipLodBiaxMin) || (pDesc->MipLODBias > DxilMipLodBiaxMax)) { EAT(DiagPrinter << "Static sampler: MipLODBias must be in the " << "range [" << DxilMipLodBiaxMin << " to " << DxilMipLodBiaxMax <<"]. " << pDesc->MipLODBias << "specified.\n"); } if (pDesc->MaxAnisotropy > DxilMapAnisotropy) { EAT(DiagPrinter << "Static sampler: MaxAnisotropy must be in " << "the range [0 to " << DxilMapAnisotropy << "]. " << pDesc->MaxAnisotropy << " specified.\n"); } if (bIsComparison && !IsDxilComparisonFunc(pDesc->ComparisonFunc)) { EAT(DiagPrinter << "Static sampler: ComparisonFunc unrecognized."); } if (isNaN(pDesc->MinLOD)) { EAT(DiagPrinter << "Static sampler: MinLOD be in the range [-INF to +INF]. " << pDesc->MinLOD << " specified.\n"); } if (isNaN(pDesc->MaxLOD)) { EAT(DiagPrinter << "Static sampler: MaxLOD be in the range [-INF to +INF]. " << pDesc->MaxLOD << " specified.\n"); } } ////////////////////////////////////////////////////////////////////////////// template void DeleteRootSignatureTemplate(const T &RS) { for (unsigned i = 0; i < RS.NumParameters; i++) { const auto &P = RS.pParameters[i]; if (P.ParameterType == DxilRootParameterType::DescriptorTable) { delete[] P.DescriptorTable.pDescriptorRanges; } } delete[] RS.pParameters; delete[] RS.pStaticSamplers; } void DeleteRootSignature(const DxilVersionedRootSignatureDesc * pRootSignature) { if (pRootSignature == nullptr) return; switch (pRootSignature->Version) { case DxilRootSignatureVersion::Version_1_0: DeleteRootSignatureTemplate(pRootSignature->Desc_1_0); break; case DxilRootSignatureVersion::Version_1_1: default: DXASSERT(pRootSignature->Version == DxilRootSignatureVersion::Version_1_1, "else version is incorrect"); DeleteRootSignatureTemplate(pRootSignature->Desc_1_1); break; } delete pRootSignature; } // GetFlags/SetFlags overloads. static DxilRootDescriptorFlags GetFlags(const DxilRootDescriptor &) { // Upconvert root parameter flags to be volatile. return DxilRootDescriptorFlags::DataVolatile; } static void SetFlags(DxilRootDescriptor &, DxilRootDescriptorFlags) { // Drop the flags; none existed in rs_1_0. } static DxilRootDescriptorFlags GetFlags(const DxilRootDescriptor1 &D) { return D.Flags; } static void SetFlags(DxilRootDescriptor1 &D, DxilRootDescriptorFlags Flags) { D.Flags = Flags; } static DxilRootDescriptorFlags GetFlags(const DxilContainerRootDescriptor1 &D) { return (DxilRootDescriptorFlags)D.Flags; } static void SetFlags(DxilContainerRootDescriptor1 &D, DxilRootDescriptorFlags Flags) { D.Flags = (uint32_t)Flags; } static DxilDescriptorRangeFlags GetFlags(const DxilDescriptorRange &D) { // Upconvert range flags to be volatile. DxilDescriptorRangeFlags Flags = DxilDescriptorRangeFlags::DescriptorsVolatile; // Sampler does not have data. if (D.RangeType != DxilDescriptorRangeType::Sampler) Flags = (DxilDescriptorRangeFlags)((unsigned)Flags | (unsigned)DxilDescriptorRangeFlags::DataVolatile); return Flags; } static void SetFlags(DxilDescriptorRange &, DxilDescriptorRangeFlags) { } static DxilDescriptorRangeFlags GetFlags(const DxilContainerDescriptorRange &D) { // Upconvert range flags to be volatile. DxilDescriptorRangeFlags Flags = DxilDescriptorRangeFlags::DescriptorsVolatile; // Sampler does not have data. if (D.RangeType != (uint32_t)DxilDescriptorRangeType::Sampler) Flags |= DxilDescriptorRangeFlags::DataVolatile; return Flags; } static void SetFlags(DxilContainerDescriptorRange &, DxilDescriptorRangeFlags) { } static DxilDescriptorRangeFlags GetFlags(const DxilDescriptorRange1 &D) { return D.Flags; } static void SetFlags(DxilDescriptorRange1 &D, DxilDescriptorRangeFlags Flags) { D.Flags = Flags; } static DxilDescriptorRangeFlags GetFlags(const DxilContainerDescriptorRange1 &D) { return (DxilDescriptorRangeFlags)D.Flags; } static void SetFlags(DxilContainerDescriptorRange1 &D, DxilDescriptorRangeFlags Flags) { D.Flags = (uint32_t)Flags; } template void ConvertRootSignatureTemplate(const IN_DXIL_ROOT_SIGNATURE_DESC &DescIn, DxilRootSignatureVersion DescVersionOut, OUT_DXIL_ROOT_SIGNATURE_DESC &DescOut) { const IN_DXIL_ROOT_SIGNATURE_DESC *pDescIn = &DescIn; OUT_DXIL_ROOT_SIGNATURE_DESC *pDescOut = &DescOut; // Root signature descriptor. pDescOut->Flags = pDescIn->Flags; pDescOut->NumParameters = 0; pDescOut->NumStaticSamplers = 0; // Intialize all pointers early so that clean up works properly. pDescOut->pParameters = nullptr; pDescOut->pStaticSamplers = nullptr; // Root signature parameters. if (pDescIn->NumParameters > 0) { pDescOut->pParameters = new OUT_DXIL_ROOT_PARAMETER[pDescIn->NumParameters]; pDescOut->NumParameters = pDescIn->NumParameters; memset((void *)pDescOut->pParameters, 0, pDescOut->NumParameters*sizeof(OUT_DXIL_ROOT_PARAMETER)); } for (unsigned iRP = 0; iRP < pDescIn->NumParameters; iRP++) { const auto &ParamIn = pDescIn->pParameters[iRP]; OUT_DXIL_ROOT_PARAMETER &ParamOut = (OUT_DXIL_ROOT_PARAMETER &)pDescOut->pParameters[iRP]; ParamOut.ParameterType = ParamIn.ParameterType; ParamOut.ShaderVisibility = ParamIn.ShaderVisibility; switch (ParamIn.ParameterType) { case DxilRootParameterType::DescriptorTable: { ParamOut.DescriptorTable.pDescriptorRanges = nullptr; unsigned NumRanges = ParamIn.DescriptorTable.NumDescriptorRanges; if (NumRanges > 0) { ParamOut.DescriptorTable.pDescriptorRanges = new OUT_DXIL_DESCRIPTOR_RANGE[NumRanges]; ParamOut.DescriptorTable.NumDescriptorRanges = NumRanges; } for (unsigned i = 0; i < NumRanges; i++) { const auto &RangeIn = ParamIn.DescriptorTable.pDescriptorRanges[i]; OUT_DXIL_DESCRIPTOR_RANGE &RangeOut = (OUT_DXIL_DESCRIPTOR_RANGE &)ParamOut.DescriptorTable.pDescriptorRanges[i]; RangeOut.RangeType = RangeIn.RangeType; RangeOut.NumDescriptors = RangeIn.NumDescriptors; RangeOut.BaseShaderRegister = RangeIn.BaseShaderRegister; RangeOut.RegisterSpace = RangeIn.RegisterSpace; RangeOut.OffsetInDescriptorsFromTableStart = RangeIn.OffsetInDescriptorsFromTableStart; DxilDescriptorRangeFlags Flags = GetFlags(RangeIn); SetFlags(RangeOut, Flags); } break; } case DxilRootParameterType::Constants32Bit: { ParamOut.Constants.Num32BitValues = ParamIn.Constants.Num32BitValues; ParamOut.Constants.ShaderRegister = ParamIn.Constants.ShaderRegister; ParamOut.Constants.RegisterSpace = ParamIn.Constants.RegisterSpace; break; } case DxilRootParameterType::CBV: case DxilRootParameterType::SRV: case DxilRootParameterType::UAV: { ParamOut.Descriptor.ShaderRegister = ParamIn.Descriptor.ShaderRegister; ParamOut.Descriptor.RegisterSpace = ParamIn.Descriptor.RegisterSpace; DxilRootDescriptorFlags Flags = GetFlags(ParamIn.Descriptor); SetFlags(ParamOut.Descriptor, Flags); break; } default: IFT(E_FAIL); } } // Static samplers. if (pDescIn->NumStaticSamplers > 0) { pDescOut->pStaticSamplers = new DxilStaticSamplerDesc[pDescIn->NumStaticSamplers]; pDescOut->NumStaticSamplers = pDescIn->NumStaticSamplers; memcpy((void*)pDescOut->pStaticSamplers, pDescIn->pStaticSamplers, pDescOut->NumStaticSamplers*sizeof(DxilStaticSamplerDesc)); } } void ConvertRootSignature(const DxilVersionedRootSignatureDesc * pRootSignatureIn, DxilRootSignatureVersion RootSignatureVersionOut, const DxilVersionedRootSignatureDesc ** ppRootSignatureOut) { IFTBOOL(pRootSignatureIn != nullptr && ppRootSignatureOut != nullptr, E_INVALIDARG); *ppRootSignatureOut = nullptr; if (pRootSignatureIn->Version == RootSignatureVersionOut){ // No conversion. Return the original root signature pointer; no cloning. *ppRootSignatureOut = pRootSignatureIn; return; } DxilVersionedRootSignatureDesc *pRootSignatureOut = nullptr; try { pRootSignatureOut = new DxilVersionedRootSignatureDesc(); memset(pRootSignatureOut, 0, sizeof(*pRootSignatureOut)); // Convert root signature. switch (RootSignatureVersionOut) { case DxilRootSignatureVersion::Version_1_0: switch (pRootSignatureIn->Version) { case DxilRootSignatureVersion::Version_1_1: pRootSignatureOut->Version = DxilRootSignatureVersion::Version_1_0; ConvertRootSignatureTemplate< DxilRootSignatureDesc1, DxilRootSignatureDesc, DxilRootParameter, DxilRootDescriptor, DxilDescriptorRange>(pRootSignatureIn->Desc_1_1, DxilRootSignatureVersion::Version_1_0, pRootSignatureOut->Desc_1_0); break; default: IFT(E_INVALIDARG); } break; case DxilRootSignatureVersion::Version_1_1: switch (pRootSignatureIn->Version) { case DxilRootSignatureVersion::Version_1_0: pRootSignatureOut->Version = DxilRootSignatureVersion::Version_1_1; ConvertRootSignatureTemplate< DxilRootSignatureDesc, DxilRootSignatureDesc1, DxilRootParameter1, DxilRootDescriptor1, DxilDescriptorRange1>(pRootSignatureIn->Desc_1_0, DxilRootSignatureVersion::Version_1_1, pRootSignatureOut->Desc_1_1); break; default: IFT(E_INVALIDARG); } break; default: IFT(E_INVALIDARG); break; } } catch (...) { DeleteRootSignature(pRootSignatureOut); throw; } *ppRootSignatureOut = pRootSignatureOut; } template void SerializeRootSignatureTemplate(_In_ const T_ROOT_SIGNATURE_DESC* pRootSignature, DxilRootSignatureVersion DescVersion, _COM_Outptr_ IDxcBlob** ppBlob, DiagnosticPrinter &DiagPrinter, _In_ bool bAllowReservedRegisterSpace) { DxilContainerRootSignatureDesc RS; uint32_t Offset; SimpleSerializer Serializer; IFT(Serializer.AddBlock(&RS, sizeof(RS), &Offset)); IFTBOOL(Offset == 0, E_FAIL); const T_ROOT_SIGNATURE_DESC *pRS = pRootSignature; RS.Version = (uint32_t)DescVersion; RS.Flags = (uint32_t)pRS->Flags; RS.NumParameters = pRS->NumParameters; RS.NumStaticSamplers = pRS->NumStaticSamplers; DxilContainerRootParameter *pRP; IFT(Serializer.ReserveBlock((void**)&pRP, sizeof(DxilContainerRootParameter)*RS.NumParameters, &RS.RootParametersOffset)); for (uint32_t iRP = 0; iRP < RS.NumParameters; iRP++) { const T_ROOT_PARAMETER *pInRP = &pRS->pParameters[iRP]; DxilContainerRootParameter *pOutRP = &pRP[iRP]; pOutRP->ParameterType = (uint32_t)pInRP->ParameterType; pOutRP->ShaderVisibility = (uint32_t)pInRP->ShaderVisibility; switch (pInRP->ParameterType) { case DxilRootParameterType::DescriptorTable: { DxilContainerRootDescriptorTable *p1; IFT(Serializer.ReserveBlock((void**)&p1, sizeof(DxilContainerRootDescriptorTable), &pOutRP->PayloadOffset)); p1->NumDescriptorRanges = pInRP->DescriptorTable.NumDescriptorRanges; T_DESCRIPTOR_RANGE_INTERNAL *p2; IFT(Serializer.ReserveBlock((void**)&p2, sizeof(T_DESCRIPTOR_RANGE_INTERNAL)*p1->NumDescriptorRanges, &p1->DescriptorRangesOffset)); for (uint32_t i = 0; i < p1->NumDescriptorRanges; i++) { p2[i].RangeType = (uint32_t)pInRP->DescriptorTable.pDescriptorRanges[i].RangeType; p2[i].NumDescriptors = pInRP->DescriptorTable.pDescriptorRanges[i].NumDescriptors; p2[i].BaseShaderRegister = pInRP->DescriptorTable.pDescriptorRanges[i].BaseShaderRegister; p2[i].RegisterSpace = pInRP->DescriptorTable.pDescriptorRanges[i].RegisterSpace; p2[i].OffsetInDescriptorsFromTableStart = pInRP->DescriptorTable.pDescriptorRanges[i].OffsetInDescriptorsFromTableStart; DxilDescriptorRangeFlags Flags = GetFlags(pInRP->DescriptorTable.pDescriptorRanges[i]); SetFlags(p2[i], Flags); } break; } case DxilRootParameterType::Constants32Bit: { DxilRootConstants *p; IFT(Serializer.ReserveBlock((void**)&p, sizeof(DxilRootConstants), &pOutRP->PayloadOffset)); p->Num32BitValues = pInRP->Constants.Num32BitValues; p->ShaderRegister = pInRP->Constants.ShaderRegister; p->RegisterSpace = pInRP->Constants.RegisterSpace; break; } case DxilRootParameterType::CBV: case DxilRootParameterType::SRV: case DxilRootParameterType::UAV: { T_ROOT_DESCRIPTOR_INTERNAL *p; IFT(Serializer.ReserveBlock((void**)&p, sizeof(T_ROOT_DESCRIPTOR_INTERNAL), &pOutRP->PayloadOffset)); p->ShaderRegister = pInRP->Descriptor.ShaderRegister; p->RegisterSpace = pInRP->Descriptor.RegisterSpace; DxilRootDescriptorFlags Flags = GetFlags(pInRP->Descriptor); SetFlags(*p, Flags); break; } default: EAT(DiagPrinter << "D3DSerializeRootSignature: unknown root parameter type (" << (uint32_t)pInRP->ParameterType << ")\n"); } } DxilStaticSamplerDesc *pSS; unsigned StaticSamplerSize = sizeof(DxilStaticSamplerDesc)*RS.NumStaticSamplers; IFT(Serializer.ReserveBlock((void**)&pSS, StaticSamplerSize, &RS.StaticSamplersOffset)); memcpy(pSS, pRS->pStaticSamplers, StaticSamplerSize); // Create the result blob. CDxcMallocHeapPtr bytes(DxcGetThreadMallocNoRef()); CComPtr pBlob; unsigned cb = Serializer.GetSize(); DXASSERT_NOMSG((cb & 0x3) == 0); IFTBOOL(bytes.Allocate(cb), E_OUTOFMEMORY); IFT(Serializer.Compact(bytes.m_pData, cb)); IFT(DxcCreateBlobOnHeap(bytes.m_pData, cb, ppBlob)); bytes.Detach(); // Ownership transfered to ppBlob. } _Use_decl_annotations_ void SerializeRootSignature(const DxilVersionedRootSignatureDesc *pRootSignature, IDxcBlob **ppBlob, IDxcBlobEncoding **ppErrorBlob, bool bAllowReservedRegisterSpace) { DXASSERT_NOMSG(pRootSignature != nullptr); DXASSERT_NOMSG(ppBlob != nullptr); DXASSERT_NOMSG(ppErrorBlob != nullptr); *ppBlob = nullptr; *ppErrorBlob = nullptr; RootSignatureVerifier RSV; // TODO: change SerializeRootSignature to take raw_ostream& string DiagString; raw_string_ostream DiagStream(DiagString); DiagnosticPrinterRawOStream DiagPrinter(DiagStream); // Verify root signature. RSV.AllowReservedRegisterSpace(bAllowReservedRegisterSpace); try { RSV.VerifyRootSignature(pRootSignature, DiagPrinter); switch (pRootSignature->Version) { case DxilRootSignatureVersion::Version_1_0: SerializeRootSignatureTemplate< DxilRootSignatureDesc, DxilRootParameter, DxilRootDescriptor, DxilContainerDescriptorRange>(&pRootSignature->Desc_1_0, DxilRootSignatureVersion::Version_1_0, ppBlob, DiagPrinter, bAllowReservedRegisterSpace); break; case DxilRootSignatureVersion::Version_1_1: default: DXASSERT(pRootSignature->Version == DxilRootSignatureVersion::Version_1_1, "else VerifyRootSignature didn't validate"); SerializeRootSignatureTemplate< DxilRootSignatureDesc1, DxilRootParameter1, DxilContainerRootDescriptor1, DxilContainerDescriptorRange1>(&pRootSignature->Desc_1_1, DxilRootSignatureVersion::Version_1_1, ppBlob, DiagPrinter, bAllowReservedRegisterSpace); break; } } catch (...) { DiagStream.flush(); DxcCreateBlobWithEncodingOnHeapCopy(DiagString.c_str(), DiagString.size(), CP_UTF8, ppErrorBlob); } } //============================================================================= // // CVersionedRootSignatureDeserializer. // //============================================================================= class CVersionedRootSignatureDeserializer { protected: const DxilVersionedRootSignatureDesc *m_pRootSignature; const DxilVersionedRootSignatureDesc *m_pRootSignature10; const DxilVersionedRootSignatureDesc *m_pRootSignature11; public: CVersionedRootSignatureDeserializer(); ~CVersionedRootSignatureDeserializer(); void Initialize(_In_reads_bytes_(SrcDataSizeInBytes) const void *pSrcData, _In_ uint32_t SrcDataSizeInBytes); const DxilVersionedRootSignatureDesc *GetRootSignatureDescAtVersion(DxilRootSignatureVersion convertToVersion); const DxilVersionedRootSignatureDesc *GetUnconvertedRootSignatureDesc(); }; CVersionedRootSignatureDeserializer::CVersionedRootSignatureDeserializer() : m_pRootSignature(nullptr) , m_pRootSignature10(nullptr) , m_pRootSignature11(nullptr) { } CVersionedRootSignatureDeserializer::~CVersionedRootSignatureDeserializer() { DeleteRootSignature(m_pRootSignature10); DeleteRootSignature(m_pRootSignature11); } void CVersionedRootSignatureDeserializer::Initialize(_In_reads_bytes_(SrcDataSizeInBytes) const void *pSrcData, _In_ uint32_t SrcDataSizeInBytes) { const DxilVersionedRootSignatureDesc *pRootSignature = nullptr; DeserializeRootSignature(pSrcData, SrcDataSizeInBytes, &pRootSignature); switch (pRootSignature->Version) { case DxilRootSignatureVersion::Version_1_0: m_pRootSignature10 = pRootSignature; break; case DxilRootSignatureVersion::Version_1_1: m_pRootSignature11 = pRootSignature; break; default: DeleteRootSignature(pRootSignature); return; } m_pRootSignature = pRootSignature; } const DxilVersionedRootSignatureDesc * CVersionedRootSignatureDeserializer::GetUnconvertedRootSignatureDesc() { return m_pRootSignature; } const DxilVersionedRootSignatureDesc * CVersionedRootSignatureDeserializer::GetRootSignatureDescAtVersion(DxilRootSignatureVersion ConvertToVersion) { switch (ConvertToVersion) { case DxilRootSignatureVersion::Version_1_0: if (m_pRootSignature10 == nullptr) { ConvertRootSignature(m_pRootSignature, ConvertToVersion, (const DxilVersionedRootSignatureDesc **)&m_pRootSignature10); } return m_pRootSignature10; case DxilRootSignatureVersion::Version_1_1: if (m_pRootSignature11 == nullptr) { ConvertRootSignature(m_pRootSignature, ConvertToVersion, (const DxilVersionedRootSignatureDesc **)&m_pRootSignature11); } return m_pRootSignature11; default: IFTBOOL(false, E_FAIL); } return nullptr; } template void DeserializeRootSignatureTemplate(_In_reads_bytes_(SrcDataSizeInBytes) const void *pSrcData, _In_ uint32_t SrcDataSizeInBytes, DxilRootSignatureVersion DescVersion, T_ROOT_SIGNATURE_DESC &RootSignatureDesc) { // Note that in case of failure, outside code must deallocate memory. T_ROOT_SIGNATURE_DESC *pRootSignature = &RootSignatureDesc; const char *pData = (const char *)pSrcData; const char *pMaxPtr = pData + SrcDataSizeInBytes; UNREFERENCED_PARAMETER(DescVersion); DXASSERT_NOMSG(((uint32_t*)pData)[0] == (uint32_t)DescVersion); // Root signature. IFTBOOL(pData + sizeof(DxilContainerRootSignatureDesc) <= pMaxPtr, E_FAIL); DxilContainerRootSignatureDesc *pRS = (DxilContainerRootSignatureDesc *)pData; pRootSignature->Flags = (DxilRootSignatureFlags)pRS->Flags; pRootSignature->NumParameters = pRS->NumParameters; pRootSignature->NumStaticSamplers = pRS->NumStaticSamplers; // Intialize all pointers early so that clean up works properly. pRootSignature->pParameters = nullptr; pRootSignature->pStaticSamplers = nullptr; size_t s = sizeof(DxilContainerRootParameter)*pRS->NumParameters; DxilContainerRootParameter *pInRTS = (DxilContainerRootParameter *)(pData + pRS->RootParametersOffset); IFTBOOL(((char*)pInRTS) + s <= pMaxPtr, E_FAIL); if (pRootSignature->NumParameters) { pRootSignature->pParameters = new T_ROOT_PARAMETER[pRootSignature->NumParameters]; } memset((void *)pRootSignature->pParameters, 0, s); for(unsigned iRP = 0; iRP < pRootSignature->NumParameters; iRP++) { DxilRootParameterType ParameterType = (DxilRootParameterType)pInRTS[iRP].ParameterType; T_ROOT_PARAMETER *pOutRTS = (T_ROOT_PARAMETER *)&pRootSignature->pParameters[iRP]; pOutRTS->ParameterType = ParameterType; pOutRTS->ShaderVisibility = (DxilShaderVisibility)pInRTS[iRP].ShaderVisibility; switch(ParameterType) { case DxilRootParameterType::DescriptorTable: { DxilContainerRootDescriptorTable *p1 = (DxilContainerRootDescriptorTable*)(pData + pInRTS[iRP].PayloadOffset); IFTBOOL((char*)p1 + sizeof(DxilContainerRootDescriptorTable) <= pMaxPtr, E_FAIL); pOutRTS->DescriptorTable.NumDescriptorRanges = p1->NumDescriptorRanges; pOutRTS->DescriptorTable.pDescriptorRanges = nullptr; T_DESCRIPTOR_RANGE_INTERNAL *p2 = (T_DESCRIPTOR_RANGE_INTERNAL*)(pData + p1->DescriptorRangesOffset); IFTBOOL((char*)p2 + sizeof(T_DESCRIPTOR_RANGE_INTERNAL) <= pMaxPtr, E_FAIL); if (p1->NumDescriptorRanges) { pOutRTS->DescriptorTable.pDescriptorRanges = new T_DESCRIPTOR_RANGE[p1->NumDescriptorRanges]; } for (unsigned i = 0; i < p1->NumDescriptorRanges; i++) { T_DESCRIPTOR_RANGE *p3 = (T_DESCRIPTOR_RANGE *)&pOutRTS->DescriptorTable.pDescriptorRanges[i]; p3->RangeType = (DxilDescriptorRangeType)p2[i].RangeType; p3->NumDescriptors = p2[i].NumDescriptors; p3->BaseShaderRegister = p2[i].BaseShaderRegister; p3->RegisterSpace = p2[i].RegisterSpace; p3->OffsetInDescriptorsFromTableStart = p2[i].OffsetInDescriptorsFromTableStart; DxilDescriptorRangeFlags Flags = GetFlags(p2[i]); SetFlags(*p3, Flags); } break; } case DxilRootParameterType::Constants32Bit: { DxilRootConstants *p = (DxilRootConstants*)(pData + pInRTS[iRP].PayloadOffset); IFTBOOL((char*)p + sizeof(DxilRootConstants) <= pMaxPtr, E_FAIL); pOutRTS->Constants.Num32BitValues = p->Num32BitValues; pOutRTS->Constants.ShaderRegister = p->ShaderRegister; pOutRTS->Constants.RegisterSpace = p->RegisterSpace; break; } case DxilRootParameterType::CBV: case DxilRootParameterType::SRV: case DxilRootParameterType::UAV: { T_ROOT_DESCRIPTOR *p = (T_ROOT_DESCRIPTOR *)(pData + pInRTS[iRP].PayloadOffset); IFTBOOL((char*)p + sizeof(T_ROOT_DESCRIPTOR) <= pMaxPtr, E_FAIL); pOutRTS->Descriptor.ShaderRegister = p->ShaderRegister; pOutRTS->Descriptor.RegisterSpace = p->RegisterSpace; DxilRootDescriptorFlags Flags = GetFlags(*p); SetFlags(pOutRTS->Descriptor, Flags); break; } default: IFT(E_FAIL); } } s = sizeof(DxilStaticSamplerDesc)*pRS->NumStaticSamplers; DxilStaticSamplerDesc *pInSS = (DxilStaticSamplerDesc *)(pData + pRS->StaticSamplersOffset); IFTBOOL(((char*)pInSS) + s <= pMaxPtr, E_FAIL); if (pRootSignature->NumStaticSamplers) { pRootSignature->pStaticSamplers = new DxilStaticSamplerDesc[pRootSignature->NumStaticSamplers]; } memcpy((void*)pRootSignature->pStaticSamplers, pInSS, s); } _Use_decl_annotations_ void DeserializeRootSignature(const void *pSrcData, uint32_t SrcDataSizeInBytes, const DxilVersionedRootSignatureDesc **ppRootSignature) { DxilVersionedRootSignatureDesc *pRootSignature = nullptr; IFTBOOL(pSrcData != nullptr && SrcDataSizeInBytes != 0 && ppRootSignature != nullptr, E_INVALIDARG); IFTBOOL(*ppRootSignature == nullptr, E_INVALIDARG); const char *pData = (const char *)pSrcData; IFTBOOL(pData + sizeof(uint32_t) < pData + SrcDataSizeInBytes, E_FAIL); DxilRootSignatureVersion Version = (DxilRootSignatureVersion)((uint32_t*)pData)[0]; pRootSignature = new DxilVersionedRootSignatureDesc(); try { switch (Version) { case DxilRootSignatureVersion::Version_1_0: pRootSignature->Version = DxilRootSignatureVersion::Version_1_0; DeserializeRootSignatureTemplate< DxilRootSignatureDesc, DxilRootParameter, DxilRootDescriptor, DxilRootDescriptor, DxilDescriptorRange, DxilContainerDescriptorRange>(pSrcData, SrcDataSizeInBytes, DxilRootSignatureVersion::Version_1_0, pRootSignature->Desc_1_0); break; case DxilRootSignatureVersion::Version_1_1: pRootSignature->Version = DxilRootSignatureVersion::Version_1_1; DeserializeRootSignatureTemplate< DxilRootSignatureDesc1, DxilRootParameter1, DxilRootDescriptor1, DxilContainerRootDescriptor1, DxilDescriptorRange1, DxilContainerDescriptorRange1>(pSrcData, SrcDataSizeInBytes, DxilRootSignatureVersion::Version_1_1, pRootSignature->Desc_1_1); break; default: IFT(E_FAIL); break; } } catch(...) { DeleteRootSignature(pRootSignature); throw; } *ppRootSignature = pRootSignature; } static DxilShaderVisibility GetVisibilityType(DXIL::ShaderKind ShaderKind) { switch(ShaderKind) { case DXIL::ShaderKind::Pixel: return DxilShaderVisibility::Pixel; case DXIL::ShaderKind::Vertex: return DxilShaderVisibility::Vertex; case DXIL::ShaderKind::Geometry: return DxilShaderVisibility::Geometry; case DXIL::ShaderKind::Hull: return DxilShaderVisibility::Hull; case DXIL::ShaderKind::Domain: return DxilShaderVisibility::Domain; default: return DxilShaderVisibility::All; } } _Use_decl_annotations_ bool VerifyRootSignatureWithShaderPSV(const DxilVersionedRootSignatureDesc *pDesc, DXIL::ShaderKind ShaderKind, const void *pPSVData, uint32_t PSVSize, llvm::raw_ostream &DiagStream) { try { RootSignatureVerifier RSV; DiagnosticPrinterRawOStream DiagPrinter(DiagStream); RSV.VerifyRootSignature(pDesc, DiagPrinter); RSV.VerifyShader(GetVisibilityType(ShaderKind), pPSVData, PSVSize, DiagPrinter); } catch (...) { return false; } return true; } } // namespace hlsl