Browse Source

[spirv] Add support for VK_EXT_scalar_block_layout (#1716)

Ref: https://github.com/KhronosGroup/Vulkan-Docs/issues/854
Lei Zhang 6 years ago
parent
commit
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
 ~~~~~~~~~~~~~~~~~~~
 
-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:
 
 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``
    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``.
+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
 ``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:
 
-============== ====== ====== ====== ====== ====== ======
-     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
 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">;
 def fvk_use_dx_layout: Flag<["-"], "fvk-use-dx-layout">, Group<spirv_Group>, Flags<[CoreOption, DriverOption]>,
   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]>,
   HelpText<"Emit additional SPIR-V instructions to aid reflection">;
 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
   FxcCTBuffer,       // fxc.exe layout rule for cbuffer/tbuffer
   FxcSBuffer,        // fxc.exe layout rule for structured buffers
+  Scalar,            // VK_EXT_scalar_block_layout
 };
 
 struct SpirvCodeGenOptions {
@@ -50,6 +51,7 @@ struct SpirvCodeGenOptions {
   bool noWarnIgnoredFeatures;
   bool useDxLayout;
   bool useGlLayout;
+  bool useScalarLayout;
   SpirvLayoutRule cBufferLayoutRule;
   SpirvLayoutRule sBufferLayoutRule;
   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.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.useScalarLayout = Args.hasFlag(OPT_fvk_use_scalar_layout, 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.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_gl_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_Wno_vk_ignored_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);
 }
 
-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);
 
   tools.SetMessageConsumer(
@@ -257,8 +257,13 @@ bool spirvToolsValidate(spv_target_env env, std::vector<uint32_t> *module,
   // GL: strict block layout rules
   // VK: relaxed 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);
 }
@@ -625,6 +630,10 @@ SPIRVEmitter::SPIRVEmitter(CompilerInstance &ci)
     spirvOptions.cBufferLayoutRule = SpirvLayoutRule::GLSLStd140;
     spirvOptions.tBufferLayoutRule = SpirvLayoutRule::GLSLStd430;
     spirvOptions.sBufferLayoutRule = SpirvLayoutRule::GLSLStd430;
+  } else if (spirvOptions.useScalarLayout) {
+    spirvOptions.cBufferLayoutRule = SpirvLayoutRule::Scalar;
+    spirvOptions.tBufferLayoutRule = SpirvLayoutRule::Scalar;
+    spirvOptions.sBufferLayoutRule = SpirvLayoutRule::Scalar;
   } else {
     spirvOptions.cBufferLayoutRule = SpirvLayoutRule::RelaxedGLSLStd140;
     spirvOptions.tBufferLayoutRule = SpirvLayoutRule::RelaxedGLSLStd430;
@@ -736,9 +745,9 @@ void SPIRVEmitter::HandleTranslationUnit(ASTContext &context) {
   // Validate the generated SPIR-V code
   if (!spirvOptions.disableValidation) {
     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;
       emitNote("please file a bug report on "
                "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);
     }
 
+
     // The vk::offset attribute takes precedence over all.
     if (const auto *offsetAttr = decl->getAttr<VKOffsetAttr>()) {
       offset = offsetAttr->getOffset();
@@ -1713,9 +1714,10 @@ TypeTranslator::getAlignmentAndSize(QualType type, SpirvLayoutRule rule,
     if (isVectorType(type, &elemType, &elemCount)) {
       uint32_t alignment = 0, size = 0;
       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 &&
-          rule != SpirvLayoutRule::FxcSBuffer)
+          rule != SpirvLayoutRule::FxcSBuffer &&
+          rule != SpirvLayoutRule::Scalar)
         alignment = (elemCount == 3 ? 4 : elemCount) * size;
 
       return {alignment, elemCount * size};
@@ -1737,9 +1739,11 @@ TypeTranslator::getAlignmentAndSize(QualType type, SpirvLayoutRule rule,
 
       const uint32_t vecStorageSize = isRowMajor ? colCount : rowCount;
 
-      if (rule == SpirvLayoutRule::FxcSBuffer) {
+      if (rule == SpirvLayoutRule::FxcSBuffer ||
+          rule == SpirvLayoutRule::Scalar) {
         *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};
       }
 
@@ -1794,6 +1798,12 @@ TypeTranslator::getAlignmentAndSize(QualType type, SpirvLayoutRule rule,
       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 ||
         rule == SpirvLayoutRule::RelaxedGLSLStd140 ||
         rule == SpirvLayoutRule::FxcCTBuffer) {
@@ -1817,9 +1827,11 @@ TypeTranslator::getAlignmentAndSize(QualType type, SpirvLayoutRule rule,
     std::tie(alignment, size) =
         getAlignmentAndSize(arrayType->getElementType(), rule, stride);
 
-    if (rule == SpirvLayoutRule::FxcSBuffer) {
+    if (rule == SpirvLayoutRule::FxcSBuffer ||
+        rule == SpirvLayoutRule::Scalar) {
       *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};
     }
 

+ 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");
 }
 
+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, VulkanSubpassInputBinding) {
   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);
 
     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) {
     ASSERT_TRUE(compileOk);
 
@@ -127,8 +128,9 @@ void FileTest::runFileTest(llvm::StringRef filename, Expect expect,
     ASSERT_EQ(result.status(), effcee::Result::Status::Ok);
 
     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) {
     ASSERT_FALSE(compileOk);
 
@@ -154,9 +156,9 @@ void FileTest::runFileTest(llvm::StringRef filename, Expect expect,
         generatedBinary, &generatedSpirvAsm, true /* generateHeader */));
 
     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()
                        .SetChecksName(filename.str())
                        .SetInputName("<val-message>");

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

@@ -35,6 +35,7 @@ public:
   void setRelaxLogicalPointer() { relaxLogicalPointer = true; }
   void setGlLayout() { glLayout = true; }
   void setDxLayout() { dxLayout = true; }
+  void setScalarLayout() { scalarLayout = true; }
 
   /// \brief Runs a File Test! (See class description for more info)
   void runFileTest(llvm::StringRef path, Expect expect = Expect::Success,
@@ -55,6 +56,7 @@ private:
   bool relaxLogicalPointer;
   bool glLayout;
   bool dxLayout;
+  bool scalarLayout;
 };
 
 } // 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 relaxLogicalPointer, bool glLayout, bool dxLayout,
-                         std::string *message) {
+                         bool scalarLayout, std::string *message) {
   spvtools::ValidatorOptions options;
   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);
   spirvTools.SetMessageConsumer([message](spv_message_level_t, const char *,
                                           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.
 bool validateSpirvBinary(spv_target_env, std::vector<uint32_t> &binary,
                          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
 /// 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) {
     EXPECT_TRUE(utils::validateSpirvBinary(
         targetEnv, generatedBinary,
-        /*relaxLogicalPointer=*/false, /*glLayout=*/false, /*dxLayout=*/false));
+        /*relaxLogicalPointer=*/false, /*glLayout=*/false, /*dxLayout=*/false,
+        /*scalarLayout=*/false));
   }
 }