Browse Source

[spirv] Support non-constant offsets in .Gather*() methods (#1232)

They are emulated via 4 separate OpImage*Gather instructions.
Lei Zhang 7 years ago
parent
commit
4491a30fba

+ 3 - 1
docs/SPIR-V.rst

@@ -2199,7 +2199,9 @@ There are a few overloads for these functions:
 
 - For those overloads taking 4 offset parameters, those offset parameters will
   be conveyed as an additional ``ConstOffsets`` image operands to the
-  instruction. So those offset parameters must all be constant values.
+  instruction if those offset parameters are all constants. Otherwise,
+  4 separate ``OpImageGather`` instructions will be emitted to get each texel
+  from each offset, using the ``Offset`` image operands.
 - For those overloads with the ``status`` parameter, ``OpImageSparseGather``
   is used instead, and the resulting SPIR-V ``Residency Code`` will be
   written to ``status``.

+ 31 - 11
tools/clang/lib/SPIRV/SPIRVEmitter.cpp

@@ -2939,6 +2939,7 @@ uint32_t SPIRVEmitter::processTextureGatherRGBACmpRGBA(
   const uint32_t compareVal = isCmp ? doExpr(expr->getArg(2)) : 0;
 
   // Handle offsets (if any).
+  bool needsEmulation = false;
   uint32_t constOffset = 0, varOffset = 0, constOffsets = 0;
   if (numOffsetArgs == 1) {
     // The offset arg is not optional.
@@ -2949,21 +2950,40 @@ uint32_t SPIRVEmitter::processTextureGatherRGBACmpRGBA(
     const auto offset2 = tryToEvaluateAsConst(expr->getArg(4 + isCmp));
     const auto offset3 = tryToEvaluateAsConst(expr->getArg(5 + isCmp));
 
-    // Make sure we can generate the ConstOffsets image operands in SPIR-V.
-    if (!offset0 || !offset1 || !offset2 || !offset3) {
-      emitError("all offset parameters to '%0' method call must be constants",
-                expr->getExprLoc())
-          << callee->getName() << expr->getSourceRange();
-      return 0;
+    // If any of the offsets is not constant, we then need to emulate the call
+    // using 4 OpImageGather instructions. Otherwise, we can leverage the
+    // ConstOffsets image operand.
+    if (offset0 && offset1 && offset2 && offset3) {
+      const uint32_t v2i32 =
+          theBuilder.getVecType(theBuilder.getInt32Type(), 2);
+      const uint32_t offsetType =
+          theBuilder.getArrayType(v2i32, theBuilder.getConstantUint32(4));
+      constOffsets = theBuilder.getConstantComposite(
+          offsetType, {offset0, offset1, offset2, offset3});
+    } else {
+      needsEmulation = true;
     }
-    const uint32_t v2i32 = theBuilder.getVecType(theBuilder.getInt32Type(), 2);
-    const uint32_t offsetType =
-        theBuilder.getArrayType(v2i32, theBuilder.getConstantUint32(4));
-    constOffsets = theBuilder.getConstantComposite(
-        offsetType, {offset0, offset1, offset2, offset3});
   }
 
   const auto status = hasStatusArg ? doExpr(expr->getArg(numArgs - 1)) : 0;
+
+  if (needsEmulation) {
+    const auto elemType = typeTranslator.translateType(
+        hlsl::GetHLSLVecElementType(callee->getReturnType()));
+
+    uint32_t texels[4];
+    for (uint32_t i = 0; i < 4; ++i) {
+      varOffset = doExpr(expr->getArg(2 + isCmp + i));
+      const uint32_t gatherRet = theBuilder.createImageGather(
+          retTypeId, imageTypeId, image, sampler, coordinate,
+          theBuilder.getConstantInt32(component), compareVal, /*constOffset*/ 0,
+          varOffset, /*constOffsets*/ 0, /*sampleNumber*/ 0, status);
+      texels[i] = theBuilder.createCompositeExtract(elemType, gatherRet, {i});
+    }
+    return theBuilder.createCompositeConstruct(
+        retTypeId, {texels[0], texels[1], texels[2], texels[3]});
+  }
+
   return theBuilder.createImageGather(
       retTypeId, imageTypeId, image, sampler, coordinate,
       theBuilder.getConstantInt32(component), compareVal, constOffset,

+ 14 - 1
tools/clang/test/CodeGenSPIRV/texture.array.gather-red.hlsl

@@ -21,7 +21,7 @@ TextureCubeArray<int4> tCubeArray : register(t3);
 
 // CHECK: %SparseResidencyStruct_0 = OpTypeStruct %uint %v4int
 
-float4 main(float3 location: A) : SV_Target {
+float4 main(float3 location: A, int2 offset : B) : SV_Target {
 // CHECK:            [[t2f4:%\d+]] = OpLoad %type_2d_image_array %t2f4
 // CHECK-NEXT:   [[gSampler:%\d+]] = OpLoad %type_sampler %gSampler
 // CHECK-NEXT:        [[loc:%\d+]] = OpLoad %v3float %location
@@ -85,5 +85,18 @@ float4 main(float3 location: A) : SV_Target {
 // CHECK-NEXT:                         OpStore %g [[result]]
     int4 g = tCubeArray.GatherRed(gSampler, /*location*/ float4(1.5, 1.5, 1.5, 1.5), status);
 
+// CHECK: [[offset:%\d+]] = OpLoad %v2int %offset
+// CHECK: [[gather:%\d+]] = OpImageGather %v4float {{%\d+}} {{%\d+}} %int_0 Offset [[offset]]
+// CHECK: [[texel0:%\d+]] = OpCompositeExtract %float [[gather]] 0
+// CHECK: [[gather:%\d+]] = OpImageGather %v4float {{%\d+}} {{%\d+}} %int_0 Offset [[c34]]
+// CHECK: [[texel1:%\d+]] = OpCompositeExtract %float [[gather]] 1
+// CHECK: [[gather:%\d+]] = OpImageGather %v4float {{%\d+}} {{%\d+}} %int_0 Offset [[c56]]
+// CHECK: [[texel2:%\d+]] = OpCompositeExtract %float [[gather]] 2
+// CHECK: [[offset:%\d+]] = OpLoad %v2int %offset
+// CHECK: [[gather:%\d+]] = OpImageGather %v4float {{%\d+}} {{%\d+}} %int_0 Offset [[offset]]
+// CHECK: [[texel3:%\d+]] = OpCompositeExtract %float [[gather]] 3
+// CHECK:                   OpCompositeConstruct %v4float [[texel0]] [[texel1]] [[texel2]] [[texel3]]
+    float4 h = t2f4.GatherRed(gSampler, location, offset, int2(3, 4), int2(5, 6), offset);
+
     return 1.0;
 }

+ 14 - 1
tools/clang/test/CodeGenSPIRV/texture.gather-red.hlsl

@@ -22,7 +22,7 @@ TextureCube<int4> tCube : register(t3);
 
 // CHECK: %SparseResidencyStruct_0 = OpTypeStruct %uint %v4int
 
-float4 main(float2 location: A) : SV_Target {
+float4 main(float2 location: A, int2 offset : B) : SV_Target {
 // CHECK:            [[t2f4:%\d+]] = OpLoad %type_2d_image %t2f4
 // CHECK-NEXT:   [[gSampler:%\d+]] = OpLoad %type_sampler %gSampler
 // CHECK-NEXT:        [[loc:%\d+]] = OpLoad %v2float %location
@@ -86,5 +86,18 @@ float4 main(float2 location: A) : SV_Target {
 // CHECK-NEXT:                        OpStore %g [[result]]
     int4 g = tCube.GatherRed(gSampler, /*location*/ float3(1.5, 1.5, 1.5), status);
 
+// CHECK: [[gather:%\d+]] = OpImageGather %v4float {{%\d+}} {{%\d+}} %int_0 Offset [[c12]]
+// CHECK: [[texel0:%\d+]] = OpCompositeExtract %float [[gather]] 0
+// CHECK: [[offset:%\d+]] = OpLoad %v2int %offset
+// CHECK: [[gather:%\d+]] = OpImageGather %v4float {{%\d+}} {{%\d+}} %int_0 Offset [[offset]]
+// CHECK: [[texel1:%\d+]] = OpCompositeExtract %float [[gather]] 1
+// CHECK: [[offset:%\d+]] = OpLoad %v2int %offset
+// CHECK: [[gather:%\d+]] = OpImageGather %v4float {{%\d+}} {{%\d+}} %int_0 Offset [[offset]]
+// CHECK: [[texel2:%\d+]] = OpCompositeExtract %float [[gather]] 2
+// CHECK: [[gather:%\d+]] = OpImageGather %v4float {{%\d+}} {{%\d+}} %int_0 Offset [[c78]]
+// CHECK: [[texel3:%\d+]] = OpCompositeExtract %float [[gather]] 3
+// CHECK:                   OpCompositeConstruct %v4float [[texel0]] [[texel1]] [[texel2]] [[texel3]]
+    float4 h = t2f4.GatherRed(gSampler, location, int2(1, 2), offset, offset, int2(7, 8));
+
     return 1.0;
 }