diff --git a/docs/ReleaseNotes.md b/docs/ReleaseNotes.md index 19c0d75174..200e1c5310 100644 --- a/docs/ReleaseNotes.md +++ b/docs/ReleaseNotes.md @@ -30,6 +30,8 @@ line upon naming the release. Refer to previous for appropriate section names. - SPIR-V: Fixed an invalid `OpSelect` being generated when optimizing for SPIR-V 1.3 and earlier [#8603](https://github.com/microsoft/DirectXShaderCompiler/issues/8603). +- SPIR-V: Fixed user-defined types whose names match built-in resource types + being incorrectly lowered as resources. #### HLSL Language diff --git a/tools/clang/include/clang/AST/HlslTypes.h b/tools/clang/include/clang/AST/HlslTypes.h index cc72c51048..3524f567dd 100644 --- a/tools/clang/include/clang/AST/HlslTypes.h +++ b/tools/clang/include/clang/AST/HlslTypes.h @@ -520,6 +520,14 @@ bool IsHLSLRayQueryType(clang::QualType type); bool GetHLSLNodeIORecordType(const clang::ParmVarDecl *parmDecl, NodeFlags &nodeKind); +/// Returns the DXIL ResourceKind for this type, or ResourceKind::Invalid if +/// not an HLSL resource type. +DXIL::ResourceKind GetHLSLResourceKind(clang::QualType type); + +/// Returns the DXIL ResourceClass for this type, or ResourceClass::Invalid if +/// not an HLSL resource type. +DXIL::ResourceClass GetHLSLResourceClass(clang::QualType type); + bool IsArrayConstantStringType(const clang::QualType type); bool IsPointerStringType(const clang::QualType type); bool IsStringType(const clang::QualType type); diff --git a/tools/clang/include/clang/Basic/Attr.td b/tools/clang/include/clang/Basic/Attr.td index 21b57c8934..31e0feaa93 100644 --- a/tools/clang/include/clang/Basic/Attr.td +++ b/tools/clang/include/clang/Basic/Attr.td @@ -1205,6 +1205,16 @@ def HLSLHitObject : InheritableAttr { let Documentation = [Undocumented]; } +// Vulkan SubpassInput / SubpassInputMS Attribute +// Marks the built-in SubpassInput and SubpassInputMS record types as +// distinct from any user-defined type with the same unqualified name. +def HLSLVkSubpassInput : InheritableAttr { + let Spellings = []; // No spellings! + let Args = [BoolArgument<"IsMultiSampled">]; + let Subjects = SubjectList<[CXXRecord]>; + let Documentation = [Undocumented]; +} + // HLSL Dynamic Resource Attribute // Marks the builtin `.Resource` / `.Sampler` placeholder record types used // for descriptor heap indexing (ResourceDescriptorHeap[i] / diff --git a/tools/clang/lib/AST/HlslTypes.cpp b/tools/clang/lib/AST/HlslTypes.cpp index eeed7fe8bf..da26d5b72f 100644 --- a/tools/clang/lib/AST/HlslTypes.cpp +++ b/tools/clang/lib/AST/HlslTypes.cpp @@ -615,6 +615,18 @@ bool IsHLSLStructuredBufferType(clang::QualType type) { return false; } +DXIL::ResourceKind GetHLSLResourceKind(clang::QualType type) { + if (const HLSLResourceAttr *Attr = getAttr(type)) + return Attr->getResKind(); + return DXIL::ResourceKind::Invalid; +} + +DXIL::ResourceClass GetHLSLResourceClass(clang::QualType type) { + if (const HLSLResourceAttr *Attr = getAttr(type)) + return Attr->getResClass(); + return DXIL::ResourceClass::Invalid; +} + bool IsHLSLSubobjectType(clang::QualType type) { DXIL::SubobjectKind kind; DXIL::HitGroupType hgType; diff --git a/tools/clang/lib/SPIRV/AstTypeProbe.cpp b/tools/clang/lib/SPIRV/AstTypeProbe.cpp index a871bd3577..ebbfe0611a 100644 --- a/tools/clang/lib/SPIRV/AstTypeProbe.cpp +++ b/tools/clang/lib/SPIRV/AstTypeProbe.cpp @@ -296,31 +296,21 @@ bool isMxNMatrix(QualType type, QualType *elemType, uint32_t *numRows, return false; } -bool isInputPatch(QualType type) { - if (const auto *rt = type->getAs()) - return rt->getDecl()->getName() == "InputPatch"; +bool isInputPatch(QualType type) { return hlsl::IsHLSLInputPatchType(type); } - return false; -} - -bool isOutputPatch(QualType type) { - if (const auto *rt = type->getAs()) - return rt->getDecl()->getName() == "OutputPatch"; - - return false; -} +bool isOutputPatch(QualType type) { return hlsl::IsHLSLOutputPatchType(type); } bool isSubpassInput(QualType type) { - if (const auto *rt = type->getAs()) - return rt->getDecl()->getName() == "SubpassInput"; - + if (const HLSLVkSubpassInputAttr *Attr = + getAttr(type)) + return !Attr->getIsMultiSampled(); return false; } bool isSubpassInputMS(QualType type) { - if (const auto *rt = type->getAs()) - return rt->getDecl()->getName() == "SubpassInputMS"; - + if (const HLSLVkSubpassInputAttr *Attr = + getAttr(type)) + return Attr->getIsMultiSampled(); return false; } @@ -339,22 +329,14 @@ bool isConstantBuffer(clang::QualType type) { // Strip outer arrayness first while (type->isArrayType()) type = type->getAsArrayTypeUnsafe()->getElementType(); - if (const RecordType *RT = type->getAs()) { - StringRef name = RT->getDecl()->getName(); - return name == "ConstantBuffer"; - } - return false; + return hlsl::GetHLSLResourceKind(type) == hlsl::DXIL::ResourceKind::CBuffer; } bool isTextureBuffer(clang::QualType type) { // Strip outer arrayness first while (type->isArrayType()) type = type->getAsArrayTypeUnsafe()->getElementType(); - if (const RecordType *RT = type->getAs()) { - StringRef name = RT->getDecl()->getName(); - return name == "TextureBuffer"; - } - return false; + return hlsl::GetHLSLResourceKind(type) == hlsl::DXIL::ResourceKind::TBuffer; } bool isConstantTextureBuffer(QualType type) { @@ -882,67 +864,45 @@ bool isStructuredBuffer(QualType type) { const auto *recordType = type->getAs(); if (!recordType) return false; + if (!hlsl::IsHLSLStructuredBufferType(type)) + return false; + // Exclude AppendStructuredBuffer and ConsumeStructuredBuffer, which share + // the same ResourceKind but have distinct access semantics. const auto name = recordType->getDecl()->getName(); - return name == "StructuredBuffer" || name == "RWStructuredBuffer" || - name == "RasterizerOrderedStructuredBuffer"; + return name != "AppendStructuredBuffer" && name != "ConsumeStructuredBuffer"; } bool isNonWritableStructuredBuffer(QualType type) { - const auto *recordType = type->getAs(); - if (!recordType) - return false; - const auto name = recordType->getDecl()->getName(); - return name == "StructuredBuffer"; + return hlsl::IsHLSLStructuredBufferType(type) && + hlsl::GetHLSLResourceClass(type) == hlsl::DXIL::ResourceClass::SRV; } bool isByteAddressBuffer(QualType type) { - if (const auto *rt = type->getAs()) { - return rt->getDecl()->getName() == "ByteAddressBuffer"; - } - return false; + return hlsl::GetHLSLResourceKind(type) == + hlsl::DXIL::ResourceKind::RawBuffer && + hlsl::GetHLSLResourceClass(type) == hlsl::DXIL::ResourceClass::SRV; } bool isRWBuffer(QualType type) { - if (const auto *rt = type->getAs()) { - const auto name = rt->getDecl()->getName(); - return name == "RWBuffer" || name == "RasterizerOrderedBuffer"; - } - return false; + return hlsl::GetHLSLResourceKind(type) == + hlsl::DXIL::ResourceKind::TypedBuffer && + hlsl::GetHLSLResourceClass(type) == hlsl::DXIL::ResourceClass::UAV; } bool isBuffer(QualType type) { - if (const auto *rt = type->getAs()) { - return rt->getDecl()->getName() == "Buffer"; - } - return false; + return hlsl::GetHLSLResourceKind(type) == + hlsl::DXIL::ResourceKind::TypedBuffer && + hlsl::GetHLSLResourceClass(type) == hlsl::DXIL::ResourceClass::SRV; } bool isRWTexture(QualType type) { - if (const auto *rt = type->getAs()) { - const auto name = rt->getDecl()->getName(); - if (name == "RWTexture1D" || name == "RWTexture1DArray" || - name == "RWTexture2D" || name == "RWTexture2DArray" || - name == "RWTexture3D" || name == "RasterizerOrderedTexture1D" || - name == "RasterizerOrderedTexture1DArray" || - name == "RasterizerOrderedTexture2D" || - name == "RasterizerOrderedTexture2DArray" || - name == "RasterizerOrderedTexture3D") - return true; - } - return false; + return hlsl::DXIL::IsAnyTexture(hlsl::GetHLSLResourceKind(type)) && + hlsl::GetHLSLResourceClass(type) == hlsl::DXIL::ResourceClass::UAV; } bool isTexture(QualType type) { - if (const auto *rt = type->getAs()) { - const auto name = rt->getDecl()->getName(); - if (name == "Texture1D" || name == "Texture1DArray" || - name == "Texture2D" || name == "Texture2DArray" || - name == "Texture2DMS" || name == "Texture2DMSArray" || - name == "TextureCube" || name == "TextureCubeArray" || - name == "Texture3D") - return true; - } - return false; + return hlsl::DXIL::IsAnyTexture(hlsl::GetHLSLResourceKind(type)) && + hlsl::GetHLSLResourceClass(type) == hlsl::DXIL::ResourceClass::SRV; } bool isSampledTexture(QualType type) { @@ -954,12 +914,9 @@ bool isSampledTexture(QualType type) { } bool isTextureMS(QualType type) { - if (const auto *rt = type->getAs()) { - const auto name = rt->getDecl()->getName(); - if (name == "Texture2DMS" || name == "Texture2DMSArray") - return true; - } - return false; + auto resKind = hlsl::GetHLSLResourceKind(type); + return resKind == hlsl::DXIL::ResourceKind::Texture2DMS || + resKind == hlsl::DXIL::ResourceKind::Texture2DMSArray; } bool isSampledTextureMS(QualType type) { @@ -975,58 +932,51 @@ bool isSampledTextureMS(QualType type) { } bool isSampler(QualType type) { - if (const auto *rt = type->getAs()) { - const auto name = rt->getDecl()->getName(); - if (name == "SamplerState" || name == "SamplerComparisonState") - return true; - } - return false; + return hlsl::GetHLSLResourceKind(type) == hlsl::DXIL::ResourceKind::Sampler; } bool isRWByteAddressBuffer(QualType type) { - if (const auto *rt = type->getAs()) { - const auto name = rt->getDecl()->getName(); - return name == "RWByteAddressBuffer" || - name == "RasterizerOrderedByteAddressBuffer"; - } - return false; + return hlsl::GetHLSLResourceKind(type) == + hlsl::DXIL::ResourceKind::RawBuffer && + hlsl::GetHLSLResourceClass(type) == hlsl::DXIL::ResourceClass::UAV; } bool isAppendStructuredBuffer(QualType type) { // Strip outer arrayness first while (type->isArrayType()) type = type->getAsArrayTypeUnsafe()->getElementType(); - - const auto *recordType = type->getAs(); - if (!recordType) + if (!hlsl::IsHLSLStructuredBufferType(type)) return false; - const auto name = recordType->getDecl()->getName(); - return name == "AppendStructuredBuffer"; + const auto *recordType = type->getAs(); + return recordType && + recordType->getDecl()->getName() == "AppendStructuredBuffer"; } bool isConsumeStructuredBuffer(QualType type) { // Strip outer arrayness first while (type->isArrayType()) type = type->getAsArrayTypeUnsafe()->getElementType(); - - const auto *recordType = type->getAs(); - if (!recordType) + if (!hlsl::IsHLSLStructuredBufferType(type)) return false; - const auto name = recordType->getDecl()->getName(); - return name == "ConsumeStructuredBuffer"; + const auto *recordType = type->getAs(); + return recordType && + recordType->getDecl()->getName() == "ConsumeStructuredBuffer"; } bool isRWStructuredBuffer(QualType type) { // Strip outer arrayness first while (type->isArrayType()) type = type->getAsArrayTypeUnsafe()->getElementType(); - - if (const RecordType *recordType = type->getAs()) { - StringRef name = recordType->getDecl()->getName(); - return name == "RWStructuredBuffer" || - name == "RasterizerOrderedStructuredBuffer"; - } - return false; + if (!hlsl::IsHLSLStructuredBufferType(type)) + return false; + if (hlsl::GetHLSLResourceClass(type) != hlsl::DXIL::ResourceClass::UAV) + return false; + // Exclude AppendStructuredBuffer and ConsumeStructuredBuffer. + const auto *recordType = type->getAs(); + if (!recordType) + return false; + const auto name = recordType->getDecl()->getName(); + return name != "AppendStructuredBuffer" && name != "ConsumeStructuredBuffer"; } bool isRWAppendConsumeSBuffer(QualType type) { @@ -1059,33 +1009,27 @@ bool isAKindOfStructuredOrByteBuffer(QualType type) { while (type->isArrayType()) type = type->getAsArrayTypeUnsafe()->getElementType(); - if (const RecordType *recordType = type->getAs()) { - StringRef name = recordType->getDecl()->getName(); - return name == "StructuredBuffer" || name == "RWStructuredBuffer" || - name == "RasterizerOrderedStructuredBuffer" || - name == "ByteAddressBuffer" || name == "RWByteAddressBuffer" || - name == "RasterizerOrderedByteAddressBuffer" || - name == "AppendStructuredBuffer" || - name == "ConsumeStructuredBuffer" || name == ".Resource" || - name == ".Sampler"; - } - return false; + // Use attribute-based check to avoid false positives with user-defined types + // that share names with HLSL built-in types. + auto resKind = hlsl::GetHLSLResourceKind(type); + if (resKind == hlsl::DXIL::ResourceKind::StructuredBuffer || + resKind == hlsl::DXIL::ResourceKind::RawBuffer) + return true; + + // Dynamic resource placeholder types (.Resource and .Sampler) also behave + // like structured buffers for layout purposes. + return hlsl::IsHLSLDynamicResourceType(type) || + hlsl::IsHLSLDynamicSamplerType(type); } bool isOrContainsAKindOfStructuredOrByteBuffer(QualType type) { while (type->isArrayType()) type = type->getAsArrayTypeUnsafe()->getElementType(); - if (const RecordType *recordType = type->getAs()) { - StringRef name = recordType->getDecl()->getName(); - if (name == "StructuredBuffer" || name == "RWStructuredBuffer" || - name == "RasterizerOrderedStructuredBuffer" || - name == "ByteAddressBuffer" || name == "RWByteAddressBuffer" || - name == "RasterizerOrderedByteAddressBuffer" || - name == "AppendStructuredBuffer" || name == "ConsumeStructuredBuffer" || - name == ".Resource" || name == ".Sampler") - return true; + if (isAKindOfStructuredOrByteBuffer(type)) + return true; + if (const RecordType *recordType = type->getAs()) { for (const auto *field : recordType->getDecl()->fields()) { if (isOrContainsAKindOfStructuredOrByteBuffer(field->getType())) return true; @@ -1103,51 +1047,15 @@ bool isOrContainsAKindOfStructuredOrByteBuffer(QualType type) { } bool isOpaqueType(QualType type) { - if (const auto *recordType = type->getAs()) { - const auto name = recordType->getDecl()->getName(); - - if (name == "Texture1D" || name == "RWTexture1D" || - name == "RasterizerOrderedTexture1D") - return true; - if (name == "Texture2D" || name == "RWTexture2D" || - name == "RasterizerOrderedTexture2D") - return true; - if (name == "Texture2DMS" || name == "RWTexture2DMS") - return true; - if (name == "Texture3D" || name == "RWTexture3D" || - name == "RasterizerOrderedTexture3D") - return true; - if (name == "TextureCube" || name == "RWTextureCube") - return true; - - if (name == "Texture1DArray" || name == "RWTexture1DArray" || - name == "RasterizerOrderedTexture1DArray") - return true; - if (name == "Texture2DArray" || name == "RWTexture2DArray" || - name == "RasterizerOrderedTexture2DArray") - return true; - if (name == "Texture2DMSArray" || name == "RWTexture2DMSArray") - return true; - if (name == "TextureCubeArray" || name == "RWTextureCubeArray") - return true; - - if (name == "Buffer" || name == "RWBuffer" || - name == "RasterizerOrderedBuffer") - return true; - - if (name == "SamplerState" || name == "SamplerComparisonState") - return true; - - if (name == "RaytracingAccelerationStructure") - return true; - - if (name == "SubpassInput") - return true; - } - - if (hlsl::IsHLSLRayQueryType(type)) + const auto resKind = hlsl::GetHLSLResourceKind(type); + if (hlsl::DXIL::IsAnyTexture(resKind) || + resKind == hlsl::DXIL::ResourceKind::TypedBuffer || + resKind == hlsl::DXIL::ResourceKind::Sampler || + resKind == hlsl::DXIL::ResourceKind::RTAccelerationStructure || + hlsl::IsHLSLRayQueryType(type)) + return true; + if (isSubpassInput(type)) return true; - return false; } @@ -1159,33 +1067,14 @@ std::string getHlslResourceTypeName(QualType type) { while (type->isArrayType()) type = type->getAsArrayTypeUnsafe()->getElementType(); - if (const RecordType *recordType = type->getAs()) { - StringRef name = recordType->getDecl()->getName(); - if (name == "StructuredBuffer" || name == "RWStructuredBuffer" || - name == "RasterizerOrderedStructuredBuffer" || - name == "ByteAddressBuffer" || name == "RWByteAddressBuffer" || - name == "RasterizerOrderedByteAddressBuffer" || - name == "AppendStructuredBuffer" || name == "ConsumeStructuredBuffer" || - name == "Texture1D" || name == "Texture2D" || name == "Texture3D" || - name == "TextureCube" || name == "Texture1DArray" || - name == "Texture2DArray" || name == "Texture2DMS" || - name == "Texture2DMSArray" || name == "TextureCubeArray" || - name == "RWTexture1D" || name == "RWTexture2D" || - name == "RWTexture3D" || name == "RWTexture1DArray" || - name == "RWTexture2DArray" || name == "RasterizerOrderedTexture1D" || - name == "RasterizerOrderedTexture1DArray" || - name == "RasterizerOrderedTexture2D" || - name == "RasterizerOrderedTexture2DArray" || - name == "RasterizerOrderedTexture3D" || name == "Buffer" || - name == "RWBuffer" || name == "RasterizerOrderedBuffer" || - name == "SubpassInput" || name == "SubpassInputMS" || - name == "InputPatch" || name == "OutputPatch" || - name == "ConstantBuffer" || name == "TextureBuffer" || - name == "RaytracingAccelerationStructure") { - // Get resource type name with template params. Operation is safe because - // type has already been null checked. - return type.getLocalUnqualifiedType().getAsString(); - } + // Use attribute-based checks to identify HLSL resource types. + if (hlsl::IsHLSLResourceType(type) || hlsl::IsHLSLRayQueryType(type) || + hlsl::IsHLSLInputPatchType(type) || hlsl::IsHLSLOutputPatchType(type) || + hlsl::IsHLSLStreamOutputType(type) || isSubpassInput(type) || + isSubpassInputMS(type)) { + // Get resource type name with template params. Operation is safe because + // type has already been null checked. + return type.getLocalUnqualifiedType().getAsString(); } return ""; @@ -1250,21 +1139,11 @@ bool isRelaxedPrecisionType(QualType type, const SpirvCodeGenOptions &opts) { } // Images with RelaxedPrecision sampled type. - if (const auto *recordType = type->getAs()) { - const llvm::StringRef name = recordType->getDecl()->getName(); - if (name == "Texture1D" || name == "Texture2D" || name == "Texture3D" || - name == "TextureCube" || name == "Texture1DArray" || - name == "Texture2DArray" || name == "Texture2DMS" || - name == "Texture2DMSArray" || name == "TextureCubeArray" || - name == "RWTexture1D" || name == "RWTexture2D" || - name == "RWTexture3D" || name == "RWTexture1DArray" || - name == "RWTexture2DArray" || name == "RasterizerOrderedTexture1D" || - name == "RasterizerOrderedTexture1DArray" || - name == "RasterizerOrderedTexture2D" || - name == "RasterizerOrderedTexture2DArray" || - name == "RasterizerOrderedTexture3D" || name == "Buffer" || - name == "RWBuffer" || name == "RasterizerOrderedBuffer" || - name == "SubpassInput" || name == "SubpassInputMS") { + { + auto resKind = hlsl::GetHLSLResourceKind(type); + if (hlsl::DXIL::IsAnyTexture(resKind) || + resKind == hlsl::DXIL::ResourceKind::TypedBuffer || + isSubpassInput(type) || isSubpassInputMS(type)) { const auto sampledType = hlsl::GetHLSLResourceResultType(type); return isRelaxedPrecisionType(sampledType, opts); } @@ -1286,16 +1165,14 @@ bool isRasterizerOrderedView(QualType type) { while (type->isArrayType()) type = type->getAsArrayTypeUnsafe()->getElementType(); + // ROV types are UAV resources; only built-in HLSL types have HLSLResourceAttr + // so this check prevents false positives from user-defined types. + if (hlsl::GetHLSLResourceClass(type) != hlsl::DXIL::ResourceClass::UAV) + return false; + if (const RecordType *recordType = type->getAs()) { StringRef name = recordType->getDecl()->getName(); - return name == "RasterizerOrderedBuffer" || - name == "RasterizerOrderedByteAddressBuffer" || - name == "RasterizerOrderedStructuredBuffer" || - name == "RasterizerOrderedTexture1D" || - name == "RasterizerOrderedTexture1DArray" || - name == "RasterizerOrderedTexture2D" || - name == "RasterizerOrderedTexture2DArray" || - name == "RasterizerOrderedTexture3D"; + return name.startswith("RasterizerOrdered"); } return false; } diff --git a/tools/clang/lib/SPIRV/LowerTypeVisitor.cpp b/tools/clang/lib/SPIRV/LowerTypeVisitor.cpp index b330d40d85..89f20e3471 100644 --- a/tools/clang/lib/SPIRV/LowerTypeVisitor.cpp +++ b/tools/clang/lib/SPIRV/LowerTypeVisitor.cpp @@ -901,76 +901,111 @@ LowerTypeVisitor::lowerResourceType(QualType type, SpirvLayoutRule rule, return lowerVkTypeInVkNamespace(type, name, rule, isRowMajor, srcLoc); } - // TODO: avoid string comparison once hlsl::IsHLSLResouceType() does that. + const hlsl::DXIL::ResourceKind resKind = hlsl::GetHLSLResourceKind(type); + const hlsl::DXIL::ResourceClass resClass = hlsl::GetHLSLResourceClass(type); { // Texture types spv::Dim dim = {}; - bool isArray = {}; - if ((dim = spv::Dim::Dim1D, isArray = false, name == "Texture1D") || - (dim = spv::Dim::Dim2D, isArray = false, name == "Texture2D") || - (dim = spv::Dim::Dim3D, isArray = false, name == "Texture3D") || - (dim = spv::Dim::Cube, isArray = false, name == "TextureCube") || - (dim = spv::Dim::Dim1D, isArray = true, name == "Texture1DArray") || - (dim = spv::Dim::Dim2D, isArray = true, name == "Texture2DArray") || - (dim = spv::Dim::Dim2D, isArray = false, name == "Texture2DMS") || - (dim = spv::Dim::Dim2D, isArray = true, name == "Texture2DMSArray") || - // There is no Texture3DArray - (dim = spv::Dim::Cube, isArray = true, name == "TextureCubeArray")) { - const bool isMS = (name == "Texture2DMS" || name == "Texture2DMSArray"); - const auto sampledType = hlsl::GetHLSLResourceResultType(type); - auto loweredType = - lowerType(getElementType(astContext, sampledType), rule, - /*isRowMajor*/ llvm::None, srcLoc); - // Bool does not have a defined size in SPIR-V, so it cannot be - // used in the external interface. - if (loweredType == spvContext.getBoolType()) { - loweredType = spvContext.getUIntType(32); - } - return spvContext.getImageType( - loweredType, dim, ImageType::WithDepth::Unknown, isArray, isMS, - ImageType::WithSampler::Yes, spv::ImageFormat::Unknown); + bool isArray = false; + bool isTexture = false; + switch (resKind) { + case hlsl::DXIL::ResourceKind::Texture1D: + dim = spv::Dim::Dim1D; + isArray = false; + isTexture = true; + break; + case hlsl::DXIL::ResourceKind::Texture2D: + dim = spv::Dim::Dim2D; + isArray = false; + isTexture = true; + break; + case hlsl::DXIL::ResourceKind::Texture2DMS: + dim = spv::Dim::Dim2D; + isArray = false; + isTexture = true; + break; + case hlsl::DXIL::ResourceKind::Texture3D: + dim = spv::Dim::Dim3D; + isArray = false; + isTexture = true; + break; + case hlsl::DXIL::ResourceKind::TextureCube: + dim = spv::Dim::Cube; + isArray = false; + isTexture = true; + break; + case hlsl::DXIL::ResourceKind::Texture1DArray: + dim = spv::Dim::Dim1D; + isArray = true; + isTexture = true; + break; + case hlsl::DXIL::ResourceKind::Texture2DArray: + dim = spv::Dim::Dim2D; + isArray = true; + isTexture = true; + break; + case hlsl::DXIL::ResourceKind::Texture2DMSArray: + dim = spv::Dim::Dim2D; + isArray = true; + isTexture = true; + break; + case hlsl::DXIL::ResourceKind::TextureCubeArray: + dim = spv::Dim::Cube; + isArray = true; + isTexture = true; + break; + default: + break; } - // There is no RWTexture3DArray - if ((dim = spv::Dim::Dim1D, isArray = false, - name == "RWTexture1D" || name == "RasterizerOrderedTexture1D") || - (dim = spv::Dim::Dim2D, isArray = false, - name == "RWTexture2D" || name == "RasterizerOrderedTexture2D") || - (dim = spv::Dim::Dim3D, isArray = false, - name == "RWTexture3D" || name == "RasterizerOrderedTexture3D") || - (dim = spv::Dim::Dim1D, isArray = true, - name == "RWTexture1DArray" || - name == "RasterizerOrderedTexture1DArray") || - (dim = spv::Dim::Dim2D, isArray = true, - name == "RWTexture2DArray" || - name == "RasterizerOrderedTexture2DArray")) { - const auto sampledType = hlsl::GetHLSLResourceResultType(type); - const auto format = - translateSampledTypeToImageFormat(sampledType, srcLoc); - return spvContext.getImageType( - lowerType(getElementType(astContext, sampledType), rule, - /*isRowMajor*/ llvm::None, srcLoc), - dim, ImageType::WithDepth::Unknown, isArray, - /*isMultiSampled=*/false, /*sampled=*/ImageType::WithSampler::No, - format); + if (isTexture) { + const bool isMS = resKind == hlsl::DXIL::ResourceKind::Texture2DMS || + resKind == hlsl::DXIL::ResourceKind::Texture2DMSArray; + + if (resClass == hlsl::DXIL::ResourceClass::SRV) { + const auto sampledType = hlsl::GetHLSLResourceResultType(type); + auto loweredType = + lowerType(getElementType(astContext, sampledType), rule, + /*isRowMajor*/ llvm::None, srcLoc); + // Bool does not have a defined size in SPIR-V, so it cannot be + // used in the external interface. + if (loweredType == spvContext.getBoolType()) { + loweredType = spvContext.getUIntType(32); + } + return spvContext.getImageType( + loweredType, dim, ImageType::WithDepth::Unknown, isArray, isMS, + ImageType::WithSampler::Yes, spv::ImageFormat::Unknown); + } + + // UAV textures (RWTexture* and RasterizerOrderedTexture*) - no + // Texture3DArray in DXIL/HLSL. + if (resClass == hlsl::DXIL::ResourceClass::UAV) { + const auto sampledType = hlsl::GetHLSLResourceResultType(type); + const auto format = + translateSampledTypeToImageFormat(sampledType, srcLoc); + return spvContext.getImageType( + lowerType(getElementType(astContext, sampledType), rule, + /*isRowMajor*/ llvm::None, srcLoc), + dim, ImageType::WithDepth::Unknown, isArray, + /*isMultiSampled=*/isMS, /*sampled=*/ImageType::WithSampler::No, + format); + } } } // Sampler types - if (name == "SamplerState" || name == "SamplerComparisonState") { + if (resKind == hlsl::DXIL::ResourceKind::Sampler) { return spvContext.getSamplerType(); } - if (name == "RaytracingAccelerationStructure") { + if (resKind == hlsl::DXIL::ResourceKind::RTAccelerationStructure) { return spvContext.getAccelerationStructureTypeNV(); } if (hlsl::IsHLSLRayQueryType(type)) return spvContext.getRayQueryTypeKHR(); - if (name == "StructuredBuffer" || name == "RWStructuredBuffer" || - name == "RasterizerOrderedStructuredBuffer" || - name == "AppendStructuredBuffer" || name == "ConsumeStructuredBuffer") { + if (resKind == hlsl::DXIL::ResourceKind::StructuredBuffer) { // StructureBuffer will be translated into an OpTypeStruct with one // field, which is an OpTypeRuntimeArray of OpTypeStruct (S). @@ -1005,7 +1040,7 @@ LowerTypeVisitor::lowerResourceType(QualType type, SpirvLayoutRule rule, // The stride of the runtime array is the size of the struct. const auto *raType = spvContext.getRuntimeArrayType(structType, arrayStride); - const bool isReadOnly = (name == "StructuredBuffer"); + const bool isReadOnly = resClass == hlsl::DXIL::ResourceClass::SRV; // Attach matrix stride decorations if this is a *StructuredBuffer. llvm::Optional matrixStride = llvm::None; @@ -1029,10 +1064,11 @@ LowerTypeVisitor::lowerResourceType(QualType type, SpirvLayoutRule rule, return valType; } - if (name == "ConstantBuffer" || name == "TextureBuffer") { + if (resKind == hlsl::DXIL::ResourceKind::CBuffer || + resKind == hlsl::DXIL::ResourceKind::TBuffer) { // ConstantBuffer and TextureBuffer are lowered as T - const bool forTBuffer = name == "TextureBuffer"; + const bool forTBuffer = resKind == hlsl::DXIL::ResourceKind::TBuffer; if (rule == SpirvLayoutRule::Void) { rule = forTBuffer ? getCodeGenOptions().tBufferLayoutRule @@ -1067,10 +1103,9 @@ LowerTypeVisitor::lowerResourceType(QualType type, SpirvLayoutRule rule, } // ByteAddressBuffer and RWByteAddressBuffer types. - if (name == "ByteAddressBuffer" || name == "RWByteAddressBuffer" || - name == "RasterizerOrderedByteAddressBuffer") { + if (resKind == hlsl::DXIL::ResourceKind::RawBuffer) { const auto *bufferType = spvContext.getByteAddressBufferType( - /*isRW*/ name != "ByteAddressBuffer"); + /*isRW*/ resClass != hlsl::DXIL::ResourceClass::SRV); if (rule == SpirvLayoutRule::Void) { // All byte address buffers are in the Uniform storage class. return spvContext.getPointerType(bufferType, spv::StorageClass::Uniform); @@ -1079,8 +1114,7 @@ LowerTypeVisitor::lowerResourceType(QualType type, SpirvLayoutRule rule, } // Buffer and RWBuffer types - if (name == "Buffer" || name == "RWBuffer" || - name == "RasterizerOrderedBuffer") { + if (resKind == hlsl::DXIL::ResourceKind::TypedBuffer) { const auto sampledType = hlsl::GetHLSLResourceResultType(type); const auto format = translateSampledTypeToImageFormat(sampledType, srcLoc); return spvContext.getImageType( @@ -1088,13 +1122,14 @@ LowerTypeVisitor::lowerResourceType(QualType type, SpirvLayoutRule rule, /*isRowMajor*/ llvm::None, srcLoc), spv::Dim::Buffer, ImageType::WithDepth::Unknown, /*isArrayed=*/false, /*isMultiSampled=*/false, - /*sampled*/ name == "Buffer" ? ImageType::WithSampler::Yes - : ImageType::WithSampler::No, + /*sampled*/ resClass == hlsl::DXIL::ResourceClass::SRV + ? ImageType::WithSampler::Yes + : ImageType::WithSampler::No, format); } // InputPatch - if (name == "InputPatch") { + if (hlsl::IsHLSLInputPatchType(type)) { const auto elemType = hlsl::GetHLSLInputPatchElementType(type); const auto elemCount = hlsl::GetHLSLInputPatchCount(type); return spvContext.getArrayType( @@ -1102,7 +1137,7 @@ LowerTypeVisitor::lowerResourceType(QualType type, SpirvLayoutRule rule, /*ArrayStride*/ llvm::None); } // OutputPatch - if (name == "OutputPatch") { + if (hlsl::IsHLSLOutputPatchType(type)) { const auto elemType = hlsl::GetHLSLOutputPatchElementType(type); const auto elemCount = hlsl::GetHLSLOutputPatchCount(type); return spvContext.getArrayType( @@ -1110,20 +1145,19 @@ LowerTypeVisitor::lowerResourceType(QualType type, SpirvLayoutRule rule, /*ArrayStride*/ llvm::None); } // Output stream objects (TriangleStream, LineStream, and PointStream) - if (name == "TriangleStream" || name == "LineStream" || - name == "PointStream") { + if (hlsl::IsHLSLStreamOutputType(type)) { return lowerType(hlsl::GetHLSLResourceResultType(type), rule, /*isRowMajor*/ llvm::None, srcLoc); } - if (name == "SubpassInput" || name == "SubpassInputMS") { + if (isSubpassInput(type) || isSubpassInputMS(type)) { const auto sampledType = hlsl::GetHLSLResourceResultType(type); return spvContext.getImageType( lowerType(getElementType(astContext, sampledType), rule, /*isRowMajor*/ llvm::None, srcLoc), spv::Dim::SubpassData, ImageType::WithDepth::Unknown, /*isArrayed=*/false, - /*isMultipleSampled=*/name == "SubpassInputMS", + /*isMultipleSampled=*/isSubpassInputMS(type), ImageType::WithSampler::No, spv::ImageFormat::Unknown); } diff --git a/tools/clang/lib/Sema/SemaHLSL.cpp b/tools/clang/lib/Sema/SemaHLSL.cpp index f818cbcb46..f09b5a12ea 100644 --- a/tools/clang/lib/Sema/SemaHLSL.cpp +++ b/tools/clang/lib/Sema/SemaHLSL.cpp @@ -4242,6 +4242,12 @@ class HLSLExternalSource : public ExternalSemaSource { } m_objectTypeDecls[i] = recordDecl; m_objectTypeDeclsMap[i] = std::make_pair(recordDecl, i); +#ifdef ENABLE_SPIRV_CODEGEN + if (kind == AR_OBJECT_VK_SUBPASS_INPUT || + kind == AR_OBJECT_VK_SUBPASS_INPUT_MS) + recordDecl->addAttr(HLSLVkSubpassInputAttr::CreateImplicit( + *m_context, kind == AR_OBJECT_VK_SUBPASS_INPUT_MS)); +#endif // ENABLE_SPIRV_CODEGEN } // Create an alias for SamplerState. 'sampler' is very commonly used. diff --git a/tools/clang/test/CodeGenSPIRV/user-defined-type.builtin-name.buffers.hlsl b/tools/clang/test/CodeGenSPIRV/user-defined-type.builtin-name.buffers.hlsl new file mode 100644 index 0000000000..225b43caa5 --- /dev/null +++ b/tools/clang/test/CodeGenSPIRV/user-defined-type.builtin-name.buffers.hlsl @@ -0,0 +1,43 @@ +// RUN: %dxc -E main -T cs_6_5 -fspv-target-env=vulkan1.2 -spirv %s | FileCheck %s + +// Verify that user-defined types in namespaces with the same names as HLSL +// built-in buffer types are NOT misidentified as HLSL resource types. The +// user-defined types should compile correctly without errors. + +// CHECK: OpCapability Shader +// CHECK: OpStore + +namespace myns { + struct StructuredBuffer { float data; }; + struct RWStructuredBuffer { float data; }; + struct AppendStructuredBuffer { float data; }; + struct ConsumeStructuredBuffer { float data; }; + struct ByteAddressBuffer { int raw; }; + struct RWByteAddressBuffer { int raw; }; + struct ConstantBuffer { float v; }; + struct TextureBuffer { float v; }; +} + +RWStructuredBuffer output : register(u0); + +[numthreads(1,1,1)] +void main() { + myns::StructuredBuffer sb; + sb.data = 1.0; + myns::RWStructuredBuffer rwsb; + rwsb.data = 2.0; + myns::AppendStructuredBuffer asb; + asb.data = 3.0; + myns::ConsumeStructuredBuffer csb; + csb.data = 4.0; + myns::ByteAddressBuffer bab; + bab.raw = 42; + myns::RWByteAddressBuffer rwbab; + rwbab.raw = 43; + myns::ConstantBuffer cb; + cb.v = 5.0; + myns::TextureBuffer tb; + tb.v = 6.0; + output[0] = sb.data + rwsb.data + asb.data + csb.data + bab.raw + + rwbab.raw + cb.v + tb.v; +} diff --git a/tools/clang/test/CodeGenSPIRV/user-defined-type.builtin-name.rayquery.hlsl b/tools/clang/test/CodeGenSPIRV/user-defined-type.builtin-name.rayquery.hlsl new file mode 100644 index 0000000000..e6fa4e7d71 --- /dev/null +++ b/tools/clang/test/CodeGenSPIRV/user-defined-type.builtin-name.rayquery.hlsl @@ -0,0 +1,26 @@ +// RUN: %dxc -E main -T cs_6_5 -fspv-target-env=vulkan1.2 -spirv %s | FileCheck %s + +// Verify that a user-defined type named RayQuery in a namespace is NOT +// misidentified as the built-in RayQuery type. The user-defined type should +// be lowered as a plain struct, not as the OpTypeRayQueryKHR SPIR-V type. + +// CHECK-NOT: OpCapability RayQueryKHR +// CHECK-NOT: OpTypeRayQueryKHR + +namespace myns { + struct RayQuery { + float x; + float y; + }; +} + +RWStructuredBuffer output : register(u0); + +[numthreads(1,1,1)] +void main() { + myns::RayQuery q; + q.x = 1.0; + q.y = 2.0; + // CHECK: OpStore + output[0] = q.x + q.y; +} diff --git a/tools/clang/test/CodeGenSPIRV/user-defined-type.builtin-name.textures.hlsl b/tools/clang/test/CodeGenSPIRV/user-defined-type.builtin-name.textures.hlsl new file mode 100644 index 0000000000..ecfacb2fa5 --- /dev/null +++ b/tools/clang/test/CodeGenSPIRV/user-defined-type.builtin-name.textures.hlsl @@ -0,0 +1,90 @@ +// RUN: %dxc -E main -T cs_6_5 -fspv-target-env=vulkan1.2 -spirv %s | FileCheck %s + +// Verify that user-defined types in namespaces with the same names as HLSL +// built-in resource types are NOT misidentified as HLSL resource types. Each +// user-defined type should be lowered as a plain struct. + +// CHECK-NOT: OpTypeSampledImage +// CHECK-NOT: OpTypeImage +// CHECK-NOT: OpTypeSampler +// CHECK-NOT: OpTypeAccelerationStructure + +// CHECK: %float_300 = OpConstant %float 300 +// CHECK: OpStore {{.*}} %float_300 + +namespace myns { + struct Texture1D { float value; }; + struct Texture2D { float r; float g; }; + struct Texture3D { float r; float g; float b; }; + struct TextureCube { float x; }; + struct Texture1DArray { float v; }; + struct Texture2DArray { float v; }; + struct Texture2DMS { float v; }; + struct Texture2DMSArray { float v; }; + struct TextureCubeArray { float v; }; + struct RWTexture1D { float rw; }; + struct RWTexture2D { float rw; }; + struct RWTexture3D { float rw; }; + struct RasterizerOrderedTexture2D { float rov; }; + struct SamplerState { int id; }; + struct SamplerComparisonState { int id; }; + struct RaytracingAccelerationStructure { float x; }; + struct Buffer { float data; }; + struct RWBuffer { float data; }; + struct RasterizerOrderedBuffer { float data; }; + struct SubpassInput { float value; }; + struct SubpassInputMS { float value; }; +} + +RWStructuredBuffer output : register(u0); + +[numthreads(1,1,1)] +void main() { + myns::Texture1D t1; + t1.value = 1.0; + myns::Texture2D t2; + t2.r = 2.0; t2.g = 3.0; + myns::Texture3D t3; + t3.r = 4.0; t3.g = 5.0; t3.b = 6.0; + myns::TextureCube tc; + tc.x = 7.0; + myns::Texture1DArray t1a; + t1a.v = 8.0; + myns::Texture2DArray t2a; + t2a.v = 9.0; + myns::Texture2DMS t2ms; + t2ms.v = 10.0; + myns::Texture2DMSArray t2msa; + t2msa.v = 11.0; + myns::TextureCubeArray tca; + tca.v = 12.0; + myns::RWTexture1D rwt1; + rwt1.rw = 13.0; + myns::RWTexture2D rwt2; + rwt2.rw = 14.0; + myns::RWTexture3D rwt3; + rwt3.rw = 15.0; + myns::RasterizerOrderedTexture2D rovt2; + rovt2.rov = 16.0; + myns::SamplerState ss; + ss.id = 17; + myns::SamplerComparisonState scs; + scs.id = 18; + myns::RaytracingAccelerationStructure rtas; + rtas.x = 19.0; + myns::Buffer buf; + buf.data = 20.0; + myns::RWBuffer rwbuf; + rwbuf.data = 21.0; + myns::RasterizerOrderedBuffer rovbuf; + rovbuf.data = 22.0; + myns::SubpassInput subpass; + subpass.value = 23.0; + myns::SubpassInputMS subpassms; + subpassms.value = 24.0; + output[0] = t1.value + t2.r + t2.g + t3.r + t3.g + t3.b + tc.x + + t1a.v + t2a.v + t2ms.v + t2msa.v + tca.v + rwt1.rw + + rwt2.rw + rwt3.rw + rovt2.rov + ss.id + scs.id + rtas.x + + buf.data + rwbuf.data + rovbuf.data + subpass.value + + subpassms.value; +}