Browse Source

[spirv] Add support for arrays of structured/byte buffers (#1257)

They are translated into, of course, (runtime) arrays of
structured/byte buffers.
Lei Zhang 7 năm trước cách đây
mục cha
commit
2095fef62c

+ 12 - 0
docs/SPIR-V.rst

@@ -578,6 +578,18 @@ HLSL Interpolation Modifier SPIR-V Decoration   SPIR-V Capability
 ``sample``                  ``Sample``        ``SampleRateShading``
 =========================== ================= =====================
 
+Arrays
+------
+
+Sized (either explicitly or implicitly) arrays are translated into SPIR-V
+`OpTypeArray`. Unsized arrays are translated into `OpTypeRuntimeArray`.
+
+Arrays, if used for external resources (residing in SPIR-V `Uniform` or
+`UniformConstant` storage class), will need layout decorations like SPIR-V
+`ArrayStride` decoration. For arrays of opaque types, e.g., HLSL textures
+or samplers, we don't decorate with `ArrayStride` decorations since there is
+no meaningful strides. Similarly for arrays of structured/byte buffers.
+
 User-defined types
 ------------------
 

+ 16 - 7
tools/clang/lib/SPIRV/SPIRVEmitter.cpp

@@ -181,9 +181,11 @@ const DeclContext *isConstantTextureBufferDeclRef(const Expr *expr) {
 
 /// Returns true if
 /// * the given expr is an DeclRefExpr referencing a kind of structured or byte
-/// buffer and it is non-alias one, or
+///   buffer and it is non-alias one, or
 /// * the given expr is an CallExpr returning a kind of structured or byte
-/// buffer.
+///   buffer.
+/// * the given expr is an ArraySubscriptExpr referencing a kind of structured
+///   or byte buffer.
 ///
 /// Note: legalization specific code
 bool isReferencingNonAliasStructuredOrByteBuffer(const Expr *expr) {
@@ -195,6 +197,8 @@ bool isReferencingNonAliasStructuredOrByteBuffer(const Expr *expr) {
   } else if (const auto *callExpr = dyn_cast<CallExpr>(expr)) {
     if (TypeTranslator::isAKindOfStructuredOrByteBuffer(callExpr->getType()))
       return true;
+  } else if (const auto *arrSubExpr = dyn_cast<ArraySubscriptExpr>(expr)) {
+    return isReferencingNonAliasStructuredOrByteBuffer(arrSubExpr->getBase());
   }
   return false;
 }
@@ -1258,11 +1262,16 @@ void SPIRVEmitter::doVarDecl(const VarDecl *decl) {
               decl->getLocation());
   }
 
-  if (const auto *arrayType =
-          astContext.getAsConstantArrayType(decl->getType())) {
-    if (TypeTranslator::isAKindOfStructuredOrByteBuffer(
-            arrayType->getElementType())) {
-      emitError("arrays of structured/byte buffers unsupported",
+  // Reject arrays of RW/append/consume structured buffers. They have assoicated
+  // counters, which are quite nasty to handle.
+  if (decl->getType()->isArrayType()) {
+    auto type = decl->getType();
+    do {
+      type = type->getAsArrayTypeUnsafe()->getElementType();
+    } while (type->isArrayType());
+
+    if (TypeTranslator::isRWAppendConsumeSBuffer(type)) {
+      emitError("arrays of RW/append/consume structured buffers unsupported",
                 decl->getLocation());
       return;
     }

+ 12 - 4
tools/clang/lib/SPIRV/TypeTranslator.cpp

@@ -623,10 +623,14 @@ uint32_t TypeTranslator::translateType(QualType type, LayoutRule rule) {
 
   // Array type
   if (const auto *arrayType = astContext.getAsArrayType(type)) {
-    const uint32_t elemType = translateType(arrayType->getElementType(), rule);
+    const auto elemType = arrayType->getElementType();
+    const uint32_t elemTypeId = translateType(elemType, rule);
 
     llvm::SmallVector<const Decoration *, 4> decorations;
-    if (rule != LayoutRule::Void) {
+    if (rule != LayoutRule::Void &&
+        // We won't have stride information for structured/byte buffers since
+        // they contain runtime arrays.
+        !isAKindOfStructuredOrByteBuffer(elemType)) {
       uint32_t stride = 0;
       (void)getAlignmentAndSize(type, rule, &stride);
       decorations.push_back(
@@ -636,7 +640,7 @@ uint32_t TypeTranslator::translateType(QualType type, LayoutRule rule) {
     if (const auto *caType = astContext.getAsConstantArrayType(type)) {
       const auto size = static_cast<uint32_t>(caType->getSize().getZExtValue());
       return theBuilder.getArrayType(
-          elemType, theBuilder.getConstantUint32(size), decorations);
+          elemTypeId, theBuilder.getConstantUint32(size), decorations);
     } else {
       assert(type->isIncompleteArrayType());
       // Runtime arrays of resources needs additional capability.
@@ -646,7 +650,7 @@ uint32_t TypeTranslator::translateType(QualType type, LayoutRule rule) {
         theBuilder.requireCapability(
             spv::Capability::RuntimeDescriptorArrayEXT);
       }
-      return theBuilder.getRuntimeArrayType(elemType, decorations);
+      return theBuilder.getRuntimeArrayType(elemTypeId, decorations);
     }
   }
 
@@ -760,6 +764,10 @@ bool TypeTranslator::isRWAppendConsumeSBuffer(QualType type) {
 }
 
 bool TypeTranslator::isAKindOfStructuredOrByteBuffer(QualType type) {
+  // Strip outer arrayness first
+  while (type->isArrayType())
+    type = type->getAsArrayTypeUnsafe()->getElementType();
+
   if (const RecordType *recordType = type->getAs<RecordType>()) {
     StringRef name = recordType->getDecl()->getName();
     return name == "StructuredBuffer" || name == "RWStructuredBuffer" ||

+ 4 - 10
tools/clang/test/CodeGenSPIRV/type.structured-buffer.array.error.hlsl

@@ -5,18 +5,12 @@ struct T {
   float3 b;
 };
 
-StructuredBuffer<T> myStructuredBuffer[3];
 RWStructuredBuffer<T> myRWStructuredBuffer[4];
-AppendStructuredBuffer<T> myAppendStructuredBuffer[2];
+AppendStructuredBuffer<T> myAppendStructuredBuffer[];
 ConsumeStructuredBuffer<T> myConsumeStructuredBuffer[2];
-ByteAddressBuffer myBAB[2];
-RWByteAddressBuffer myRWBAB[2];
 
 void main() {}
 
-// CHECK: :8:21: error: arrays of structured/byte buffers unsupported
-// CHECK: :9:23: error: arrays of structured/byte buffers unsupported
-// CHECK: :10:27: error: arrays of structured/byte buffers unsupported
-// CHECK: :11:28: error: arrays of structured/byte buffers unsupported
-// CHECK: :12:19: error: arrays of structured/byte buffers unsupported
-// CHECK: :13:21: error: arrays of structured/byte buffers unsupported
+// CHECK: :8:23: error: arrays of RW/append/consume structured buffers unsupported
+// CHECK: :9:27: error: arrays of RW/append/consume structured buffers unsupported
+// CHECK: :10:28: error: arrays of RW/append/consume structured buffers unsupported

+ 41 - 0
tools/clang/test/CodeGenSPIRV/type.structured-buffer.array.hlsl

@@ -0,0 +1,41 @@
+// Run: %dxc -T ps_6_0 -E main
+
+// CHECK: %_runtimearr_v4float = OpTypeRuntimeArray %v4float
+
+// CHECK: %type_StructuredBuffer_v4float = OpTypeStruct %_runtimearr_v4float
+// CHECK: %_arr_type_StructuredBuffer_v4float_uint_8 = OpTypeArray %type_StructuredBuffer_v4float %uint_8
+
+// CHECK: %_runtimearr_uint = OpTypeRuntimeArray %uint
+
+// CHECK: %type_ByteAddressBuffer = OpTypeStruct %_runtimearr_uint
+// CHECK: %_runtimearr_type_ByteAddressBuffer = OpTypeRuntimeArray %type_ByteAddressBuffer
+
+// CHECK: %type_RWByteAddressBuffer = OpTypeStruct %_runtimearr_uint
+// CHECK: %_arr_type_RWByteAddressBuffer_uint_4 = OpTypeArray %type_RWByteAddressBuffer %uint_4
+
+// CHECK:    %MySBuffer = OpVariable %_ptr_Uniform__arr_type_StructuredBuffer_v4float_uint_8 Uniform
+StructuredBuffer<float4>   MySBuffer[8];
+// CHECK:   %MyBABuffer = OpVariable %_ptr_Uniform__runtimearr_type_ByteAddressBuffer Uniform
+ByteAddressBuffer          MyBABuffer[];
+// CHECK: %MyRWBABuffer = OpVariable %_ptr_Uniform__arr_type_RWByteAddressBuffer_uint_4 Uniform
+RWByteAddressBuffer        MyRWBABuffer[4];
+
+float4 main(uint index : INDEX) : SV_Target {
+// CHECK: [[ptr:%\d+]] = OpAccessChain %_ptr_Uniform_type_ByteAddressBuffer %MyBABuffer {{%\d+}}
+// CHECK:                OpAccessChain %_ptr_Uniform_uint [[ptr]] %uint_0 {{%\d+}}
+    uint val1 = MyBABuffer[index].Load(index);
+
+// CHECK: [[ptr:%\d+]] = OpAccessChain %_ptr_Uniform_type_RWByteAddressBuffer %MyRWBABuffer {{%\d+}}
+// CHECK:                OpAccessChain %_ptr_Uniform_uint [[ptr]] %uint_0 {{%\d+}}
+    MyRWBABuffer[index].Store(index, val1);
+
+// CHECK: [[ptr:%\d+]] = OpAccessChain %_ptr_Uniform_type_StructuredBuffer_v4float %MySBuffer {{%\d+}}
+// CHECK:                OpStore %localSBuffer [[ptr]]
+    StructuredBuffer<float4>   localSBuffer = MySBuffer[index];
+
+// CHECK: [[ptr:%\d+]] = OpLoad %_ptr_Uniform_type_StructuredBuffer_v4float %localSBuffer
+// CHECK:                OpAccessChain %_ptr_Uniform_v4float [[ptr]] %int_0 {{%\d+}}
+    float4 val2 = localSBuffer[index];
+
+    return val1 * val2;
+}

+ 4 - 1
tools/clang/unittests/SPIRV/CodeGenSPIRVTest.cpp

@@ -69,7 +69,10 @@ TEST_F(FileTest, TextureBufferType) { runFileTest("type.texture-buffer.hlsl"); }
 TEST_F(FileTest, StructuredBufferType) {
   runFileTest("type.structured-buffer.hlsl");
 }
-TEST_F(FileTest, StructuredBufferArrayTypeError) {
+TEST_F(FileTest, StructuredByteBufferArray) {
+  runFileTest("type.structured-buffer.array.hlsl");
+}
+TEST_F(FileTest, StructuredByteBufferArrayError) {
   runFileTest("type.structured-buffer.array.error.hlsl", Expect::Failure);
 }
 TEST_F(FileTest, AppendStructuredBufferType) {