Parcourir la source

[spirv] tbuffer and TextureBuffer support. (#787)

Ehsan il y a 7 ans
Parent
commit
4bd93f66a3

+ 14 - 0
docs/SPIR-V.rst

@@ -389,6 +389,8 @@ Please see the following sections for the details of each type. As a summary:
 =========================== ================== ========================== ==================== =================
 ``cbuffer``                   Uniform Buffer      GLSL ``std140``            ``Uniform``        ``Block``
 ``ConstantBuffer``            Uniform Buffer      GLSL ``std140``            ``Uniform``        ``Block``
+``tbuffer``                   Storage Buffer      GLSL ``std430``            ``Uniform``        ``BufferBlock``
+``TextureBuffer``             Storage Buffer      GLSL ``std430``            ``Uniform``        ``BufferBlock``
 ``StructuredBuffer``          Storage Buffer      GLSL ``std430``            ``Uniform``        ``BufferBlock``
 ``RWStructuredBuffer``        Storage Buffer      GLSL ``std430``            ``Uniform``        ``BufferBlock``
 ``AppendStructuredBuffer``    Storage Buffer      GLSL ``std430``            ``Uniform``        ``BufferBlock``
@@ -438,6 +440,18 @@ will be translated into
   ; Variable
   %myCbuffer = OpVariable %_ptr_Uniform_type_ConstantBuffer_T Uniform
 
+``tbuffer`` and ``TextureBuffer``
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+These two buffer types are treated as storage buffers using Vulkan's
+terminology. They are translated into an ``OpTypeStruct`` with the
+necessary layout decorations (``Offset``, ``ArrayStride``, ``MatrixStride``,
+``RowMajor``, ``ColMajor``) and the ``BufferBlock`` decoration. All the struct
+members are also decorated with ``NonWritable`` decoration. The layout rule
+used is GLSL ``std430`` (by default). A variable declared as one of these
+types will be placed in the ``Uniform`` storage class.
+
+
 ``StructuredBuffer`` and ``RWStructuredBuffer``
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 

+ 39 - 25
tools/clang/lib/SPIRV/DeclResultIdMapper.cpp

@@ -203,9 +203,7 @@ SpirvEvalInfo DeclResultIdMapper::getDeclResultId(const NamedDecl *decl) {
           cast<VarDecl>(decl)->getType(),
           // We need to set decorateLayout here to avoid creating SPIR-V
           // instructions for the current type without decorations.
-          // According to the Vulkan spec, cbuffer should follow standrad
-          // uniform buffer layout, which GLSL std140 rules statisfies.
-          LayoutRule::GLSLStd140);
+          info->layoutRule);
 
       const uint32_t elemId = theBuilder.createAccessChain(
           theBuilder.getPointerType(varType, info->storageClass),
@@ -290,13 +288,28 @@ uint32_t DeclResultIdMapper::createExternVar(const VarDecl *var) {
   return id;
 }
 
-uint32_t
-DeclResultIdMapper::createVarOfExplicitLayoutStruct(const DeclContext *decl,
-                                                    llvm::StringRef typeName,
-                                                    llvm::StringRef varName) {
+uint32_t DeclResultIdMapper::createVarOfExplicitLayoutStruct(
+    const DeclContext *decl, llvm::StringRef typeName, llvm::StringRef varName,
+    bool isCBuffer) {
+  LayoutRule layoutRule =
+      isCBuffer ? LayoutRule::GLSLStd140 : LayoutRule::GLSLStd430;
+  const auto *blockDec =
+      isCBuffer ? Decoration::getBlock(*theBuilder.getSPIRVContext())
+                : Decoration::getBufferBlock(*theBuilder.getSPIRVContext());
+
+  // Get the type for the whole buffer
+  // cbuffers are translated into OpTypeStruct with Block decoration.
+  // tbuffers are translated into OpTypeStruct with BufferBlock decoration.
+  // Both cbuffers and tbuffers have the SPIR-V Uniform storage class. cbuffers
+  // satisfy GLSL std140 layout rules, and tbuffers satisfy GLSL std430 layout
+  // rules.
+  auto decorations = typeTranslator.getLayoutDecorations(decl, layoutRule);
+  decorations.push_back(blockDec);
+
   // Collect the type and name for each field
   llvm::SmallVector<uint32_t, 4> fieldTypes;
   llvm::SmallVector<llvm::StringRef, 4> fieldNames;
+  uint32_t fieldIndex = 0;
   for (const auto *subDecl : decl->decls()) {
     // Ignore implicit generated struct declarations/constructors/destructors.
     if (subDecl->isImplicit())
@@ -311,19 +324,19 @@ DeclResultIdMapper::createVarOfExplicitLayoutStruct(const DeclContext *decl,
     auto varType = declDecl->getType();
     varType.removeLocalConst();
 
-    fieldTypes.push_back(
-        typeTranslator.translateType(varType, LayoutRule::GLSLStd140,
-                                     declDecl->hasAttr<HLSLRowMajorAttr>()));
+    fieldTypes.push_back(typeTranslator.translateType(
+        varType, layoutRule, declDecl->hasAttr<HLSLRowMajorAttr>()));
     fieldNames.push_back(declDecl->getName());
+
+    // tbuffer/TextureBuffers are non-writable SSBOs. OpMemberDecorate
+    // NonWritable must be applied to all fields.
+    if (!isCBuffer) {
+      decorations.push_back(Decoration::getNonWritable(
+          *theBuilder.getSPIRVContext(), fieldIndex));
+    }
+    ++fieldIndex;
   }
 
-  // Get the type for the whole buffer
-  // cbuffers are translated into OpTypeStruct with Block decoration. They
-  // should follow standard uniform buffer layout according to the Vulkan spec.
-  // GLSL std140 rules satisfies.
-  auto decorations =
-      typeTranslator.getLayoutDecorations(decl, LayoutRule::GLSLStd140);
-  decorations.push_back(Decoration::getBlock(*theBuilder.getSPIRVContext()));
   const uint32_t structType =
       theBuilder.getStructType(fieldTypes, typeName, fieldNames, decorations);
 
@@ -335,8 +348,8 @@ DeclResultIdMapper::createVarOfExplicitLayoutStruct(const DeclContext *decl,
 uint32_t DeclResultIdMapper::createCTBuffer(const HLSLBufferDecl *decl) {
   const std::string structName = "type." + decl->getName().str();
   const std::string varName = "var." + decl->getName().str();
-  const uint32_t bufferVar =
-      createVarOfExplicitLayoutStruct(decl, structName, varName);
+  const uint32_t bufferVar = createVarOfExplicitLayoutStruct(
+      decl, structName, varName, decl->isCBuffer());
 
   // We still register all VarDecls seperately here. All the VarDecls are
   // mapped to the <result-id> of the buffer object, which means when querying
@@ -345,9 +358,10 @@ uint32_t DeclResultIdMapper::createCTBuffer(const HLSLBufferDecl *decl) {
   int index = 0;
   for (const auto *subDecl : decl->decls()) {
     const auto *varDecl = cast<VarDecl>(subDecl);
-    // TODO: std140 rules may not suit tbuffers.
     astDecls[varDecl] = {bufferVar, spv::StorageClass::Uniform,
-                         LayoutRule::GLSLStd140, index++};
+                         decl->isCBuffer() ? LayoutRule::GLSLStd140
+                                           : LayoutRule::GLSLStd430,
+                         index++};
   }
   resourceVars.emplace_back(
       bufferVar, ResourceVar::Category::Other, getResourceBinding(decl),
@@ -363,15 +377,15 @@ uint32_t DeclResultIdMapper::createCTBuffer(const VarDecl *decl) {
   const bool isCBuffer = context->isCBuffer();
 
   const std::string structName =
-      "type." + std::string(isCBuffer ? "ConstantBuffer." : "TextureBuffer") +
+      "type." + std::string(isCBuffer ? "ConstantBuffer." : "TextureBuffer.") +
       recordType->getDecl()->getName().str();
   const uint32_t bufferVar = createVarOfExplicitLayoutStruct(
-      recordType->getDecl(), structName, decl->getName());
+      recordType->getDecl(), structName, decl->getName(), isCBuffer);
 
   // We register the VarDecl here.
-  // TODO: std140 rules may not suit tbuffers.
   astDecls[decl] = {bufferVar, spv::StorageClass::Uniform,
-                    LayoutRule::GLSLStd140};
+                    isCBuffer ? LayoutRule::GLSLStd140
+                              : LayoutRule::GLSLStd430};
   resourceVars.emplace_back(
       bufferVar, ResourceVar::Category::Other, getResourceBinding(context),
       decl->getAttr<VKBindingAttr>(), decl->getAttr<VKCounterBindingAttr>());

+ 6 - 1
tools/clang/lib/SPIRV/DeclResultIdMapper.h

@@ -306,10 +306,15 @@ private:
   /// fields. The struct type will be named as typeName, and the variable
   /// will be named as varName.
   ///
+  /// This method should only be used for cbuffers/ContantBuffers and
+  /// tbuffers/TextureBuffers. isCBuffer must be set appropriately based on the
+  /// type of the buffer.
+  ///
   /// Panics if the DeclContext is neither HLSLBufferDecl or RecordDecl.
   uint32_t createVarOfExplicitLayoutStruct(const DeclContext *decl,
                                            llvm::StringRef typeName,
-                                           llvm::StringRef varName);
+                                           llvm::StringRef varName,
+                                           bool isCBuffer);
 
   /// Creates all the stage variables mapped from semantics on the given decl.
   /// Returns true on sucess.

+ 35 - 0
tools/clang/test/CodeGenSPIRV/op.tbuffer.access.hlsl

@@ -0,0 +1,35 @@
+// Run: %dxc -T vs_6_0 -E main
+
+struct S {
+    float  f;
+};
+
+tbuffer MyTbuffer : register(t0) {
+    float    a;
+    float2   b;
+    float3x4 c;
+    S        s;
+    float    t[4];
+};
+
+float main() : A {
+// CHECK:      [[a:%\d+]] = OpAccessChain %_ptr_Uniform_float %var_MyTbuffer %int_0
+// CHECK-NEXT: {{%\d+}} = OpLoad %float [[a]]
+
+// CHECK:      [[b:%\d+]] = OpAccessChain %_ptr_Uniform_v2float %var_MyTbuffer %int_1
+// CHECK-NEXT: [[b0:%\d+]] = OpAccessChain %_ptr_Uniform_float [[b]] %int_0
+// CHECK-NEXT: {{%\d+}} = OpLoad %float [[b0]]
+
+// CHECK:      [[c:%\d+]] = OpAccessChain %_ptr_Uniform_mat3v4float %var_MyTbuffer %int_2
+// CHECK-NEXT: [[c12:%\d+]] = OpAccessChain %_ptr_Uniform_float [[c]] %uint_1 %uint_2
+// CHECK-NEXT: {{%\d+}} = OpLoad %float [[c12]]
+
+// CHECK:      [[s:%\d+]] = OpAccessChain %_ptr_Uniform_S %var_MyTbuffer %int_3
+// CHECK-NEXT: [[s0:%\d+]] = OpAccessChain %_ptr_Uniform_float [[s]] %int_0
+// CHECK-NEXT: {{%\d+}} = OpLoad %float [[s0]]
+
+// CHECK:      [[t:%\d+]] = OpAccessChain %_ptr_Uniform__arr_float_uint_4 %var_MyTbuffer %int_4
+// CHECK-NEXT: [[t3:%\d+]] = OpAccessChain %_ptr_Uniform_float [[t]] %int_3
+// CHECK-NEXT: {{%\d+}} = OpLoad %float [[t3]]
+    return a + b.x + c[1][2] + s.f + t[3];
+}

+ 35 - 0
tools/clang/test/CodeGenSPIRV/op.texture-buffer.access.hlsl

@@ -0,0 +1,35 @@
+// Run: %dxc -T vs_6_0 -E main
+
+struct S {
+    float  f;
+};
+
+struct T {
+    float    a;
+    float2   b;
+    float3x4 c;
+    S        s;
+    float    t[4];
+};
+
+
+TextureBuffer<T> MyTB : register(t1);
+
+float main() : A {
+// CHECK:      [[a:%\d+]] = OpAccessChain %_ptr_Uniform_float %MyTB %int_0
+// CHECK-NEXT: {{%\d+}} = OpLoad %float [[a]]
+
+// 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:      [[c12:%\d+]] = OpAccessChain %_ptr_Uniform_float %MyTB %int_2 %uint_1 %uint_2
+// 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:      [[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];
+}

+ 46 - 0
tools/clang/test/CodeGenSPIRV/type.tbuffer.hlsl

@@ -0,0 +1,46 @@
+// Run: %dxc -T vs_6_0 -E main
+
+// CHECK:      OpName %type_MyTbuffer "type.MyTbuffer"
+// CHECK-NEXT: OpMemberName %type_MyTbuffer 0 "a"
+// CHECK-NEXT: OpMemberName %type_MyTbuffer 1 "b"
+// CHECK-NEXT: OpMemberName %type_MyTbuffer 2 "c"
+// CHECK-NEXT: OpMemberName %type_MyTbuffer 3 "d"
+// CHECK-NEXT: OpMemberName %type_MyTbuffer 4 "s"
+// CHECK-NEXT: OpMemberName %type_MyTbuffer 5 "t"
+
+// CHECK:      OpName %var_MyTbuffer "var.MyTbuffer"
+
+// CHECK:      OpName %type_AnotherTbuffer "type.AnotherTbuffer"
+// CHECK-NEXT: OpMemberName %type_AnotherTbuffer 0 "m"
+// CHECK-NEXT: OpMemberName %type_AnotherTbuffer 1 "n"
+
+// CHECK:      OpName %var_AnotherTbuffer "var.AnotherTbuffer"
+
+struct S {
+    float  f1;
+    float3 f2;
+};
+
+tbuffer MyTbuffer : register(t1) {
+    bool     a;
+    int      b;
+    uint2    c;
+    float3x4 d;
+    S        s;
+    float    t[4];
+};
+
+tbuffer AnotherTbuffer : register(t2) {
+    float3 m;
+    float4 n;
+}
+
+// CHECK: %type_MyTbuffer = OpTypeStruct %bool %int %v2uint %mat3v4float %S %_arr_float_uint_4
+
+// CHECK: %type_AnotherTbuffer = OpTypeStruct %v3float %v4float
+
+// CHECK: %var_MyTbuffer = OpVariable %_ptr_Uniform_type_MyTbuffer Uniform
+// CHECK: %var_AnotherTbuffer = OpVariable %_ptr_Uniform_type_AnotherTbuffer Uniform
+
+void main() {
+}

+ 37 - 0
tools/clang/test/CodeGenSPIRV/type.texture-buffer.hlsl

@@ -0,0 +1,37 @@
+// Run: %dxc -T vs_6_0 -E main
+
+// CHECK:      OpName %type_TextureBuffer_T "type.TextureBuffer.T"
+// CHECK-NEXT: OpMemberName %type_TextureBuffer_T 0 "a"
+// CHECK-NEXT: OpMemberName %type_TextureBuffer_T 1 "b"
+// CHECK-NEXT: OpMemberName %type_TextureBuffer_T 2 "c"
+// CHECK-NEXT: OpMemberName %type_TextureBuffer_T 3 "d"
+// CHECK-NEXT: OpMemberName %type_TextureBuffer_T 4 "s"
+// CHECK-NEXT: OpMemberName %type_TextureBuffer_T 5 "t"
+
+// CHECK:      OpName %MyTbuffer "MyTbuffer"
+// CHECK:      OpName %AnotherTBuffer "AnotherTBuffer"
+
+struct S {
+  float  f1;
+  float3 f2;
+};
+
+// CHECK: %type_TextureBuffer_T = OpTypeStruct %bool %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;
+  uint2    c;
+  float3x4 d;
+  S        s;
+  float    t[4];
+};
+
+// 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);
+
+void main() {
+}

+ 48 - 0
tools/clang/test/CodeGenSPIRV/vk.layout.tbuffer.std430.hlsl

@@ -0,0 +1,48 @@
+// Run: %dxc -T vs_6_0 -E main
+
+// CHECK: OpDecorate %_arr_float_uint_2 ArrayStride 4
+// CHECK: OpDecorate %_arr_v3float_uint_2 ArrayStride 16
+// CHECK: OpDecorate %_arr_mat2v3float_uint_2 ArrayStride 32
+// CHECK: OpDecorate %_arr_mat2v3float_uint_2_0 ArrayStride 24
+
+// CHECK: OpMemberDecorate %S 0 Offset 0
+// CHECK: OpMemberDecorate %S 1 Offset 16
+// CHECK: OpMemberDecorate %S 2 Offset 48
+// CHECK: OpMemberDecorate %S 2 MatrixStride 16
+// CHECK: OpMemberDecorate %S 2 ColMajor
+// CHECK: OpMemberDecorate %S 3 Offset 112
+// CHECK: OpMemberDecorate %S 3 MatrixStride 8
+// CHECK: OpMemberDecorate %S 3 RowMajor
+// CHECK: OpMemberDecorate %S 4 Offset 160
+// CHECK: OpMemberDecorate %S 4 MatrixStride 8
+// CHECK: OpMemberDecorate %S 4 RowMajor
+// CHECK: OpMemberDecorate %S 5 Offset 208
+
+// CHECK: OpDecorate %_arr_S_uint_2 ArrayStride 224
+
+// CHECK: OpMemberDecorate %type_myTbuffer 0 Offset 0
+// CHECK: OpMemberDecorate %type_myTbuffer 1 Offset 448
+
+// CHECK: OpDecorate %type_myTbuffer BufferBlock
+
+// CHECK: OpMemberDecorate %type_myTbuffer 0 NonWritable
+// CHECK: OpMemberDecorate %type_myTbuffer 1 NonWritable
+
+struct S {
+                 float    a[2];
+                 float3   b[2];
+    row_major    float2x3 c[2];
+    column_major float2x3 d[2];
+                 float2x3 e[2];
+                 int      f;
+};
+
+tbuffer myTbuffer : register(t0)
+{
+  S s[2];
+  uint t;
+};
+
+float main() : A {
+  return 1.0;
+}

+ 49 - 0
tools/clang/test/CodeGenSPIRV/vk.layout.texture-buffer.std430.hlsl

@@ -0,0 +1,49 @@
+// Run: %dxc -T vs_6_0 -E main
+
+// CHECK: OpDecorate %_arr_float_uint_2 ArrayStride 4
+// CHECK: OpDecorate %_arr_v3float_uint_2 ArrayStride 16
+// CHECK: OpDecorate %_arr_mat2v3float_uint_2 ArrayStride 32
+// CHECK: OpDecorate %_arr_mat2v3float_uint_2_0 ArrayStride 24
+
+// CHECK: OpMemberDecorate %S 0 Offset 0
+// CHECK: OpMemberDecorate %S 1 Offset 16
+// CHECK: OpMemberDecorate %S 2 Offset 48
+// CHECK: OpMemberDecorate %S 2 MatrixStride 16
+// CHECK: OpMemberDecorate %S 2 ColMajor
+// CHECK: OpMemberDecorate %S 3 Offset 112
+// CHECK: OpMemberDecorate %S 3 MatrixStride 8
+// CHECK: OpMemberDecorate %S 3 RowMajor
+// CHECK: OpMemberDecorate %S 4 Offset 160
+// CHECK: OpMemberDecorate %S 4 MatrixStride 8
+// CHECK: OpMemberDecorate %S 4 RowMajor
+// CHECK: OpMemberDecorate %S 5 Offset 208
+
+// CHECK: OpDecorate %_arr_S_uint_2 ArrayStride 224
+
+// CHECK: OpMemberDecorate %type_TextureBuffer_T 0 Offset 0
+// CHECK: OpMemberDecorate %type_TextureBuffer_T 1 Offset 448
+
+// CHECK: OpDecorate %type_TextureBuffer_T BufferBlock
+
+// CHECK: OpMemberDecorate %type_TextureBuffer_T 0 NonWritable
+// CHECK: OpMemberDecorate %type_TextureBuffer_T 1 NonWritable
+
+struct S {
+                 float    a[2];
+                 float3   b[2];
+    row_major    float2x3 c[2];
+    column_major float2x3 d[2];
+                 float2x3 e[2];
+                 int      f;
+};
+
+struct T {
+    S    s[2];
+    uint t;
+};
+
+TextureBuffer<T> buffer2;
+
+float main() : A {
+    return 1.0;
+}

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

@@ -64,6 +64,8 @@ TEST_F(FileTest, CBufferType) { runFileTest("type.cbuffer.hlsl"); }
 TEST_F(FileTest, ConstantBufferType) {
   runFileTest("type.constant-buffer.hlsl");
 }
+TEST_F(FileTest, TBufferType) { runFileTest("type.tbuffer.hlsl"); }
+TEST_F(FileTest, TextureBufferType) { runFileTest("type.texture-buffer.hlsl"); }
 TEST_F(FileTest, StructuredBufferType) {
   runFileTest("type.structured-buffer.hlsl");
 }
@@ -238,6 +240,10 @@ TEST_F(FileTest, OpCBufferAccess) { runFileTest("op.cbuffer.access.hlsl"); }
 TEST_F(FileTest, OpConstantBufferAccess) {
   runFileTest("op.constant-buffer.access.hlsl");
 }
+TEST_F(FileTest, OpTBufferAccess) { runFileTest("op.tbuffer.access.hlsl"); }
+TEST_F(FileTest, OpTextureBufferAccess) {
+  runFileTest("op.texture-buffer.access.hlsl");
+}
 TEST_F(FileTest, OpStructuredBufferAccess) {
   runFileTest("op.structured-buffer.access.hlsl");
 }
@@ -862,6 +868,12 @@ TEST_F(FileTest, VulkanLayoutAppendSBufferStd430) {
 TEST_F(FileTest, VulkanLayoutConsumeSBufferStd430) {
   runFileTest("vk.layout.csbuffer.std430.hlsl");
 }
+TEST_F(FileTest, VulkanLayoutTBufferStd430) {
+  runFileTest("vk.layout.tbuffer.std430.hlsl");
+}
+TEST_F(FileTest, VulkanLayoutTextureBufferStd430) {
+  runFileTest("vk.layout.texture-buffer.std430.hlsl");
+}
 
 // HS: for different Patch Constant Functions
 TEST_F(FileTest, HullShaderPCFVoid) { runFileTest("hs.pcf.void.hlsl"); }