| /////////////////////////////////////////////////////////////////////////////// |
| // // |
| // 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/DXIL/DxilConstants.h" |
| #include "dxc/DxilRootSignature/DxilRootSignature.h" |
| #include "dxc/Support/FileIOHelper.h" |
| #include "dxc/Support/Global.h" |
| #include "dxc/Support/WinFunctions.h" |
| #include "dxc/Support/WinIncludes.h" |
| #include "dxc/dxcapi.h" |
| |
| #include "llvm/IR/DiagnosticPrinter.h" |
| #include "llvm/Support/raw_ostream.h" |
| |
| #include <algorithm> |
| #include <ios> |
| #include <set> |
| #include <string> |
| #include <utility> |
| #include <vector> |
| |
| #include "dxc/DxilContainer/DxilPipelineStateValidation.h" |
| #include <assert.h> // Needed for DxilPipelineStateValidation.h |
| |
| #include "DxilRootSignatureHelper.h" |
| |
| using namespace llvm; |
| using std::string; |
| |
| namespace hlsl { |
| |
| using namespace root_sig_helper; |
| |
| ////////////////////////////////////////////////////////////////////////////// |
| // Interval helper. |
| |
| template <typename T> class CIntervalCollection { |
| private: |
| std::set<T> 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::MaxValue; |
| static const unsigned kMinDescType = (unsigned)DxilDescriptorRangeType::SRV; |
| static const unsigned kMaxDescType = |
| (unsigned)DxilDescriptorRangeType::MaxValue; |
| |
| 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<RegisterRange> 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: |
| static_assert(DxilDescriptorRangeType::Sampler == |
| DxilDescriptorRangeType::MaxValue, |
| "otherwise, need to update cases here"); |
| 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"}; |
| static_assert(_countof(RangeType) == |
| ((unsigned)DxilDescriptorRangeType::MaxValue + 1), |
| "otherwise, need to update name array"); |
| return (rt <= DxilDescriptorRangeType::MaxValue) ? RangeType[(unsigned)rt] |
| : "unknown"; |
| } |
| |
| const char *VisTypeString(DxilShaderVisibility vis) { |
| static const char *Vis[] = {"ALL", "VERTEX", "HULL", "DOMAIN", |
| "GEOMETRY", "PIXEL", "AMPLIFICATION", "MESH"}; |
| static_assert(_countof(Vis) == ((unsigned)DxilShaderVisibility::MaxValue + 1), |
| "otherwise, need to update name array"); |
| unsigned idx = (unsigned)vis; |
| return vis <= DxilShaderVisibility::MaxValue ? Vis[idx] : "unknown"; |
| } |
| |
| static bool IsDxilShaderVisibility(DxilShaderVisibility v) { |
| return v <= DxilShaderVisibility::MaxValue; |
| } |
| |
| 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: |
| static_assert(DxilRootParameterType::UAV == DxilRootParameterType::MaxValue, |
| "otherwise, need to add cases here."); |
| 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: |
| static_assert(DxilRootParameterType::UAV == |
| DxilRootParameterType::MaxValue, |
| "otherwise, need to add cases here."); |
| 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; |
| case DxilShaderVisibility::Amplification: |
| if ((m_RootSignatureFlags & |
| DxilRootSignatureFlags::DenyAmplificationShaderRootAccess) != |
| DxilRootSignatureFlags::None) { |
| bShaderDeniedByRootSig = true; |
| } |
| break; |
| case DxilShaderVisibility::Mesh: |
| if ((m_RootSignatureFlags & |
| DxilRootSignatureFlags::DenyMeshShaderRootAccess) != |
| 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"); |
| } |
| } |
| |
| 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; |
| case DXIL::ShaderKind::Amplification: |
| return DxilShaderVisibility::Amplification; |
| case DXIL::ShaderKind::Mesh: |
| return DxilShaderVisibility::Mesh; |
| default: |
| return DxilShaderVisibility::All; |
| } |
| } |
| |
| 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; |
| } |
| |
| bool VerifyRootSignature(const DxilVersionedRootSignatureDesc *pDesc, |
| llvm::raw_ostream &DiagStream, |
| bool bAllowReservedRegisterSpace) { |
| try { |
| RootSignatureVerifier RSV; |
| RSV.AllowReservedRegisterSpace(bAllowReservedRegisterSpace); |
| DiagnosticPrinterRawOStream DiagPrinter(DiagStream); |
| RSV.VerifyRootSignature(pDesc, DiagPrinter); |
| } catch (...) { |
| return false; |
| } |
| |
| return true; |
| } |
| |
| } // namespace hlsl |