Skip to content

Commit

Permalink
[SPIR-V] Add support for SampleCmpLevel (#6618)
Browse files Browse the repository at this point in the history
SampleCmpLevel is similar to SampleCmpLevel0, except the LOD level can
be specified using either a const-offset, or a variable. This should be
available starting SM6.7

Fixes #6613

---------

Signed-off-by: Nathan Gauër <brioche@google.com>
  • Loading branch information
Keenuts authored May 22, 2024
1 parent 9ee3f23 commit 9ad095d
Show file tree
Hide file tree
Showing 3 changed files with 209 additions and 33 deletions.
141 changes: 112 additions & 29 deletions tools/clang/lib/SPIRV/SpirvEmitter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5143,10 +5143,13 @@ SpirvEmitter::processIntrinsicMemberCall(const CXXMemberCallExpr *expr,
retVal = processTextureSampleGrad(expr);
break;
case IntrinsicOp::MOP_SampleCmp:
retVal = processTextureSampleCmpCmpLevelZero(expr, /*isCmp=*/true);
retVal = processTextureSampleCmp(expr);
break;
case IntrinsicOp::MOP_SampleCmpLevelZero:
retVal = processTextureSampleCmpCmpLevelZero(expr, /*isCmp=*/false);
retVal = processTextureSampleCmpLevelZero(expr);
break;
case IntrinsicOp::MOP_SampleCmpLevel:
retVal = processTextureSampleCmpLevel(expr);
break;
case IntrinsicOp::MOP_GatherRed:
retVal = processTextureGatherRGBACmpRGBA(expr, /*isCmp=*/false, 0);
Expand Down Expand Up @@ -5573,8 +5576,7 @@ SpirvEmitter::processTextureSampleGrad(const CXXMemberCallExpr *expr) {
}

SpirvInstruction *
SpirvEmitter::processTextureSampleCmpCmpLevelZero(const CXXMemberCallExpr *expr,
const bool isCmp) {
SpirvEmitter::processTextureSampleCmp(const CXXMemberCallExpr *expr) {
// .SampleCmp() Signature:
//
// For Texture1D, Texture1DArray, Texture2D, Texture2DArray:
Expand All @@ -5595,10 +5597,55 @@ SpirvEmitter::processTextureSampleCmpCmpLevelZero(const CXXMemberCallExpr *expr,
// [, float Clamp]
// [, out uint Status]
// );
//

const auto numArgs = expr->getNumArgs();
const bool hasStatusArg =
expr->getArg(numArgs - 1)->getType()->isUnsignedIntegerType();
auto *status = hasStatusArg ? doExpr(expr->getArg(numArgs - 1)) : nullptr;

SpirvInstruction *clamp = nullptr;
if (numArgs > 3 && expr->getArg(3)->getType()->isFloatingType())
clamp = doExpr(expr->getArg(3));
else if (numArgs > 4 && expr->getArg(4)->getType()->isFloatingType())
clamp = doExpr(expr->getArg(4));
const bool hasClampArg = clamp != nullptr;

const auto *imageExpr = expr->getImplicitObjectArgument();
auto *image = loadIfGLValue(imageExpr);
auto *sampler = doExpr(expr->getArg(0));
auto *coordinate = doExpr(expr->getArg(1));
auto *compareVal = doExpr(expr->getArg(2));
// If offset is present in .SampleCmp(), it will be the fourth argument.
SpirvInstruction *constOffset = nullptr, *varOffset = nullptr;

// Subtract 1 for clamp (if it exists), 1 for status (if it exists),
// and 3 for sampler_state, location, and compare_value.
const bool hasOffsetArg = numArgs - hasStatusArg - hasClampArg - 3 > 0;
if (hasOffsetArg)
handleOffsetInMethodCall(expr, 3, &constOffset, &varOffset);

const auto retType = expr->getDirectCallee()->getReturnType();
const auto imageType = imageExpr->getType();

if (spvContext.isCS()) {
addDerivativeGroupExecutionMode();
}

return createImageSample(
retType, imageType, image, sampler, coordinate, compareVal,
/*bias*/ nullptr, /*lod*/ nullptr, std::make_pair(nullptr, nullptr),
constOffset, varOffset, /*constOffsets*/ nullptr,
/*sampleNumber*/ nullptr, /*minLod*/ clamp, status,
expr->getCallee()->getLocStart(), expr->getSourceRange());
}

SpirvInstruction *
SpirvEmitter::processTextureSampleCmpLevelZero(const CXXMemberCallExpr *expr) {
// .SampleCmpLevelZero() is identical to .SampleCmp() on mipmap level 0 only.
// It never takes a clamp argument, which is good because lod and clamp may
// not be used together.
// .SampleCmpLevel() is identical to .SampleCmpLevel, except the LOD level
// is taken as a float argument.
//
// .SampleCmpLevelZero() Signature:
//
Expand All @@ -5624,46 +5671,82 @@ SpirvEmitter::processTextureSampleCmpCmpLevelZero(const CXXMemberCallExpr *expr,
expr->getArg(numArgs - 1)->getType()->isUnsignedIntegerType();
auto *status = hasStatusArg ? doExpr(expr->getArg(numArgs - 1)) : nullptr;

SpirvInstruction *clamp = nullptr;
// The .SampleCmpLevelZero() methods do not take the clamp argument.
if (isCmp) {
if (numArgs > 3 && expr->getArg(3)->getType()->isFloatingType())
clamp = doExpr(expr->getArg(3));
else if (numArgs > 4 && expr->getArg(4)->getType()->isFloatingType())
clamp = doExpr(expr->getArg(4));
}
const bool hasClampArg = clamp != nullptr;

// Subtract 1 for clamp (if it exists), 1 for status (if it exists),
// and 3 for sampler_state, location, and compare_value.
const bool hasOffsetArg = numArgs - hasClampArg - hasStatusArg - 3 > 0;

const auto *imageExpr = expr->getImplicitObjectArgument();
auto *image = loadIfGLValue(imageExpr);
auto *sampler = doExpr(expr->getArg(0));
auto *coordinate = doExpr(expr->getArg(1));
auto *compareVal = doExpr(expr->getArg(2));
auto *lod =
spvBuilder.getConstantFloat(astContext.FloatTy, llvm::APFloat(0.0f));

// If offset is present in .SampleCmp(), it will be the fourth argument.
SpirvInstruction *constOffset = nullptr, *varOffset = nullptr;
const bool hasOffsetArg = numArgs - hasStatusArg - 3 > 0;
if (hasOffsetArg)
handleOffsetInMethodCall(expr, 3, &constOffset, &varOffset);
auto *lod = isCmp ? nullptr
: spvBuilder.getConstantFloat(astContext.FloatTy,
llvm::APFloat(0.0f));

const auto retType = expr->getDirectCallee()->getReturnType();
const auto imageType = imageExpr->getType();

if (!lod && spvContext.isCS()) {
addDerivativeGroupExecutionMode();
}
return createImageSample(
retType, imageType, image, sampler, coordinate, compareVal,
/*bias*/ nullptr, /*lod*/ lod, std::make_pair(nullptr, nullptr),
constOffset, varOffset, /*constOffsets*/ nullptr,
/*sampleNumber*/ nullptr, /*clamp*/ nullptr, status,
expr->getCallee()->getLocStart(), expr->getSourceRange());
}

SpirvInstruction *
SpirvEmitter::processTextureSampleCmpLevel(const CXXMemberCallExpr *expr) {
// .SampleCmpLevel() is identical to .SampleCmpLevel, except the LOD level
// is taken as a float argument.
//
// For Texture1D, Texture1DArray, Texture2D, Texture2DArray:
// float Object.SampleCmpLevel(
// SamplerComparisonState S,
// float Location,
// float CompareValue,
// float LOD,
// [, int Offset]
// [, out uint Status]
// );
//
// For TextureCube and TextureCubeArray:
// float Object.SampleCmpLevel(
// SamplerComparisonState S,
// float Location,
// float CompareValue
// float LOD,
// [, out uint Status]
// );

const auto numArgs = expr->getNumArgs();
const bool hasStatusArg =
expr->getArg(numArgs - 1)->getType()->isUnsignedIntegerType();
auto *status = hasStatusArg ? doExpr(expr->getArg(numArgs - 1)) : nullptr;

const auto *imageExpr = expr->getImplicitObjectArgument();
auto *image = loadIfGLValue(imageExpr);
auto *sampler = doExpr(expr->getArg(0));
auto *coordinate = doExpr(expr->getArg(1));
auto *compareVal = doExpr(expr->getArg(2));
auto *lod = doExpr(expr->getArg(3));

// If offset is present in .SampleCmp(), it will be the fourth argument.
SpirvInstruction *constOffset = nullptr, *varOffset = nullptr;
const bool hasOffsetArg = numArgs - hasStatusArg - 4 > 0;
if (hasOffsetArg)
handleOffsetInMethodCall(expr, 4, &constOffset, &varOffset);

const auto retType = expr->getDirectCallee()->getReturnType();
const auto imageType = imageExpr->getType();

return createImageSample(
retType, imageType, image, sampler, coordinate, compareVal,
/*bias*/ nullptr, lod, std::make_pair(nullptr, nullptr), constOffset,
varOffset,
/*constOffsets*/ nullptr, /*sampleNumber*/ nullptr, /*minLod*/ clamp,
status, expr->getCallee()->getLocStart(), expr->getSourceRange());
/*bias*/ nullptr, /*lod*/ lod, std::make_pair(nullptr, nullptr),
constOffset, varOffset, /*constOffsets*/ nullptr,
/*sampleNumber*/ nullptr, /*clamp*/ nullptr, status,
expr->getCallee()->getLocStart(), expr->getSourceRange());
}

SpirvInstruction *
Expand Down
12 changes: 8 additions & 4 deletions tools/clang/lib/SPIRV/SpirvEmitter.h
Original file line number Diff line number Diff line change
Expand Up @@ -985,11 +985,15 @@ class SpirvEmitter : public ASTConsumer {
/// \brief Processes .SampleGrad() method call for texture objects.
SpirvInstruction *processTextureSampleGrad(const CXXMemberCallExpr *expr);

/// \brief Processes .SampleCmp() or .SampleCmpLevelZero() method call for
/// texture objects.
/// \brief Processes .SampleCmp() method call for texture objects.
SpirvInstruction *processTextureSampleCmp(const CXXMemberCallExpr *expr);

/// \brief Processes .SampleCmpLevelZero() method call for texture objects.
SpirvInstruction *
processTextureSampleCmpCmpLevelZero(const CXXMemberCallExpr *expr,
bool isCmp);
processTextureSampleCmpLevelZero(const CXXMemberCallExpr *expr);

/// \brief Processes .SampleCmpLevel() method call for texture objects.
SpirvInstruction *processTextureSampleCmpLevel(const CXXMemberCallExpr *expr);

/// \brief Handles .Gather{|Cmp}{Red|Green|Blue|Alpha}() calls on texture
/// types.
Expand Down
89 changes: 89 additions & 0 deletions tools/clang/test/CodeGenSPIRV/texture.sample-cmp-level.hlsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
// RUN: %dxc -T ps_6_7 -spirv -fcgl %s | FileCheck %s

Texture1D<float4> tex1d;
Texture2D<float4> tex2d;
TextureCube<float4> texCube;
TextureCubeArray<float4> texCubeArray;
SamplerComparisonState samplerComparisonState;
RWStructuredBuffer<uint> data;

// CHECK-DAG: [[v2f_0_0:%[0-9]+]] = OpConstantComposite %v2float %float_0 %float_0
// CHECK-DAG: [[v3f_0_0_0:%[0-9]+]] = OpConstantComposite %v3float %float_0 %float_0 %float_0
// CHECK-DAG: [[v4f_0_0_0_0:%[0-9]+]] = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0
// CHECK-DAG: [[t_1d_sampled_image:%[^ ]+]] = OpTypeSampledImage %type_1d_image
// CHECK-DAG: [[t_2d_sampled_image:%[^ ]+]] = OpTypeSampledImage %type_2d_image
// CHECK-DAG: [[t_cube_sampled_image:%[^ ]+]] = OpTypeSampledImage %type_cube_image
// CHECK-DAG: [[t_cube_array_sampled_image:%[^ ]+]] = OpTypeSampledImage %type_cube_image_array

float4 main() : SV_Target {
// CHECK: [[texture:%[0-9]+]] = OpLoad %type_1d_image %tex1d
// CHECK-DAG: [[sampler:%[0-9]+]] = OpLoad %type_sampler %samplerComparisonState
// CHECK-DAG: [[sampledImage:%[0-9]+]] = OpSampledImage [[t_1d_sampled_image]] [[texture]] [[sampler]]
// CHECK-NEXT: OpImageSampleDrefExplicitLod %float [[sampledImage]] %float_1 %float_2 Lod %float_0
float4 a = tex1d.SampleCmpLevelZero(samplerComparisonState, 1, 2);

// CHECK: [[texture:%[0-9]+]] = OpLoad %type_1d_image %tex1d
// CHECK-DAG: [[sampler:%[0-9]+]] = OpLoad %type_sampler %samplerComparisonState
// CHECK-DAG: [[sampledImage:%[0-9]+]] = OpSampledImage [[t_1d_sampled_image]] [[texture]] [[sampler]]
// CHECK-NEXT: OpImageSampleDrefExplicitLod %float [[sampledImage]] %float_1 %float_2 Lod|ConstOffset %float_0 %int_3
float4 b = tex1d.SampleCmpLevelZero(samplerComparisonState, 1, 2, 3);

// CHECK: [[texture:%[0-9]+]] = OpLoad %type_1d_image %tex1d
// CHECK-DAG: [[sampler:%[0-9]+]] = OpLoad %type_sampler %samplerComparisonState
// CHECK-DAG: [[sampledImage:%[0-9]+]] = OpSampledImage [[t_1d_sampled_image]] [[texture]] [[sampler]]
// CHECK-DAG: [[ptr:%[0-9]+]] = OpAccessChain %_ptr_Uniform_uint %data %int_0 %uint_0
// CHECK-DAG: [[tmp:%[0-9]+]] = OpLoad %uint [[ptr]]
// CHECK-DAG: [[offset:%[0-9]+]] = OpBitcast %int [[tmp]]
// CHECK-NEXT: OpImageSampleDrefExplicitLod %float [[sampledImage]] %float_1 %float_2 Lod|Offset %float_0 [[offset]]
float4 c = tex1d.SampleCmpLevelZero(samplerComparisonState, 1, 2, data[0]);

// CHECK: [[texture:%[0-9]+]] = OpLoad %type_1d_image %tex1d
// CHECK-DAG: [[sampler:%[0-9]+]] = OpLoad %type_sampler %samplerComparisonState
// CHECK-DAG: [[sampledImage:%[0-9]+]] = OpSampledImage [[t_1d_sampled_image]] [[texture]] [[sampler]]
// CHECK-DAG: [[ptr:%[0-9]+]] = OpAccessChain %_ptr_Uniform_uint %data %int_0 %uint_0
// CHECK-DAG: [[tmp:%[0-9]+]] = OpLoad %uint [[ptr]]
// CHECK-DAG: [[offset:%[0-9]+]] = OpBitcast %int [[tmp]]
// CHECK-NEXT: [[tmp:%[0-9]+]] = OpImageSparseSampleDrefExplicitLod %SparseResidencyStruct [[sampledImage]] %float_1 %float_2 Lod|Offset %float_0 [[offset]]
// CHECK-NEXT: [[res:%[0-9]+]] = OpCompositeExtract %uint [[tmp]] 0
// CHECK-NEXT: OpStore %status_0 [[res]]
uint status_0;
float4 d = tex1d.SampleCmpLevelZero(samplerComparisonState, 1, 2, data[0], status_0);

// CHECK: [[texture:%[0-9]+]] = OpLoad %type_2d_image %tex2d
// CHECK-DAG: [[sampler:%[0-9]+]] = OpLoad %type_sampler %samplerComparisonState
// CHECK-DAG: [[sampledImage:%[0-9]+]] = OpSampledImage [[t_2d_sampled_image]] [[texture]] [[sampler]]
// CHECK-NEXT: OpImageSampleDrefExplicitLod %float [[sampledImage]] [[v2f_0_0]] %float_2 Lod %float_3
float4 e = tex2d.SampleCmpLevel(samplerComparisonState, float2(0, 0), 2, 3);

// CHECK: [[texture:%[0-9]+]] = OpLoad %type_cube_image %texCube
// CHECK-DAG: [[sampler:%[0-9]+]] = OpLoad %type_sampler %samplerComparisonState
// CHECK-DAG: [[sampledImage:%[0-9]+]] = OpSampledImage [[t_cube_sampled_image]] [[texture]] [[sampler]]
// CHECK-NEXT: OpImageSampleDrefExplicitLod %float [[sampledImage]] [[v3f_0_0_0]] %float_1 Lod %float_2
float4 f = texCube.SampleCmpLevel(samplerComparisonState, float3(0, 0, 0), 1, 2);

// CHECK: [[texture:%[0-9]+]] = OpLoad %type_cube_image %texCube
// CHECK-DAG: [[sampler:%[0-9]+]] = OpLoad %type_sampler %samplerComparisonState
// CHECK-DAG: [[sampledImage:%[0-9]+]] = OpSampledImage [[t_cube_sampled_image]] [[texture]] [[sampler]]
// CHECK-NEXT: [[tmp:%[0-9]+]] = OpImageSparseSampleDrefExplicitLod %SparseResidencyStruct [[sampledImage]] [[v3f_0_0_0]] %float_1 Lod %float_2
// CHECK-NEXT: [[res:%[0-9]+]] = OpCompositeExtract %uint [[tmp]] 0
// CHECK-NEXT: OpStore %status_1 [[res]]
uint status_1;
float4 g = texCube.SampleCmpLevel(samplerComparisonState, float3(0, 0, 0), 1, 2, status_1);

// CHECK: [[texture:%[0-9]+]] = OpLoad %type_cube_image_array %texCubeArray
// CHECK-DAG: [[sampler:%[0-9]+]] = OpLoad %type_sampler %samplerComparisonState
// CHECK-DAG: [[sampledImage:%[0-9]+]] = OpSampledImage [[t_cube_array_sampled_image]] [[texture]] [[sampler]]
// CHECK-NEXT: OpImageSampleDrefExplicitLod %float [[sampledImage]] [[v4f_0_0_0_0]] %float_1 Lod %float_2
float4 h = texCubeArray.SampleCmpLevel(samplerComparisonState, float4(0, 0, 0, 0), 1, 2);

// CHECK: [[texture:%[0-9]+]] = OpLoad %type_cube_image_array %texCubeArray
// CHECK-DAG: [[sampler:%[0-9]+]] = OpLoad %type_sampler %samplerComparisonState
// CHECK-DAG: [[sampledImage:%[0-9]+]] = OpSampledImage [[t_cube_array_sampled_image]] [[texture]] [[sampler]]
// CHECK-NEXT: [[tmp:%[0-9]+]] = OpImageSparseSampleDrefExplicitLod %SparseResidencyStruct [[sampledImage]] [[v4f_0_0_0_0]] %float_1 Lod %float_2
// CHECK-NEXT: [[res:%[0-9]+]] = OpCompositeExtract %uint [[tmp]] 0
// CHECK-NEXT: OpStore %status_2 [[res]]
uint status_2;
float4 i = texCubeArray.SampleCmpLevel(samplerComparisonState, float4(0, 0, 0, 0), 1, 2, status_2);

return float4(0, 0, 0, 0);
}

0 comments on commit 9ad095d

Please sign in to comment.