diff --git a/include/dxc/dxcapi.internal.h b/include/dxc/dxcapi.internal.h index 46a485206e..780b35ced9 100644 --- a/include/dxc/dxcapi.internal.h +++ b/include/dxc/dxcapi.internal.h @@ -136,7 +136,8 @@ enum LEGAL_INTRINSIC_COMPTYPES { #ifdef ENABLE_SPIRV_CODEGEN LICOMPTYPE_VK_BUFFER_POINTER = 54, - LICOMPTYPE_COUNT = 55 + LICOMPTYPE_VK_SAMPLED_TEXTURE2D = 55, + LICOMPTYPE_COUNT = 56 #else LICOMPTYPE_COUNT = 54 #endif diff --git a/tools/clang/include/clang/AST/HlslTypes.h b/tools/clang/include/clang/AST/HlslTypes.h index 43c1effdb8..2f4afe673a 100644 --- a/tools/clang/include/clang/AST/HlslTypes.h +++ b/tools/clang/include/clang/AST/HlslTypes.h @@ -407,6 +407,11 @@ clang::CXXRecordDecl * DeclareVkBufferPointerType(clang::ASTContext &context, clang::DeclContext *declContext); +clang::CXXRecordDecl *DeclareVkSampledTextureType( + clang::ASTContext &context, clang::DeclContext *declContext, + llvm::StringRef hlslTypeName, clang::QualType defaultParamType, + clang::QualType coordinateType, clang::QualType offsetType); + clang::CXXRecordDecl *DeclareInlineSpirvType(clang::ASTContext &context, clang::DeclContext *declContext, llvm::StringRef typeName, diff --git a/tools/clang/include/clang/Basic/DiagnosticSemaKinds.td b/tools/clang/include/clang/Basic/DiagnosticSemaKinds.td index 34a2195cbc..451b788779 100644 --- a/tools/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/tools/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -7610,6 +7610,9 @@ def err_hlsl_unsupported_typedbuffer_template_parameter : Error< "elements of typed buffers and textures must be scalars or vectors">; def err_hlsl_unsupported_typedbuffer_template_parameter_size : Error< "elements of typed buffers and textures must fit in four 32-bit quantities">; +def err_hlsl_unsupported_vk_sampledtexture_template_parameter + : Error<"%0 cannot be used as a type parameter where it must be of type " + "float, int, uint or their vector equivalents">; def err_hlsl_unsupported_payload_access_qualifier : Error< "payload access qualifiers are only allowed for member variables of a payload structure">; def err_hlsl_unsupported_payload_access_qualifier_struct : Error< diff --git a/tools/clang/include/clang/SPIRV/AstTypeProbe.h b/tools/clang/include/clang/SPIRV/AstTypeProbe.h index 45bff1bad4..1479075f12 100644 --- a/tools/clang/include/clang/SPIRV/AstTypeProbe.h +++ b/tools/clang/include/clang/SPIRV/AstTypeProbe.h @@ -257,6 +257,9 @@ bool isTexture(QualType); /// Texture2DMSArray type. bool isTextureMS(QualType); +/// \brief Returns true if the given type is an HLSL SampledTexture type. +bool isSampledTexture(QualType); + /// \brief Returns true if the given type is an HLSL RWTexture type. bool isRWTexture(QualType); diff --git a/tools/clang/include/clang/SPIRV/SpirvBuilder.h b/tools/clang/include/clang/SPIRV/SpirvBuilder.h index 1d012568d6..d4c0a963e1 100644 --- a/tools/clang/include/clang/SPIRV/SpirvBuilder.h +++ b/tools/clang/include/clang/SPIRV/SpirvBuilder.h @@ -286,6 +286,10 @@ class SpirvBuilder { /// If compareVal is given a non-zero value, *Dref* variants of OpImageSample* /// will be generated. /// + /// If the of `image` is a sampled image, then that image will be sampled. + /// In this case, `sampler` must be `nullptr`. If `image` is not a sampled + /// image, a sampled image will be created by combining `image` and `sampler`. + /// /// If lod or grad is given a non-zero value, *ExplicitLod variants of /// OpImageSample* will be generated; otherwise, *ImplicitLod variant will /// be generated. @@ -334,6 +338,10 @@ class SpirvBuilder { /// \brief Creates SPIR-V instructions for gathering the given image. /// + /// If the of `image` is a sampled image, then that image will be gathered. + /// In this case, `sampler` must be `nullptr`. If `image` is not a sampled + /// image, a sampled image will be created by combining `image` and `sampler`. + /// /// If compareVal is given a non-null value, OpImageDrefGather or /// OpImageSparseDrefGather will be generated; otherwise, OpImageGather or /// OpImageSparseGather will be generated. diff --git a/tools/clang/lib/AST/ASTContextHLSL.cpp b/tools/clang/lib/AST/ASTContextHLSL.cpp index 913b28ced8..497963d0cd 100644 --- a/tools/clang/lib/AST/ASTContextHLSL.cpp +++ b/tools/clang/lib/AST/ASTContextHLSL.cpp @@ -1369,6 +1369,144 @@ CXXRecordDecl *hlsl::DeclareNodeOrRecordType( } #ifdef ENABLE_SPIRV_CODEGEN +static void AddSampleFunction(ASTContext &context, CXXRecordDecl *recordDecl, + QualType returnType, QualType coordinateType, + QualType offsetType) { + QualType floatType = context.FloatTy; + QualType uintType = context.UnsignedIntTy; + + // Sample(location) + CXXMethodDecl *sampleDecl = CreateObjectFunctionDeclarationWithParams( + context, recordDecl, returnType, ArrayRef(coordinateType), + ArrayRef(StringRef("location")), + context.DeclarationNames.getIdentifier(&context.Idents.get("Sample")), + /*isConst*/ true); + sampleDecl->addAttr(HLSLIntrinsicAttr::CreateImplicit( + context, "op", "", static_cast(hlsl::IntrinsicOp::MOP_Sample))); + sampleDecl->addAttr(HLSLCXXOverloadAttr::CreateImplicit(context)); + + // Sample(location, offset) + QualType params2[] = {coordinateType, offsetType}; + StringRef names2[] = {"location", "offset"}; + CXXMethodDecl *sampleDecl2 = CreateObjectFunctionDeclarationWithParams( + context, recordDecl, returnType, params2, names2, + context.DeclarationNames.getIdentifier(&context.Idents.get("Sample")), + /*isConst*/ true); + sampleDecl2->addAttr(HLSLIntrinsicAttr::CreateImplicit( + context, "op", "", static_cast(hlsl::IntrinsicOp::MOP_Sample))); + sampleDecl2->addAttr(HLSLCXXOverloadAttr::CreateImplicit(context)); + + // Sample(location, offset, clamp) + QualType params3[] = {coordinateType, offsetType, floatType}; + StringRef names3[] = {"location", "offset", "clamp"}; + CXXMethodDecl *sampleDecl3 = CreateObjectFunctionDeclarationWithParams( + context, recordDecl, returnType, params3, names3, + context.DeclarationNames.getIdentifier(&context.Idents.get("Sample")), + /*isConst*/ true); + sampleDecl3->addAttr(HLSLIntrinsicAttr::CreateImplicit( + context, "op", "", static_cast(hlsl::IntrinsicOp::MOP_Sample))); + sampleDecl3->addAttr(HLSLCXXOverloadAttr::CreateImplicit(context)); + + // Sample(location, offset, clamp, status) + QualType params4[] = {coordinateType, offsetType, floatType, + context.getLValueReferenceType(uintType)}; + StringRef names4[] = {"location", "offset", "clamp", "status"}; + CXXMethodDecl *sampleDecl4 = CreateObjectFunctionDeclarationWithParams( + context, recordDecl, returnType, params4, names4, + context.DeclarationNames.getIdentifier(&context.Idents.get("Sample")), + /*isConst*/ true); + sampleDecl4->addAttr(HLSLIntrinsicAttr::CreateImplicit( + context, "op", "", static_cast(hlsl::IntrinsicOp::MOP_Sample))); + sampleDecl4->addAttr(HLSLCXXOverloadAttr::CreateImplicit(context)); +} + +static void AddCalculateLevelOfDetailFunction(ASTContext &context, + CXXRecordDecl *recordDecl, + QualType coordinateType, + bool unclamped) { + QualType floatType = context.FloatTy; + + const char *functionName = + unclamped ? "CalculateLevelOfDetailUnclamped" : "CalculateLevelOfDetail"; + const auto intrinsicOp = + unclamped ? hlsl::IntrinsicOp::MOP_CalculateLevelOfDetailUnclamped + : hlsl::IntrinsicOp::MOP_CalculateLevelOfDetail; + + // // CalculateLevelOfDetail(location), + // CalculateLevelOfDetailUnclamped(location) + CXXMethodDecl *lodDecl = CreateObjectFunctionDeclarationWithParams( + context, recordDecl, floatType, ArrayRef(coordinateType), + ArrayRef(StringRef("location")), + context.DeclarationNames.getIdentifier(&context.Idents.get(functionName)), + /*isConst*/ true); + lodDecl->addAttr(HLSLIntrinsicAttr::CreateImplicit( + context, "op", "", static_cast(intrinsicOp))); + lodDecl->addAttr(HLSLCXXOverloadAttr::CreateImplicit(context)); +} + +static void AddGatherFunction(ASTContext &context, CXXRecordDecl *recordDecl, + QualType returnType, QualType coordinateType, + QualType offsetType) { + // Gather(location) + CXXMethodDecl *gatherDecl = CreateObjectFunctionDeclarationWithParams( + context, recordDecl, returnType, ArrayRef(coordinateType), + ArrayRef(StringRef("location")), + context.DeclarationNames.getIdentifier(&context.Idents.get("Gather")), + /*isConst*/ true); + gatherDecl->addAttr(HLSLIntrinsicAttr::CreateImplicit( + context, "op", "", static_cast(hlsl::IntrinsicOp::MOP_Gather))); + gatherDecl->addAttr(HLSLCXXOverloadAttr::CreateImplicit(context)); + + // Gather(location, offset) + QualType gatherParams2[] = {coordinateType, offsetType}; + StringRef gatherNames2[] = {"location", "offset"}; + CXXMethodDecl *gatherDecl2 = CreateObjectFunctionDeclarationWithParams( + context, recordDecl, returnType, gatherParams2, gatherNames2, + context.DeclarationNames.getIdentifier(&context.Idents.get("Gather")), + /*isConst*/ true); + gatherDecl2->addAttr(HLSLIntrinsicAttr::CreateImplicit( + context, "op", "", static_cast(hlsl::IntrinsicOp::MOP_Gather))); + gatherDecl2->addAttr(HLSLCXXOverloadAttr::CreateImplicit(context)); + + // Gather(location, offset, status) + QualType gatherParams3[] = { + coordinateType, offsetType, + context.getLValueReferenceType(context.UnsignedIntTy)}; + StringRef gatherNames3[] = {"location", "offset", "status"}; + CXXMethodDecl *gatherDecl3 = CreateObjectFunctionDeclarationWithParams( + context, recordDecl, returnType, gatherParams3, gatherNames3, + context.DeclarationNames.getIdentifier(&context.Idents.get("Gather")), + /*isConst*/ true); + gatherDecl3->addAttr(HLSLIntrinsicAttr::CreateImplicit( + context, "op", "", static_cast(hlsl::IntrinsicOp::MOP_Gather))); + gatherDecl3->addAttr(HLSLCXXOverloadAttr::CreateImplicit(context)); +} + +CXXRecordDecl *hlsl::DeclareVkSampledTextureType( + ASTContext &context, DeclContext *declContext, llvm::StringRef hlslTypeName, + QualType defaultParamType, QualType coordinateType, QualType offsetType) { + BuiltinTypeDeclBuilder Builder(declContext, hlslTypeName, + TagDecl::TagKind::TTK_Struct); + + TemplateTypeParmDecl *TyParamDecl = + Builder.addTypeTemplateParam("SampledTextureType", defaultParamType); + + Builder.startDefinition(); + + QualType paramType = QualType(TyParamDecl->getTypeForDecl(), 0); + CXXRecordDecl *recordDecl = Builder.getRecordDecl(); + + AddSampleFunction(context, recordDecl, paramType, coordinateType, offsetType); + AddCalculateLevelOfDetailFunction(context, recordDecl, coordinateType, + /*unclamped=*/false); + AddCalculateLevelOfDetailFunction(context, recordDecl, coordinateType, + /*unclamped=*/true); + AddGatherFunction(context, recordDecl, paramType, coordinateType, offsetType); + + Builder.completeDefinition(); + return recordDecl; +} + CXXRecordDecl *hlsl::DeclareVkBufferPointerType(ASTContext &context, DeclContext *declContext) { BuiltinTypeDeclBuilder Builder(declContext, "BufferPointer", diff --git a/tools/clang/lib/SPIRV/AstTypeProbe.cpp b/tools/clang/lib/SPIRV/AstTypeProbe.cpp index fda9a3ab3e..48c3012501 100644 --- a/tools/clang/lib/SPIRV/AstTypeProbe.cpp +++ b/tools/clang/lib/SPIRV/AstTypeProbe.cpp @@ -926,6 +926,17 @@ bool isTexture(QualType type) { return false; } +bool isSampledTexture(QualType type) { + if (const auto *rt = type->getAs()) { + const auto name = rt->getDecl()->getName(); + // TODO(https://github.com/microsoft/DirectXShaderCompiler/issues/7979): Add + // other sampled texture types as needed. + if (name == "SampledTexture2D") + return true; + } + return false; +} + bool isTextureMS(QualType type) { if (const auto *rt = type->getAs()) { const auto name = rt->getDecl()->getName(); diff --git a/tools/clang/lib/SPIRV/LowerTypeVisitor.cpp b/tools/clang/lib/SPIRV/LowerTypeVisitor.cpp index b660ea70df..38d2adf166 100644 --- a/tools/clang/lib/SPIRV/LowerTypeVisitor.cpp +++ b/tools/clang/lib/SPIRV/LowerTypeVisitor.cpp @@ -849,6 +849,23 @@ const SpirvType *LowerTypeVisitor::lowerVkTypeInVkNamespace( assert(visitedTypeStack.size() == visitedTypeStackSize); return pointerType; } + if (name == "SampledTexture2D") { + 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); + } + + const auto *imageType = spvContext.getImageType( + loweredType, spv::Dim::Dim2D, ImageType::WithDepth::No, + false /* array */, false /* ms */, ImageType::WithSampler::Yes, + spv::ImageFormat::Unknown); + return spvContext.getSampledImageType(imageType); + } emitError("unknown type %0 in vk namespace", srcLoc) << type; return nullptr; } @@ -892,7 +909,8 @@ LowerTypeVisitor::lowerResourceType(QualType type, SpirvLayoutRule rule, auto loweredType = lowerType(getElementType(astContext, sampledType), rule, /*isRowMajor*/ llvm::None, srcLoc); - // Treat bool textures as uint for compatibility with OpTypeImage. + // 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); } diff --git a/tools/clang/lib/SPIRV/SpirvBuilder.cpp b/tools/clang/lib/SPIRV/SpirvBuilder.cpp index 86701f48fd..319436749f 100644 --- a/tools/clang/lib/SPIRV/SpirvBuilder.cpp +++ b/tools/clang/lib/SPIRV/SpirvBuilder.cpp @@ -620,8 +620,15 @@ SpirvInstruction *SpirvBuilder::createImageSample( assert(lod == nullptr || minLod == nullptr); // An OpSampledImage is required to do the image sampling. - auto *sampledImage = - createSampledImage(imageType, image, sampler, loc, range); + // Skip creating OpSampledImage if the imageType is a sampled texture. + SpirvInstruction *sampledImage = nullptr; + if (isSampledTexture(imageType)) { + assert(!sampler && + "sampler must be null when sampling from a sampled texture"); + sampledImage = image; + } else { + sampledImage = createSampledImage(imageType, image, sampler, loc, range); + } const auto mask = composeImageOperandsMask( bias, lod, grad, constOffset, varOffset, constOffsets, sample, minLod); @@ -707,8 +714,15 @@ SpirvInstruction *SpirvBuilder::createImageGather( assert(insertPoint && "null insert point"); // An OpSampledImage is required to do the image sampling. - auto *sampledImage = - createSampledImage(imageType, image, sampler, loc, range); + // Skip creating OpSampledImage if the imageType is a sampled texture. + SpirvInstruction *sampledImage = nullptr; + if (isSampledTexture(imageType)) { + assert(!sampler && + "sampler must be null when sampling from a sampled texture"); + sampledImage = image; + } else { + sampledImage = createSampledImage(imageType, image, sampler, loc, range); + } // TODO: Update ImageGather to accept minLod if necessary. const auto mask = composeImageOperandsMask( diff --git a/tools/clang/lib/SPIRV/SpirvEmitter.cpp b/tools/clang/lib/SPIRV/SpirvEmitter.cpp index 2c8b8a3440..d772a93286 100644 --- a/tools/clang/lib/SPIRV/SpirvEmitter.cpp +++ b/tools/clang/lib/SPIRV/SpirvEmitter.cpp @@ -4451,15 +4451,27 @@ SpirvEmitter::processTextureLevelOfDetail(const CXXMemberCallExpr *expr, // Texture2D(Array).CalculateLevelOfDetail(SamplerState S, float2 xy); // TextureCube(Array).CalculateLevelOfDetail(SamplerState S, float3 xyz); // Texture3D.CalculateLevelOfDetail(SamplerState S, float3 xyz); + // SampledTexture2D.CalculateLevelOfDetail(float2 xy); // Return type is always a single float (LOD). - assert(expr->getNumArgs() == 2u); - const auto *object = expr->getImplicitObjectArgument(); - auto *objectInfo = loadIfGLValue(object); - auto *samplerState = doExpr(expr->getArg(0)); - auto *coordinate = doExpr(expr->getArg(1)); - auto *sampledImage = spvBuilder.createSampledImage( - object->getType(), objectInfo, samplerState, expr->getExprLoc()); + const auto *imageExpr = expr->getImplicitObjectArgument(); + const QualType imageType = imageExpr->getType(); + // numarg is 1 if isSampledTexture(imageType). otherwise 2. + assert(expr->getNumArgs() == (isSampledTexture(imageType) ? 1u : 2u)); + + auto *objectInfo = loadIfGLValue(imageExpr); + + SpirvInstruction *samplerState, *coordinate, *sampledImage; + if (isSampledTexture(imageType)) { + samplerState = nullptr; + coordinate = doExpr(expr->getArg(0)); + sampledImage = objectInfo; + } else { + samplerState = doExpr(expr->getArg(0)); + coordinate = doExpr(expr->getArg(1)); + sampledImage = spvBuilder.createSampledImage( + imageExpr->getType(), objectInfo, samplerState, expr->getExprLoc()); + } // The result type of OpImageQueryLod must be a float2. const QualType queryResultType = @@ -5812,36 +5824,59 @@ SpirvEmitter::processTextureSampleGather(const CXXMemberCallExpr *expr, // float2|3|4 Location // [, uint Status]); // + // For SampledTexture2D: + // DXGI_FORMAT Object.Sample(float Location + // [, int Offset] + // [, float Clamp] + // [, out uint Status]); + // // Other Texture types do not have a Gather method. - const auto numArgs = expr->getNumArgs(); const auto loc = expr->getExprLoc(); const auto range = expr->getSourceRange(); const bool hasStatusArg = expr->getArg(numArgs - 1)->getType()->isUnsignedIntegerType(); + const auto *imageExpr = expr->getImplicitObjectArgument(); + const QualType imageType = imageExpr->getType(); + const bool isImageSampledTexture = isSampledTexture(imageType); + + int samplerIndex, clampIndex, coordIndex, offsetIndex; + if (isImageSampledTexture) { + samplerIndex = -1; // non-existant + coordIndex = 0; + offsetIndex = 1; + clampIndex = 2; + } else { + samplerIndex = 0; + coordIndex = 1; + offsetIndex = 2; + clampIndex = 3; + } + SpirvInstruction *clamp = nullptr; - if (numArgs > 2 && expr->getArg(2)->getType()->isFloatingType()) - clamp = doExpr(expr->getArg(2)); - else if (numArgs > 3 && expr->getArg(3)->getType()->isFloatingType()) - clamp = doExpr(expr->getArg(3)); + if (numArgs > offsetIndex && + expr->getArg(offsetIndex)->getType()->isFloatingType()) + clamp = doExpr(expr->getArg(offsetIndex)); + else if (numArgs > offsetIndex + 1 && + expr->getArg(offsetIndex + 1)->getType()->isFloatingType()) + clamp = doExpr(expr->getArg(offsetIndex + 1)); const bool hasClampArg = (clamp != 0); const auto status = hasStatusArg ? doExpr(expr->getArg(numArgs - 1)) : nullptr; - // Subtract 1 for status (if it exists), subtract 1 for clamp (if it exists), - // and subtract 2 for sampler_state and location. - const bool hasOffsetArg = numArgs - hasStatusArg - hasClampArg - 2 > 0; - - const auto *imageExpr = expr->getImplicitObjectArgument(); - const QualType imageType = imageExpr->getType(); auto *image = loadIfGLValue(imageExpr); - auto *sampler = doExpr(expr->getArg(0)); - auto *coordinate = doExpr(expr->getArg(1)); + SpirvInstruction *sampler = + samplerIndex >= 0 ? doExpr(expr->getArg(samplerIndex)) : nullptr; + auto *coordinate = doExpr(expr->getArg(coordIndex)); // .Sample()/.Gather() may have a third optional paramter for offset. SpirvInstruction *constOffset = nullptr, *varOffset = nullptr; + // Subtract 1 for status (if it exists), subtract 1 for clamp (if it exists), + // and subtract offsetIndex for sampler_state (if exists) location. + const bool hasOffsetArg = + numArgs - hasStatusArg - hasClampArg - offsetIndex > 0; if (hasOffsetArg) - handleOffsetInMethodCall(expr, 2, &constOffset, &varOffset); + handleOffsetInMethodCall(expr, offsetIndex, &constOffset, &varOffset); const auto retType = expr->getDirectCallee()->getReturnType(); if (isSample) { diff --git a/tools/clang/lib/Sema/SemaHLSL.cpp b/tools/clang/lib/Sema/SemaHLSL.cpp index e9c8c90a2d..4fa1dfb5d4 100644 --- a/tools/clang/lib/Sema/SemaHLSL.cpp +++ b/tools/clang/lib/Sema/SemaHLSL.cpp @@ -198,6 +198,7 @@ enum ArBasicKind { AR_OBJECT_VK_SPV_INTRINSIC_TYPE, AR_OBJECT_VK_SPV_INTRINSIC_RESULT_ID, AR_OBJECT_VK_BUFFER_POINTER, + AR_OBJECT_VK_SAMPLED_TEXTURE2D, #endif // ENABLE_SPIRV_CODEGEN // SPIRV change ends @@ -554,6 +555,7 @@ const UINT g_uBasicKindProps[] = { BPROP_OBJECT, // AR_OBJECT_VK_SPV_INTRINSIC_TYPE use recordType BPROP_OBJECT, // AR_OBJECT_VK_SPV_INTRINSIC_RESULT_ID use recordType BPROP_OBJECT, // AR_OBJECT_VK_BUFFER_POINTER use recordType + BPROP_OBJECT | BPROP_RBUFFER, // AR_OBJECT_VK_SAMPLED_TEXTURE2D #endif // ENABLE_SPIRV_CODEGEN // SPIRV change ends @@ -1250,6 +1252,8 @@ static const ArBasicKind g_DxHitObjectCT[] = {AR_OBJECT_HIT_OBJECT, #ifdef ENABLE_SPIRV_CODEGEN static const ArBasicKind g_VKBufferPointerCT[] = {AR_OBJECT_VK_BUFFER_POINTER, AR_BASIC_UNKNOWN}; +static const ArBasicKind g_VKSampledTexture2DCT[] = { + AR_OBJECT_VK_SAMPLED_TEXTURE2D, AR_BASIC_UNKNOWN}; #endif // Basic kinds, indexed by a LEGAL_INTRINSIC_COMPTYPES value. @@ -1310,7 +1314,8 @@ const ArBasicKind *g_LegalIntrinsicCompTypes[] = { g_RayQueryCT, // LICOMPTYPE_RAY_QUERY g_LinAlgCT, // LICOMPTYPE_LINALG #ifdef ENABLE_SPIRV_CODEGEN - g_VKBufferPointerCT, // LICOMPTYPE_VK_BUFFER_POINTER + g_VKBufferPointerCT, // LICOMPTYPE_VK_BUFFER_POINTER + g_VKSampledTexture2DCT, // LICOMPTYPE_VK_SAMPLED_TEXTURE2D #endif }; static_assert( @@ -1370,7 +1375,7 @@ static const ArBasicKind g_ArBasicKindsAsTypes[] = { AR_OBJECT_VK_SPIRV_TYPE, AR_OBJECT_VK_SPIRV_OPAQUE_TYPE, AR_OBJECT_VK_INTEGRAL_CONSTANT, AR_OBJECT_VK_LITERAL, AR_OBJECT_VK_SPV_INTRINSIC_TYPE, AR_OBJECT_VK_SPV_INTRINSIC_RESULT_ID, - AR_OBJECT_VK_BUFFER_POINTER, + AR_OBJECT_VK_BUFFER_POINTER, AR_OBJECT_VK_SAMPLED_TEXTURE2D, #endif // ENABLE_SPIRV_CODEGEN // SPIRV change ends @@ -1478,6 +1483,7 @@ static const uint8_t g_ArBasicKindsTemplateCount[] = { 1, // AR_OBJECT_VK_SPV_INTRINSIC_TYPE 1, // AR_OBJECT_VK_SPV_INTRINSIC_RESULT_ID 2, // AR_OBJECT_VK_BUFFER_POINTER + 1, // AR_OBJECT_VK_SAMPLED_TEXTURE2D #endif // ENABLE_SPIRV_CODEGEN // SPIRV change ends @@ -1627,6 +1633,7 @@ static const SubscriptOperatorRecord g_ArBasicKindsSubscripts[] = { {0, MipsFalse, SampleFalse}, // AR_OBJECT_VK_SPV_INTRINSIC_TYPE {0, MipsFalse, SampleFalse}, // AR_OBJECT_VK_SPV_INTRINSIC_RESULT_ID {0, MipsFalse, SampleFalse}, // AR_OBJECT_VK_BUFFER_POINTER + {0, MipsFalse, SampleFalse}, // AR_OBJECT_VK_SAMPLED_TEXTURE2D #endif // ENABLE_SPIRV_CODEGEN // SPIRV change ends @@ -1792,6 +1799,7 @@ static const char *g_ArBasicTypeNames[] = { "ext_type", "ext_result_id", "BufferPointer", + "SampledTexture2D", #endif // ENABLE_SPIRV_CODEGEN // SPIRV change ends @@ -3013,6 +3021,7 @@ class HLSLExternalSource : public ExternalSemaSource { ClassTemplateDecl *m_vkIntegralConstantTemplateDecl; ClassTemplateDecl *m_vkLiteralTemplateDecl; ClassTemplateDecl *m_vkBufferPointerTemplateDecl; + ClassTemplateDecl *m_vkSampledTexture2DTemplateDecl; // Declarations for Work Graph Output Record types ClassTemplateDecl *m_GroupNodeOutputRecordsTemplateDecl; @@ -4004,6 +4013,17 @@ class HLSLExternalSource : public ExternalSemaSource { recordDecl = DeclareVkBufferPointerType(*m_context, m_vkNSDecl); recordDecl->setImplicit(true); m_vkBufferPointerTemplateDecl = recordDecl->getDescribedClassTemplate(); + } else if (kind == AR_OBJECT_VK_SAMPLED_TEXTURE2D) { + if (!m_vkNSDecl) + continue; + recordDecl = DeclareVkSampledTextureType( + *m_context, m_vkNSDecl, "SampledTexture2D", + LookupVectorType(HLSLScalarType::HLSLScalarType_float, 4), + LookupVectorType(HLSLScalarType::HLSLScalarType_float, 2), + LookupVectorType(HLSLScalarType::HLSLScalarType_int, 2)); + recordDecl->setImplicit(true); + m_vkSampledTexture2DTemplateDecl = + recordDecl->getDescribedClassTemplate(); } #endif else if (templateArgCount == 0) { @@ -4117,7 +4137,8 @@ class HLSLExternalSource : public ExternalSemaSource { : m_matrixTemplateDecl(nullptr), m_vectorTemplateDecl(nullptr), m_vkIntegralConstantTemplateDecl(nullptr), m_vkLiteralTemplateDecl(nullptr), - m_vkBufferPointerTemplateDecl(nullptr), m_hlslNSDecl(nullptr), + m_vkBufferPointerTemplateDecl(nullptr), + m_vkSampledTexture2DTemplateDecl(nullptr), m_hlslNSDecl(nullptr), m_vkNSDecl(nullptr), m_dxNSDecl(nullptr), m_context(nullptr), m_sema(nullptr), m_hlslStringTypedef(nullptr) { memset(m_matrixTypes, 0, sizeof(m_matrixTypes)); @@ -5444,6 +5465,14 @@ class HLSLExternalSource : public ExternalSemaSource { recordType->getDecl()->getName().equals("SpirvOpaqueType"))) { return true; } +#endif +#ifdef ENABLE_SPIRV_CODEGEN + if (const auto *namespaceDecl = dyn_cast( + recordType->getDecl()->getDeclContext()); + namespaceDecl && namespaceDecl->getName().equals("vk") && + recordType->getDecl()->getName().equals("SampledTexture2D")) { + return true; + } #endif m_sema->Diag(argLoc, diag::err_hlsl_unsupported_object_context) << type << static_cast(TypeDiagContext::TypeParameter); @@ -5607,6 +5636,42 @@ class HLSLExternalSource : public ExternalSemaSource { } } + } else if (Template->getQualifiedNameAsString() == "vk::SampledTexture2D") { + if (TemplateArgList.size() == 1) { + const TemplateArgumentLoc &ArgLoc = TemplateArgList[0]; + const TemplateArgument &Arg = ArgLoc.getArgument(); + if (Arg.getKind() == TemplateArgument::ArgKind::Type) { + QualType ArgType = Arg.getAsType(); + if (!ArgType->isDependentType()) { + QualType EltTy = ArgType; + if (IsVectorType(m_sema, ArgType)) { + EltTy = hlsl::GetHLSLVecElementType(ArgType); + } + + bool isAllowedType = false; + if (const BuiltinType *BT = EltTy->getAs()) { + switch (BT->getKind()) { + case BuiltinType::Float: + case BuiltinType::Int: + case BuiltinType::UInt: + isAllowedType = true; + break; + default: + break; + } + } + + if (!isAllowedType) { + m_sema->Diag( + ArgLoc.getLocation(), + diag:: + err_hlsl_unsupported_vk_sampledtexture_template_parameter) + << ArgType; + return true; + } + } + } + } } else if (Template->getTemplatedDecl()->hasAttr()) { DXASSERT(TemplateArgList.size() == 1, diff --git a/tools/clang/test/CodeGenSPIRV/vk.sampledtexture.calculate-lod-unclamped.hlsl b/tools/clang/test/CodeGenSPIRV/vk.sampledtexture.calculate-lod-unclamped.hlsl new file mode 100644 index 0000000000..aebc57738b --- /dev/null +++ b/tools/clang/test/CodeGenSPIRV/vk.sampledtexture.calculate-lod-unclamped.hlsl @@ -0,0 +1,21 @@ +// RUN: %dxc -T ps_6_8 -E main -fcgl %s -spirv | FileCheck %s + +// CHECK: OpCapability ImageQuery + +vk::SampledTexture2D t1 : register(t0); + +// CHECK: %type_2d_image = OpTypeImage %float 2D 0 0 0 1 Unknown +// CHECK: %type_sampled_image = OpTypeSampledImage %type_2d_image +// CHECK: [[ptr:%[a-zA-Z0-9_]+]] = OpTypePointer UniformConstant %type_sampled_image + +// CHECK: %t1 = OpVariable [[ptr]] UniformConstant + +void main() { + float2 xy = float2(0.5, 0.5); + +//CHECK: [[tex1:%[a-zA-Z0-9_]+]] = OpLoad %type_sampled_image %t1 +//CHECK-NEXT: [[xy_load:%[a-zA-Z0-9_]+]] = OpLoad %v2float %xy +//CHECK-NEXT: [[query:%[a-zA-Z0-9_]+]] = OpImageQueryLod %v2float [[tex1]] [[xy_load]] +//CHECK-NEXT: {{%[0-9]+}} = OpCompositeExtract %float [[query]] 1 + float lod1 = t1.CalculateLevelOfDetailUnclamped(xy); +} \ No newline at end of file diff --git a/tools/clang/test/CodeGenSPIRV/vk.sampledtexture.calculate-lod.hlsl b/tools/clang/test/CodeGenSPIRV/vk.sampledtexture.calculate-lod.hlsl new file mode 100644 index 0000000000..01d3efb5aa --- /dev/null +++ b/tools/clang/test/CodeGenSPIRV/vk.sampledtexture.calculate-lod.hlsl @@ -0,0 +1,21 @@ +// RUN: %dxc -T ps_6_0 -E main -fcgl %s -spirv | FileCheck %s + +// CHECK: OpCapability ImageQuery + +vk::SampledTexture2D t1 : register(t0); + +// CHECK: %type_2d_image = OpTypeImage %float 2D 0 0 0 1 Unknown +// CHECK: %type_sampled_image = OpTypeSampledImage %type_2d_image +// CHECK: [[ptr:%[a-zA-Z0-9_]+]] = OpTypePointer UniformConstant %type_sampled_image + +// CHECK: %t1 = OpVariable [[ptr]] UniformConstant + +void main() { + float2 xy = float2(0.5, 0.5); + +//CHECK: [[tex1:%[a-zA-Z0-9_]+]] = OpLoad %type_sampled_image %t1 +//CHECK-NEXT: [[xy_load:%[a-zA-Z0-9_]+]] = OpLoad %v2float %xy +//CHECK-NEXT: [[query:%[a-zA-Z0-9_]+]] = OpImageQueryLod %v2float [[tex1]] [[xy_load]] +//CHECK-NEXT: {{%[0-9]+}} = OpCompositeExtract %float [[query]] 0 + float lod = t1.CalculateLevelOfDetail(xy); +} diff --git a/tools/clang/test/CodeGenSPIRV/vk.sampledtexture.gather.hlsl b/tools/clang/test/CodeGenSPIRV/vk.sampledtexture.gather.hlsl new file mode 100644 index 0000000000..e5f79fc188 --- /dev/null +++ b/tools/clang/test/CodeGenSPIRV/vk.sampledtexture.gather.hlsl @@ -0,0 +1,36 @@ +// RUN: %dxc -T ps_6_0 -E main -fcgl %s -spirv | FileCheck %s + +// CHECK: OpCapability SparseResidency + +// CHECK: [[v2fc:%[0-9]+]] = OpConstantComposite %v2float %float_0_5 %float_0_25 +// CHECK: [[v2ic:%[0-9]+]] = OpConstantComposite %v2int %int_2 %int_3 + +// CHECK: [[type_2d_image_1:%[a-zA-Z0-9_]+]] = OpTypeImage %float 2D 0 0 0 1 Unknown +// CHECK: [[type_sampled_image_1:%[a-zA-Z0-9_]+]] = OpTypeSampledImage [[type_2d_image_1]] +// CHECK: [[ptr_type_1:%[a-zA-Z0-9_]+]] = OpTypePointer UniformConstant [[type_sampled_image_1]] + +// CHECK: %SparseResidencyStruct = OpTypeStruct %uint %v4float + +// CHECK: [[tex1:%[a-zA-Z0-9_]+]] = OpVariable [[ptr_type_1]] UniformConstant + +vk::SampledTexture2D tex1 : register(t1); + +float4 main() : SV_Target { + +// CHECK: [[tex1_load:%[a-zA-Z0-9_]+]] = OpLoad [[type_sampled_image_1]] [[tex1]] +// CHECK: [[val1:%[a-zA-Z0-9_]+]] = OpImageGather %v4float [[tex1_load]] [[v2fc]] %int_0 None + float4 val1 = tex1.Gather(float2(0.5, 0.25)); + +// CHECK: [[tex2_load:%[a-zA-Z0-9_]+]] = OpLoad [[type_sampled_image_1]] [[tex1]] +// CHECK: [[val2:%[a-zA-Z0-9_]+]] = OpImageGather %v4float [[tex2_load]] [[v2fc]] %int_0 ConstOffset [[v2ic]] + float4 val2 = tex1.Gather(float2(0.5, 0.25), int2(2, 3)); + +// CHECK: [[tex3_load:%[a-zA-Z0-9_]+]] = OpLoad [[type_sampled_image_1]] [[tex1]] +// CHECK: [[val3:%[a-zA-Z0-9_]+]] = OpImageSparseGather %SparseResidencyStruct [[tex3_load]] [[v2fc]] %int_0 ConstOffset [[v2ic]] +// CHECK: [[status_0:%[a-zA-Z0-9_]+]] = OpCompositeExtract %uint [[val3]] 0 +// CHECK: OpStore %status [[status_0]] + uint status; + float4 val3 = tex1.Gather(float2(0.5, 0.25), int2(2, 3), status); + + return 1.0; +} diff --git a/tools/clang/test/CodeGenSPIRV/vk.sampledtexture.sample.hlsl b/tools/clang/test/CodeGenSPIRV/vk.sampledtexture.sample.hlsl new file mode 100644 index 0000000000..c00aa7b51c --- /dev/null +++ b/tools/clang/test/CodeGenSPIRV/vk.sampledtexture.sample.hlsl @@ -0,0 +1,50 @@ +// RUN: %dxc -T ps_6_0 -E main -fcgl %s -spirv | FileCheck %s + +// CHECK: OpCapability MinLod +// CHECK: OpCapability SparseResidency + +// CHECK: [[v2fc:%[0-9]+]] = OpConstantComposite %v2float %float_0_5 %float_0_25 +// CHECK: [[v2ic:%[0-9]+]] = OpConstantComposite %v2int %int_2 %int_3 + +// CHECK: [[type_2d_image_1:%[a-zA-Z0-9_]+]] = OpTypeImage %float 2D 0 0 0 1 Unknown +// CHECK: [[type_sampled_image_1:%[a-zA-Z0-9_]+]] = OpTypeSampledImage [[type_2d_image_1]] +// CHECK: [[ptr_type_1:%[a-zA-Z0-9_]+]] = OpTypePointer UniformConstant [[type_sampled_image_1]] + +// CHECK: [[type_2d_image_2:%[a-zA-Z0-9_]+]] = OpTypeImage %uint 2D 0 0 0 1 Unknown +// CHECK: [[type_sampled_image_2:%[a-zA-Z0-9_]+]] = OpTypeSampledImage [[type_2d_image_2]] +// CHECK: [[ptr_type_2:%[a-zA-Z0-9_]+]] = OpTypePointer UniformConstant [[type_sampled_image_2]] + +// CHECK: [[tex1:%[a-zA-Z0-9_]+]] = OpVariable [[ptr_type_1]] UniformConstant +// CHECK: [[tex2:%[a-zA-Z0-9_]+]] = OpVariable [[ptr_type_1]] UniformConstant +// CHECK: [[tex3:%[a-zA-Z0-9_]+]] = OpVariable [[ptr_type_2]] UniformConstant + +vk::SampledTexture2D tex1 : register(t0); +vk::SampledTexture2D tex2 : register(t1); +vk::SampledTexture2D tex3 : register(t2); + +float4 main() : SV_Target { +// CHECK: [[tex1_load:%[a-zA-Z0-9_]+]] = OpLoad [[type_sampled_image_1]] [[tex1]] +// CHECK: [[sampled_result1:%[a-zA-Z0-9_]+]] = OpImageSampleImplicitLod %v4float [[tex1_load]] [[v2fc]] None + float4 val1 = tex1.Sample(float2(0.5, 0.25)); + +// CHECK: [[tex2_load:%[a-zA-Z0-9_]+]] = OpLoad [[type_sampled_image_1]] [[tex2]] +// CHECK: [[sampled_result2:%[a-zA-Z0-9_]+]] = OpImageSampleImplicitLod %v4float [[tex2_load]] [[v2fc]] ConstOffset [[v2ic]] + float4 val2 = tex2.Sample(float2(0.5, 0.25), int2(2, 3)); + +// CHECK: [[tex3_load:%[a-zA-Z0-9_]+]] = OpLoad [[type_sampled_image_1]] [[tex2]] +// CHECK: [[sampled_result3:%[a-zA-Z0-9_]+]] = OpImageSampleImplicitLod %v4float [[tex3_load]] [[v2fc]] ConstOffset|MinLod [[v2ic]] %float_1 + float4 val3 = tex2.Sample(float2(0.5, 0.25), int2(2, 3), 1.0f); + +// CHECK: [[tex4_load:%[a-zA-Z0-9_]+]] = OpLoad [[type_sampled_image_1]] [[tex2]] +// CHECK: [[sampled_result4:%[a-zA-Z0-9_]+]] = OpImageSparseSampleImplicitLod %SparseResidencyStruct [[tex4_load]] [[v2fc]] ConstOffset|MinLod [[v2ic]] %float_1 +// CHECK: [[status_0:%[a-zA-Z0-9_]+]] = OpCompositeExtract %uint [[sampled_result4]] 0 +// CHECK: OpStore %status [[status_0]] + uint status; + float4 val4 = tex2.Sample(float2(0.5, 0.25), int2(2, 3), 1.0f, status); + +// CHECK: [[tex5_load:%[a-zA-Z0-9_]+]] = OpLoad [[type_sampled_image_2]] [[tex3]] +// CHECK: [[sampled_result5:%[a-zA-Z0-9_]+]] = OpImageSampleImplicitLod %v4uint [[tex5_load]] [[v2fc]] None +// CHECK: [[val5:%[a-zA-Z0-9_]+]] = OpCompositeExtract %uint [[sampled_result5]] 0 + uint val5 = tex3.Sample(float2(0.5, 0.25)); + return 1.0; +} diff --git a/tools/clang/test/SemaHLSL/vk.sampledtexture.error.hlsl b/tools/clang/test/SemaHLSL/vk.sampledtexture.error.hlsl new file mode 100644 index 0000000000..317878db26 --- /dev/null +++ b/tools/clang/test/SemaHLSL/vk.sampledtexture.error.hlsl @@ -0,0 +1,26 @@ +// REQUIRES: spirv +// RUN: %dxc -T ps_6_0 -E main -spirv -verify %s + +struct MyStruct { + float4 a; +}; + +[[vk::binding(0, 0)]] +vk::SampledTexture2D tex1; // expected-error {{'MyStruct' cannot be used as a type parameter where it must be of type float, int, uint or their vector equivalents}} + +[[vk::binding(1, 0)]] +vk::SampledTexture2D tex2; // expected-error {{'float [4]' cannot be used as a type parameter where it must be of type float, int, uint or their vector equivalents}} + +[[vk::binding(2, 0)]] +vk::SampledTexture2D tex3; + +[[vk::binding(3, 0)]] +vk::SampledTexture2D tex4; + +[[vk::binding(4, 0)]] +vk::SampledTexture2D tex5; + +[[vk::binding(5, 0)]] +vk::SampledTexture2D tex6; + +void main() {}