mirror of
https://github.com/KhronosGroup/SPIRV-Tools
synced 2024-10-18 19:20:05 +00:00
Layout validation: Permit {vec3; float} tight packing
Fixes https://github.com/KhronosGroup/SPIRV-Tools/issues/1666
This commit is contained in:
parent
9795137c44
commit
1a283f41ed
@ -273,7 +273,7 @@ spv_result_t checkLayout(uint32_t struct_id, const char* storage_class_str,
|
|||||||
<< "at offset " << offset << " is not aligned to " << alignment;
|
<< "at offset " << offset << " is not aligned to " << alignment;
|
||||||
// SPIR-V requires struct members to be specified in memory address order,
|
// SPIR-V requires struct members to be specified in memory address order,
|
||||||
// and they should not overlap.
|
// and they should not overlap.
|
||||||
if (size > 0 && (align(offset + size, alignment) <= prevOffset))
|
if (size > 0 && (offset + size <= prevOffset))
|
||||||
return fail(memberIdx)
|
return fail(memberIdx)
|
||||||
<< "at offset " << offset << " has a lower offset than member "
|
<< "at offset " << offset << " has a lower offset than member "
|
||||||
<< memberIdx - 1;
|
<< memberIdx - 1;
|
||||||
@ -320,7 +320,12 @@ spv_result_t checkLayout(uint32_t struct_id, const char* storage_class_str,
|
|||||||
<< " not satisfying alignment to " << alignment;
|
<< " not satisfying alignment to " << alignment;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
nextValidOffset = align(offset + size, alignment);
|
nextValidOffset = offset + size;
|
||||||
|
if (blockRules && (SpvOpTypeArray == opcode || SpvOpTypeStruct == opcode)) {
|
||||||
|
// Uniform block rules don't permit anything in the padding of a struct
|
||||||
|
// or array.
|
||||||
|
nextValidOffset = align(nextValidOffset, alignment);
|
||||||
|
}
|
||||||
prevOffset = offset;
|
prevOffset = offset;
|
||||||
}
|
}
|
||||||
return SPV_SUCCESS;
|
return SPV_SUCCESS;
|
||||||
|
@ -1506,6 +1506,70 @@ TEST_F(ValidateDecorations, BlockStandardUniformBufferLayout) {
|
|||||||
EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState());
|
EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(ValidateDecorations, BlockLayoutPermitsTightVec3ScalarPackingGood) {
|
||||||
|
// See https://github.com/KhronosGroup/SPIRV-Tools/issues/1666
|
||||||
|
string spirv = R"(
|
||||||
|
OpCapability Shader
|
||||||
|
OpMemoryModel Logical GLSL450
|
||||||
|
OpEntryPoint Vertex %main "main"
|
||||||
|
OpSource GLSL 450
|
||||||
|
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
|
||||||
|
%v3float = OpTypeVector %float 3
|
||||||
|
%S = OpTypeStruct %v3float %float
|
||||||
|
%_ptr_Uniform_S = OpTypePointer Uniform %S
|
||||||
|
%B = OpVariable %_ptr_Uniform_S Uniform
|
||||||
|
%main = OpFunction %void None %3
|
||||||
|
%5 = OpLabel
|
||||||
|
OpReturn
|
||||||
|
OpFunctionEnd
|
||||||
|
)";
|
||||||
|
|
||||||
|
CompileSuccessfully(spirv);
|
||||||
|
EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState())
|
||||||
|
<< getDiagnosticString();
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ValidateDecorations, BlockLayoutForbidsTightScalarVec3PackingBad) {
|
||||||
|
// See https://github.com/KhronosGroup/SPIRV-Tools/issues/1666
|
||||||
|
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);
|
||||||
|
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState());
|
||||||
|
EXPECT_THAT(
|
||||||
|
getDiagnosticString(),
|
||||||
|
HasSubstr("Structure id 2 decorated as Block for variable in Uniform "
|
||||||
|
"storage class must follow standard uniform buffer layout "
|
||||||
|
"rules: member 1 at offset 4 is not aligned to 16"));
|
||||||
|
}
|
||||||
|
|
||||||
TEST_F(ValidateDecorations, BufferBlock16bitStandardStorageBufferLayout) {
|
TEST_F(ValidateDecorations, BufferBlock16bitStandardStorageBufferLayout) {
|
||||||
string spirv = R"(
|
string spirv = R"(
|
||||||
OpCapability Shader
|
OpCapability Shader
|
||||||
@ -1621,6 +1685,69 @@ TEST_F(ValidateDecorations, PushConstantArrayBadAlignmentBad) {
|
|||||||
"member 1 at offset 7 is not aligned to 4"));
|
"member 1 at offset 7 is not aligned to 4"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(ValidateDecorations,
|
||||||
|
PushConstantLayoutPermitsTightVec3ScalarPackingGood) {
|
||||||
|
// See https://github.com/KhronosGroup/SPIRV-Tools/issues/1666
|
||||||
|
string spirv = R"(
|
||||||
|
OpCapability Shader
|
||||||
|
OpMemoryModel Logical GLSL450
|
||||||
|
OpEntryPoint Vertex %main "main"
|
||||||
|
OpSource GLSL 450
|
||||||
|
OpMemberDecorate %S 0 Offset 0
|
||||||
|
OpMemberDecorate %S 1 Offset 12
|
||||||
|
OpDecorate %S Block
|
||||||
|
%void = OpTypeVoid
|
||||||
|
%3 = OpTypeFunction %void
|
||||||
|
%float = OpTypeFloat 32
|
||||||
|
%v3float = OpTypeVector %float 3
|
||||||
|
%S = OpTypeStruct %v3float %float
|
||||||
|
%_ptr_PushConstant_S = OpTypePointer PushConstant %S
|
||||||
|
%B = OpVariable %_ptr_PushConstant_S PushConstant
|
||||||
|
%main = OpFunction %void None %3
|
||||||
|
%5 = OpLabel
|
||||||
|
OpReturn
|
||||||
|
OpFunctionEnd
|
||||||
|
)";
|
||||||
|
|
||||||
|
CompileSuccessfully(spirv);
|
||||||
|
EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState())
|
||||||
|
<< getDiagnosticString();
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ValidateDecorations,
|
||||||
|
PushConstantLayoutForbidsTightScalarVec3PackingBad) {
|
||||||
|
// See https://github.com/KhronosGroup/SPIRV-Tools/issues/1666
|
||||||
|
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
|
||||||
|
%void = OpTypeVoid
|
||||||
|
%3 = OpTypeFunction %void
|
||||||
|
%float = OpTypeFloat 32
|
||||||
|
%v3float = OpTypeVector %float 3
|
||||||
|
%S = OpTypeStruct %float %v3float
|
||||||
|
%_ptr_Uniform_S = OpTypePointer PushConstant %S
|
||||||
|
%B = OpVariable %_ptr_Uniform_S PushConstant
|
||||||
|
%main = OpFunction %void None %3
|
||||||
|
%5 = OpLabel
|
||||||
|
OpReturn
|
||||||
|
OpFunctionEnd
|
||||||
|
)";
|
||||||
|
|
||||||
|
CompileSuccessfully(spirv);
|
||||||
|
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState());
|
||||||
|
EXPECT_THAT(
|
||||||
|
getDiagnosticString(),
|
||||||
|
HasSubstr(
|
||||||
|
"Structure id 2 decorated as Block for variable in PushConstant "
|
||||||
|
"storage class must follow standard storage buffer layout "
|
||||||
|
"rules: member 1 at offset 4 is not aligned to 16"));
|
||||||
|
}
|
||||||
|
|
||||||
TEST_F(ValidateDecorations, StorageBufferStorageClassArrayBaseAlignmentGood) {
|
TEST_F(ValidateDecorations, StorageBufferStorageClassArrayBaseAlignmentGood) {
|
||||||
// Spot check buffer rules when using StorageBuffer storage class with Block
|
// Spot check buffer rules when using StorageBuffer storage class with Block
|
||||||
// decoration.
|
// decoration.
|
||||||
@ -1758,6 +1885,75 @@ TEST_F(ValidateDecorations, BufferBlockStandardStorageBufferLayout) {
|
|||||||
EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState());
|
EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(ValidateDecorations,
|
||||||
|
StorageBufferLayoutPermitsTightVec3ScalarPackingGood) {
|
||||||
|
// See https://github.com/KhronosGroup/SPIRV-Tools/issues/1666
|
||||||
|
string spirv = R"(
|
||||||
|
OpCapability Shader
|
||||||
|
OpExtension "SPV_KHR_storage_buffer_storage_class"
|
||||||
|
OpMemoryModel Logical GLSL450
|
||||||
|
OpEntryPoint Vertex %main "main"
|
||||||
|
OpSource GLSL 450
|
||||||
|
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
|
||||||
|
%v3float = OpTypeVector %float 3
|
||||||
|
%S = OpTypeStruct %v3float %float
|
||||||
|
%_ptr_StorageBuffer_S = OpTypePointer StorageBuffer %S
|
||||||
|
%B = OpVariable %_ptr_StorageBuffer_S StorageBuffer
|
||||||
|
%main = OpFunction %void None %3
|
||||||
|
%5 = OpLabel
|
||||||
|
OpReturn
|
||||||
|
OpFunctionEnd
|
||||||
|
)";
|
||||||
|
|
||||||
|
CompileSuccessfully(spirv);
|
||||||
|
EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState())
|
||||||
|
<< getDiagnosticString();
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ValidateDecorations,
|
||||||
|
StorageBufferLayoutForbidsTightScalarVec3PackingBad) {
|
||||||
|
// See https://github.com/KhronosGroup/SPIRV-Tools/issues/1666
|
||||||
|
string spirv = R"(
|
||||||
|
OpCapability Shader
|
||||||
|
OpExtension "SPV_KHR_storage_buffer_storage_class"
|
||||||
|
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_StorageBuffer_S = OpTypePointer StorageBuffer %S
|
||||||
|
%B = OpVariable %_ptr_StorageBuffer_S StorageBuffer
|
||||||
|
%main = OpFunction %void None %3
|
||||||
|
%5 = OpLabel
|
||||||
|
OpReturn
|
||||||
|
OpFunctionEnd
|
||||||
|
)";
|
||||||
|
|
||||||
|
CompileSuccessfully(spirv);
|
||||||
|
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState());
|
||||||
|
EXPECT_THAT(
|
||||||
|
getDiagnosticString(),
|
||||||
|
HasSubstr(
|
||||||
|
"Structure id 2 decorated as Block for variable in StorageBuffer "
|
||||||
|
"storage class must follow standard storage buffer layout "
|
||||||
|
"rules: member 1 at offset 4 is not aligned to 16"));
|
||||||
|
}
|
||||||
|
|
||||||
TEST_F(ValidateDecorations,
|
TEST_F(ValidateDecorations,
|
||||||
BlockStandardUniformBufferLayoutIncorrectOffset0Bad) {
|
BlockStandardUniformBufferLayoutIncorrectOffset0Bad) {
|
||||||
string spirv = R"(
|
string spirv = R"(
|
||||||
@ -1993,7 +2189,8 @@ TEST_F(ValidateDecorations,
|
|||||||
"layout rules: member 1 at offset 8 is not aligned to 16"));
|
"layout rules: member 1 at offset 8 is not aligned to 16"));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ValidateDecorations, BlockUniformBufferLayoutOffsetInsidePaddingBad) {
|
TEST_F(ValidateDecorations,
|
||||||
|
BlockUniformBufferLayoutOffsetInsideArrayPaddingBad) {
|
||||||
// In this case the 2nd member fits entirely within the padding.
|
// In this case the 2nd member fits entirely within the padding.
|
||||||
string spirv = R"(
|
string spirv = R"(
|
||||||
OpCapability Shader
|
OpCapability Shader
|
||||||
@ -2032,6 +2229,40 @@ TEST_F(ValidateDecorations, BlockUniformBufferLayoutOffsetInsidePaddingBad) {
|
|||||||
"offset 20 overlaps previous member ending at offset 31"));
|
"offset 20 overlaps previous member ending at offset 31"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(ValidateDecorations,
|
||||||
|
BlockUniformBufferLayoutOffsetInsideStructPaddingBad) {
|
||||||
|
// In this case the 2nd member fits entirely within the padding.
|
||||||
|
string spirv = R"(
|
||||||
|
OpCapability Shader
|
||||||
|
OpMemoryModel Logical GLSL450
|
||||||
|
OpEntryPoint GLCompute %1 "main"
|
||||||
|
OpMemberDecorate %_struct_6 0 Offset 0
|
||||||
|
OpMemberDecorate %_struct_2 0 Offset 0
|
||||||
|
OpMemberDecorate %_struct_2 1 Offset 4
|
||||||
|
OpDecorate %_struct_2 Block
|
||||||
|
%void = OpTypeVoid
|
||||||
|
%4 = OpTypeFunction %void
|
||||||
|
%float = OpTypeFloat 32
|
||||||
|
%_struct_6 = OpTypeStruct %float
|
||||||
|
%_struct_2 = OpTypeStruct %_struct_6 %float
|
||||||
|
%_ptr_Uniform__struct_2 = OpTypePointer Uniform %_struct_2
|
||||||
|
%8 = OpVariable %_ptr_Uniform__struct_2 Uniform
|
||||||
|
%1 = OpFunction %void None %4
|
||||||
|
%9 = OpLabel
|
||||||
|
OpReturn
|
||||||
|
OpFunctionEnd
|
||||||
|
)";
|
||||||
|
|
||||||
|
CompileSuccessfully(spirv);
|
||||||
|
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState());
|
||||||
|
EXPECT_THAT(
|
||||||
|
getDiagnosticString(),
|
||||||
|
HasSubstr(
|
||||||
|
"Structure id 3 decorated as Block for variable in Uniform storage "
|
||||||
|
"class must follow standard uniform buffer layout rules: member 1 at "
|
||||||
|
"offset 4 overlaps previous member ending at offset 15"));
|
||||||
|
}
|
||||||
|
|
||||||
TEST_F(ValidateDecorations, BlockLayoutOffsetOutOfOrderBad) {
|
TEST_F(ValidateDecorations, BlockLayoutOffsetOutOfOrderBad) {
|
||||||
string spirv = R"(
|
string spirv = R"(
|
||||||
OpCapability Shader
|
OpCapability Shader
|
||||||
|
Loading…
Reference in New Issue
Block a user