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")) {