Преглед на файлове

[spirv] Fix image sample instruction return types. (#932)

Ehsan преди 7 години
родител
ревизия
9a502c0e10

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

@@ -180,8 +180,8 @@ public:
   /// residency code will also be emitted.
   uint32_t createImageSample(uint32_t texelType, uint32_t imageType,
                              uint32_t image, uint32_t sampler,
-                             uint32_t coordinate, uint32_t bias,
-                             uint32_t compareVal, uint32_t lod,
+                             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,

+ 90 - 45
tools/clang/lib/SPIRV/SPIRVEmitter.cpp

@@ -2350,8 +2350,8 @@ uint32_t SPIRVEmitter::processTextureGatherCmp(const CXXMemberCallExpr *expr) {
   const auto status = hasStatusArg ? doExpr(expr->getArg(numArgs - 1)) : 0;
 
   return theBuilder.createImageGather(
-      retType, imageType, image, sampler, coordinate,
-      /*component*/ 0, comparator, constOffset, varOffset, /*constOffsets*/ 0,
+      retType, imageType, image, sampler, coordinate, /*component*/ 0,
+      comparator, constOffset, varOffset, /*constOffsets*/ 0,
       /*sampleNumber*/ 0, status);
 }
 
@@ -2390,9 +2390,6 @@ SpirvEvalInfo SPIRVEmitter::processBufferTextureLoad(
     emitError("buffer/texture type unimplemented", object->getExprLoc());
     return 0;
   }
-  const uint32_t resultTypeId =
-      elemCount == 1 ? elemTypeId
-                     : theBuilder.getVecType(elemTypeId, elemCount);
 
   // OpImageFetch and OpImageRead can only fetch a vector of 4 elements.
   const uint32_t texelTypeId = theBuilder.getVecType(elemTypeId, 4u);
@@ -2400,26 +2397,9 @@ SpirvEvalInfo SPIRVEmitter::processBufferTextureLoad(
       doFetch, texelTypeId, type, objectId, locationId, lod, constOffset,
       varOffset, /*constOffsets*/ 0, sampleNumber, residencyCode);
 
-  uint32_t retVal = texel;
   // If the result type is a vec1, vec2, or vec3, some extra processing
   // (extraction) is required.
-  switch (elemCount) {
-  case 1:
-    retVal = theBuilder.createCompositeExtract(elemTypeId, texel, {0});
-    break;
-  case 2:
-    retVal = theBuilder.createVectorShuffle(resultTypeId, texel, texel, {0, 1});
-    break;
-  case 3:
-    retVal =
-        theBuilder.createVectorShuffle(resultTypeId, texel, texel, {0, 1, 2});
-    break;
-  case 4:
-    break;
-  default:
-    llvm_unreachable("vector element count must be 1, 2, 3, or 4");
-  }
-
+  uint32_t retVal = extractVecFromVec4(texel, elemCount, elemTypeId);
   return SpirvEvalInfo(retVal).setRValue();
 }
 
@@ -2768,6 +2748,52 @@ SPIRVEmitter::processIntrinsicMemberCall(const CXXMemberCallExpr *expr,
   return SpirvEvalInfo(retVal).setRValue();
 }
 
+uint32_t SPIRVEmitter::createImageSample(
+    QualType retType, 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 minLod,
+    uint32_t residencyCodeId) {
+
+  const auto retTypeId = typeTranslator.translateType(retType);
+
+  // SampleDref* instructions in SPIR-V always return a scalar.
+  // They also have the correct type in HLSL.
+  if (compareVal) {
+    return theBuilder.createImageSample(retTypeId, imageType, image, sampler,
+                                        coordinate, compareVal, bias, lod, grad,
+                                        constOffset, varOffset, constOffsets,
+                                        sample, minLod, residencyCodeId);
+  }
+
+  // Non-Dref Sample instructions in SPIR-V must always return a vec4.
+  auto texelTypeId = retTypeId;
+  QualType elemType = {};
+  uint32_t elemTypeId = 0;
+  uint32_t retVecSize = 0;
+  if (TypeTranslator::isVectorType(retType, &elemType, &retVecSize) &&
+      retVecSize != 4) {
+    elemTypeId = typeTranslator.translateType(elemType);
+    texelTypeId = theBuilder.getVecType(elemTypeId, 4);
+  } else if (TypeTranslator::isScalarType(retType)) {
+    retVecSize = 1;
+    elemTypeId = typeTranslator.translateType(retType);
+    texelTypeId = theBuilder.getVecType(elemTypeId, 4);
+  }
+
+  uint32_t retVal = theBuilder.createImageSample(
+      texelTypeId, imageType, image, sampler, coordinate, compareVal, bias, lod,
+      grad, constOffset, varOffset, constOffsets, sample, minLod,
+      residencyCodeId);
+
+  // Extract smaller vector from the vec4 result if necessary.
+  if (texelTypeId != retTypeId) {
+    retVal = extractVecFromVec4(retVal, retVecSize, elemTypeId);
+  }
+
+  return retVal;
+}
+
 uint32_t SPIRVEmitter::processTextureSampleGather(const CXXMemberCallExpr *expr,
                                                   const bool isSample) {
   // Signatures:
@@ -2823,18 +2849,16 @@ uint32_t SPIRVEmitter::processTextureSampleGather(const CXXMemberCallExpr *expr,
   if (hasOffsetArg)
     handleOffsetInMethodCall(expr, 2, &constOffset, &varOffset);
 
-  const auto retType =
-      typeTranslator.translateType(expr->getDirectCallee()->getReturnType());
-
+  const auto retType = expr->getDirectCallee()->getReturnType();
+  const auto retTypeId = typeTranslator.translateType(retType);
   if (isSample) {
-    // TODO: Handle sparse cases for this method.
-    return theBuilder.createImageSample(
+    return createImageSample(
         retType, imageType, image, sampler, coordinate, /*compareVal*/ 0,
         /*bias*/ 0, /*lod*/ 0, std::make_pair(0, 0), constOffset, varOffset,
         /*constOffsets*/ 0, /*sampleNumber*/ 0, /*minLod*/ clamp, status);
   } else {
     return theBuilder.createImageGather(
-        retType, imageType, image, sampler, coordinate,
+        retTypeId, imageType, image, sampler, coordinate,
         // .Gather() doc says we return four components of red data.
         theBuilder.getConstantInt32(0), /*compareVal*/ 0, constOffset,
         varOffset, /*constOffsets*/ 0, /*sampleNumber*/ 0, status);
@@ -2909,13 +2933,12 @@ SPIRVEmitter::processTextureSampleBiasLevel(const CXXMemberCallExpr *expr,
   if (hasOffsetArg)
     handleOffsetInMethodCall(expr, 3, &constOffset, &varOffset);
 
-  const auto retType =
-      typeTranslator.translateType(expr->getDirectCallee()->getReturnType());
+  const auto retType = expr->getDirectCallee()->getReturnType();
 
-  return theBuilder.createImageSample(
-      retType, imageType, image, sampler, coordinate, /*compareVal*/ 0, bias,
-      lod, std::make_pair(0, 0), constOffset, varOffset, /*constOffsets*/ 0,
-      /*sampleNumber*/ 0, /*minLod*/ clamp, status);
+  return createImageSample(retType, imageType, image, sampler, coordinate,
+                           /*compareVal*/ 0, bias, lod, std::make_pair(0, 0),
+                           constOffset, varOffset, /*constOffsets*/ 0,
+                           /*sampleNumber*/ 0, /*minLod*/ clamp, status);
 }
 
 uint32_t SPIRVEmitter::processTextureSampleGrad(const CXXMemberCallExpr *expr) {
@@ -2965,10 +2988,8 @@ uint32_t SPIRVEmitter::processTextureSampleGrad(const CXXMemberCallExpr *expr) {
   if (hasOffsetArg)
     handleOffsetInMethodCall(expr, 4, &constOffset, &varOffset);
 
-  const auto retType =
-      typeTranslator.translateType(expr->getDirectCallee()->getReturnType());
-
-  return theBuilder.createImageSample(
+  const auto retType = expr->getDirectCallee()->getReturnType();
+  return createImageSample(
       retType, imageType, image, sampler, coordinate, /*compareVal*/ 0,
       /*bias*/ 0, /*lod*/ 0, std::make_pair(ddx, ddy), constOffset, varOffset,
       /*constOffsets*/ 0, /*sampleNumber*/ 0, /*minLod*/ clamp, status);
@@ -3051,14 +3072,13 @@ SPIRVEmitter::processTextureSampleCmpCmpLevelZero(const CXXMemberCallExpr *expr,
     handleOffsetInMethodCall(expr, 3, &constOffset, &varOffset);
   const uint32_t lod = isCmp ? 0 : theBuilder.getConstantFloat32(0);
 
-  const auto retType =
-      typeTranslator.translateType(expr->getDirectCallee()->getReturnType());
+  const auto retType = expr->getDirectCallee()->getReturnType();
   const auto imageType = typeTranslator.translateType(imageExpr->getType());
 
-  return theBuilder.createImageSample(
-      retType, imageType, image, sampler, coordinate, compareVal, /*bias*/ 0,
-      lod, std::make_pair(0, 0), constOffset, varOffset, /*constOffsets*/ 0,
-      /*sampleNumber*/ 0, /*minLod*/ clamp, status);
+  return createImageSample(retType, imageType, image, sampler, coordinate,
+                           compareVal, /*bias*/ 0, lod, std::make_pair(0, 0),
+                           constOffset, varOffset, /*constOffsets*/ 0,
+                           /*sampleNumber*/ 0, /*minLod*/ clamp, status);
 }
 
 SpirvEvalInfo
@@ -7265,5 +7285,30 @@ void SPIRVEmitter::processSwitchStmtUsingIfStmts(const SwitchStmt *switchStmt) {
     doStmt(defaultBody);
 }
 
+uint32_t SPIRVEmitter::extractVecFromVec4(uint32_t fromId,
+                                          uint32_t targetVecSize,
+                                          uint32_t targetElemTypeId) {
+  assert(targetVecSize > 0 && targetVecSize < 5);
+  const uint32_t retType =
+      targetVecSize == 1
+          ? targetElemTypeId
+          : theBuilder.getVecType(targetElemTypeId, targetVecSize);
+  switch (targetVecSize) {
+  case 1:
+    return theBuilder.createCompositeExtract(retType, fromId, {0});
+    break;
+  case 2:
+    return theBuilder.createVectorShuffle(retType, fromId, fromId, {0, 1});
+    break;
+  case 3:
+    return theBuilder.createVectorShuffle(retType, fromId, fromId, {0, 1, 2});
+    break;
+  case 4:
+    return fromId;
+  default:
+    llvm_unreachable("vector element count must be 1, 2, 3, or 4");
+  }
+}
+
 } // end namespace spirv
 } // end namespace clang

+ 24 - 0
tools/clang/lib/SPIRV/SPIRVEmitter.h

@@ -674,6 +674,30 @@ private:
   /// primitive in GS.
   uint32_t processStreamOutputRestart(const CXXMemberCallExpr *expr);
 
+private:
+  /// \brief Takes a vector of size 4, and returns a vector of size 1 or 2 or 3
+  /// or 4. Creates a CompositeExtract or VectorShuffle instruction to extract
+  /// a scalar or smaller vector from the beginning of the input vector if
+  /// necessary. Assumes that 'fromId' is the <result-id> of a vector of size 4.
+  /// Panics if the target vector size is not 1, 2, 3, or 4.
+  uint32_t extractVecFromVec4(uint32_t fromId, uint32_t targetVecSize,
+                              uint32_t targetElemTypeId);
+
+  /// \brief Creates SPIR-V instructions for sampling the given image.
+  /// It utilizes the ModuleBuilder's createImageSample and it ensures that the
+  /// returned type is handled correctly.
+  /// HLSL image sampling methods may return a scalar, vec1, vec2, vec3, or
+  /// vec4. But non-Dref image sampling instructions in SPIR-V must always
+  /// return a vec4. As a result, an extra processing step is necessary.
+  uint32_t createImageSample(QualType retType, 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 minLod, uint32_t residencyCodeId);
+
 private:
   /// \brief Wrapper method to create a fatal error message and report it
   /// in the diagnostic engine associated with this consumer.

+ 17 - 1
tools/clang/test/CodeGenSPIRV/texture.array.sample-bias.hlsl

@@ -7,6 +7,8 @@ SamplerState gSampler : register(s5);
 Texture1DArray   <float4> t1 : register(t1);
 Texture2DArray   <float4> t2 : register(t2);
 TextureCubeArray <float4> t3 : register(t3);
+Texture2DArray   <float>  t4 : register(t4);
+TextureCubeArray <float3> t5 : register(t5);
 
 // CHECK: OpCapability ImageGatherExtended
 // CHECK: OpCapability MinLod
@@ -77,6 +79,20 @@ float4 main(int2 offset : A) : SV_Target {
 // CHECK-NEXT:       [[result:%\d+]] = OpCompositeExtract %v4float [[structResult]] 1
 // CHECK-NEXT:                         OpStore %val7 [[result]]
     float4 val7 = t3.SampleBias(gSampler, float4(0.1, 0.2, 0.3, 1), 0.5, /*clamp*/ 2.5f, status);
-    
+
+/////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Make sure OpImageSampleImplicitLod returns a vec4.
+// Make sure OpImageSparseSampleImplicitLod returns a struct, in which the second member is a vec4.
+/////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+// CHECK: [[v4result:%\d+]] = OpImageSampleImplicitLod %v4float {{%\d+}} {{%\d+}} Bias|Offset %float_0_5 {{%\d+}}
+// CHECK:           {{%\d+}} = OpCompositeExtract %float [[v4result]] 0
+    float  val8 = t4.SampleBias(gSampler, float3(0.1, 0.2, 1), 0.5, offset);
+
+// CHECK: [[structResult:%\d+]] = OpImageSparseSampleImplicitLod %SparseResidencyStruct {{%\d+}} {{%\d+}} Bias|MinLod %float_0_5 %float_2_5
+// CHECK:     [[v4result:%\d+]] = OpCompositeExtract %v4float [[structResult]] 1
+// CHECK:              {{%\d+}} = OpVectorShuffle %v3float [[v4result]] [[v4result]] 0 1 2
+    float3 val9 = t5.SampleBias(gSampler, float4(0.1, 0.2, 0.3, 1), 0.5, /*clamp*/ 2.5f, status);
+
     return 1.0;
 }

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

@@ -7,6 +7,9 @@ SamplerState gSampler : register(s5);
 Texture1DArray   <float4> t1 : register(t1);
 Texture2DArray   <float4> t2 : register(t2);
 TextureCubeArray <float4> t3 : register(t3);
+Texture2DArray   <float>  t4 : register(t4);
+TextureCubeArray <float2> t5 : register(t5);
+
 
 // CHECK: OpCapability ImageGatherExtended
 // CHECK: OpCapability MinLod
@@ -84,5 +87,19 @@ float4 main(int2 offset : A) : SV_Target {
 // CHECK-NEXT:                         OpStore %val7 [[result]]
     float4 val7 = 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, status);
 
+/////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Make sure OpImageSampleExplicitLod returns a vec4.
+// Make sure OpImageSparseSampleExplicitLod returns a struct, in which the second member is a vec4.
+/////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+// CHECK: [[v4result:%\d+]] = OpImageSampleExplicitLod %v4float {{%\d+}} {{%\d+}} Grad|Offset {{%\d+}} {{%\d+}} {{%\d+}}
+// CHECK:          {{%\d+}} = OpCompositeExtract %float [[v4result]] 0
+	float  val8 = t4.SampleGrad(gSampler, float3(0.1, 0.1, 0.1), float2(0.2, 0.2), float2(0.3, 0.3), offset);
+
+// CHECK: [[structResult:%\d+]] = OpImageSparseSampleExplicitLod %SparseResidencyStruct {{%\d+}} {{%\d+}} Grad|MinLod {{%\d+}} {{%\d+}} {{%\d+}}
+// CHECK:     [[v4result:%\d+]] = OpCompositeExtract %v4float [[structResult]] 1
+// CHECK:              {{%\d+}} = OpVectorShuffle %v2float [[v4result]] [[v4result]] 0 1
+    float2 val9 = t5.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, status);
+
     return 1.0;
 }

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

@@ -7,6 +7,8 @@ SamplerState gSampler : register(s5);
 Texture1DArray   <float4> t1 : register(t1);
 Texture2DArray   <float4> t2 : register(t2);
 TextureCubeArray <float4> t3 : register(t3);
+Texture2DArray   <float>  t4 : register(t4);
+TextureCubeArray <float3> t5 : register(t5);
 
 // CHECK: OpCapability ImageGatherExtended
 // CHECK: OpCapability SparseResidency
@@ -63,5 +65,19 @@ float4 main(int2 offset : A) : SV_Target {
 // CHECK-NEXT:                         OpStore %val5 [[result]]
     float4 val5 = t3.SampleLevel(gSampler, float4(0.1, 0.2, 0.3, 1), 30, status);
 
+/////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Make sure OpImageSampleExplicitLod returns a vec4.
+// Make sure OpImageSparseSampleExplicitLod returns a struct, in which the second member is a vec4.
+/////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+// CHECK: [[v4result:%\d+]] = OpImageSampleExplicitLod %v4float {{%\d+}} {{%\d+}} Lod|Offset %float_20 {{%\d+}}
+// CHECK:          {{%\d+}} = OpCompositeExtract %float [[v4result]] 0
+    float val6 = t4.SampleLevel(gSampler, float3(0.1, 0.2, 1), 20, offset);
+
+// CHECK: [[structResult:%\d+]] = OpImageSparseSampleExplicitLod %SparseResidencyStruct {{%\d+}} {{%\d+}} Lod %float_30
+// CHECK:     [[v4result:%\d+]] = OpCompositeExtract %v4float [[structResult]] 1
+// CHECK:              {{%\d+}} = OpVectorShuffle %v3float [[v4result]] [[v4result]] 0 1 2
+    float3 val7 = t5.SampleLevel(gSampler, float4(0.1, 0.2, 0.3, 1), 30, status);
+
     return 1.0;
 }

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

@@ -7,6 +7,8 @@ SamplerState gSampler : register(s5);
 Texture1DArray   <float4> t1 : register(t1);
 Texture2DArray   <float4> t2 : register(t2);
 TextureCubeArray <float4> t3 : register(t3);
+Texture1DArray   <float>  t4 : register(t4);
+TextureCubeArray <float3> t5 : register(t5);
 
 // CHECK: OpCapability MinLod
 // CHECK: OpCapability SparseResidency
@@ -76,5 +78,19 @@ float4 main() : SV_Target {
 // CHECK-NEXT:                         OpStore %val7 [[result]]
     float4 val7 = t3.Sample(gSampler, float4(0.1, 0.2, 0.3, 1), /*clamp*/ 1.5, status);
 
+/////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Make sure OpImageSampleImplicitLod returns a vec4.
+// Make sure OpImageSparseSampleImplicitLod returns a struct, in which the second member is a vec4.
+/////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+// CHECK: [[v4result:%\d+]] = OpImageSampleImplicitLod %v4float {{%\d+}} {{%\d+}} ConstOffset %int_1
+// CHECK:          {{%\d+}} = OpCompositeExtract %float [[v4result]] 0
+    float val8 = t4.Sample(gSampler, float2(0.1, 1), 1);
+
+// CHECK: [[structResult:%\d+]] = OpImageSparseSampleImplicitLod %SparseResidencyStruct {{%\d+}} {{%\d+}} MinLod %float_1_5
+// CHECK:     [[v4result:%\d+]] = OpCompositeExtract %v4float [[structResult]] 1
+// CHECK:              {{%\d+}} = OpVectorShuffle %v3float [[v4result]] [[v4result]] 0 1 2
+    float3 val9 = t5.Sample(gSampler, float4(0.1, 0.2, 0.3, 1), /*clamp*/ 1.5, status);
+
     return 1.0;
 }

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

@@ -8,6 +8,8 @@ Texture1D   <float4> t1 : register(t1);
 Texture2D   <float4> t2 : register(t2);
 Texture3D   <float4> t3 : register(t3);
 TextureCube <float4> t4 : register(t4);
+Texture1D   <float>  t5 : register(t5);
+Texture3D   <float2> t6 : register(t6);
 
 // CHECK: OpCapability ImageGatherExtended
 // CHECK: OpCapability MinLod
@@ -88,6 +90,19 @@ float4 main(int3 offset: A) : SV_Target {
 // CHECK-NEXT:                         OpStore %val8 [[result]]
     float4 val8 = t4.SampleBias(gSampler, float3(0.1, 0.2, 0.3), 0.5, /*clamp*/ 2.5, status);
 
+/////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Make sure OpImageSampleImplicitLod returns a vec4.
+// Make sure OpImageSparseSampleImplicitLod returns a struct, in which the second member is a vec4.
+/////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+// CHECK:  [[v4result:%\d+]] = OpImageSampleImplicitLod %v4float {{%\d+}} %float_0_1 Bias %float_0_5
+// CHECK:           {{%\d+}} = OpCompositeExtract %float [[v4result]] 0
+    float val9 = t5.SampleBias(gSampler, 0.1, 0.5);
+
+// CHECK: [[structResult:%\d+]] = OpImageSparseSampleImplicitLod %SparseResidencyStruct {{%\d+}} {{%\d+}} Bias|Offset|MinLod %float_0_5 {{%\d+}} {{%\d+}}
+// CHECK:     [[v4result:%\d+]] = OpCompositeExtract %v4float [[structResult]] 1
+// CHECK:              {{%\d+}} = OpVectorShuffle %v2float [[v4result]] [[v4result]] 0 1
+    float2 val10 = t6.SampleBias(gSampler, float3(0.1, 0.2, 0.3), 0.5, offset, clamp, status);
+
     return 1.0;
 }
-// CHECK-WHOLE-SPIR-V:

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

@@ -8,6 +8,8 @@ Texture1D   <float4> t1 : register(t1);
 Texture2D   <float4> t2 : register(t2);
 Texture3D   <float4> t3 : register(t3);
 TextureCube <float4> t4 : register(t4);
+Texture1D   <float>  t5 : register(t5);
+Texture2D   <float2> t6 : register(t6);
 
 // CHECK: OpCapability ImageGatherExtended
 // CHECK: OpCapability MinLod
@@ -92,5 +94,19 @@ float4 main(int2 offset : A) : SV_Target {
 // CHECK-NEXT:                         OpStore %val8 [[result]]
     float4 val8 = 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, status);
 
+/////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Make sure OpImageSampleExplicitLod returns a vec4.
+// Make sure OpImageSparseSampleExplicitLod returns a struct, in which the second member is a vec4.
+/////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+// CHECK: [[v4result:%\d+]] = OpImageSampleExplicitLod %v4float {{%\d+}} %float_0_1 Grad %float_0_2 %float_0_3
+// CHECK:          {{%\d+}} = OpCompositeExtract %float [[v4result]] 0
+    float val9  = t5.SampleGrad(gSampler, 0.1, 0.2, 0.3);
+
+// CHECK: [[structResult:%\d+]] = OpImageSparseSampleExplicitLod %SparseResidencyStruct {{%\d+}} {{%\d+}} Grad|Offset|MinLod {{%\d+}} {{%\d+}} {{%\d+}} {{%\d+}}
+// CHECK:     [[v4result:%\d+]] = OpCompositeExtract %v4float [[structResult]] 1
+// CHECK:              {{%\d+}} = OpVectorShuffle %v2float [[v4result]] [[v4result]] 0 1
+    float2 val10 = t6.SampleGrad(gSampler, float2(0.1, 0.1), float2(0.2, 0.2), float2(0.3, 0.3), offset, clamp, status);
+
     return 1.0;
 }

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

@@ -8,6 +8,8 @@ Texture1D   <float4> t1 : register(t1);
 Texture2D   <float4> t2 : register(t2);
 Texture3D   <float4> t3 : register(t3);
 TextureCube <float4> t4 : register(t4);
+Texture3D   <float>  t5 : register(t5);
+TextureCube <float2> t6 : register(t6);
 
 // CHECK: OpCapability ImageGatherExtended
 // CHECK: OpCapability SparseResidency
@@ -71,5 +73,19 @@ float4 main(int3 offset: A) : SV_Target {
 // CHECK-NEXT:                         OpStore %val6 [[result]]
     float4 val6 = t4.SampleLevel(gSampler, float3(0.1, 0.2, 0.3), 10, status);
 
+/////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Make sure OpImageSampleExplicitLod returns a vec4.
+// Make sure OpImageSparseSampleExplicitLod returns a struct, in which the second member is a vec4.
+/////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+// CHECK: [[v4result:%\d+]] = OpImageSampleExplicitLod %v4float {{%\d+}} {{%\d+}} Lod|Offset %float_10 {{%\d+}}
+// CHECK:          {{%\d+}} = OpCompositeExtract %float [[v4result]] 0
+    float  val7 = t5.SampleLevel(gSampler, float3(0.1, 0.2, 0.3), 10, offset);
+
+// CHECK: [[structResult:%\d+]] = OpImageSparseSampleExplicitLod %SparseResidencyStruct {{%\d+}} {{%\d+}} Lod %float_10
+// CHECK:     [[v4result:%\d+]] = OpCompositeExtract %v4float [[structResult]] 1
+// CHECK:              {{%\d+}} = OpVectorShuffle %v2float [[v4result]] [[v4result]] 0 1
+    float2 val8 = t6.SampleLevel(gSampler, float3(0.1, 0.2, 0.3), 10, status);
+
     return 1.0;
 }

+ 18 - 1
tools/clang/test/CodeGenSPIRV/texture.sample.hlsl

@@ -8,6 +8,8 @@ Texture1D   <float4> t1 : register(t1);
 Texture2D   <float4> t2 : register(t2);
 Texture3D   <float4> t3 : register(t3);
 TextureCube <float4> t4 : register(t4);
+Texture1D   <float>  t5 : register(t5);
+TextureCube <float3> t6 : register(t6);
 
 // CHECK: OpCapability ImageGatherExtended
 // CHECK: OpCapability MinLod
@@ -87,6 +89,21 @@ float4 main(int2 offset: A) : SV_Target {
 // CHECK-NEXT:       [[result:%\d+]] = OpCompositeExtract %v4float [[structResult]] 1
 // CHECK-NEXT:                         OpStore %val8 [[result]]
     float4 val8 = t4.Sample(gSampler, float3(0.1, 0.2, 0.3), /*clamp*/ 2.0f, status);
-    
+
+
+/////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Make sure OpImageSampleImplicitLod returns a vec4.
+// Make sure OpImageSparseSampleImplicitLod returns a struct, in which the second member is a vec4.
+/////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+// CHECK: [[v4result:%\d+]] = OpImageSampleImplicitLod %v4float {{%\d+}} %float_0_1 None
+// CHECK:          {{%\d+}} = OpCompositeExtract %float [[v4result]] 0
+	float  val9  = t5.Sample(gSampler, 0.1);
+
+// CHECK: [[structResult:%\d+]] = OpImageSparseSampleImplicitLod %SparseResidencyStruct {{%\d+}} {{%\d+}} MinLod %float_2
+// CHECK:     [[v4result:%\d+]] = OpCompositeExtract %v4float [[structResult]] 1
+// CHECK:              {{%\d+}} = OpVectorShuffle %v3float [[v4result]] [[v4result]] 0 1 2
+	float3 val10 = t6.Sample(gSampler, float3(0.1, 0.2, 0.3), /*clamp*/ 2.0f, status);
+
     return 1.0;
 }