Procházet zdrojové kódy

[spirv] Support Clamp argument in Sample methods. (#887)

Ehsan před 7 roky
rodič
revize
4d4c4efd66

+ 4 - 2
tools/clang/include/clang/SPIRV/ModuleBuilder.h

@@ -180,7 +180,8 @@ public:
                              uint32_t compareVal, uint32_t lod,
                              std::pair<uint32_t, uint32_t> grad,
                              uint32_t constOffset, uint32_t varOffset,
-                             uint32_t constOffsets, uint32_t sample);
+                             uint32_t constOffsets, uint32_t sample,
+                             uint32_t minLod);
 
   /// \brief Creates SPIR-V instructions for reading a texel from an image. If
   /// doImageFetch is true, OpImageFetch is used. OpImageRead is used otherwise.
@@ -403,7 +404,8 @@ private:
   spv::ImageOperandsMask composeImageOperandsMask(
       uint32_t bias, uint32_t lod, const std::pair<uint32_t, uint32_t> &grad,
       uint32_t constOffset, uint32_t varOffset, uint32_t constOffsets,
-      uint32_t sample, llvm::SmallVectorImpl<uint32_t> *orderedParams);
+      uint32_t sample, uint32_t minLod,
+      llvm::SmallVectorImpl<uint32_t> *orderedParams);
 
   SPIRVContext &theContext; ///< The SPIR-V context.
   SPIRVModule theModule;    ///< The module under building.

+ 27 - 12
tools/clang/lib/SPIRV/ModuleBuilder.cpp

@@ -284,7 +284,8 @@ uint32_t ModuleBuilder::createAtomicCompareExchange(
 spv::ImageOperandsMask ModuleBuilder::composeImageOperandsMask(
     uint32_t bias, uint32_t lod, const std::pair<uint32_t, uint32_t> &grad,
     uint32_t constOffset, uint32_t varOffset, uint32_t constOffsets,
-    uint32_t sample, llvm::SmallVectorImpl<uint32_t> *orderedParams) {
+    uint32_t sample, uint32_t minLod,
+    llvm::SmallVectorImpl<uint32_t> *orderedParams) {
   using spv::ImageOperandsMask;
   // SPIR-V Image Operands from least significant bit to most significant bit
   // Bias, Lod, Grad, ConstOffset, Offset, ConstOffsets, Sample, MinLod
@@ -329,6 +330,12 @@ spv::ImageOperandsMask ModuleBuilder::composeImageOperandsMask(
     orderedParams->push_back(sample);
   }
 
+  if (minLod) {
+    requireCapability(spv::Capability::MinLod);
+    mask = mask | ImageOperandsMask::MinLod;
+    orderedParams->push_back(minLod);
+  }
+
   return mask;
 }
 
@@ -348,9 +355,18 @@ uint32_t ModuleBuilder::createImageSample(
     uint32_t texelType, uint32_t imageType, uint32_t image, uint32_t sampler,
     uint32_t coordinate, uint32_t compareVal, uint32_t bias, uint32_t lod,
     std::pair<uint32_t, uint32_t> grad, uint32_t constOffset,
-    uint32_t varOffset, uint32_t constOffsets, uint32_t sample) {
+    uint32_t varOffset, uint32_t constOffsets, uint32_t sample, uint32_t minLod) {
   assert(insertPoint && "null insert point");
 
+  // The Lod and Grad image operands requires explicit-lod instructions.
+  // Otherwise we use implicit-lod instructions.
+  const bool mustUseExplicitInstr = lod || (grad.first && grad.second);
+
+  // minLod is only valid with Implicit instructions and Grad instructions.
+  // This means that we cannot have Lod and minLod together because Lod requires
+  // explicit insturctions. So either lod or minLod or both must be zero.
+  assert(lod == 0 || minLod == 0);
+
   // An OpSampledImage is required to do the image sampling.
   const uint32_t sampledImgId = theContext.takeNextId();
   const uint32_t sampledImgTy = getSampledImageType(imageType);
@@ -359,15 +375,14 @@ uint32_t ModuleBuilder::createImageSample(
 
   const uint32_t texelId = theContext.takeNextId();
   llvm::SmallVector<uint32_t, 4> params;
-  const auto mask = composeImageOperandsMask(
-      bias, lod, grad, constOffset, varOffset, constOffsets, sample, &params);
+  const auto mask =
+      composeImageOperandsMask(bias, lod, grad, constOffset, varOffset,
+                               constOffsets, sample, minLod, &params);
 
   // If depth-comparison is needed when sampling, we use the OpImageSampleDref*
   // instructions.
   if (compareVal) {
-    // The Lod and Grad image operands requires explicit-lod instructions.
-    // Otherwise we use implicit-lod instructions.
-    if (lod || (grad.first && grad.second)) {
+    if (mustUseExplicitInstr) {
       instBuilder.opImageSampleDrefExplicitLod(texelType, texelId, sampledImgId,
                                                coordinate, compareVal, mask);
     } else {
@@ -376,9 +391,7 @@ uint32_t ModuleBuilder::createImageSample(
           llvm::Optional<spv::ImageOperandsMask>(mask));
     }
   } else {
-    // The Lod and Grad image operands requires explicit-lod instructions.
-    // Otherwise we use implicit-lod instructions.
-    if (lod || (grad.first && grad.second)) {
+    if (mustUseExplicitInstr) {
       instBuilder.opImageSampleExplicitLod(texelType, texelId, sampledImgId,
                                            coordinate, mask);
     } else {
@@ -411,11 +424,12 @@ uint32_t ModuleBuilder::createImageFetchOrRead(
     uint32_t constOffsets, uint32_t sample) {
   assert(insertPoint && "null insert point");
 
+  // TODO: Update ImageFetch/ImageRead to accept minLod if necessary.
   llvm::SmallVector<uint32_t, 2> params;
   const auto mask =
       llvm::Optional<spv::ImageOperandsMask>(composeImageOperandsMask(
           /*bias*/ 0, lod, std::make_pair(0, 0), constOffset, varOffset,
-          constOffsets, sample, &params));
+          constOffsets, sample, /*minLod*/ 0, &params));
 
   const uint32_t texelId = theContext.takeNextId();
   if (doImageFetch) {
@@ -455,10 +469,11 @@ uint32_t ModuleBuilder::createImageGather(
 
   llvm::SmallVector<uint32_t, 2> params;
 
+  // TODO: Update ImageGather to accept minLod if necessary.
   const auto mask =
       llvm::Optional<spv::ImageOperandsMask>(composeImageOperandsMask(
           /*bias*/ 0, /*lod*/ 0, std::make_pair(0, 0), constOffset, varOffset,
-          constOffsets, sample, &params));
+          constOffsets, sample, /*minLod*/ 0, &params));
   uint32_t texelId = theContext.takeNextId();
 
   if (compareVal) {

+ 154 - 25
tools/clang/lib/SPIRV/SPIRVEmitter.cpp

@@ -2781,9 +2781,18 @@ SPIRVEmitter::processIntrinsicMemberCall(const CXXMemberCallExpr *expr,
 uint32_t SPIRVEmitter::processTextureSampleGather(const CXXMemberCallExpr *expr,
                                                   const bool isSample) {
   // Signatures:
+  // For Texture1D, Texture1DArray, Texture2D, Texture2DArray, Texture3D:
   // DXGI_FORMAT Object.Sample(sampler_state S,
   //                           float Location
-  //                           [, int Offset]);
+  //                           [, int Offset]
+  //                           [, float Clamp]
+  //                           [, out uint Status]);
+  //
+  // For TextureCube and TextureCubeArray:
+  // DXGI_FORMAT Object.Sample(sampler_state S,
+  //                           float Location
+  //                           [, float Clamp]
+  //                           [, out uint Status]);
   //
   // For Texture2D/Texture2DArray:
   // <Template Type>4 Object.Gather(sampler_state S,
@@ -2801,10 +2810,18 @@ uint32_t SPIRVEmitter::processTextureSampleGather(const CXXMemberCallExpr *expr,
   const auto numArgs = expr->getNumArgs();
   const bool hasStatusArg =
       expr->getArg(numArgs - 1)->getType()->isUnsignedIntegerType();
+
+  uint32_t clamp = 0;
+  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));
+  const bool hasClampArg = (clamp != 0);
   const auto status = hasStatusArg ? doExpr(expr->getArg(numArgs - 1)) : 0;
 
-  // Subtract 1 for status (if it exists), and 2 for sampler_state and location.
-  const bool hasOffsetArg = numArgs - hasStatusArg - 2 > 0;
+  // 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 uint32_t imageType = typeTranslator.translateType(imageExpr->getType());
@@ -2824,7 +2841,7 @@ uint32_t SPIRVEmitter::processTextureSampleGather(const CXXMemberCallExpr *expr,
     return theBuilder.createImageSample(
         retType, imageType, image, sampler, coordinate, /*compareVal*/ 0,
         /*bias*/ 0, /*lod*/ 0, std::make_pair(0, 0), constOffset, varOffset,
-        /*constOffsets*/ 0, /*sampleNumber*/ 0);
+        /*constOffsets*/ 0, /*sampleNumber*/ 0, /*minLod*/ clamp);
   } else {
     return theBuilder.createImageGather(
         retType, imageType, image, sampler, coordinate,
@@ -2838,19 +2855,54 @@ uint32_t
 SPIRVEmitter::processTextureSampleBiasLevel(const CXXMemberCallExpr *expr,
                                             const bool isBias) {
   // Signatures:
+  // For Texture1D, Texture1DArray, Texture2D, Texture2DArray, and Texture3D:
+  // DXGI_FORMAT Object.SampleBias(sampler_state S,
+  //                               float Location,
+  //                               float Bias
+  //                               [, int Offset]
+  //                               [, float clamp]
+  //                               [, out uint Status]);
+  //
+  // For TextureCube and TextureCubeArray:
   // DXGI_FORMAT Object.SampleBias(sampler_state S,
   //                               float Location,
   //                               float Bias
-  //                               [, int Offset]);
+  //                               [, float clamp]
+  //                               [, out uint Status]);
   //
+  // For Texture1D, Texture1DArray, Texture2D, Texture2DArray, and Texture3D:
   // DXGI_FORMAT Object.SampleLevel(sampler_state S,
   //                                float Location,
   //                                float LOD
-  //                                [, int Offset]);
-  const auto *imageExpr = expr->getImplicitObjectArgument();
+  //                                [, int Offset]
+  //                                [, out uint Status]);
+  //
+  // For TextureCube and TextureCubeArray:
+  // DXGI_FORMAT Object.SampleLevel(sampler_state S,
+  //                                float Location,
+  //                                float LOD
+  //                                [, out uint Status]);
 
-  const uint32_t imageType = typeTranslator.translateType(imageExpr->getType());
+  const auto numArgs = expr->getNumArgs();
+  const bool hasStatusArg =
+      expr->getArg(numArgs - 1)->getType()->isUnsignedIntegerType();
+
+  uint32_t clamp = 0;
+  // The .SampleLevel() methods do not take the clamp argument.
+  if (isBias) {
+    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 != 0;
 
+  // Subtract 1 for clamp (if it exists), 1 for status (if it exists),
+  // and 3 for sampler_state, location, and Bias/LOD.
+  const bool hasOffsetArg = numArgs - hasClampArg - hasStatusArg - 3 > 0;
+
+  const auto *imageExpr = expr->getImplicitObjectArgument();
+  const uint32_t imageType = typeTranslator.translateType(imageExpr->getType());
   const uint32_t image = loadIfGLValue(imageExpr);
   const uint32_t sampler = doExpr(expr->getArg(0));
   const uint32_t coordinate = doExpr(expr->getArg(1));
@@ -2861,9 +2913,10 @@ SPIRVEmitter::processTextureSampleBiasLevel(const CXXMemberCallExpr *expr,
   } else {
     lod = doExpr(expr->getArg(2));
   }
-  // .Bias()/.SampleLevel() has a fourth optional paramter for offset.
+  // If offset is present in .Bias()/.SampleLevel(), it is the fourth argument.
   uint32_t constOffset = 0, varOffset = 0;
-  handleOptionalOffsetInMethodCall(expr, 3, &constOffset, &varOffset);
+  if(hasOffsetArg)
+    handleOffsetInMethodCall(expr, 3, &constOffset, &varOffset);
 
   const auto retType =
       typeTranslator.translateType(expr->getDirectCallee()->getReturnType());
@@ -2871,29 +2924,54 @@ SPIRVEmitter::processTextureSampleBiasLevel(const CXXMemberCallExpr *expr,
   return theBuilder.createImageSample(
       retType, imageType, image, sampler, coordinate, /*compareVal*/ 0, bias,
       lod, std::make_pair(0, 0), constOffset, varOffset, /*constOffsets*/ 0,
-      /*sampleNumber*/ 0);
+      /*sampleNumber*/ 0, /*minLod*/ clamp);
 }
 
 uint32_t SPIRVEmitter::processTextureSampleGrad(const CXXMemberCallExpr *expr) {
   // Signature:
+  // For Texture1D, Texture1DArray, Texture2D, Texture2DArray, and Texture3D:
   // DXGI_FORMAT Object.SampleGrad(sampler_state S,
   //                               float Location,
   //                               float DDX,
   //                               float DDY
-  //                               [, int Offset]);
+  //                               [, int Offset]
+  //                               [, float Clamp]
+  //                               [, out uint Status]);
+  //
+  // For TextureCube and TextureCubeArray:
+  // DXGI_FORMAT Object.SampleGrad(sampler_state S,
+  //                               float Location,
+  //                               float DDX,
+  //                               float DDY
+  //                               [, float Clamp]
+  //                               [, out uint Status]);
 
-  const auto *imageExpr = expr->getImplicitObjectArgument();
+  const auto numArgs = expr->getNumArgs();
+  const bool hasStatusArg =
+      expr->getArg(numArgs - 1)->getType()->isUnsignedIntegerType();
 
-  const uint32_t imageType = typeTranslator.translateType(imageExpr->getType());
+  uint32_t clamp = 0;
+  if (numArgs > 4 && expr->getArg(4)->getType()->isFloatingType())
+    clamp = doExpr(expr->getArg(4));
+  else if (numArgs > 5 && expr->getArg(5)->getType()->isFloatingType())
+    clamp = doExpr(expr->getArg(5));
+  const bool hasClampArg = clamp != 0;
 
+  // Subtract 1 for clamp (if it exists), 1 for status (if it exists),
+  // and 4 for sampler_state, location, DDX, and DDY;
+  const bool hasOffsetArg = numArgs - hasClampArg - hasStatusArg - 4 > 0;
+
+  const auto *imageExpr = expr->getImplicitObjectArgument();
+  const uint32_t imageType = typeTranslator.translateType(imageExpr->getType());
   const uint32_t image = loadIfGLValue(imageExpr);
   const uint32_t sampler = doExpr(expr->getArg(0));
   const uint32_t coordinate = doExpr(expr->getArg(1));
   const uint32_t ddx = doExpr(expr->getArg(2));
   const uint32_t ddy = doExpr(expr->getArg(3));
-  // .SampleGrad() has a fifth optional paramter for offset.
+  // If offset is present in .SampleGrad(), it is the fifth argument.
   uint32_t constOffset = 0, varOffset = 0;
-  handleOptionalOffsetInMethodCall(expr, 4, &constOffset, &varOffset);
+  if (hasOffsetArg)
+    handleOffsetInMethodCall(expr, 4, &constOffset, &varOffset);
 
   const auto retType =
       typeTranslator.translateType(expr->getDirectCallee()->getReturnType());
@@ -2901,8 +2979,7 @@ uint32_t SPIRVEmitter::processTextureSampleGrad(const CXXMemberCallExpr *expr) {
   return theBuilder.createImageSample(
       retType, imageType, image, sampler, coordinate, /*compareVal*/ 0,
       /*bias*/ 0, /*lod*/ 0, std::make_pair(ddx, ddy), constOffset, varOffset,
-      /*constOffsets*/ 0,
-      /*sampleNumber*/ 0);
+      /*constOffsets*/ 0, /*sampleNumber*/ 0, /*minLod*/ clamp);
 }
 
 uint32_t
@@ -2910,23 +2987,75 @@ SPIRVEmitter::processTextureSampleCmpCmpLevelZero(const CXXMemberCallExpr *expr,
                                                   const bool isCmp) {
   // .SampleCmp() Signature:
   //
+  // For Texture1D, Texture1DArray, Texture2D, Texture2DArray:
+  // float Object.SampleCmp(
+  //   SamplerComparisonState S,
+  //   float Location,
+  //   float CompareValue
+  //   [, int Offset]
+  //   [, float Clamp]
+  //   [, out uint Status]
+  // );
+  //
+  // For TextureCube and TextureCubeArray:
   // float Object.SampleCmp(
   //   SamplerComparisonState S,
   //   float Location,
-  //   float CompareValue,
-  //   [int Offset]
+  //   float CompareValue
+  //   [, float Clamp]
+  //   [, out uint Status]
   // );
   //
   // .SampleCmpLevelZero() is identical to .SampleCmp() on mipmap level 0 only.
-  const auto *imageExpr = expr->getImplicitObjectArgument();
+  // It never takes a clamp argument, which is good because lod and clamp may
+  // not be used together.
+  //
+  // .SampleCmpLevelZero() Signature:
+  //
+  // For Texture1D, Texture1DArray, Texture2D, Texture2DArray:
+  // float Object.SampleCmpLevelZero(
+  //   SamplerComparisonState S,
+  //   float Location,
+  //   float CompareValue
+  //   [, int Offset]
+  //   [, out uint Status]
+  // );
+  //
+  // For TextureCube and TextureCubeArray:
+  // float Object.SampleCmpLevelZero(
+  //   SamplerComparisonState S,
+  //   float Location,
+  //   float CompareValue
+  //   [, out uint Status]
+  // );
 
+  const auto numArgs = expr->getNumArgs();
+  const bool hasStatusArg =
+      expr->getArg(numArgs - 1)->getType()->isUnsignedIntegerType();
+
+  uint32_t clamp = 0;
+  // 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 != 0;
+
+  // 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();
   const uint32_t image = loadIfGLValue(imageExpr);
   const uint32_t sampler = doExpr(expr->getArg(0));
   const uint32_t coordinate = doExpr(expr->getArg(1));
   const uint32_t compareVal = doExpr(expr->getArg(2));
-  // .SampleCmp() has a fourth optional paramter for offset.
+  // If offset is present in .SampleCmp(), it will be the fourth argument.
   uint32_t constOffset = 0, varOffset = 0;
-  handleOptionalOffsetInMethodCall(expr, 3, &constOffset, &varOffset);
+  if(hasOffsetArg)
+    handleOffsetInMethodCall(expr, 3, &constOffset, &varOffset);
   const uint32_t lod = isCmp ? 0 : theBuilder.getConstantFloat32(0);
 
   const auto retType =
@@ -2935,8 +3064,8 @@ SPIRVEmitter::processTextureSampleCmpCmpLevelZero(const CXXMemberCallExpr *expr,
 
   return theBuilder.createImageSample(
       retType, imageType, image, sampler, coordinate, compareVal, /*bias*/ 0,
-      lod, std::make_pair(0, 0), constOffset, varOffset,
-      /*constOffsets*/ 0, /*sampleNumber*/ 0);
+      lod, std::make_pair(0, 0), constOffset, varOffset, /*constOffsets*/ 0,
+      /*sampleNumber*/ 0, /*minLod*/ clamp);
 }
 
 SpirvEvalInfo

+ 15 - 0
tools/clang/test/CodeGenSPIRV/texture.array.sample-bias.hlsl

@@ -9,6 +9,7 @@ Texture2DArray   <float4> t2 : register(t2);
 TextureCubeArray <float4> t3 : register(t3);
 
 // CHECK: OpCapability ImageGatherExtended
+// CHECK: OpCapability MinLod
 
 // CHECK: %type_sampled_image = OpTypeSampledImage %type_1d_image_array
 // CHECK: %type_sampled_image_0 = OpTypeSampledImage %type_2d_image_array
@@ -38,5 +39,19 @@ float4 main(int2 offset : A) : SV_Target {
 // CHECK-NEXT:            {{%\d+}} = OpImageSampleImplicitLod %v4float [[sampledImg]] [[v4fc]] Bias %float_0_5
     float4 val3 = t3.SampleBias(gSampler, float4(0.1, 0.2, 0.3, 1), 0.5);
 
+    float clamp;
+// CHECK:           [[clamp:%\d+]] = OpLoad %float %clamp
+// CHECK-NEXT:         [[t1:%\d+]] = OpLoad %type_1d_image_array %t1
+// CHECK-NEXT:   [[gSampler:%\d+]] = OpLoad %type_sampler %gSampler
+// CHECK-NEXT: [[sampledImg:%\d+]] = OpSampledImage %type_sampled_image [[t1]] [[gSampler]]
+// CHECK-NEXT:            {{%\d+}} = OpImageSampleImplicitLod %v4float [[sampledImg]] [[v2fc]] Bias|ConstOffset|MinLod %float_0_5 %int_1 [[clamp]]
+    float4 val4 = t1.SampleBias(gSampler, float2(0.1, 1), 0.5, 1, clamp);
+
+// CHECK:              [[t3:%\d+]] = OpLoad %type_cube_image_array %t3
+// CHECK-NEXT:   [[gSampler:%\d+]] = OpLoad %type_sampler %gSampler
+// CHECK-NEXT: [[sampledImg:%\d+]] = OpSampledImage %type_sampled_image_1 [[t3]] [[gSampler]]
+// CHECK-NEXT:            {{%\d+}} = OpImageSampleImplicitLod %v4float [[sampledImg]] [[v4fc]] Bias|MinLod %float_0_5 %float_2_5
+    float4 val5 = t3.SampleBias(gSampler, float4(0.1, 0.2, 0.3, 1), 0.5, /*clamp*/ 2.5f);
+
     return 1.0;
 }

+ 19 - 0
tools/clang/test/CodeGenSPIRV/texture.array.sample-cmp.hlsl

@@ -6,6 +6,8 @@ Texture1DArray   <float4> t1 : register(t1);
 Texture2DArray   <float3> t2 : register(t2);
 TextureCubeArray <float>  t3 : register(t3);
 
+// CHECK: OpCapability MinLod
+
 // CHECK: [[v2fc:%\d+]] = OpConstantComposite %v2float %float_0_1 %float_1
 // CHECK: [[v3fc:%\d+]] = OpConstantComposite %v3float %float_0_1 %float_0_2 %float_1
 // CHECK: [[v4fc:%\d+]] = OpConstantComposite %v4float %float_0_1 %float_0_2 %float_0_3 %float_1
@@ -33,5 +35,22 @@ float4 main(int2 offset: A, float comparator: B) : SV_Target {
 // CHECK-NEXT:            {{%\d+}} = OpImageSampleDrefImplicitLod %float [[sampledImg]] [[v4fc]] [[comparator]]
     float val3 = t3.SampleCmp(gSampler, float4(0.1, 0.2, 0.3, 1), comparator);
 
+    float clamp;
+// CHECK:           [[clamp:%\d+]] = OpLoad %float %clamp
+// CHECK-NEXT:         [[t2:%\d+]] = OpLoad %type_2d_image_array %t2
+// CHECK-NEXT:   [[gSampler:%\d+]] = OpLoad %type_sampler %gSampler
+// CHECK-NEXT: [[comparator:%\d+]] = OpLoad %float %comparator
+// CHECK-NEXT:     [[offset:%\d+]] = OpLoad %v2int %offset
+// CHECK-NEXT: [[sampledImg:%\d+]] = OpSampledImage %type_sampled_image_0 [[t2]] [[gSampler]]
+// CHECK-NEXT:            {{%\d+}} = OpImageSampleDrefImplicitLod %float [[sampledImg]] [[v3fc]] [[comparator]] Offset|MinLod [[offset]] [[clamp]]
+    float val4 = t2.SampleCmp(gSampler, float3(0.1, 0.2, 1), comparator, offset, clamp);
+
+// CHECK:              [[t3:%\d+]] = OpLoad %type_cube_image_array %t3
+// CHECK-NEXT:   [[gSampler:%\d+]] = OpLoad %type_sampler %gSampler
+// CHECK-NEXT: [[comparator:%\d+]] = OpLoad %float %comparator
+// CHECK-NEXT: [[sampledImg:%\d+]] = OpSampledImage %type_sampled_image_1 [[t3]] [[gSampler]]
+// CHECK-NEXT:            {{%\d+}} = OpImageSampleDrefImplicitLod %float [[sampledImg]] [[v4fc]] [[comparator]] MinLod %float_1_5
+    float val5 = t3.SampleCmp(gSampler, float4(0.1, 0.2, 0.3, 1), comparator, /*clamp*/ 1.5);
+    
     return 1.0;
 }

+ 16 - 0
tools/clang/test/CodeGenSPIRV/texture.array.sample-grad.hlsl

@@ -9,6 +9,7 @@ Texture2DArray   <float4> t2 : register(t2);
 TextureCubeArray <float4> t3 : register(t3);
 
 // CHECK: OpCapability ImageGatherExtended
+// CHECK: OpCapability MinLod
 
 // CHECK: %type_sampled_image = OpTypeSampledImage %type_1d_image
 // CHECK: %type_sampled_image_0 = OpTypeSampledImage %type_2d_image
@@ -42,5 +43,20 @@ float4 main(int2 offset : A) : SV_Target {
 // CHECK-NEXT:            {{%\d+}} = OpImageSampleExplicitLod %v4float [[sampledImg]] [[v4f_0_1]] Grad [[v3f_0_2]] [[v3f_0_3]]
     float4 val3 = t3.SampleGrad(gSampler, float4(0.1, 0.1, 0.1, 0.1), float3(0.2, 0.2, 0.2), float3(0.3, 0.3, 0.3));
 
+// CHECK:              [[t2:%\d+]] = OpLoad %type_2d_image_array %t2
+// CHECK-NEXT:   [[gSampler:%\d+]] = OpLoad %type_sampler %gSampler
+// CHECK-NEXT:     [[offset:%\d+]] = OpLoad %v2int %offset
+// CHECK-NEXT: [[sampledImg:%\d+]] = OpSampledImage %type_sampled_image_0 [[t2]] [[gSampler]]
+// CHECK-NEXT:            {{%\d+}} = OpImageSampleExplicitLod %v4float [[sampledImg]] [[v3f_0_1]] Grad|Offset|MinLod [[v2f_0_2]] [[v2f_0_3]] [[offset]] %float_2_5
+    float4 val4 = t2.SampleGrad(gSampler, float3(0.1, 0.1, 0.1), float2(0.2, 0.2), float2(0.3, 0.3), offset, /*clamp*/2.5);
+
+    float clamp;
+// CHECK:           [[clamp:%\d+]] = OpLoad %float %clamp
+// CHECK-NEXT:         [[t3:%\d+]] = OpLoad %type_cube_image_array %t3
+// CHECK-NEXT:   [[gSampler:%\d+]] = OpLoad %type_sampler %gSampler
+// CHECK-NEXT: [[sampledImg:%\d+]] = OpSampledImage %type_sampled_image_1 [[t3]] [[gSampler]]
+// CHECK-NEXT:            {{%\d+}} = OpImageSampleExplicitLod %v4float [[sampledImg]] [[v4f_0_1]] Grad|MinLod [[v3f_0_2]] [[v3f_0_3]] [[clamp]]
+    float4 val5 = t3.SampleGrad(gSampler, float4(0.1, 0.1, 0.1, 0.1), float3(0.2, 0.2, 0.2), float3(0.3, 0.3, 0.3), clamp);
+    
     return 1.0;
 }

+ 16 - 0
tools/clang/test/CodeGenSPIRV/texture.array.sample.hlsl

@@ -8,6 +8,8 @@ Texture1DArray   <float4> t1 : register(t1);
 Texture2DArray   <float4> t2 : register(t2);
 TextureCubeArray <float4> t3 : register(t3);
 
+// CHECK: OpCapability MinLod
+
 // CHECK: %type_sampled_image = OpTypeSampledImage %type_1d_image_array
 // CHECK: %type_sampled_image_0 = OpTypeSampledImage %type_2d_image_array
 // CHECK: %type_sampled_image_1 = OpTypeSampledImage %type_cube_image_array
@@ -35,5 +37,19 @@ float4 main() : SV_Target {
 // CHECK-NEXT:            {{%\d+}} = OpImageSampleImplicitLod %v4float [[sampledImg]] [[v4fc]]
     float4 val3 = t3.Sample(gSampler, float4(0.1, 0.2, 0.3, 1));
 
+    float clamp;
+// CHECK:           [[clamp:%\d+]] = OpLoad %float %clamp
+// CHECK-NEXT:         [[t1:%\d+]] = OpLoad %type_1d_image_array %t1
+// CHECK-NEXT:   [[gSampler:%\d+]] = OpLoad %type_sampler %gSampler
+// CHECK-NEXT: [[sampledImg:%\d+]] = OpSampledImage %type_sampled_image [[t1]] [[gSampler]]
+// CHECK-NEXT:            {{%\d+}} = OpImageSampleImplicitLod %v4float [[sampledImg]] [[v2fc]] ConstOffset|MinLod %int_1 [[clamp]]
+    float4 val4 = t1.Sample(gSampler, float2(0.1, 1), 1, clamp);
+
+// CHECK:              [[t3:%\d+]] = OpLoad %type_cube_image_array %t3
+// CHECK-NEXT:   [[gSampler:%\d+]] = OpLoad %type_sampler %gSampler
+// CHECK-NEXT: [[sampledImg:%\d+]] = OpSampledImage %type_sampled_image_1 [[t3]] [[gSampler]]
+// CHECK-NEXT:            {{%\d+}} = OpImageSampleImplicitLod %v4float [[sampledImg]] [[v4fc]] MinLod %float_1_5
+    float4 val5 = t3.Sample(gSampler, float4(0.1, 0.2, 0.3, 1), /*clamp*/ 1.5);
+
     return 1.0;
 }

+ 16 - 2
tools/clang/test/CodeGenSPIRV/texture.sample-bias.hlsl

@@ -10,6 +10,7 @@ Texture3D   <float4> t3 : register(t3);
 TextureCube <float4> t4 : register(t4);
 
 // CHECK: OpCapability ImageGatherExtended
+// CHECK: OpCapability MinLod
 
 // CHECK: %type_sampled_image = OpTypeSampledImage %type_1d_image
 // CHECK: %type_sampled_image_0 = OpTypeSampledImage %type_2d_image
@@ -46,7 +47,20 @@ float4 main(int3 offset: A) : SV_Target {
 // CHECK-NEXT:            {{%\d+}} = OpImageSampleImplicitLod %v4float [[sampledImg]] [[v3fc]] Bias %float_0_5
     float4 val4 = t4.SampleBias(gSampler, float3(0.1, 0.2, 0.3), 0.5);
 
+    float clamp;
+// CHECK:           [[clamp:%\d+]] = OpLoad %float %clamp
+// CHECK-NEXT:         [[t3:%\d+]] = OpLoad %type_3d_image %t3
+// CHECK-NEXT:   [[gSampler:%\d+]] = OpLoad %type_sampler %gSampler
+// CHECK-NEXT:     [[offset:%\d+]] = OpLoad %v3int %offset
+// CHECK-NEXT: [[sampledImg:%\d+]] = OpSampledImage %type_sampled_image_1 [[t3]] [[gSampler]]
+// CHECK-NEXT:            {{%\d+}} = OpImageSampleImplicitLod %v4float [[sampledImg]] [[v3fc]] Bias|Offset|MinLod %float_0_5 [[offset]] [[clamp]]
+    float4 val5 = t3.SampleBias(gSampler, float3(0.1, 0.2, 0.3), 0.5, offset, clamp);
+
+// CHECK:              [[t4:%\d+]] = OpLoad %type_cube_image %t4
+// CHECK-NEXT:   [[gSampler:%\d+]] = OpLoad %type_sampler %gSampler
+// CHECK-NEXT: [[sampledImg:%\d+]] = OpSampledImage %type_sampled_image_2 [[t4]] [[gSampler]]
+// CHECK-NEXT:            {{%\d+}} = OpImageSampleImplicitLod %v4float [[sampledImg]] [[v3fc]] Bias|MinLod %float_0_5 %float_2_5
+    float4 val6 = t4.SampleBias(gSampler, float3(0.1, 0.2, 0.3), 0.5, /*clamp*/ 2.5);
+    
     return 1.0;
 }
-
-

+ 19 - 0
tools/clang/test/CodeGenSPIRV/texture.sample-cmp.hlsl

@@ -7,6 +7,8 @@ Texture2D   <float2> t2 : register(t2);
 TextureCube <float>  t4 : register(t4);
 // No .SampleCmp() for Texture3D.
 
+// CHECK: OpCapability MinLod
+
 // CHECK: [[v2fc:%\d+]] = OpConstantComposite %v2float %float_0_1 %float_0_2
 // CHECK: [[v3fc:%\d+]] = OpConstantComposite %v3float %float_0_1 %float_0_2 %float_0_3
 
@@ -33,5 +35,22 @@ float4 main(int2 offset: A, float comparator: B) : SV_Target {
 // CHECK-NEXT:            {{%\d+}} = OpImageSampleDrefImplicitLod %float [[sampledImg]] [[v3fc]] [[comparator]]
     float val4 = t4.SampleCmp(gSampler, float3(0.1, 0.2, 0.3), comparator);
 
+    float clamp;
+// CHECK:           [[clamp:%\d+]] = OpLoad %float %clamp
+// CHECK-NEXT:         [[t2:%\d+]] = OpLoad %type_2d_image %t2
+// CHECK-NEXT:   [[gSampler:%\d+]] = OpLoad %type_sampler %gSampler
+// CHECK-NEXT: [[comparator:%\d+]] = OpLoad %float %comparator
+// CHECK-NEXT:     [[offset:%\d+]] = OpLoad %v2int %offset
+// CHECK-NEXT: [[sampledImg:%\d+]] = OpSampledImage %type_sampled_image_0 [[t2]] [[gSampler]]
+// CHECK-NEXT:            {{%\d+}} = OpImageSampleDrefImplicitLod %float [[sampledImg]] [[v2fc]] [[comparator]] Offset|MinLod [[offset]] [[clamp]]
+    float val5 = t2.SampleCmp(gSampler, float2(0.1, 0.2), comparator, offset, clamp);
+
+// CHECK:              [[t4:%\d+]] = OpLoad %type_cube_image %t4
+// CHECK-NEXT:   [[gSampler:%\d+]] = OpLoad %type_sampler %gSampler
+// CHECK-NEXT: [[comparator:%\d+]] = OpLoad %float %comparator
+// CHECK-NEXT: [[sampledImg:%\d+]] = OpSampledImage %type_sampled_image_1 [[t4]] [[gSampler]]
+// CHECK-NEXT:            {{%\d+}} = OpImageSampleDrefImplicitLod %float [[sampledImg]] [[v3fc]] [[comparator]] MinLod %float_2_5
+    float val6 = t4.SampleCmp(gSampler, float3(0.1, 0.2, 0.3), comparator, /*clamp*/2.5);
+
     return 1.0;
 }

+ 16 - 0
tools/clang/test/CodeGenSPIRV/texture.sample-grad.hlsl

@@ -10,6 +10,7 @@ Texture3D   <float4> t3 : register(t3);
 TextureCube <float4> t4 : register(t4);
 
 // CHECK: OpCapability ImageGatherExtended
+// CHECK: OpCapability MinLod
 
 // CHECK: %type_sampled_image = OpTypeSampledImage %type_1d_image
 // CHECK: %type_sampled_image_0 = OpTypeSampledImage %type_2d_image
@@ -50,5 +51,20 @@ float4 main(int2 offset : A) : SV_Target {
 // CHECK-NEXT:            {{%\d+}} = OpImageSampleExplicitLod %v4float [[sampledImg]] [[v3f_0_1]] Grad [[v3f_0_2]] [[v3f_0_3]]
     float4 val4 = t4.SampleGrad(gSampler, float3(0.1, 0.1, 0.1), float3(0.2, 0.2, 0.2), float3(0.3, 0.3, 0.3));
 
+    float clamp;
+// CHECK:           [[clamp:%\d+]] = OpLoad %float %clamp
+// CHECK-NEXT:         [[t2:%\d+]] = OpLoad %type_2d_image %t2
+// CHECK-NEXT:   [[gSampler:%\d+]] = OpLoad %type_sampler %gSampler
+// CHECK-NEXT:     [[offset:%\d+]] = OpLoad %v2int %offset
+// CHECK-NEXT: [[sampledImg:%\d+]] = OpSampledImage %type_sampled_image_0 [[t2]] [[gSampler]]
+// CHECK-NEXT:            {{%\d+}} = OpImageSampleExplicitLod %v4float [[sampledImg]] [[v2f_0_1]] Grad|Offset|MinLod [[v2f_0_2]] [[v2f_0_3]] [[offset]] [[clamp]]
+    float4 val5 = t2.SampleGrad(gSampler, float2(0.1, 0.1), float2(0.2, 0.2), float2(0.3, 0.3), offset, clamp);
+
+// CHECK:              [[t4:%\d+]] = OpLoad %type_cube_image %t4
+// CHECK-NEXT:   [[gSampler:%\d+]] = OpLoad %type_sampler %gSampler
+// CHECK-NEXT: [[sampledImg:%\d+]] = OpSampledImage %type_sampled_image_2 [[t4]] [[gSampler]]
+// CHECK-NEXT:            {{%\d+}} = OpImageSampleExplicitLod %v4float [[sampledImg]] [[v3f_0_1]] Grad|MinLod [[v3f_0_2]] [[v3f_0_3]] %float_3_5
+    float4 val6 = t4.SampleGrad(gSampler, float3(0.1, 0.1, 0.1), float3(0.2, 0.2, 0.2), float3(0.3, 0.3, 0.3), /*clamp*/3.5);
+
     return 1.0;
 }

+ 16 - 0
tools/clang/test/CodeGenSPIRV/texture.sample.hlsl

@@ -10,6 +10,7 @@ Texture3D   <float4> t3 : register(t3);
 TextureCube <float4> t4 : register(t4);
 
 // CHECK: OpCapability ImageGatherExtended
+// CHECK: OpCapability MinLod
 
 // CHECK: %type_sampled_image = OpTypeSampledImage %type_1d_image
 // CHECK: %type_sampled_image_0 = OpTypeSampledImage %type_2d_image
@@ -46,5 +47,20 @@ float4 main(int2 offset: A) : SV_Target {
 // CHECK-NEXT:            {{%\d+}} = OpImageSampleImplicitLod %v4float [[sampledImg]] [[v3fc]]
     float4 val4 = t4.Sample(gSampler, float3(0.1, 0.2, 0.3));
 
+    float clamp;
+// CHECK:           [[clamp:%\d+]] = OpLoad %float %clamp
+// CHECK-NEXT:         [[t2:%\d+]] = OpLoad %type_2d_image %t2
+// CHECK-NEXT:   [[gSampler:%\d+]] = OpLoad %type_sampler %gSampler
+// CHECK-NEXT:     [[offset:%\d+]] = OpLoad %v2int %offset
+// CHECK-NEXT: [[sampledImg:%\d+]] = OpSampledImage %type_sampled_image_0 [[t2]] [[gSampler]]
+// CHECK-NEXT:            {{%\d+}} = OpImageSampleImplicitLod %v4float [[sampledImg]] [[v2fc]] Offset|MinLod [[offset]] [[clamp]]
+    float4 val5 = t2.Sample(gSampler, float2(0.1, 0.2), offset, clamp);
+
+// CHECK:              [[t4:%\d+]] = OpLoad %type_cube_image %t4
+// CHECK-NEXT:   [[gSampler:%\d+]] = OpLoad %type_sampler %gSampler
+// CHECK-NEXT: [[sampledImg:%\d+]] = OpSampledImage %type_sampled_image_2 [[t4]] [[gSampler]]
+// CHECK-NEXT:            {{%\d+}} = OpImageSampleImplicitLod %v4float [[sampledImg]] [[v3fc]] MinLod %float_2
+    float4 val6 = t4.Sample(gSampler, float3(0.1, 0.2, 0.3), /*clamp*/ 2.0f);
+
     return 1.0;
 }