Validator: Support VK_EXT_scalar_block_layout

Adds validator option to specify scalar block layout rules.

Both VK_KHR_relax_block_layout and VK_EXT_scalar_block_layout can be
enabled at the same time.  But scalar block layout is as permissive
as relax block layout.

Also, scalar block layout does not require padding at the end of a
struct.

Add test for scalar layout testing ArrayStride 12 on array of vec3s

Cleanup: The internal getSize method does not need a round-up argument,
so remove it.
This commit is contained in:
David Neto 2018-10-09 20:43:09 -04:00
parent 28d8d7bc67
commit 8e9be303b0
8 changed files with 394 additions and 31 deletions

View File

@ -484,13 +484,35 @@ SPIRV_TOOLS_EXPORT void spvValidatorOptionsSetRelaxStoreStruct(
SPIRV_TOOLS_EXPORT void spvValidatorOptionsSetRelaxLogicalPointer(
spv_validator_options options, bool val);
// Records whether or not the validator should relax the rules on block layout.
// Records whether the validator should use "relaxed" block layout rules.
// Relaxed layout rules are described by Vulkan extension
// VK_KHR_relaxed_block_layout, and they affect uniform blocks, storage blocks,
// and push constants.
//
// When relaxed, it will enable VK_KHR_relaxed_block_layout when validating
// standard uniform/storage block layout.
// This is enabled by default when targeting Vulkan 1.1 or later.
// Relaxed layout is more permissive than the default rules in Vulkan 1.0.
SPIRV_TOOLS_EXPORT void spvValidatorOptionsSetRelaxBlockLayout(
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.
//
// See Vulkan extnesion VK_EXT_scalar_block_layout. The scalar alignment is
// defined as follows:
// - scalar alignment of a scalar is the scalar size
// - scalar alignment of a vector is the scalar alignment of its component
// - scalar alignment of a matrix is the scalar alignment of its component
// - scalar alignment of an array is the scalar alignment of its element
// - scalar alignment of a struct is the max scalar alignment among its
// members
//
// For a struct in Uniform, StorageClass, or PushConstant:
// - a member Offset must be a multiple of the member's scalar alignment
// - ArrayStride or MatrixStride must be a multiple of the array or matrix
// scalar alignment
SPIRV_TOOLS_EXPORT void spvValidatorOptionsSetScalarBlockLayout(
spv_validator_options options, bool val);
// Records whether or not the validator should skip validating standard
// uniform/storage block layout.
SPIRV_TOOLS_EXPORT void spvValidatorOptionsSetSkipBlockLayout(

View File

@ -82,12 +82,20 @@ class ValidatorOptions {
}
// Enables VK_KHR_relaxed_block_layout when validating standard
// uniform/storage buffer layout.
// uniform/storage buffer/push-constant layout. If true, disables
// scalar block layout rules.
void SetRelaxBlockLayout(bool val) {
spvValidatorOptionsSetRelaxBlockLayout(options_, val);
}
// Skips validating standard uniform/storage buffer layout.
// Enables VK_EXT_scalar_block_layout when validating standard
// uniform/storage buffer/push-constant layout. If true, disables
// relaxed block layout rules.
void SetScalarBlockLayout(bool val) {
spvValidatorOptionsSetScalarBlockLayout(options_, val);
}
// Skips validating standard uniform/storage buffer/push-constant layout.
void SetSkipBlockLayout(bool val) {
spvValidatorOptionsSetSkipBlockLayout(options_, val);
}

View File

@ -95,6 +95,11 @@ void spvValidatorOptionsSetRelaxBlockLayout(spv_validator_options options,
options->relax_block_layout = val;
}
void spvValidatorOptionsSetScalarBlockLayout(spv_validator_options options,
bool val) {
options->scalar_block_layout = val;
}
void spvValidatorOptionsSetSkipBlockLayout(spv_validator_options options,
bool val) {
options->skip_block_layout = val;

View File

@ -43,12 +43,14 @@ struct spv_validator_options_t {
relax_struct_store(false),
relax_logical_pointer(false),
relax_block_layout(false),
scalar_block_layout(false),
skip_block_layout(false) {}
validator_universal_limits_t universal_limits_;
bool relax_struct_store;
bool relax_logical_pointer;
bool relax_block_layout;
bool scalar_block_layout;
bool skip_block_layout;
};

View File

@ -217,23 +217,57 @@ uint32_t getBaseAlignment(uint32_t member_id, bool roundUp,
return baseAlignment;
}
// Returns scalar alignment of a type.
uint32_t getScalarAlignment(uint32_t type_id, ValidationState_t& vstate) {
const auto inst = vstate.FindDef(type_id);
const auto& words = inst->words();
switch (inst->opcode()) {
case SpvOpTypeInt:
case SpvOpTypeFloat:
return words[2] / 8;
case SpvOpTypeVector:
case SpvOpTypeMatrix:
case SpvOpTypeArray:
case SpvOpTypeRuntimeArray: {
const auto compositeMemberTypeId = words[2];
return getScalarAlignment(compositeMemberTypeId, vstate);
}
case SpvOpTypeStruct: {
const auto members = getStructMembers(type_id, vstate);
uint32_t max_member_alignment = 1;
for (uint32_t memberIdx = 0, numMembers = uint32_t(members.size());
memberIdx < numMembers; ++memberIdx) {
const auto id = members[memberIdx];
uint32_t member_alignment = getScalarAlignment(id, vstate);
if (member_alignment > max_member_alignment) {
max_member_alignment = member_alignment;
}
}
return max_member_alignment;
} break;
default:
assert(0);
break;
}
return 1;
}
// Returns size of a struct member. Doesn't include padding at the end of struct
// or array. Assumes that in the struct case, all members have offsets.
uint32_t getSize(uint32_t member_id, bool roundUp,
const LayoutConstraints& inherited,
uint32_t getSize(uint32_t member_id, const LayoutConstraints& inherited,
MemberConstraints& constraints, ValidationState_t& vstate) {
const auto inst = vstate.FindDef(member_id);
const auto& words = inst->words();
switch (inst->opcode()) {
case SpvOpTypeInt:
case SpvOpTypeFloat:
return getBaseAlignment(member_id, roundUp, inherited, constraints,
vstate);
return words[2] / 8;
case SpvOpTypeVector: {
const auto componentId = words[2];
const auto numComponents = words[3];
const auto componentSize =
getSize(componentId, roundUp, inherited, constraints, vstate);
getSize(componentId, inherited, constraints, vstate);
const auto size = componentSize * numComponents;
return size;
}
@ -244,7 +278,7 @@ uint32_t getSize(uint32_t member_id, bool roundUp,
const uint32_t num_elem = sizeInst->words()[3];
const uint32_t elem_type = words[2];
const uint32_t elem_size =
getSize(elem_type, roundUp, inherited, constraints, vstate);
getSize(elem_type, inherited, constraints, vstate);
// Account for gaps due to alignments in the first N-1 elements,
// then add the size of the last element.
const auto size =
@ -264,7 +298,7 @@ uint32_t getSize(uint32_t member_id, bool roundUp,
const auto num_rows = component_inst->words()[3];
const auto scalar_elem_type = component_inst->words()[2];
const uint32_t scalar_elem_size =
getSize(scalar_elem_type, roundUp, inherited, constraints, vstate);
getSize(scalar_elem_type, inherited, constraints, vstate);
return (num_rows - 1) * inherited.matrix_stride +
num_columns * scalar_elem_size;
}
@ -286,8 +320,7 @@ uint32_t getSize(uint32_t member_id, bool roundUp,
// has been checked earlier in the flow.
assert(offset != 0xffffffff);
const auto& constraint = constraints[std::make_pair(lastMember, lastIdx)];
return offset +
getSize(lastMember, roundUp, constraint, constraints, vstate);
return offset + getSize(lastMember, constraint, constraints, vstate);
}
default:
assert(0);
@ -306,7 +339,7 @@ bool hasImproperStraddle(uint32_t id, uint32_t offset,
const LayoutConstraints& inherited,
MemberConstraints& constraints,
ValidationState_t& vstate) {
const auto size = getSize(id, false, inherited, constraints, vstate);
const auto size = getSize(id, inherited, constraints, vstate);
const auto F = offset;
const auto L = offset + size - 1;
if (size <= 16) {
@ -334,19 +367,28 @@ spv_result_t checkLayout(uint32_t struct_id, const char* storage_class_str,
ValidationState_t& vstate) {
if (vstate.options()->skip_block_layout) return SPV_SUCCESS;
// 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.
const bool relaxed_block_layout = vstate.IsRelaxedBlockLayout();
const bool scalar_block_layout = vstate.options()->scalar_block_layout;
auto fail = [&vstate, struct_id, storage_class_str, decoration_str,
blockRules](uint32_t member_idx) -> DiagnosticStream {
blockRules, relaxed_block_layout,
scalar_block_layout](uint32_t member_idx) -> DiagnosticStream {
DiagnosticStream ds =
std::move(vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(struct_id))
<< "Structure id " << struct_id << " decorated as "
<< decoration_str << " for variable in " << storage_class_str
<< " storage class must follow standard "
<< " storage class must follow "
<< (scalar_block_layout
? "scalar "
: (relaxed_block_layout ? "relaxed " : "standard "))
<< (blockRules ? "uniform buffer" : "storage buffer")
<< " layout rules: member " << member_idx << " ");
return ds;
};
const bool relaxed_block_layout = vstate.IsRelaxedBlockLayout();
const auto& members = getStructMembers(struct_id, vstate);
// To check for member overlaps, we want to traverse the members in
@ -389,20 +431,25 @@ spv_result_t checkLayout(uint32_t struct_id, const char* storage_class_str,
auto id = members[member_offset.member];
const LayoutConstraints& constraint =
constraints[std::make_pair(struct_id, uint32_t(memberIdx))];
// Scalar layout takes precedence because it's more permissive, and implying
// an alignment that divides evenly into the alignment that would otherwise
// be used.
const auto alignment =
getBaseAlignment(id, blockRules, constraint, constraints, vstate);
scalar_block_layout
? getScalarAlignment(id, vstate)
: getBaseAlignment(id, blockRules, constraint, constraints, vstate);
const auto inst = vstate.FindDef(id);
const auto opcode = inst->opcode();
const auto size = getSize(id, blockRules, constraint, constraints, vstate);
const auto size = getSize(id, constraint, constraints, vstate);
// Check offset.
if (offset == 0xffffffff)
return fail(memberIdx) << "is missing an Offset decoration";
if (relaxed_block_layout && opcode == SpvOpTypeVector) {
if (!scalar_block_layout && relaxed_block_layout &&
opcode == SpvOpTypeVector) {
// In relaxed block layout, the vector offset must be aligned to the
// vector's scalar element type.
const auto componentId = inst->words()[2];
const auto scalar_alignment = getBaseAlignment(
componentId, blockRules, constraint, constraints, vstate);
const auto scalar_alignment = getScalarAlignment(componentId, vstate);
if (!IsAlignedTo(offset, scalar_alignment)) {
return fail(memberIdx)
<< "at offset " << offset
@ -410,7 +457,7 @@ spv_result_t checkLayout(uint32_t struct_id, const char* storage_class_str,
}
} else {
// Without relaxed block layout, the offset must be divisible by the
// base alignment.
// alignment requirement.
if (!IsAlignedTo(offset, alignment)) {
return fail(memberIdx)
<< "at offset " << offset << " is not aligned to " << alignment;
@ -420,7 +467,7 @@ spv_result_t checkLayout(uint32_t struct_id, const char* storage_class_str,
return fail(memberIdx) << "at offset " << offset
<< " overlaps previous member ending at offset "
<< nextValidOffset - 1;
if (relaxed_block_layout) {
if (!scalar_block_layout && relaxed_block_layout) {
// Check improper straddle of vectors.
if (SpvOpTypeVector == opcode &&
hasImproperStraddle(id, offset, constraint, constraints, vstate))
@ -463,7 +510,8 @@ spv_result_t checkLayout(uint32_t struct_id, const char* storage_class_str,
}
}
nextValidOffset = offset + size;
if (blockRules && (SpvOpTypeArray == opcode || SpvOpTypeStruct == opcode)) {
if (!scalar_block_layout && blockRules &&
(SpvOpTypeArray == opcode || SpvOpTypeStruct == opcode)) {
// Uniform block rules don't permit anything in the padding of a struct
// or array.
nextValidOffset = align(nextValidOffset, alignment);

View File

@ -90,6 +90,19 @@ class ValidationState_t {
// Allow an OpTypeInt with 8 bit width to be used in more than just int
// conversion opcodes
bool use_int8_type = false;
// Use scalar block layout. See VK_EXT_scalar_block_layout:
// Defines scalar alignment:
// - scalar alignment equals the scalar size in bytes
// - array alignment is same as its element alignment
// - array alignment is max alignment of any of its members
// - vector alignment is same as component alignment
// - matrix alignment is same as component alignment
// For struct in Uniform, StorageBuffer, PushConstant:
// - Offset of a member is multiple of scalar alignment of that member
// - ArrayStride and MatrixStride are multiples of scalar alignment
// Members need not be listed in offset order
bool scalar_block_layout = false;
};
ValidationState_t(const spv_const_context context,

View File

@ -1559,7 +1559,7 @@ TEST_F(ValidateDecorations,
getDiagnosticString(),
HasSubstr(
"Structure id 2 decorated as Block for variable in Uniform storage "
"class must follow standard uniform buffer layout rules: member 1 at "
"class must follow relaxed uniform buffer layout rules: member 1 at "
"offset 5 is not aligned to scalar element size 4"));
}
@ -1596,6 +1596,263 @@ TEST_F(ValidateDecorations,
EXPECT_THAT(getDiagnosticString(), Eq(""));
}
TEST_F(ValidateDecorations,
BlockLayoutPermitsTightScalarVec3PackingWithScalarLayoutGood) {
// Same as previous test, but with scalar block layout.
std::string spirv = R"(
OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint Vertex %main "main"
OpSource GLSL 450
OpMemberDecorate %S 0 Offset 0
OpMemberDecorate %S 1 Offset 4
OpDecorate %S Block
OpDecorate %B DescriptorSet 0
OpDecorate %B Binding 0
%void = OpTypeVoid
%3 = OpTypeFunction %void
%float = OpTypeFloat 32
%v3float = OpTypeVector %float 3
%S = OpTypeStruct %float %v3float
%_ptr_Uniform_S = OpTypePointer Uniform %S
%B = OpVariable %_ptr_Uniform_S Uniform
%main = OpFunction %void None %3
%5 = OpLabel
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(spirv);
spvValidatorOptionsSetScalarBlockLayout(getValidatorOptions(), true);
EXPECT_EQ(SPV_SUCCESS,
ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0));
EXPECT_THAT(getDiagnosticString(), Eq(""));
}
TEST_F(ValidateDecorations,
BlockLayoutPermitsScalarAlignedArrayWithScalarLayoutGood) {
// The array at offset 4 is ok with scalar block layout.
std::string spirv = R"(
OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint Vertex %main "main"
OpSource GLSL 450
OpMemberDecorate %S 0 Offset 0
OpMemberDecorate %S 1 Offset 4
OpDecorate %S Block
OpDecorate %B DescriptorSet 0
OpDecorate %B Binding 0
OpDecorate %arr_float ArrayStride 4
%void = OpTypeVoid
%3 = OpTypeFunction %void
%uint = OpTypeInt 32 0
%uint_3 = OpConstant %uint 3
%float = OpTypeFloat 32
%arr_float = OpTypeArray %float %uint_3
%S = OpTypeStruct %float %arr_float
%_ptr_Uniform_S = OpTypePointer Uniform %S
%B = OpVariable %_ptr_Uniform_S Uniform
%main = OpFunction %void None %3
%5 = OpLabel
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(spirv);
spvValidatorOptionsSetScalarBlockLayout(getValidatorOptions(), true);
EXPECT_EQ(SPV_SUCCESS,
ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0));
EXPECT_THAT(getDiagnosticString(), Eq(""));
}
TEST_F(ValidateDecorations,
BlockLayoutPermitsScalarAlignedArrayOfVec3WithScalarLayoutGood) {
// The array at offset 4 is ok with scalar block layout, even though
// its elements are vec3.
// This is the same as the previous case, but the array elements are vec3
// instead of float.
std::string spirv = R"(
OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint Vertex %main "main"
OpSource GLSL 450
OpMemberDecorate %S 0 Offset 0
OpMemberDecorate %S 1 Offset 4
OpDecorate %S Block
OpDecorate %B DescriptorSet 0
OpDecorate %B Binding 0
OpDecorate %arr_vec3 ArrayStride 12
%void = OpTypeVoid
%3 = OpTypeFunction %void
%uint = OpTypeInt 32 0
%uint_3 = OpConstant %uint 3
%float = OpTypeFloat 32
%vec3 = OpTypeVector %float 3
%arr_vec3 = OpTypeArray %vec3 %uint_3
%S = OpTypeStruct %float %arr_vec3
%_ptr_Uniform_S = OpTypePointer Uniform %S
%B = OpVariable %_ptr_Uniform_S Uniform
%main = OpFunction %void None %3
%5 = OpLabel
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(spirv);
spvValidatorOptionsSetScalarBlockLayout(getValidatorOptions(), true);
EXPECT_EQ(SPV_SUCCESS,
ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0));
EXPECT_THAT(getDiagnosticString(), Eq(""));
}
TEST_F(ValidateDecorations,
BlockLayoutPermitsScalarAlignedStructWithScalarLayoutGood) {
// Scalar block layout permits the struct at offset 4, even though
// it contains a vector with base alignment 8 and scalar alignment 4.
std::string spirv = R"(
OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint Vertex %main "main"
OpSource GLSL 450
OpMemberDecorate %S 0 Offset 0
OpMemberDecorate %S 1 Offset 4
OpMemberDecorate %st 0 Offset 0
OpMemberDecorate %st 1 Offset 8
OpDecorate %S Block
OpDecorate %B DescriptorSet 0
OpDecorate %B Binding 0
%void = OpTypeVoid
%3 = OpTypeFunction %void
%float = OpTypeFloat 32
%vec2 = OpTypeVector %float 2
%st = OpTypeStruct %vec2 %float
%S = OpTypeStruct %float %st
%_ptr_Uniform_S = OpTypePointer Uniform %S
%B = OpVariable %_ptr_Uniform_S Uniform
%main = OpFunction %void None %3
%5 = OpLabel
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(spirv);
spvValidatorOptionsSetScalarBlockLayout(getValidatorOptions(), true);
EXPECT_EQ(SPV_SUCCESS,
ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0));
EXPECT_THAT(getDiagnosticString(), Eq(""));
}
TEST_F(
ValidateDecorations,
BlockLayoutPermitsFieldsInBaseAlignmentPaddingAtEndOfStructWithScalarLayoutGood) {
// Scalar block layout permits fields in what would normally be the padding at
// the end of a struct.
std::string spirv = R"(
OpCapability Shader
OpCapability Float64
OpMemoryModel Logical GLSL450
OpEntryPoint Vertex %main "main"
OpSource GLSL 450
OpMemberDecorate %st 0 Offset 0
OpMemberDecorate %st 1 Offset 8
OpMemberDecorate %S 0 Offset 0
OpMemberDecorate %S 1 Offset 12
OpDecorate %S Block
OpDecorate %B DescriptorSet 0
OpDecorate %B Binding 0
%void = OpTypeVoid
%3 = OpTypeFunction %void
%float = OpTypeFloat 32
%double = OpTypeFloat 64
%st = OpTypeStruct %double %float
%S = OpTypeStruct %st %float
%_ptr_Uniform_S = OpTypePointer Uniform %S
%B = OpVariable %_ptr_Uniform_S Uniform
%main = OpFunction %void None %3
%5 = OpLabel
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(spirv);
spvValidatorOptionsSetScalarBlockLayout(getValidatorOptions(), true);
EXPECT_EQ(SPV_SUCCESS,
ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0));
EXPECT_THAT(getDiagnosticString(), Eq(""));
}
TEST_F(
ValidateDecorations,
BlockLayoutPermitsStraddlingVectorWithScalarLayoutOverrideRelaxBlockLayoutGood) {
// Same as previous, but set relaxed block layout first. Scalar layout always
// wins.
std::string spirv = R"(
OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint Vertex %main "main"
OpSource GLSL 450
OpMemberDecorate %S 0 Offset 0
OpMemberDecorate %S 1 Offset 4
OpDecorate %S Block
OpDecorate %B DescriptorSet 0
OpDecorate %B Binding 0
%void = OpTypeVoid
%3 = OpTypeFunction %void
%float = OpTypeFloat 32
%vec4 = OpTypeVector %float 4
%S = OpTypeStruct %float %vec4
%_ptr_Uniform_S = OpTypePointer Uniform %S
%B = OpVariable %_ptr_Uniform_S Uniform
%main = OpFunction %void None %3
%5 = OpLabel
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(spirv);
spvValidatorOptionsSetRelaxBlockLayout(getValidatorOptions(), true);
spvValidatorOptionsSetScalarBlockLayout(getValidatorOptions(), true);
EXPECT_EQ(SPV_SUCCESS,
ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0));
EXPECT_THAT(getDiagnosticString(), Eq(""));
}
TEST_F(
ValidateDecorations,
BlockLayoutPermitsStraddlingVectorWithRelaxedLayoutOverridenByScalarBlockLayoutGood) {
// Same as previous, but set scalar block layout first. Scalar layout always
// wins.
std::string spirv = R"(
OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint Vertex %main "main"
OpSource GLSL 450
OpMemberDecorate %S 0 Offset 0
OpMemberDecorate %S 1 Offset 4
OpDecorate %S Block
OpDecorate %B DescriptorSet 0
OpDecorate %B Binding 0
%void = OpTypeVoid
%3 = OpTypeFunction %void
%float = OpTypeFloat 32
%vec4 = OpTypeVector %float 4
%S = OpTypeStruct %float %vec4
%_ptr_Uniform_S = OpTypePointer Uniform %S
%B = OpVariable %_ptr_Uniform_S Uniform
%main = OpFunction %void None %3
%5 = OpLabel
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(spirv);
spvValidatorOptionsSetScalarBlockLayout(getValidatorOptions(), true);
spvValidatorOptionsSetRelaxBlockLayout(getValidatorOptions(), true);
EXPECT_EQ(SPV_SUCCESS,
ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0));
EXPECT_THAT(getDiagnosticString(), Eq(""));
}
TEST_F(ValidateDecorations, BufferBlock16bitStandardStorageBufferLayout) {
std::string spirv = R"(
OpCapability Shader
@ -1783,7 +2040,7 @@ TEST_F(ValidateDecorations, BlockArrayBadAlignmentWithVulkan1_1StillBad) {
getDiagnosticString(),
HasSubstr(
"Structure id 3 decorated as Block for variable in Uniform "
"storage class must follow standard uniform buffer layout rules: "
"storage class must follow relaxed uniform buffer layout rules: "
"member 1 at offset 8 is not aligned to 16"));
}

View File

@ -48,9 +48,15 @@ Options:
--max-id-bound <maximum value for the id bound>
--relax-logical-pointer Allow allocating an object of a pointer type and returning
a pointer value from a function in logical addressing mode
--relax-block-layout Enable VK_HR_relaxed_block_layout when checking standard
uniform/storage buffer layout
--skip-block-layout Skip checking standard uniform/storage buffer layout
--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.
--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
this will override the --relax-block-layout option.
--skip-block-layout Skip checking standard uniform/storage buffer layout.
Overrides any --relax-block-layout or --scalar-block-layout option.
--relax-struct-store Allow store from one struct type to a
different type with compatible layout and
members.
@ -128,6 +134,8 @@ int main(int argc, char** argv) {
options.SetRelaxLogicalPointer(true);
} else if (0 == strcmp(cur_arg, "--relax-block-layout")) {
options.SetRelaxBlockLayout(true);
} else if (0 == strcmp(cur_arg, "--scalar-block-layout")) {
options.SetScalarBlockLayout(true);
} else if (0 == strcmp(cur_arg, "--skip-block-layout")) {
options.SetSkipBlockLayout(true);
} else if (0 == strcmp(cur_arg, "--relax-struct-store")) {