Răsfoiți Sursa

[spirv] Support TextureBuffer/ConstantBuffer arrays. (#1166)

Ehsan 7 ani în urmă
părinte
comite
3c511a0959

+ 36 - 16
tools/clang/lib/SPIRV/DeclResultIdMapper.cpp

@@ -469,9 +469,10 @@ uint32_t DeclResultIdMapper::getMatrixStructType(const VarDecl *matVar,
                                   structName, {}, decorations);
 }
 
-uint32_t DeclResultIdMapper::createVarOfExplicitLayoutStruct(
-    const DeclContext *decl, const ContextUsageKind usageKind,
-    llvm::StringRef typeName, llvm::StringRef varName) {
+uint32_t DeclResultIdMapper::createStructOrStructArrayVarOfExplicitLayout(
+    const DeclContext *decl, uint32_t arraySize,
+    const ContextUsageKind usageKind, llvm::StringRef typeName,
+    llvm::StringRef varName) {
   // cbuffers are translated into OpTypeStruct with Block decoration.
   // tbuffers are translated into OpTypeStruct with BufferBlock decoration.
   // PushConstants are translated into OpTypeStruct with Block decoration.
@@ -525,26 +526,32 @@ uint32_t DeclResultIdMapper::createVarOfExplicitLayoutStruct(
   }
 
   // Get the type for the whole struct
-  const uint32_t structType =
+  uint32_t resultType =
       theBuilder.getStructType(fieldTypes, typeName, fieldNames, decorations);
 
+  // Make an array if requested.
+  if (arraySize)
+    resultType = theBuilder.getArrayType(
+        resultType, theBuilder.getConstantUint32(arraySize));
+
   // Register the <type-id> for this decl
-  ctBufferPCTypeIds[decl] = structType;
+  ctBufferPCTypeIds[decl] = resultType;
 
   const auto sc = usageKind == ContextUsageKind::PushConstant
                       ? spv::StorageClass::PushConstant
                       : spv::StorageClass::Uniform;
 
-  // Create the variable for the whole struct
-  return theBuilder.addModuleVar(structType, sc, varName);
+  // Create the variable for the whole struct / struct array.
+  return theBuilder.addModuleVar(resultType, sc, varName);
 }
 
 uint32_t DeclResultIdMapper::createCTBuffer(const HLSLBufferDecl *decl) {
   const auto usageKind =
       decl->isCBuffer() ? ContextUsageKind::CBuffer : ContextUsageKind::TBuffer;
   const std::string structName = "type." + decl->getName().str();
-  const uint32_t bufferVar = createVarOfExplicitLayoutStruct(
-      decl, usageKind, structName, decl->getName());
+  // The front-end does not allow arrays of cbuffer/tbuffer.
+  const uint32_t bufferVar = createStructOrStructArrayVarOfExplicitLayout(
+      decl, /*arraySize*/ 0, usageKind, structName, decl->getName());
 
   // We still register all VarDecls seperately here. All the VarDecls are
   // mapped to the <result-id> of the buffer object, which means when querying
@@ -572,6 +579,16 @@ uint32_t DeclResultIdMapper::createCTBuffer(const HLSLBufferDecl *decl) {
 
 uint32_t DeclResultIdMapper::createCTBuffer(const VarDecl *decl) {
   const auto *recordType = decl->getType()->getAs<RecordType>();
+  uint32_t arraySize = 0;
+
+  // In case we have an array of ConstantBuffer/TextureBuffer:
+  if (!recordType) {
+    if (const auto *arrayType =
+            astContext.getAsConstantArrayType(decl->getType())) {
+      recordType = arrayType->getElementType()->getAs<RecordType>();
+      arraySize = static_cast<uint32_t>(arrayType->getSize().getZExtValue());
+    }
+  }
   assert(recordType);
   const auto *context = cast<HLSLBufferDecl>(decl->getDeclContext());
   const auto usageKind = context->isCBuffer() ? ContextUsageKind::CBuffer
@@ -581,8 +598,9 @@ uint32_t DeclResultIdMapper::createCTBuffer(const VarDecl *decl) {
       context->isCBuffer() ? "ConstantBuffer." : "TextureBuffer.";
   const std::string structName = "type." + std::string(ctBufferName) +
                                  recordType->getDecl()->getName().str();
-  const uint32_t bufferVar = createVarOfExplicitLayoutStruct(
-      recordType->getDecl(), usageKind, structName, decl->getName());
+
+  const uint32_t bufferVar = createStructOrStructArrayVarOfExplicitLayout(
+      recordType->getDecl(), arraySize, usageKind, structName, decl->getName());
 
   // We register the VarDecl here.
   astDecls[decl] =
@@ -598,14 +616,15 @@ uint32_t DeclResultIdMapper::createCTBuffer(const VarDecl *decl) {
 }
 
 uint32_t DeclResultIdMapper::createPushConstant(const VarDecl *decl) {
+  // The front-end errors out if non-struct type push constant is used.
   const auto *recordType = decl->getType()->getAs<RecordType>();
   assert(recordType);
 
   const std::string structName =
       "type.PushConstant." + recordType->getDecl()->getName().str();
-  const uint32_t var = createVarOfExplicitLayoutStruct(
-      recordType->getDecl(), ContextUsageKind::PushConstant, structName,
-      decl->getName());
+  const uint32_t var = createStructOrStructArrayVarOfExplicitLayout(
+      recordType->getDecl(), /*arraySize*/ 0, ContextUsageKind::PushConstant,
+      structName, decl->getName());
 
   // Register the VarDecl
   astDecls[decl] = SpirvEvalInfo(var)
@@ -622,8 +641,9 @@ void DeclResultIdMapper::createGlobalsCBuffer(const VarDecl *var) {
     return;
 
   const auto *context = var->getDeclContext();
-  const uint32_t globals = createVarOfExplicitLayoutStruct(
-      context, ContextUsageKind::Globals, "type.$Globals", "$Globals");
+  const uint32_t globals = createStructOrStructArrayVarOfExplicitLayout(
+      context, /*arraySize*/ 0, ContextUsageKind::Globals, "type.$Globals",
+      "$Globals");
 
   resourceVars.emplace_back(globals, ResourceVar::Category::Other, nullptr,
                             nullptr, nullptr);

+ 3 - 4
tools/clang/lib/SPIRV/DeclResultIdMapper.h

@@ -527,10 +527,9 @@ private:
   /// depending on the usage kind.
   ///
   /// Panics if the DeclContext is neither HLSLBufferDecl or RecordDecl.
-  uint32_t createVarOfExplicitLayoutStruct(const DeclContext *decl,
-                                           ContextUsageKind usageKind,
-                                           llvm::StringRef typeName,
-                                           llvm::StringRef varName);
+  uint32_t createStructOrStructArrayVarOfExplicitLayout(
+      const DeclContext *decl, uint32_t arraySize, ContextUsageKind usageKind,
+      llvm::StringRef typeName, llvm::StringRef varName);
 
   /// A struct containing information about a particular HLSL semantic.
   struct SemanticInfo {

+ 10 - 0
tools/clang/lib/SPIRV/SPIRVEmitter.cpp

@@ -1194,6 +1194,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",
+                decl->getLocation());
+      return;
+    }
+  }
+
   if (decl->hasAttr<VKConstantIdAttr>()) {
     // This is a VarDecl for specialization constant.
     createSpecConstant(decl);

+ 31 - 14
tools/clang/test/CodeGenSPIRV/op.constant-buffer.access.hlsl

@@ -14,23 +14,40 @@ struct T {
 
 
 ConstantBuffer<T> MyCbuffer : register(b1);
+ConstantBuffer<T> MyCbufferArray[5] : register(b2);
 
-float main() : A {
-// CHECK:      [[a:%\d+]] = OpAccessChain %_ptr_Uniform_float %MyCbuffer %int_0
-// CHECK-NEXT: {{%\d+}} = OpLoad %float [[a]]
+float main() : A{
+  // CHECK:      [[a:%\d+]] = OpAccessChain %_ptr_Uniform_float %MyCbuffer %int_0
+  // CHECK-NEXT: {{%\d+}} = OpLoad %float [[a]]
 
-// CHECK:      [[b:%\d+]] = OpAccessChain %_ptr_Uniform_v2float %MyCbuffer %int_1
-// CHECK-NEXT: [[b0:%\d+]] = OpAccessChain %_ptr_Uniform_float [[b]] %int_0
-// CHECK-NEXT: {{%\d+}} = OpLoad %float [[b0]]
+  // CHECK:      [[b:%\d+]] = OpAccessChain %_ptr_Uniform_v2float %MyCbuffer %int_1
+  // CHECK-NEXT: [[b0:%\d+]] = OpAccessChain %_ptr_Uniform_float [[b]] %int_0
+  // CHECK-NEXT: {{%\d+}} = OpLoad %float [[b0]]
 
-// CHECK:      [[c12:%\d+]] = OpAccessChain %_ptr_Uniform_float %MyCbuffer %int_2 %uint_1 %uint_2
-// CHECK-NEXT: {{%\d+}} = OpLoad %float [[c12]]
+  // CHECK:      [[c12:%\d+]] = OpAccessChain %_ptr_Uniform_float %MyCbuffer %int_2 %uint_1 %uint_2
+  // CHECK-NEXT: {{%\d+}} = OpLoad %float [[c12]]
 
-// CHECK:      [[s:%\d+]] = OpAccessChain %_ptr_Uniform_float %MyCbuffer %int_3 %int_0
-// CHECK-NEXT: {{%\d+}} = OpLoad %float [[s]]
+  // CHECK:      [[s:%\d+]] = OpAccessChain %_ptr_Uniform_float %MyCbuffer %int_3 %int_0
+  // CHECK-NEXT: {{%\d+}} = OpLoad %float [[s]]
 
-// CHECK:      [[t:%\d+]] = OpAccessChain %_ptr_Uniform_float %MyCbuffer %int_4 %int_3
-// CHECK-NEXT: {{%\d+}} = OpLoad %float [[t]]
-    return MyCbuffer.a + MyCbuffer.b.x + MyCbuffer.c[1][2] + MyCbuffer.s.f + MyCbuffer.t[3];
-}
+  // CHECK:      [[t:%\d+]] = OpAccessChain %_ptr_Uniform_float %MyCbuffer %int_4 %int_3
+  // CHECK-NEXT: {{%\d+}} = OpLoad %float [[t]]
+  return MyCbuffer.a + MyCbuffer.b.x + MyCbuffer.c[1][2] + MyCbuffer.s.f + MyCbuffer.t[3] +
+  // CHECK:      [[a:%\d+]] = OpAccessChain %_ptr_Uniform_float %MyCbufferArray %int_4 %int_0
+  // CHECK-NEXT: {{%\d+}} = OpLoad %float [[a]]
+
+  // CHECK:      [[b:%\d+]] = OpAccessChain %_ptr_Uniform_v2float %MyCbufferArray %int_3 %int_1
+  // CHECK-NEXT: [[b0:%\d+]] = OpAccessChain %_ptr_Uniform_float [[b]] %int_0
+  // CHECK-NEXT: {{%\d+}} = OpLoad %float [[b0]]
+
+  // CHECK:      [[c12:%\d+]] = OpAccessChain %_ptr_Uniform_float %MyCbufferArray %int_2 %int_2 %uint_1 %uint_2
+  // CHECK-NEXT: {{%\d+}} = OpLoad %float [[c12]]
 
+  // CHECK:      [[s:%\d+]] = OpAccessChain %_ptr_Uniform_float %MyCbufferArray %int_1 %int_3 %int_0
+  // CHECK-NEXT: {{%\d+}} = OpLoad %float [[s]]
+
+  // CHECK:      [[t:%\d+]] = OpAccessChain %_ptr_Uniform_float %MyCbufferArray %int_0 %int_4 %int_3
+  // CHECK-NEXT: {{%\d+}} = OpLoad %float [[t]]
+         MyCbufferArray[4].a + MyCbufferArray[3].b.x + MyCbufferArray[2].c[1][2] + MyCbufferArray[1].s.f + MyCbufferArray[0].t[3];
+
+}

+ 24 - 7
tools/clang/test/CodeGenSPIRV/op.texture-buffer.access.hlsl

@@ -14,22 +14,39 @@ struct T {
 
 
 TextureBuffer<T> MyTB : register(t1);
+TextureBuffer<T> MyTBArray[5] : register(t2);
 
 float main() : A {
 // CHECK:      [[a:%\d+]] = OpAccessChain %_ptr_Uniform_float %MyTB %int_0
-// CHECK-NEXT: {{%\d+}} = OpLoad %float [[a]]
+// CHECK-NEXT:   {{%\d+}} = OpLoad %float [[a]]
 
-// CHECK:      [[b:%\d+]] = OpAccessChain %_ptr_Uniform_v2float %MyTB %int_1
+// CHECK:       [[b:%\d+]] = OpAccessChain %_ptr_Uniform_v2float %MyTB %int_1
 // CHECK-NEXT: [[b0:%\d+]] = OpAccessChain %_ptr_Uniform_float [[b]] %int_0
-// CHECK-NEXT: {{%\d+}} = OpLoad %float [[b0]]
+// CHECK-NEXT:    {{%\d+}} = OpLoad %float [[b0]]
 
 // CHECK:      [[c12:%\d+]] = OpAccessChain %_ptr_Uniform_float %MyTB %int_2 %uint_1 %uint_2
-// CHECK-NEXT: {{%\d+}} = OpLoad %float [[c12]]
+// CHECK-NEXT:     {{%\d+}} = OpLoad %float [[c12]]
 
 // CHECK:      [[s:%\d+]] = OpAccessChain %_ptr_Uniform_float %MyTB %int_3 %int_0
-// CHECK-NEXT: {{%\d+}} = OpLoad %float [[s]]
+// CHECK-NEXT:   {{%\d+}} = OpLoad %float [[s]]
 
 // CHECK:      [[t:%\d+]] = OpAccessChain %_ptr_Uniform_float %MyTB %int_4 %int_3
-// CHECK-NEXT: {{%\d+}} = OpLoad %float [[t]]
-  return MyTB.a + MyTB.b.x + MyTB.c[1][2] + MyTB.s.f + MyTB.t[3];
+// CHECK-NEXT:   {{%\d+}} = OpLoad %float [[t]]
+  return MyTB.a         + MyTB.b.x         + MyTB.c[1][2]         + MyTB.s.f         + MyTB.t[3] +
+// CHECK:      [[a:%\d+]] = OpAccessChain %_ptr_Uniform_float %MyTBArray %int_4 %int_0
+// CHECK-NEXT:   {{%\d+}} = OpLoad %float [[a]]
+
+// CHECK:       [[b:%\d+]] = OpAccessChain %_ptr_Uniform_v2float %MyTBArray %int_3 %int_1
+// CHECK-NEXT: [[b0:%\d+]] = OpAccessChain %_ptr_Uniform_float [[b]] %int_0
+// CHECK-NEXT:    {{%\d+}} = OpLoad %float [[b0]]
+
+// CHECK:      [[c12:%\d+]] = OpAccessChain %_ptr_Uniform_float %MyTBArray %int_2 %int_2 %uint_1 %uint_2
+// CHECK-NEXT:     {{%\d+}} = OpLoad %float [[c12]]
+
+// CHECK:      [[s:%\d+]] = OpAccessChain %_ptr_Uniform_float %MyTBArray %int_1 %int_3 %int_0
+// CHECK-NEXT:   {{%\d+}} = OpLoad %float [[s]]
+
+// CHECK:      [[t:%\d+]] = OpAccessChain %_ptr_Uniform_float %MyTBArray %int_0 %int_4 %int_3
+// CHECK-NEXT:   {{%\d+}} = OpLoad %float [[t]]
+         MyTBArray[4].a + MyTBArray[3].b.x + MyTBArray[2].c[1][2] + MyTBArray[1].s.f + MyTBArray[0].t[3];
 }

+ 9 - 1
tools/clang/test/CodeGenSPIRV/type.constant-buffer.hlsl

@@ -10,13 +10,15 @@
 
 // CHECK:      OpName %MyCbuffer "MyCbuffer"
 // CHECK:      OpName %AnotherCBuffer "AnotherCBuffer"
+
+// CHECK:      OpDecorate %type_ConstantBuffer_T Block
+
 struct S {
     float  f1;
     float3 f2;
 };
 
 // CHECK: %type_ConstantBuffer_T = OpTypeStruct %uint %int %v2uint %mat3v4float %S %_arr_float_uint_4
-// CHECK: %_ptr_Uniform_type_ConstantBuffer_T = OpTypePointer Uniform %type_ConstantBuffer_T
 struct T {
     bool     a;
     int      b;
@@ -26,10 +28,16 @@ struct T {
     float    t[4];
 };
 
+// CHECK: %_ptr_Uniform_type_ConstantBuffer_T = OpTypePointer Uniform %type_ConstantBuffer_T
+// CHECK: %_arr_type_ConstantBuffer_T_uint_2 = OpTypeArray %type_ConstantBuffer_T %uint_2
+// CHECK: %_ptr_Uniform__arr_type_ConstantBuffer_T_uint_2 = OpTypePointer Uniform %_arr_type_ConstantBuffer_T_uint_2
+
 // CHECK: %MyCbuffer = OpVariable %_ptr_Uniform_type_ConstantBuffer_T Uniform
 ConstantBuffer<T> MyCbuffer : register(b1);
 // CHECK: %AnotherCBuffer = OpVariable %_ptr_Uniform_type_ConstantBuffer_T Uniform
 ConstantBuffer<T> AnotherCBuffer : register(b2);
+// CHECK: %MyConstantBufferArray = OpVariable %_ptr_Uniform__arr_type_ConstantBuffer_T_uint_2 Uniform
+ConstantBuffer<T> MyConstantBufferArray[2] : register(b3);
 
 void main() {
 }

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

@@ -0,0 +1,22 @@
+// Run: %dxc -T ps_6_0 -E main
+
+struct T {
+  float  a;
+  float3 b;
+};
+
+StructuredBuffer<T> myStructuredBuffer[3];
+RWStructuredBuffer<T> myRWStructuredBuffer[4];
+AppendStructuredBuffer<T> myAppendStructuredBuffer[2];
+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

+ 9 - 1
tools/clang/test/CodeGenSPIRV/type.texture-buffer.hlsl

@@ -11,13 +11,14 @@
 // CHECK:      OpName %MyTbuffer "MyTbuffer"
 // CHECK:      OpName %AnotherTBuffer "AnotherTBuffer"
 
+// CHECK:      OpDecorate %type_TextureBuffer_T BufferBlock
+
 struct S {
   float  f1;
   float3 f2;
 };
 
 // CHECK: %type_TextureBuffer_T = OpTypeStruct %uint %int %v2uint %mat3v4float %S %_arr_float_uint_4
-// CHECK: %_ptr_Uniform_type_TextureBuffer_T = OpTypePointer Uniform %type_TextureBuffer_T
 struct T {
   bool     a;
   int      b;
@@ -27,11 +28,18 @@ struct T {
   float    t[4];
 };
 
+// CHECK: %_ptr_Uniform_type_TextureBuffer_T = OpTypePointer Uniform %type_TextureBuffer_T
+// CEHCK: %_arr_type_TextureBuffer_T_uint_3 = OpTypeArray %type_TextureBuffer_T %uint_3
+// CEHCK: %_ptr_Uniform__arr_type_TextureBuffer_T_uint_3 = OpTypePointer Uniform %_arr_type_TextureBuffer_T_uint_3
+
 // CHECK: %MyTbuffer = OpVariable %_ptr_Uniform_type_TextureBuffer_T Uniform
 TextureBuffer<T> MyTbuffer : register(t1);
 
 // CHECK: %AnotherTBuffer = OpVariable %_ptr_Uniform_type_TextureBuffer_T Uniform
 TextureBuffer<T> AnotherTBuffer : register(t2);
 
+// CHECK: %myTextureBufferArray = OpVariable %_ptr_Uniform__arr_type_TextureBuffer_T_uint_3 Uniform
+TextureBuffer<T> myTextureBufferArray[3] : register(t3);
+
 void main() {
 }

+ 3 - 0
tools/clang/unittests/SPIRV/CodeGenSPIRVTest.cpp

@@ -68,6 +68,9 @@ TEST_F(FileTest, TextureBufferType) { runFileTest("type.texture-buffer.hlsl"); }
 TEST_F(FileTest, StructuredBufferType) {
   runFileTest("type.structured-buffer.hlsl");
 }
+TEST_F(FileTest, StructuredBufferArrayTypeError) {
+  runFileTest("type.structured-buffer.array.error.hlsl", Expect::Failure);
+}
 TEST_F(FileTest, AppendStructuredBufferType) {
   runFileTest("type.append-structured-buffer.hlsl");
 }