Skip to content
3 changes: 2 additions & 1 deletion include/dxc/dxcapi.internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,8 @@ enum LEGAL_INTRINSIC_COMPTYPES {

#ifdef ENABLE_SPIRV_CODEGEN
LICOMPTYPE_VK_BUFFER_POINTER = 56,
LICOMPTYPE_COUNT = 57
LICOMPTYPE_VK_SAMPLED_TEXTURE2D = 57,
LICOMPTYPE_COUNT = 58
#else
LICOMPTYPE_COUNT = 56
#endif
Expand Down
4 changes: 4 additions & 0 deletions tools/clang/include/clang/AST/HlslTypes.h
Original file line number Diff line number Diff line change
Expand Up @@ -407,6 +407,10 @@ 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::CXXRecordDecl *DeclareInlineSpirvType(clang::ASTContext &context,
clang::DeclContext *declContext,
llvm::StringRef typeName,
Expand Down
3 changes: 3 additions & 0 deletions tools/clang/include/clang/SPIRV/AstTypeProbe.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down
4 changes: 4 additions & 0 deletions tools/clang/include/clang/SPIRV/SpirvBuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,10 @@ class SpirvBuilder {
/// If compareVal is given a non-zero value, *Dref* variants of OpImageSample*
/// will be generated.
///
/// If sampler is set, it defines the sampler along *image* to create the
/// combined image sampler. Otherwise, the *image* parameter must point to a
/// sampled image.
///
/// If lod or grad is given a non-zero value, *ExplicitLod variants of
/// OpImageSample* will be generated; otherwise, *ImplicitLod variant will
/// be generated.
Expand Down
20 changes: 20 additions & 0 deletions tools/clang/lib/AST/ASTContextHLSL.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1372,6 +1372,26 @@ CXXRecordDecl *hlsl::DeclareNodeOrRecordType(
}

#ifdef ENABLE_SPIRV_CODEGEN
CXXRecordDecl *hlsl::DeclareVkSampledTextureType(ASTContext &context,
DeclContext *declContext,
llvm::StringRef hlslTypeName,
QualType defaultParamType) {
// TODO(https://github.com/microsoft/DirectXShaderCompiler/issues/7979): Later
// generalize these to all SampledTexture types.
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();

return recordDecl;
}

CXXRecordDecl *hlsl::DeclareVkBufferPointerType(ASTContext &context,
DeclContext *declContext) {
BuiltinTypeDeclBuilder Builder(declContext, "BufferPointer",
Expand Down
11 changes: 11 additions & 0 deletions tools/clang/lib/SPIRV/AstTypeProbe.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -926,6 +926,17 @@ bool isTexture(QualType type) {
return false;
}

bool isSampledTexture(QualType type) {
if (const auto *rt = type->getAs<RecordType>()) {
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<RecordType>()) {
const auto name = rt->getDecl()->getName();
Expand Down
20 changes: 19 additions & 1 deletion tools/clang/lib/SPIRV/LowerTypeVisitor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down Expand Up @@ -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);
}
Expand Down
11 changes: 9 additions & 2 deletions tools/clang/lib/SPIRV/SpirvBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
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);
Expand Down
72 changes: 51 additions & 21 deletions tools/clang/lib/SPIRV/SpirvEmitter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4474,14 +4474,27 @@ SpirvEmitter::processTextureLevelOfDetail(const CXXMemberCallExpr *expr,
// TextureCube(Array).CalculateLevelOfDetail(SamplerState S, float3 xyz);
// Texture3D.CalculateLevelOfDetail(SamplerState S, float3 xyz);
// 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));
//
// Their SampledTexture variants have the same signature without the
// sampler_state parameter.
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);

auto *sampledImage = spvBuilder.createSampledImage(
object->getType(), objectInfo, samplerState, expr->getExprLoc());
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 =
Expand Down Expand Up @@ -5817,6 +5830,9 @@ SpirvEmitter::processTextureSampleGather(const CXXMemberCallExpr *expr,
// [, float Clamp]
// [, out uint Status]);
//
// Their SampledTexture variants have the same signature without the
// sampler_state parameter.
//
// For TextureCube and TextureCubeArray:
// DXGI_FORMAT Object.Sample(sampler_state S,
// float Location
Expand All @@ -5835,35 +5851,49 @@ SpirvEmitter::processTextureSampleGather(const CXXMemberCallExpr *expr,
// [, 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;
uint32_t coordIndex;
if (isImageSampledTexture) {
samplerIndex = -1; // non-existant
coordIndex = 0;
} else {
samplerIndex = 0;
coordIndex = 1;
}

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 > coordIndex + 1 &&
expr->getArg(coordIndex + 1)->getType()->isFloatingType())
clamp = doExpr(expr->getArg(coordIndex + 1));
else if (numArgs > coordIndex + 2 &&
expr->getArg(coordIndex + 2)->getType()->isFloatingType())
clamp = doExpr(expr->getArg(coordIndex + 2));
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 - coordIndex > 1;
if (hasOffsetArg)
handleOffsetInMethodCall(expr, 2, &constOffset, &varOffset);
handleOffsetInMethodCall(expr, coordIndex + 1, &constOffset, &varOffset);

const auto retType = expr->getDirectCallee()->getReturnType();
if (isSample) {
Expand Down
31 changes: 28 additions & 3 deletions tools/clang/lib/Sema/SemaHLSL.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,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

Expand Down Expand Up @@ -559,6 +560,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

Expand Down Expand Up @@ -1267,6 +1269,8 @@ static const ArBasicKind g_LinAlgMatrixCT[] = {AR_OBJECT_LINALG_MATRIX,
#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.
Expand Down Expand Up @@ -1329,7 +1333,8 @@ const ArBasicKind *g_LegalIntrinsicCompTypes[] = {
g_LinAlgCT, // LICOMPTYPE_LINALG
g_BuiltInTrianglePositionsCT, // LICOMPTYPE_BUILTIN_TRIANGLE_POSITIONS
#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(
Expand Down Expand Up @@ -1389,7 +1394,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

Expand Down Expand Up @@ -1501,6 +1506,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

Expand Down Expand Up @@ -1654,6 +1660,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

Expand Down Expand Up @@ -1823,6 +1830,7 @@ static const char *g_ArBasicTypeNames[] = {
"ext_type",
"ext_result_id",
"BufferPointer",
"SampledTexture2D",
#endif // ENABLE_SPIRV_CODEGEN
// SPIRV change ends

Expand Down Expand Up @@ -2475,6 +2483,12 @@ static void GetIntrinsicMethods(ArBasicKind kind,
*intrinsics = g_RayQueryMethods;
*intrinsicCount = _countof(g_RayQueryMethods);
break;
#ifdef ENABLE_SPIRV_CODEGEN
case AR_OBJECT_VK_SAMPLED_TEXTURE2D:
*intrinsics = g_VkSampledTexture2DMethods;
*intrinsicCount = _countof(g_VkSampledTexture2DMethods);
break;
#endif
case AR_OBJECT_HIT_OBJECT:
*intrinsics = g_DxHitObjectMethods;
*intrinsicCount = _countof(g_DxHitObjectMethods);
Expand Down Expand Up @@ -3073,6 +3087,7 @@ class HLSLExternalSource : public ExternalSemaSource {
ClassTemplateDecl *m_vkIntegralConstantTemplateDecl;
ClassTemplateDecl *m_vkLiteralTemplateDecl;
ClassTemplateDecl *m_vkBufferPointerTemplateDecl;
ClassTemplateDecl *m_vkSampledTextureTemplateDecl;

// Declarations for Work Graph Output Record types
ClassTemplateDecl *m_GroupNodeOutputRecordsTemplateDecl;
Expand Down Expand Up @@ -4078,6 +4093,15 @@ 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;
QualType float4Type =
LookupVectorType(HLSLScalarType::HLSLScalarType_float, 4);
recordDecl = DeclareVkSampledTextureType(
*m_context, m_vkNSDecl, "SampledTexture2D", float4Type);
m_vkSampledTextureTemplateDecl =
recordDecl->getDescribedClassTemplate();
}
#endif
else if (templateArgCount == 0) {
Expand Down Expand Up @@ -4191,7 +4215,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_vkSampledTextureTemplateDecl(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));
Expand Down
21 changes: 21 additions & 0 deletions tools/clang/test/CodeGenSPIRV/vk.sampledtexture.calculate-lod.hlsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// RUN: %dxc -T ps_6_0 -E main -fcgl %s -spirv | FileCheck %s

// CHECK: OpCapability ImageQuery

vk::SampledTexture2D<float4> 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);
}
Loading