Pārlūkot izejas kodu

[spirv] Add support for VK_EXT_scalar_block_layout (#1716)

Ref: https://github.com/KhronosGroup/Vulkan-Docs/issues/854
Lei Zhang 6 gadi atpakaļ
vecāks
revīzija
7a2a95e36c

+ 22 - 14
docs/SPIR-V.rst

@@ -803,7 +803,7 @@ To know more about the Vulkan buffer types, please refer to the Vulkan spec
 Memory layout rules
 Memory layout rules
 ~~~~~~~~~~~~~~~~~~~
 ~~~~~~~~~~~~~~~~~~~
 
 
-SPIR-V CodeGen supports three sets of memory layout rules for buffer resources
+SPIR-V CodeGen supports four sets of memory layout rules for buffer resources
 right now:
 right now:
 
 
 1. Vector-relaxed OpenGL ``std140`` for uniform buffers and vector-relaxed
 1. Vector-relaxed OpenGL ``std140`` for uniform buffers and vector-relaxed
@@ -817,6 +817,14 @@ right now:
 3. Strict OpenGL ``std140`` for uniform buffers and strict OpenGL ``std430``
 3. Strict OpenGL ``std140`` for uniform buffers and strict OpenGL ``std430``
    for storage buffers: they allow packing data on the application side that
    for storage buffers: they allow packing data on the application side that
    can be shared with OpenGL. They can be enabled by ``-fvk-use-gl-layout``.
    can be shared with OpenGL. They can be enabled by ``-fvk-use-gl-layout``.
+4. Scalar layout rules introduced via `VK_EXT_scalar_block_layout`, which
+   basically aligns all aggregrate types according to their elements'
+   natural alignment. They can be enabled by ``-fvk-use-scalar-layout``.
+
+To use scalar layout, the application side need to request
+``VK_EXT_scalar_block_layout``. This is also true for using DirectX memory
+layout since there is no dedicated DirectX layout extension for Vulkan
+(at least for now). So we must request something more permissive.
 
 
 In the above, "vector-relaxed OpenGL ``std140``/``std430``" rules mean OpenGL
 In the above, "vector-relaxed OpenGL ``std140``/``std430``" rules mean OpenGL
 ``std140``/``std430`` rules with the following modification for vector type
 ``std140``/``std430`` rules with the following modification for vector type
@@ -846,19 +854,19 @@ As an exmaple, for the following HLSL definition:
 
 
 We will have the following offsets for each member:
 We will have the following offsets for each member:
 
 
-============== ====== ====== ====== ====== ====== ======
-     HLSL         Uniform Buffer      Storage Buffer
--------------- -------------------- --------------------
-    Member     1 (VK) 2 (DX) 3 (GL) 1 (VK) 2 (DX) 3 (GL)
-============== ====== ====== ====== ====== ====== ======
-``a_float``      0      0      0      0      0     0
-``b_float3``     4      4      16     4      4     16
-``c_S_float3``   16     16     32     16     16    32
-``d_float2x3``   32     32     48     32     28    48
-``e_float2x3``   80     80     96     64     52    80
-``f_int_3``      112    112    128    96     76    112
-``g_float2_2``   160    160    176    112    88    128
-============== ====== ====== ====== ====== ====== ======
+============== ====== ====== ====== ========== ====== ====== ====== ==========
+     HLSL             Uniform Buffer                Storage Buffer
+-------------- ------------------------------- -------------------------------
+    Member     1 (VK) 2 (DX) 3 (GL) 4 (Scalar) 1 (VK) 2 (DX) 3 (GL) 4 (Scalar)
+============== ====== ====== ====== ========== ====== ====== ====== ==========
+``a_float``      0      0      0        0        0      0     0        0
+``b_float3``     4      4      16       4        4      4     16       4
+``c_S_float3``   16     16     32       16       16     16    32       16
+``d_float2x3``   32     32     48       28       32     28    48       28
+``e_float2x3``   80     80     96       52       64     52    80       52
+``f_int_3``      112    112    128      76       96     76    112      76
+``g_float2_2``   160    160    176      88       112    88    128      88
+============== ====== ====== ====== ========== ====== ====== ====== ==========
 
 
 If the above layout rules do not satisfy your needs and you want to manually
 If the above layout rules do not satisfy your needs and you want to manually
 control the layout of struct members, you can use either
 control the layout of struct members, you can use either

+ 2 - 0
include/dxc/Support/HLSLOptions.td

@@ -263,6 +263,8 @@ def fvk_use_gl_layout: Flag<["-"], "fvk-use-gl-layout">, Group<spirv_Group>, Fla
   HelpText<"Use strict OpenGL std140/std430 memory layout for Vulkan resources">;
   HelpText<"Use strict OpenGL std140/std430 memory layout for Vulkan resources">;
 def fvk_use_dx_layout: Flag<["-"], "fvk-use-dx-layout">, Group<spirv_Group>, Flags<[CoreOption, DriverOption]>,
 def fvk_use_dx_layout: Flag<["-"], "fvk-use-dx-layout">, Group<spirv_Group>, Flags<[CoreOption, DriverOption]>,
   HelpText<"Use DirectX memory layout for Vulkan resources">;
   HelpText<"Use DirectX memory layout for Vulkan resources">;
+def fvk_use_scalar_layout: Flag<["-"], "fvk-use-scalar-layout">, Group<spirv_Group>, Flags<[CoreOption, DriverOption]>,
+  HelpText<"Use scalar memory layout for Vulkan resources">;
 def fspv_reflect: Flag<["-"], "fspv-reflect">, Group<spirv_Group>, Flags<[CoreOption, DriverOption]>,
 def fspv_reflect: Flag<["-"], "fspv-reflect">, Group<spirv_Group>, Flags<[CoreOption, DriverOption]>,
   HelpText<"Emit additional SPIR-V instructions to aid reflection">;
   HelpText<"Emit additional SPIR-V instructions to aid reflection">;
 def fspv_debug_EQ : Joined<["-"], "fspv-debug=">, Group<spirv_Group>, Flags<[CoreOption, DriverOption]>,
 def fspv_debug_EQ : Joined<["-"], "fspv-debug=">, Group<spirv_Group>, Flags<[CoreOption, DriverOption]>,

+ 2 - 0
include/dxc/Support/SPIRVOptions.h

@@ -31,6 +31,7 @@ enum class SpirvLayoutRule {
   RelaxedGLSLStd430, // std430 with relaxed vector layout
   RelaxedGLSLStd430, // std430 with relaxed vector layout
   FxcCTBuffer,       // fxc.exe layout rule for cbuffer/tbuffer
   FxcCTBuffer,       // fxc.exe layout rule for cbuffer/tbuffer
   FxcSBuffer,        // fxc.exe layout rule for structured buffers
   FxcSBuffer,        // fxc.exe layout rule for structured buffers
+  Scalar,            // VK_EXT_scalar_block_layout
 };
 };
 
 
 struct SpirvCodeGenOptions {
 struct SpirvCodeGenOptions {
@@ -50,6 +51,7 @@ struct SpirvCodeGenOptions {
   bool noWarnIgnoredFeatures;
   bool noWarnIgnoredFeatures;
   bool useDxLayout;
   bool useDxLayout;
   bool useGlLayout;
   bool useGlLayout;
+  bool useScalarLayout;
   SpirvLayoutRule cBufferLayoutRule;
   SpirvLayoutRule cBufferLayoutRule;
   SpirvLayoutRule sBufferLayoutRule;
   SpirvLayoutRule sBufferLayoutRule;
   SpirvLayoutRule tBufferLayoutRule;
   SpirvLayoutRule tBufferLayoutRule;

+ 2 - 0
lib/DxcSupport/HLSLOptions.cpp

@@ -636,6 +636,7 @@ int ReadDxcOpts(const OptTable *optionTable, unsigned flagsToInclude,
   opts.SpirvOptions.invertW = Args.hasFlag(OPT_fvk_use_dx_position_w, OPT_INVALID, false);
   opts.SpirvOptions.invertW = Args.hasFlag(OPT_fvk_use_dx_position_w, OPT_INVALID, false);
   opts.SpirvOptions.useGlLayout = Args.hasFlag(OPT_fvk_use_gl_layout, OPT_INVALID, false);
   opts.SpirvOptions.useGlLayout = Args.hasFlag(OPT_fvk_use_gl_layout, OPT_INVALID, false);
   opts.SpirvOptions.useDxLayout = Args.hasFlag(OPT_fvk_use_dx_layout, OPT_INVALID, false);
   opts.SpirvOptions.useDxLayout = Args.hasFlag(OPT_fvk_use_dx_layout, OPT_INVALID, false);
+  opts.SpirvOptions.useScalarLayout = Args.hasFlag(OPT_fvk_use_scalar_layout, OPT_INVALID, false);
   opts.SpirvOptions.enableReflect = Args.hasFlag(OPT_fspv_reflect, OPT_INVALID, false);
   opts.SpirvOptions.enableReflect = Args.hasFlag(OPT_fspv_reflect, OPT_INVALID, false);
   opts.SpirvOptions.noWarnIgnoredFeatures = Args.hasFlag(OPT_Wno_vk_ignored_features, OPT_INVALID, false);
   opts.SpirvOptions.noWarnIgnoredFeatures = Args.hasFlag(OPT_Wno_vk_ignored_features, OPT_INVALID, false);
   opts.SpirvOptions.noWarnEmulatedFeatures = Args.hasFlag(OPT_Wno_vk_emulated_features, OPT_INVALID, false);
   opts.SpirvOptions.noWarnEmulatedFeatures = Args.hasFlag(OPT_Wno_vk_emulated_features, OPT_INVALID, false);
@@ -711,6 +712,7 @@ int ReadDxcOpts(const OptTable *optionTable, unsigned flagsToInclude,
       Args.hasFlag(OPT_fvk_use_dx_position_w, OPT_INVALID, false) ||
       Args.hasFlag(OPT_fvk_use_dx_position_w, OPT_INVALID, false) ||
       Args.hasFlag(OPT_fvk_use_gl_layout, OPT_INVALID, false) ||
       Args.hasFlag(OPT_fvk_use_gl_layout, OPT_INVALID, false) ||
       Args.hasFlag(OPT_fvk_use_dx_layout, OPT_INVALID, false) ||
       Args.hasFlag(OPT_fvk_use_dx_layout, OPT_INVALID, false) ||
+      Args.hasFlag(OPT_fvk_use_scalar_layout, OPT_INVALID, false) ||
       Args.hasFlag(OPT_fspv_reflect, OPT_INVALID, false) ||
       Args.hasFlag(OPT_fspv_reflect, OPT_INVALID, false) ||
       Args.hasFlag(OPT_Wno_vk_ignored_features, OPT_INVALID, false) ||
       Args.hasFlag(OPT_Wno_vk_ignored_features, OPT_INVALID, false) ||
       Args.hasFlag(OPT_Wno_vk_emulated_features, OPT_INVALID, false) ||
       Args.hasFlag(OPT_Wno_vk_emulated_features, OPT_INVALID, false) ||

+ 17 - 8
tools/clang/lib/SPIRV/SPIRVEmitter.cpp

@@ -242,9 +242,9 @@ bool spirvToolsOptimize(spv_target_env env, std::vector<uint32_t> *module,
   return optimizer.Run(module->data(), module->size(), module, options);
   return optimizer.Run(module->data(), module->size(), module, options);
 }
 }
 
 
-bool spirvToolsValidate(spv_target_env env, std::vector<uint32_t> *module,
-                        std::string *messages, bool relaxLogicalPointer,
-                        bool glLayout, bool dxLayout) {
+bool spirvToolsValidate(spv_target_env env, const SpirvCodeGenOptions &opts,
+                        bool relaxLogicalPointer, std::vector<uint32_t> *module,
+                        std::string *messages) {
   spvtools::SpirvTools tools(env);
   spvtools::SpirvTools tools(env);
 
 
   tools.SetMessageConsumer(
   tools.SetMessageConsumer(
@@ -257,8 +257,13 @@ bool spirvToolsValidate(spv_target_env env, std::vector<uint32_t> *module,
   // GL: strict block layout rules
   // GL: strict block layout rules
   // VK: relaxed block layout rules
   // VK: relaxed block layout rules
   // DX: Skip block layout rules
   // DX: Skip block layout rules
-  options.SetRelaxBlockLayout(!glLayout && !dxLayout);
-  options.SetSkipBlockLayout(dxLayout);
+  if (opts.useScalarLayout || opts.useDxLayout) {
+    options.SetSkipBlockLayout(true);
+  } else if (opts.useGlLayout) {
+    // spirv-val by default checks this.
+  } else {
+    options.SetRelaxBlockLayout(true);
+  }
 
 
   return tools.Validate(module->data(), module->size(), options);
   return tools.Validate(module->data(), module->size(), options);
 }
 }
@@ -625,6 +630,10 @@ SPIRVEmitter::SPIRVEmitter(CompilerInstance &ci)
     spirvOptions.cBufferLayoutRule = SpirvLayoutRule::GLSLStd140;
     spirvOptions.cBufferLayoutRule = SpirvLayoutRule::GLSLStd140;
     spirvOptions.tBufferLayoutRule = SpirvLayoutRule::GLSLStd430;
     spirvOptions.tBufferLayoutRule = SpirvLayoutRule::GLSLStd430;
     spirvOptions.sBufferLayoutRule = SpirvLayoutRule::GLSLStd430;
     spirvOptions.sBufferLayoutRule = SpirvLayoutRule::GLSLStd430;
+  } else if (spirvOptions.useScalarLayout) {
+    spirvOptions.cBufferLayoutRule = SpirvLayoutRule::Scalar;
+    spirvOptions.tBufferLayoutRule = SpirvLayoutRule::Scalar;
+    spirvOptions.sBufferLayoutRule = SpirvLayoutRule::Scalar;
   } else {
   } else {
     spirvOptions.cBufferLayoutRule = SpirvLayoutRule::RelaxedGLSLStd140;
     spirvOptions.cBufferLayoutRule = SpirvLayoutRule::RelaxedGLSLStd140;
     spirvOptions.tBufferLayoutRule = SpirvLayoutRule::RelaxedGLSLStd430;
     spirvOptions.tBufferLayoutRule = SpirvLayoutRule::RelaxedGLSLStd430;
@@ -736,9 +745,9 @@ void SPIRVEmitter::HandleTranslationUnit(ASTContext &context) {
   // Validate the generated SPIR-V code
   // Validate the generated SPIR-V code
   if (!spirvOptions.disableValidation) {
   if (!spirvOptions.disableValidation) {
     std::string messages;
     std::string messages;
-    if (!spirvToolsValidate(
-            targetEnv, &m, &messages, declIdMapper.requiresLegalization(),
-            spirvOptions.useGlLayout, spirvOptions.useDxLayout)) {
+    if (!spirvToolsValidate(targetEnv, spirvOptions,
+                            declIdMapper.requiresLegalization(), &m,
+                            &messages)) {
       emitFatalError("generated SPIR-V is invalid: %0", {}) << messages;
       emitFatalError("generated SPIR-V is invalid: %0", {}) << messages;
       emitNote("please file a bug report on "
       emitNote("please file a bug report on "
                "https://github.com/Microsoft/DirectXShaderCompiler/issues "
                "https://github.com/Microsoft/DirectXShaderCompiler/issues "

+ 18 - 6
tools/clang/lib/SPIRV/TypeTranslator.cpp

@@ -1190,6 +1190,7 @@ llvm::SmallVector<const Decoration *, 4> TypeTranslator::getLayoutDecorations(
       offset = roundToPow2(offset, memberAlignment);
       offset = roundToPow2(offset, memberAlignment);
     }
     }
 
 
+
     // The vk::offset attribute takes precedence over all.
     // The vk::offset attribute takes precedence over all.
     if (const auto *offsetAttr = decl->getAttr<VKOffsetAttr>()) {
     if (const auto *offsetAttr = decl->getAttr<VKOffsetAttr>()) {
       offset = offsetAttr->getOffset();
       offset = offsetAttr->getOffset();
@@ -1713,9 +1714,10 @@ TypeTranslator::getAlignmentAndSize(QualType type, SpirvLayoutRule rule,
     if (isVectorType(type, &elemType, &elemCount)) {
     if (isVectorType(type, &elemType, &elemCount)) {
       uint32_t alignment = 0, size = 0;
       uint32_t alignment = 0, size = 0;
       std::tie(alignment, size) = getAlignmentAndSize(elemType, rule, stride);
       std::tie(alignment, size) = getAlignmentAndSize(elemType, rule, stride);
-      // Use element alignment for fxc rules
+      // Use element alignment for fxc rules and VK_EXT_scalar_block_layout
       if (rule != SpirvLayoutRule::FxcCTBuffer &&
       if (rule != SpirvLayoutRule::FxcCTBuffer &&
-          rule != SpirvLayoutRule::FxcSBuffer)
+          rule != SpirvLayoutRule::FxcSBuffer &&
+          rule != SpirvLayoutRule::Scalar)
         alignment = (elemCount == 3 ? 4 : elemCount) * size;
         alignment = (elemCount == 3 ? 4 : elemCount) * size;
 
 
       return {alignment, elemCount * size};
       return {alignment, elemCount * size};
@@ -1737,9 +1739,11 @@ TypeTranslator::getAlignmentAndSize(QualType type, SpirvLayoutRule rule,
 
 
       const uint32_t vecStorageSize = isRowMajor ? colCount : rowCount;
       const uint32_t vecStorageSize = isRowMajor ? colCount : rowCount;
 
 
-      if (rule == SpirvLayoutRule::FxcSBuffer) {
+      if (rule == SpirvLayoutRule::FxcSBuffer ||
+          rule == SpirvLayoutRule::Scalar) {
         *stride = vecStorageSize * size;
         *stride = vecStorageSize * size;
-        // Use element alignment for fxc structured buffers
+        // Use element alignment for fxc structured buffers and
+        // VK_EXT_scalar_block_layout
         return {alignment, rowCount * colCount * size};
         return {alignment, rowCount * colCount * size};
       }
       }
 
 
@@ -1794,6 +1798,12 @@ TypeTranslator::getAlignmentAndSize(QualType type, SpirvLayoutRule rule,
       structSize += memberSize;
       structSize += memberSize;
     }
     }
 
 
+    if (rule == SpirvLayoutRule::Scalar) {
+      // A structure has a scalar alignment equal to the largest scalar
+      // alignment of any of its members in VK_EXT_scalar_block_layout.
+      return {maxAlignment, structSize};
+    }
+
     if (rule == SpirvLayoutRule::GLSLStd140 ||
     if (rule == SpirvLayoutRule::GLSLStd140 ||
         rule == SpirvLayoutRule::RelaxedGLSLStd140 ||
         rule == SpirvLayoutRule::RelaxedGLSLStd140 ||
         rule == SpirvLayoutRule::FxcCTBuffer) {
         rule == SpirvLayoutRule::FxcCTBuffer) {
@@ -1817,9 +1827,11 @@ TypeTranslator::getAlignmentAndSize(QualType type, SpirvLayoutRule rule,
     std::tie(alignment, size) =
     std::tie(alignment, size) =
         getAlignmentAndSize(arrayType->getElementType(), rule, stride);
         getAlignmentAndSize(arrayType->getElementType(), rule, stride);
 
 
-    if (rule == SpirvLayoutRule::FxcSBuffer) {
+    if (rule == SpirvLayoutRule::FxcSBuffer ||
+        rule == SpirvLayoutRule::Scalar) {
       *stride = size;
       *stride = size;
-      // Use element alignment for fxc structured buffers
+      // Use element alignment for fxc structured buffers and
+      // VK_EXT_scalar_block_layout
       return {alignment, size * elemCount};
       return {alignment, size * elemCount};
     }
     }
 
 

+ 79 - 0
tools/clang/test/CodeGenSPIRV/vk.layout.cbuffer.scalar.hlsl

@@ -0,0 +1,79 @@
+// Run: %dxc -T vs_6_2 -E main -fvk-use-scalar-layout -enable-16bit-types
+
+struct R {     // Alignment       Offset     Size       Next
+    double rf; // 8            -> 0        + 8        = 8
+};             // 8                                     8
+
+struct S {      // Alignment    Offset                                Size        Next
+    R      sf1; // 8         -> 0                                   + 8         = 8
+    float  sf2; // 4         -> 8                                   + 4         = 12
+    float3 sf3; // 4         -> 12                                  + 4 * 3     = 24
+    float  sf4; // 4         -> 24                                  + 4         = 28
+};              // 8(max)                                                         28
+
+struct T {                     // Alignment     Offset                               Size              = Next
+              int      tf1;    // 4          -> 0                                  + 4                 = 4
+              R        tf2[3]; // 8          -> 8 (4 round up to R alignment)      + 3 * stride(8)     = 32
+              float3x2 tf3;    // 4          -> 32                                 + 4 * 3 * 2         = 56
+              S        tf4;    // 8          -> 56                                 + 28                = 84
+             float16_t tf5;    // 2          -> 84                                 + 2                 = 86
+              float    tf6;    // 4          -> 88 (86 round up to float align)    + 4                 = 92
+};                             // 8(max)                                                                 92
+
+cbuffer MyCBuffer {              // Alignment   Offset                                 Size                     Next
+                 bool     a;     // 4        -> 0                                    +     4                  = 4
+                 uint1    b;     // 4        -> 4                                    +     4                  = 8
+                 float3   c;     // 4        -> 8                                    + 3 * 4                  = 20
+    row_major    float2x3 d;     // 4        -> 20                                   + 4 * 2 * 3              = 44
+    column_major float2x3 e;     // 4        -> 44                                   + 4 * 2 * 3              = 68
+                 float2x1 f;     // 4        -> 68                                   + 4 * 2                  = 76
+    row_major    float2x3 g[3];  // 4        -> 76                                   + 4 * 2 * 3 * 3          = 148
+    column_major float2x2 h[4];  // 4        -> 148                                  + 4 * 2 * 2 * 4          = 212
+                 T        t;     // 8        -> 216 (212 round up to T    alignment) + 92                     = 308
+                 float    z;     // 4        -> 308
+};
+
+// CHECK:      OpDecorate %_arr_mat2v3float_uint_3 ArrayStride 24
+// CHECK:      OpDecorate %_arr_mat2v2float_uint_4 ArrayStride 16
+
+// CHECK:      OpMemberDecorate %R 0 Offset 0
+
+// CHECK:      OpDecorate %_arr_R_uint_3 ArrayStride 8
+
+// CHECK:      OpMemberDecorate %S 0 Offset 0
+// CHECK-NEXT: OpMemberDecorate %S 1 Offset 8
+// CHECK-NEXT: OpMemberDecorate %S 2 Offset 12
+// CHECK-NEXT: OpMemberDecorate %S 3 Offset 24
+
+// CHECK:      OpMemberDecorate %T 0 Offset 0
+// CHECK-NEXT: OpMemberDecorate %T 1 Offset 8
+// CHECK-NEXT: OpMemberDecorate %T 2 Offset 32
+// CHECK-NEXT: OpMemberDecorate %T 2 MatrixStride 12
+// CHECK-NEXT: OpMemberDecorate %T 2 RowMajor
+// CHECK-NEXT: OpMemberDecorate %T 3 Offset 56
+// CHECK-NEXT: OpMemberDecorate %T 4 Offset 84
+// CHECK-NEXT: OpMemberDecorate %T 5 Offset 88
+
+// CHECK:      OpMemberDecorate %type_MyCBuffer 0 Offset 0
+// CHECK-NEXT: OpMemberDecorate %type_MyCBuffer 1 Offset 4
+// CHECK-NEXT: OpMemberDecorate %type_MyCBuffer 2 Offset 8
+// CHECK-NEXT: OpMemberDecorate %type_MyCBuffer 3 Offset 20
+// CHECK-NEXT: OpMemberDecorate %type_MyCBuffer 3 MatrixStride 12
+// CHECK-NEXT: OpMemberDecorate %type_MyCBuffer 3 ColMajor
+// CHECK-NEXT: OpMemberDecorate %type_MyCBuffer 4 Offset 44
+// CHECK-NEXT: OpMemberDecorate %type_MyCBuffer 4 MatrixStride 8
+// CHECK-NEXT: OpMemberDecorate %type_MyCBuffer 4 RowMajor
+// CHECK-NEXT: OpMemberDecorate %type_MyCBuffer 5 Offset 68
+// CHECK-NEXT: OpMemberDecorate %type_MyCBuffer 6 Offset 76
+// CHECK-NEXT: OpMemberDecorate %type_MyCBuffer 6 MatrixStride 12
+// CHECK-NEXT: OpMemberDecorate %type_MyCBuffer 6 ColMajor
+// CHECK-NEXT: OpMemberDecorate %type_MyCBuffer 7 Offset 148
+// CHECK-NEXT: OpMemberDecorate %type_MyCBuffer 7 MatrixStride 8
+// CHECK-NEXT: OpMemberDecorate %type_MyCBuffer 7 RowMajor
+// CHECK-NEXT: OpMemberDecorate %type_MyCBuffer 8 Offset 216
+// CHECK-NEXT: OpMemberDecorate %type_MyCBuffer 9 Offset 308
+// CHECK-NEXT: OpDecorate %type_MyCBuffer Block
+
+float main() : A {
+    return 1.0;
+}

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

@@ -1623,6 +1623,12 @@ TEST_F(FileTest, VulkanLayoutFxcRulesCBuffer1) {
   runFileTest("vk.layout.cbuffer.fxc.1.hlsl");
   runFileTest("vk.layout.cbuffer.fxc.1.hlsl");
 }
 }
 
 
+TEST_F(FileTest, VulkanLayoutCBufferScalar) {
+  // VK_EXT_scalar_block_layout
+  setScalarLayout();
+  runFileTest("vk.layout.cbuffer.scalar.hlsl");
+}
+
 TEST_F(FileTest, VulkanSubpassInput) { runFileTest("vk.subpass-input.hlsl"); }
 TEST_F(FileTest, VulkanSubpassInput) { runFileTest("vk.subpass-input.hlsl"); }
 TEST_F(FileTest, VulkanSubpassInputBinding) {
 TEST_F(FileTest, VulkanSubpassInputBinding) {
   runFileTest("vk.subpass-input.binding.hlsl");
   runFileTest("vk.subpass-input.binding.hlsl");

+ 9 - 7
tools/clang/unittests/SPIRV/FileTestFixture.cpp

@@ -102,8 +102,9 @@ void FileTest::runFileTest(llvm::StringRef filename, Expect expect,
     ASSERT_EQ(result.status(), effcee::Result::Status::Ok);
     ASSERT_EQ(result.status(), effcee::Result::Status::Ok);
 
 
     if (runValidation)
     if (runValidation)
-      EXPECT_TRUE(utils::validateSpirvBinary(
-          targetEnv, generatedBinary, relaxLogicalPointer, glLayout, dxLayout));
+      EXPECT_TRUE(utils::validateSpirvBinary(targetEnv, generatedBinary,
+                                             relaxLogicalPointer, glLayout,
+                                             dxLayout, scalarLayout));
   } else if (expect == Expect::Warning) {
   } else if (expect == Expect::Warning) {
     ASSERT_TRUE(compileOk);
     ASSERT_TRUE(compileOk);
 
 
@@ -127,8 +128,9 @@ void FileTest::runFileTest(llvm::StringRef filename, Expect expect,
     ASSERT_EQ(result.status(), effcee::Result::Status::Ok);
     ASSERT_EQ(result.status(), effcee::Result::Status::Ok);
 
 
     if (runValidation)
     if (runValidation)
-      EXPECT_TRUE(utils::validateSpirvBinary(
-          targetEnv, generatedBinary, relaxLogicalPointer, glLayout, dxLayout));
+      EXPECT_TRUE(utils::validateSpirvBinary(targetEnv, generatedBinary,
+                                             relaxLogicalPointer, glLayout,
+                                             dxLayout, scalarLayout));
   } else if (expect == Expect::Failure) {
   } else if (expect == Expect::Failure) {
     ASSERT_FALSE(compileOk);
     ASSERT_FALSE(compileOk);
 
 
@@ -154,9 +156,9 @@ void FileTest::runFileTest(llvm::StringRef filename, Expect expect,
         generatedBinary, &generatedSpirvAsm, true /* generateHeader */));
         generatedBinary, &generatedSpirvAsm, true /* generateHeader */));
 
 
     std::string valMessages;
     std::string valMessages;
-    EXPECT_FALSE(utils::validateSpirvBinary(targetEnv, generatedBinary,
-                                            relaxLogicalPointer, glLayout,
-                                            dxLayout, &valMessages));
+    EXPECT_FALSE(utils::validateSpirvBinary(
+        targetEnv, generatedBinary, relaxLogicalPointer, glLayout, dxLayout,
+        scalarLayout, &valMessages));
     auto options = effcee::Options()
     auto options = effcee::Options()
                        .SetChecksName(filename.str())
                        .SetChecksName(filename.str())
                        .SetInputName("<val-message>");
                        .SetInputName("<val-message>");

+ 2 - 0
tools/clang/unittests/SPIRV/FileTestFixture.h

@@ -35,6 +35,7 @@ public:
   void setRelaxLogicalPointer() { relaxLogicalPointer = true; }
   void setRelaxLogicalPointer() { relaxLogicalPointer = true; }
   void setGlLayout() { glLayout = true; }
   void setGlLayout() { glLayout = true; }
   void setDxLayout() { dxLayout = true; }
   void setDxLayout() { dxLayout = true; }
+  void setScalarLayout() { scalarLayout = true; }
 
 
   /// \brief Runs a File Test! (See class description for more info)
   /// \brief Runs a File Test! (See class description for more info)
   void runFileTest(llvm::StringRef path, Expect expect = Expect::Success,
   void runFileTest(llvm::StringRef path, Expect expect = Expect::Success,
@@ -55,6 +56,7 @@ private:
   bool relaxLogicalPointer;
   bool relaxLogicalPointer;
   bool glLayout;
   bool glLayout;
   bool dxLayout;
   bool dxLayout;
+  bool scalarLayout;
 };
 };
 
 
 } // end namespace spirv
 } // end namespace spirv

+ 8 - 3
tools/clang/unittests/SPIRV/FileTestUtils.cpp

@@ -36,11 +36,16 @@ bool disassembleSpirvBinary(std::vector<uint32_t> &binary,
 
 
 bool validateSpirvBinary(spv_target_env env, std::vector<uint32_t> &binary,
 bool validateSpirvBinary(spv_target_env env, std::vector<uint32_t> &binary,
                          bool relaxLogicalPointer, bool glLayout, bool dxLayout,
                          bool relaxLogicalPointer, bool glLayout, bool dxLayout,
-                         std::string *message) {
+                         bool scalarLayout, std::string *message) {
   spvtools::ValidatorOptions options;
   spvtools::ValidatorOptions options;
   options.SetRelaxLogicalPointer(relaxLogicalPointer);
   options.SetRelaxLogicalPointer(relaxLogicalPointer);
-  options.SetRelaxBlockLayout(!glLayout && !dxLayout);
-  options.SetSkipBlockLayout(dxLayout);
+  if (dxLayout || scalarLayout) {
+    options.SetSkipBlockLayout(true);
+  } else if (glLayout) {
+    // The default for spirv-val.
+  } else {
+    options.SetRelaxBlockLayout(true);
+  }
   spvtools::SpirvTools spirvTools(env);
   spvtools::SpirvTools spirvTools(env);
   spirvTools.SetMessageConsumer([message](spv_message_level_t, const char *,
   spirvTools.SetMessageConsumer([message](spv_message_level_t, const char *,
                                           const spv_position_t &,
                                           const spv_position_t &,

+ 1 - 1
tools/clang/unittests/SPIRV/FileTestUtils.h

@@ -34,7 +34,7 @@ bool disassembleSpirvBinary(std::vector<uint32_t> &binary,
 /// Returns true if validation is successful; false otherwise.
 /// Returns true if validation is successful; false otherwise.
 bool validateSpirvBinary(spv_target_env, std::vector<uint32_t> &binary,
 bool validateSpirvBinary(spv_target_env, std::vector<uint32_t> &binary,
                          bool relaxLogicalPointer, bool glLayout, bool dxLayout,
                          bool relaxLogicalPointer, bool glLayout, bool dxLayout,
-                         std::string *message = nullptr);
+                         bool scalarLayout, std::string *message = nullptr);
 
 
 /// \brief Parses the Target Profile and Entry Point from the Run command
 /// \brief Parses the Target Profile and Entry Point from the Run command
 /// Returns the target profile, entry point, and the rest via arguments.
 /// Returns the target profile, entry point, and the rest via arguments.

+ 2 - 1
tools/clang/unittests/SPIRV/WholeFileTestFixture.cpp

@@ -109,7 +109,8 @@ void WholeFileTest::runWholeFileTest(llvm::StringRef filename,
   if (runSpirvValidation) {
   if (runSpirvValidation) {
     EXPECT_TRUE(utils::validateSpirvBinary(
     EXPECT_TRUE(utils::validateSpirvBinary(
         targetEnv, generatedBinary,
         targetEnv, generatedBinary,
-        /*relaxLogicalPointer=*/false, /*glLayout=*/false, /*dxLayout=*/false));
+        /*relaxLogicalPointer=*/false, /*glLayout=*/false, /*dxLayout=*/false,
+        /*scalarLayout=*/false));
   }
   }
 }
 }