瀏覽代碼

[spirv] handle texture swizzling used in interlocked op (#2458)

1. Report an error if interlocked op has a texture argument whose
result type is non-scalar. This is not allowed in SPIR-V.
2. If the argument for interlocked op is a vector swizzling of a
texture, we should use the texture instead of the swizzling.

Fixes #2409
Jaebaek Seo 6 年之前
父節點
當前提交
c25c44dabf

+ 34 - 2
tools/clang/lib/SPIRV/SpirvEmitter.cpp

@@ -7265,13 +7265,45 @@ SpirvEmitter::processIntrinsicInterlockedMethod(const CallExpr *expr,
     spvBuilder.createStore(doExpr(outputArg), toWrite, callExpr->getExprLoc());
   };
 
+  // If a vector swizzling of a texture is done as an argument of an
+  // interlocked method, we need to handle the access to the texture
+  // buffer element correctly. For example:
+  //
+  //  InterlockedAdd(myRWTexture[index].r, 1);
+  //
+  // `-CallExpr
+  //  |-ImplicitCastExpr
+  //  | `-DeclRefExpr Function 'InterlockedAdd'
+  //  |                        'void (unsigned int &, unsigned int)'
+  //  |-HLSLVectorElementExpr 'unsigned int' lvalue vectorcomponent r
+  //  | `-ImplicitCastExpr 'vector<uint, 1>':'vector<unsigned int, 1>'
+  //  |                                       <HLSLVectorSplat>
+  //  |   `-CXXOperatorCallExpr 'unsigned int' lvalue
+  const auto *cxxOpCall = dyn_cast<CXXOperatorCallExpr>(dest);
+  if (const auto *vector = dyn_cast<HLSLVectorElementExpr>(dest)) {
+    const Expr *base = vector->getBase();
+    cxxOpCall = dyn_cast<CXXOperatorCallExpr>(base);
+    if (const auto *cast = dyn_cast<CastExpr>(base)) {
+      cxxOpCall = dyn_cast<CXXOperatorCallExpr>(cast->getSubExpr());
+    }
+  }
+
   // If the argument is indexing into a texture/buffer, we need to create an
   // OpImageTexelPointer instruction.
   SpirvInstruction *ptr = nullptr;
-  if (const auto *callExpr = dyn_cast<CXXOperatorCallExpr>(dest)) {
+  if (cxxOpCall) {
     const Expr *base = nullptr;
     const Expr *index = nullptr;
-    if (isBufferTextureIndexing(callExpr, &base, &index)) {
+    if (isBufferTextureIndexing(cxxOpCall, &base, &index)) {
+      if (hlsl::IsHLSLResourceType(base->getType())) {
+        const auto resultTy = hlsl::GetHLSLResourceResultType(base->getType());
+        if (!isScalarType(resultTy, nullptr)) {
+          emitError("Interlocked operation for texture buffer whose result "
+                    "type is non-scalar type is not allowed",
+                    dest->getExprLoc());
+          return nullptr;
+        }
+      }
       auto *baseInstr = doExpr(base);
       if (baseInstr->isRValue()) {
         // OpImageTexelPointer's Image argument must have a type of

+ 16 - 0
tools/clang/test/CodeGenSPIRV/intrinsics.interlocked-methods.texture.swizzling.error.hlsl

@@ -0,0 +1,16 @@
+// Run: %dxc -T vs_6_0 -E main
+
+RWTexture3D<uint3> target : register(u1);
+
+struct VS_OUTPUT
+{
+  float4 Pos : SV_Position;
+};
+
+void main(VS_OUTPUT input)
+{
+  uint3 index = uint3(0,0,0);
+  InterlockedAdd(target[index].r, 1);
+}
+
+// CHECK: :13:18: error: Interlocked operation for texture buffer whose result type is non-scalar type is not allowed

+ 19 - 0
tools/clang/test/CodeGenSPIRV/intrinsics.interlocked-methods.texture.swizzling.hlsl

@@ -0,0 +1,19 @@
+// Run: %dxc -T vs_6_0 -E main
+
+// CHECK: %type_3d_image = OpTypeImage %uint 3D 2 0 0 2 R32ui
+// CHECK:        %target = OpVariable %_ptr_UniformConstant_type_3d_image UniformConstant
+RWTexture3D<uint> target : register(u1);
+
+struct VS_OUTPUT
+{
+  float4 Pos : SV_Position;
+};
+
+void main(VS_OUTPUT input)
+{
+  uint3 index = uint3(0,0,0);
+
+// CHECK: [[ptr:%\d+]] = OpImageTexelPointer %_ptr_Image_uint %target {{%\d+}} %uint_0
+// CHECK-NEXT:           OpAtomicIAdd %uint [[ptr]] %uint_1 %uint_0 %uint_1
+  InterlockedAdd(target[index].r, 1);
+}

+ 7 - 0
tools/clang/unittests/SPIRV/CodeGenSpirvTest.cpp

@@ -1033,6 +1033,13 @@ TEST_F(FileTest, IntrinsicsInterlockedMethodsStaticError) {
   runFileTest("intrinsics.interlocked-methods.static-error.hlsl",
               Expect::Failure);
 }
+TEST_F(FileTest, IntrinsicsInterlockedMethodsTextureSwizzling) {
+  runFileTest("intrinsics.interlocked-methods.texture.swizzling.hlsl");
+}
+TEST_F(FileTest, IntrinsicsInterlockedMethodsTextureSwizzlingError) {
+  runFileTest("intrinsics.interlocked-methods.texture.swizzling.error.hlsl",
+              Expect::Failure);
+}
 TEST_F(FileTest, IntrinsicsIsInf) { runFileTest("intrinsics.isinf.hlsl"); }
 TEST_F(FileTest, IntrinsicsIsNan) { runFileTest("intrinsics.isnan.hlsl"); }
 TEST_F(FileTest, IntrinsicsLength) { runFileTest("intrinsics.length.hlsl"); }