소스 검색

[spirv] Add support for :packoffset() (#1156)

Lei Zhang 7 년 전
부모
커밋
9b0167be56

+ 0 - 2
docs/SPIR-V.rst

@@ -2666,8 +2666,6 @@ either because of no Vulkan equivalents at the moment, or because of deprecation
   `Hull Entry Point Attributes`_ section.
 * ``cbuffer``/``tbuffer`` member initializer: no Vulkan equivalent. The compiler
   will emit an warning and ignore it.
-* ``:packoffset()``: Not supported right now. The compiler will emit an warning
-  and ignore it.
 
 Appendix
 ==========

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

@@ -1150,10 +1150,6 @@ void SPIRVEmitter::doHLSLBufferDecl(const HLSLBufferDecl *bufferDecl) {
                     init->getExprLoc())
             << bufferDecl->isCBuffer() << init->getSourceRange();
 
-      for (const auto *annotation : varMember->getUnusualAnnotations())
-        if (const auto *packing = dyn_cast<hlsl::ConstantPacking>(annotation))
-          emitWarning("packoffset ignored since not supported", packing->Loc);
-
       // We cannot handle external initialization of column-major matrices now.
       if (typeTranslator.isOrContainsNonFpColMajorMatrix(varMember->getType(),
                                                          varMember)) {

+ 29 - 2
tools/clang/lib/SPIRV/TypeTranslator.cpp

@@ -63,6 +63,15 @@ bool hasHLSLMatOrientation(QualType type, bool *pIsRowMajor) {
   return false;
 }
 
+/// Returns the :packoffset() annotation on the given decl. Returns nullptr if
+/// the decl does not have one.
+const hlsl::ConstantPacking *getPackOffset(const NamedDecl *decl) {
+  for (auto *annotation : decl->getUnusualAnnotations())
+    if (auto *packing = dyn_cast<hlsl::ConstantPacking>(annotation))
+      return packing;
+  return nullptr;
+}
+
 } // anonymous namespace
 
 bool TypeTranslator::isRelaxedPrecisionType(QualType type,
@@ -1139,13 +1148,31 @@ TypeTranslator::getLayoutDecorations(const DeclContext *decl, LayoutRule rule,
     std::tie(memberAlignment, memberSize) =
         getAlignmentAndSize(fieldType, rule, &stride);
 
+    // The next avaiable location after layouting the previos members
+    const uint32_t nextLoc = offset;
+
     alignUsingHLSLRelaxedLayout(fieldType, memberSize, &memberAlignment,
                                 &offset);
 
-    // Each structure-type member must have an Offset Decoration.
-    if (const auto *offsetAttr = field->getAttr<VKOffsetAttr>())
+    // The vk::offset attribute takes precedence over all.
+    if (const auto *offsetAttr = field->getAttr<VKOffsetAttr>()) {
       offset = offsetAttr->getOffset();
+    }
+    // The :packoffset() annotation takes precedence over normal layout
+    // calculation.
+    else if (const auto *pack = getPackOffset(declDecl)) {
+      const uint32_t packOffset =
+          pack->Subcomponent * 16 + pack->ComponentOffset * 4;
+      // Do minimal check to make sure the offset specified by packoffset does
+      // not cause overlap.
+      if (packOffset < nextLoc) {
+        emitError("packoffset caused overlap with previous members", pack->Loc);
+      } else {
+        offset = packOffset;
+      }
+    }
 
+    // Each structure-type member must have an Offset Decoration.
     decorations.push_back(Decoration::getOffset(*spirvContext, offset, index));
     offset += memberSize;
 

+ 4 - 2
tools/clang/lib/SPIRV/TypeTranslator.h

@@ -254,10 +254,12 @@ public:
 private:
   /// \brief Wrapper method to create an error message and report it
   /// in the diagnostic engine associated with this consumer.
-  template <unsigned N> DiagnosticBuilder emitError(const char (&message)[N]) {
+  template <unsigned N>
+  DiagnosticBuilder emitError(const char (&message)[N],
+                              SourceLocation loc = {}) {
     const auto diagId =
         diags.getCustomDiagID(clang::DiagnosticsEngine::Error, message);
-    return diags.Report(diagId);
+    return diags.Report(loc, diagId);
   }
 
   /// \brief Returns true if the two types can be treated as the same scalar

+ 16 - 0
tools/clang/test/CodeGenSPIRV/vk.layout.cbuffer.packoffset.error.hlsl

@@ -0,0 +1,16 @@
+// Run: %dxc -T vs_6_0 -E main
+
+cbuffer MyCBuffer {
+    float4 data1;
+    float4 data2 : packoffset(c2);
+    float  data3 : packoffset(c0);    // error: overlap
+    float  data4 : packoffset(c10.z);
+    float  data5 : packoffset(c10.z); // error: overlap
+}
+
+float4 main() : A {
+    return data1;
+}
+
+// CHECK: :6:20: error: packoffset caused overlap with previous members
+// CHECK: :8:20: error: packoffset caused overlap with previous members

+ 23 - 8
tools/clang/test/CodeGenSPIRV/vk.layout.cbuffer.packoffset.hlsl

@@ -1,14 +1,29 @@
 // Run: %dxc -T vs_6_0 -E main
 
-cbuffer MyCBuffer {
-    float4 data1;
-    float4 data2 : packoffset(c1);
-    float  data3 : packoffset(c2.y);
+// CHECK: OpMemberDecorate %type_MyCBuffer 0 Offset 0
+// CHECK: OpMemberDecorate %type_MyCBuffer 1 Offset 32
+// CHECK: OpMemberDecorate %type_MyCBuffer 2 Offset 52
+// CHECK: OpMemberDecorate %type_MyCBuffer 3 Offset 56
+// CHECK: OpMemberDecorate %type_MyCBuffer 4 Offset 60
+// CHECK: OpMemberDecorate %type_MyCBuffer 5 Offset 1600
+// CHECK: OpMemberDecorate %type_MyCBuffer 6 Offset 1760
+// CHECK: OpMemberDecorate %type_MyCBuffer 7 Offset 2400
+
+struct S {
+    float4 f;
+};
+
+cbuffer MyCBuffer {                    // Offset
+    float4   data1;                    //                  0
+    float4   data2 : packoffset(c2);   // 2 * 16         = 32
+    float    data3 : packoffset(c3.y); // 3 * 16 + 1 * 4 = 52
+    float    data4 : packoffset(c3.z); // 3 * 16 + 2 * 4 = 56
+    float    data5;                    //                  60
+    float4   data6 : packoffset(c100); // 100 * 16       = 1600
+    float2x3 data7 : packoffset(c110); // 110 * 16       = 1760
+    S        data8 : packoffset(c150); // 150 * 16       = 2400
 }
 
 float4 main() : A {
-    return data1 + data2 + data3;
+    return data1;
 }
-
-// CHECK: :5:20: warning: packoffset ignored since not supported
-// CHECK: :6:20: warning: packoffset ignored since not supported

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

@@ -1352,7 +1352,10 @@ TEST_F(FileTest, VulkanLayoutPushConstantStd430) {
 }
 
 TEST_F(FileTest, VulkanLayoutCBufferPackOffset) {
-  runFileTest("vk.layout.cbuffer.packoffset.hlsl", Expect::Warning);
+  runFileTest("vk.layout.cbuffer.packoffset.hlsl");
+}
+TEST_F(FileTest, VulkanLayoutCBufferPackOffsetError) {
+  runFileTest("vk.layout.cbuffer.packoffset.error.hlsl", Expect::Failure);
 }
 
 TEST_F(FileTest, VulkanSubpassInput) { runFileTest("vk.subpass-input.hlsl"); }