VK_KHR_uniform_buffer_standard_layout validation (#2562)

Add a command-line option to enable validating SPIR-V for
implementations that support VK_KHR_uniform_buffer_standard_layout.


diff --git a/include/spirv-tools/libspirv.h b/include/spirv-tools/libspirv.h
index b5b49ba..f794faf 100644
--- a/include/spirv-tools/libspirv.h
+++ b/include/spirv-tools/libspirv.h
@@ -504,6 +504,11 @@
 SPIRV_TOOLS_EXPORT void spvValidatorOptionsSetRelaxBlockLayout(
     spv_validator_options options, bool val);
 
+// Records whether the validator should use standard block layout rules for
+// uniform blocks.
+SPIRV_TOOLS_EXPORT void spvValidatorOptionsSetUniformBufferStandardLayout(
+    spv_validator_options options, bool val);
+
 // Records whether the validator should use "scalar" block layout rules.
 // Scalar layout rules are more permissive than relaxed block layout.
 //
diff --git a/include/spirv-tools/libspirv.hpp b/include/spirv-tools/libspirv.hpp
index 12c7055..27b87e7 100644
--- a/include/spirv-tools/libspirv.hpp
+++ b/include/spirv-tools/libspirv.hpp
@@ -88,6 +88,12 @@
     spvValidatorOptionsSetRelaxBlockLayout(options_, val);
   }
 
+  // Enables VK_KHR_uniform_buffer_standard_layout when validating standard
+  // uniform layout.  If true, disables scalar block layout rules.
+  void SetUniformBufferStandardLayout(bool val) {
+    spvValidatorOptionsSetUniformBufferStandardLayout(options_, val);
+  }
+
   // Enables VK_EXT_scalar_block_layout when validating standard
   // uniform/storage buffer/push-constant layout.  If true, disables
   // relaxed block layout rules.
diff --git a/source/spirv_validator_options.cpp b/source/spirv_validator_options.cpp
index 2e9cf26..9b522fb 100644
--- a/source/spirv_validator_options.cpp
+++ b/source/spirv_validator_options.cpp
@@ -12,11 +12,11 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+#include "source/spirv_validator_options.h"
+
 #include <cassert>
 #include <cstring>
 
-#include "source/spirv_validator_options.h"
-
 bool spvParseUniversalLimitsOptions(const char* s, spv_validator_limit* type) {
   auto match = [s](const char* b) {
     return s && (0 == strncmp(s, b, strlen(b)));
@@ -95,6 +95,11 @@
   options->relax_block_layout = val;
 }
 
+void spvValidatorOptionsSetUniformBufferStandardLayout(
+    spv_validator_options options, bool val) {
+  options->uniform_buffer_standard_layout = val;
+}
+
 void spvValidatorOptionsSetScalarBlockLayout(spv_validator_options options,
                                              bool val) {
   options->scalar_block_layout = val;
diff --git a/source/spirv_validator_options.h b/source/spirv_validator_options.h
index f426ebf..5b27de6 100644
--- a/source/spirv_validator_options.h
+++ b/source/spirv_validator_options.h
@@ -43,6 +43,7 @@
         relax_struct_store(false),
         relax_logical_pointer(false),
         relax_block_layout(false),
+        uniform_buffer_standard_layout(false),
         scalar_block_layout(false),
         skip_block_layout(false) {}
 
@@ -50,6 +51,7 @@
   bool relax_struct_store;
   bool relax_logical_pointer;
   bool relax_block_layout;
+  bool uniform_buffer_standard_layout;
   bool scalar_block_layout;
   bool skip_block_layout;
 };
diff --git a/source/val/validate_decorations.cpp b/source/val/validate_decorations.cpp
index 0aec309..a6a7ce6 100644
--- a/source/val/validate_decorations.cpp
+++ b/source/val/validate_decorations.cpp
@@ -384,6 +384,10 @@
                          ValidationState_t& vstate) {
   if (vstate.options()->skip_block_layout) return SPV_SUCCESS;
 
+  // blockRules are the same as bufferBlock rules if the uniform buffer
+  // standard layout extension is being used.
+  if (vstate.options()->uniform_buffer_standard_layout) blockRules = false;
+
   // Relaxed layout and scalar layout can both be in effect at the same time.
   // For example, relaxed layout is implied by Vulkan 1.1.  But scalar layout
   // is more permissive than relaxed layout.
diff --git a/test/val/val_decoration_test.cpp b/test/val/val_decoration_test.cpp
index 95ae3a2..d3e9d0a 100644
--- a/test/val/val_decoration_test.cpp
+++ b/test/val/val_decoration_test.cpp
@@ -2100,7 +2100,7 @@
   EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState());
 }
 
-TEST_F(ValidateDecorations, BlockArrayBaseAlignmentGood) {
+TEST_F(ValidateDecorations, BlockArrayExtendedAlignmentGood) {
   // For uniform buffer, Array base alignment is 16, and ArrayStride
   // must be a multiple of 16.
   std::string spirv = R"(
@@ -2133,7 +2133,7 @@
       << getDiagnosticString();
 }
 
-TEST_F(ValidateDecorations, BlockArrayBadAlignmentBad) {
+TEST_F(ValidateDecorations, BlockArrayBaseAlignmentBad) {
   // For uniform buffer, Array base alignment is 16.
   std::string spirv = R"(
                OpCapability Shader
@@ -2170,7 +2170,7 @@
           "member 1 at offset 8 is not aligned to 16"));
 }
 
-TEST_F(ValidateDecorations, BlockArrayBadAlignmentWithRelaxedLayoutStillBad) {
+TEST_F(ValidateDecorations, BlockArrayBaseAlignmentWithRelaxedLayoutStillBad) {
   // For uniform buffer, Array base alignment is 16, and ArrayStride
   // must be a multiple of 16.  This case uses relaxed block layout.  Relaxed
   // layout only relaxes rules for vector alignment, not array alignment.
@@ -2213,7 +2213,7 @@
           "member 1 at offset 8 is not aligned to 16"));
 }
 
-TEST_F(ValidateDecorations, BlockArrayBadAlignmentWithVulkan1_1StillBad) {
+TEST_F(ValidateDecorations, BlockArrayBaseAlignmentWithVulkan1_1StillBad) {
   // Same as previous test, but with Vulkan 1.1, which includes
   // VK_KHR_relaxed_block_layout in core.
   std::string spirv = R"(
@@ -2254,6 +2254,44 @@
           "member 1 at offset 8 is not aligned to 16"));
 }
 
+TEST_F(ValidateDecorations,
+       BlockArrayBaseAlignmentWithBlockStandardLayoutGood) {
+  // Same as previous test, but with VK_KHR_uniform_buffer_standard_layout
+  std::string spirv = R"(
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Vertex %main "main"
+               OpSource GLSL 450
+               OpDecorate %_arr_float_uint_2 ArrayStride 16
+               OpDecorate %u DescriptorSet 0
+               OpDecorate %u Binding 0
+               OpMemberDecorate %S 0 Offset 0
+               OpMemberDecorate %S 1 Offset 8
+               OpDecorate %S Block
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+    %v2float = OpTypeVector %float 2
+       %uint = OpTypeInt 32 0
+     %uint_2 = OpConstant %uint 2
+%_arr_float_uint_2 = OpTypeArray %float %uint_2
+          %S = OpTypeStruct %v2float %_arr_float_uint_2
+%_ptr_Uniform_S = OpTypePointer Uniform %S
+          %u = OpVariable %_ptr_Uniform_S Uniform
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  CompileSuccessfully(spirv);
+  spvValidatorOptionsSetUniformBufferStandardLayout(getValidatorOptions(),
+                                                    true);
+  EXPECT_EQ(SPV_SUCCESS,
+            ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0));
+  EXPECT_THAT(getDiagnosticString(), Eq(""));
+}
+
 TEST_F(ValidateDecorations, VulkanBufferBlockOnStorageBufferBad) {
   std::string spirv = R"(
             OpCapability Shader
diff --git a/tools/val/val.cpp b/tools/val/val.cpp
index 5e9b025..49ff8a9 100644
--- a/tools/val/val.cpp
+++ b/tools/val/val.cpp
@@ -51,6 +51,8 @@
   --relax-block-layout             Enable VK_KHR_relaxed_block_layout when checking standard
                                    uniform, storage buffer, and push constant layouts.
                                    This is the default when targeting Vulkan 1.1 or later.
+  --uniform-buffer-standard-layout Enable VK_KHR_uniform_buffer_standard_layout when checking standard
+                                   uniform buffer layouts.
   --scalar-block-layout            Enable VK_EXT_scalar_block_layout when checking standard
                                    uniform, storage buffer, and push constant layouts.  Scalar layout
                                    rules are more permissive than relaxed block layout so in effect
@@ -140,6 +142,8 @@
         options.SetRelaxLogicalPointer(true);
       } else if (0 == strcmp(cur_arg, "--relax-block-layout")) {
         options.SetRelaxBlockLayout(true);
+      } else if (0 == strcmp(cur_arg, "--uniform-buffer-standard-layout")) {
+        options.SetUniformBufferStandardLayout(true);
       } else if (0 == strcmp(cur_arg, "--scalar-block-layout")) {
         options.SetScalarBlockLayout(true);
       } else if (0 == strcmp(cur_arg, "--skip-block-layout")) {