Check binding annotations in resource variables (#2151)

Fixes #2007

Add checks that all uniform, uniform constant and storage buffer variables have descriptor set and binding decorations
This commit is contained in:
Alejandro Lopez 2018-12-04 10:05:41 -05:00 committed by alan-baker
parent 17cba4695c
commit a1439604ea
4 changed files with 477 additions and 15 deletions

View File

@ -520,15 +520,18 @@ spv_result_t checkLayout(uint32_t struct_id, const char* storage_class_str,
return SPV_SUCCESS;
}
// Returns true if structure id has given decoration. Handles also nested
// structures.
bool hasDecoration(uint32_t struct_id, SpvDecoration decoration,
// Returns true if variable or structure id has given decoration. Handles also
// nested structures.
bool hasDecoration(uint32_t id, SpvDecoration decoration,
ValidationState_t& vstate) {
for (auto& dec : vstate.id_decorations(struct_id)) {
for (auto& dec : vstate.id_decorations(id)) {
if (decoration == dec.dec_type()) return true;
}
for (auto id : getStructMembers(struct_id, SpvOpTypeStruct, vstate)) {
if (hasDecoration(id, decoration, vstate)) {
if (SpvOpTypeStruct != vstate.FindDef(id)->opcode()) {
return false;
}
for (auto member_id : getStructMembers(id, SpvOpTypeStruct, vstate)) {
if (hasDecoration(member_id, decoration, vstate)) {
return true;
}
}
@ -781,12 +784,39 @@ spv_result_t CheckDecorationsOfBuffers(ValidationState_t& vstate) {
for (const auto& inst : vstate.ordered_instructions()) {
const auto& words = inst.words();
if (SpvOpVariable == inst.opcode()) {
const auto var_id = inst.id();
// For storage class / decoration combinations, see Vulkan 14.5.4 "Offset
// and Stride Assignment".
const auto storageClass = words[3];
const bool uniform = storageClass == SpvStorageClassUniform;
const bool uniform_constant =
storageClass == SpvStorageClassUniformConstant;
const bool push_constant = storageClass == SpvStorageClassPushConstant;
const bool storage_buffer = storageClass == SpvStorageClassStorageBuffer;
// Vulkan 14.5.2: Check DescriptorSet and Binding decoration for
// UniformConstant which cannot be a struct.
if (spvIsVulkanEnv(vstate.context()->target_env)) {
if (uniform_constant) {
if (!hasDecoration(var_id, SpvDecorationDescriptorSet, vstate)) {
return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(var_id))
<< "UniformConstant id '" << var_id
<< "' is missing DescriptorSet decoration.\n"
<< "From Vulkan spec, section 14.5.2:\n"
<< "These variables must have DescriptorSet and Binding "
"decorations specified";
}
if (!hasDecoration(var_id, SpvDecorationBinding, vstate)) {
return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(var_id))
<< "UniformConstant id '" << var_id
<< "' is missing Binding decoration.\n"
<< "From Vulkan spec, section 14.5.2:\n"
<< "These variables must have DescriptorSet and Binding "
"decorations specified";
}
}
}
if (uniform || push_constant || storage_buffer) {
const auto ptrInst = vstate.FindDef(words[1]);
assert(SpvOpTypePointer == ptrInst->opcode());
@ -795,9 +825,13 @@ spv_result_t CheckDecorationsOfBuffers(ValidationState_t& vstate) {
MemberConstraints constraints;
ComputeMemberConstraintsForStruct(&constraints, id, LayoutConstraints(),
vstate);
// Prepare for messages
const char* sc_str =
uniform ? "Uniform"
: (push_constant ? "PushConstant" : "StorageBuffer");
// Vulkan 14.5.1: Check Block annotation for PushConstant variables.
if (spvIsVulkanEnv(vstate.context()->target_env)) {
// Vulkan 14.5.1: Check Block decoration for PushConstant variables.
if (push_constant && !hasDecoration(id, SpvDecorationBlock, vstate)) {
return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(id))
<< "PushConstant id '" << id
@ -806,12 +840,28 @@ spv_result_t CheckDecorationsOfBuffers(ValidationState_t& vstate) {
<< "Such variables must be identified with a Block "
"decoration";
}
// Vulkan 14.5.2: Check DescriptorSet and Binding decoration for
// Uniform and StorageBuffer variables.
if (uniform || storage_buffer) {
if (!hasDecoration(var_id, SpvDecorationDescriptorSet, vstate)) {
return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(var_id))
<< sc_str << " id '" << var_id
<< "' is missing DescriptorSet decoration.\n"
<< "From Vulkan spec, section 14.5.2:\n"
<< "These variables must have DescriptorSet and Binding "
"decorations specified";
}
if (!hasDecoration(var_id, SpvDecorationBinding, vstate)) {
return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(var_id))
<< sc_str << " id '" << var_id
<< "' is missing Binding decoration.\n"
<< "From Vulkan spec, section 14.5.2:\n"
<< "These variables must have DescriptorSet and Binding "
"decorations specified";
}
}
}
// Prepare for messages
const char* sc_str =
uniform ? "Uniform"
: (push_constant ? "PushConstant" : "StorageBuffer");
for (const auto& dec : vstate.id_decorations(id)) {
const bool blockDeco = SpvDecorationBlock == dec.dec_type();
const bool bufferDeco = SpvDecorationBufferBlock == dec.dec_type();
@ -825,7 +875,8 @@ spv_result_t CheckDecorationsOfBuffers(ValidationState_t& vstate) {
if (isMissingOffsetInStruct(id, vstate)) {
return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(id))
<< "Structure id " << id << " decorated as " << deco_str
<< " must be explicitly laid out with Offset decorations.";
<< " must be explicitly laid out with Offset "
"decorations.";
} else if (hasDecoration(id, SpvDecorationGLSLShared, vstate)) {
return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(id))
<< "Structure id " << id << " decorated as " << deco_str
@ -946,7 +997,8 @@ spv_result_t CheckDecorationsOfConversions(ValidationState_t& vstate) {
vstate.GetBitWidth(half_float_id) != 16) {
return vstate.diag(SPV_ERROR_INVALID_ID, inst)
<< "FPRoundingMode decoration can be applied only to the "
"Object operand of an OpStore storing through a pointer to "
"Object operand of an OpStore storing through a pointer "
"to "
"a 16-bit floating-point scalar or vector object.";
}

View File

@ -2013,6 +2013,8 @@ TEST_F(ValidateDecorations, BlockArrayBadAlignmentWithRelaxedLayoutStillBad) {
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
@ -2039,7 +2041,7 @@ TEST_F(ValidateDecorations, BlockArrayBadAlignmentWithRelaxedLayoutStillBad) {
EXPECT_THAT(
getDiagnosticString(),
HasSubstr(
"Structure id 3 decorated as Block for variable in Uniform "
"Structure id 4 decorated as Block for variable in Uniform "
"storage class must follow standard uniform buffer layout rules: "
"member 1 at offset 8 is not aligned to 16"));
}
@ -2053,6 +2055,8 @@ TEST_F(ValidateDecorations, BlockArrayBadAlignmentWithVulkan1_1StillBad) {
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
@ -2078,7 +2082,7 @@ TEST_F(ValidateDecorations, BlockArrayBadAlignmentWithVulkan1_1StillBad) {
EXPECT_THAT(
getDiagnosticString(),
HasSubstr(
"Structure id 3 decorated as Block for variable in Uniform "
"Structure id 4 decorated as Block for variable in Uniform "
"storage class must follow relaxed uniform buffer layout rules: "
"member 1 at offset 8 is not aligned to 16"));
}
@ -2279,6 +2283,375 @@ TEST_F(ValidateDecorations, VulkanPushConstantMissingBlockBad) {
"decoration"));
}
TEST_F(ValidateDecorations, VulkanUniformMissingDescriptorSetBad) {
std::string spirv = R"(
OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %1 "main"
OpExecutionMode %1 OriginUpperLeft
OpDecorate %struct Block
OpMemberDecorate %struct 0 Offset 0
OpDecorate %var Binding 0
%void = OpTypeVoid
%voidfn = OpTypeFunction %void
%float = OpTypeFloat 32
%struct = OpTypeStruct %float
%ptr = OpTypePointer Uniform %struct
%var = OpVariable %ptr Uniform
%1 = OpFunction %void None %voidfn
%label = OpLabel
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1);
EXPECT_EQ(SPV_ERROR_INVALID_ID,
ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_1));
EXPECT_THAT(getDiagnosticString(),
HasSubstr("Uniform id '3' is missing DescriptorSet decoration.\n"
"From Vulkan spec, section 14.5.2:\n"
"These variables must have DescriptorSet and Binding "
"decorations specified"));
}
TEST_F(ValidateDecorations, VulkanUniformMissingBindingBad) {
std::string spirv = R"(
OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %1 "main"
OpExecutionMode %1 OriginUpperLeft
OpDecorate %struct Block
OpMemberDecorate %struct 0 Offset 0
OpDecorate %var DescriptorSet 0
%void = OpTypeVoid
%voidfn = OpTypeFunction %void
%float = OpTypeFloat 32
%struct = OpTypeStruct %float
%ptr = OpTypePointer Uniform %struct
%var = OpVariable %ptr Uniform
%1 = OpFunction %void None %voidfn
%label = OpLabel
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1);
EXPECT_EQ(SPV_ERROR_INVALID_ID,
ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_1));
EXPECT_THAT(getDiagnosticString(),
HasSubstr("Uniform id '3' is missing Binding decoration.\n"
"From Vulkan spec, section 14.5.2:\n"
"These variables must have DescriptorSet and Binding "
"decorations specified"));
}
TEST_F(ValidateDecorations, VulkanUniformConstantMissingDescriptorSetBad) {
std::string spirv = R"(
OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %1 "main"
OpExecutionMode %1 OriginUpperLeft
OpDecorate %var Binding 0
%void = OpTypeVoid
%voidfn = OpTypeFunction %void
%sampler = OpTypeSampler
%ptr = OpTypePointer UniformConstant %sampler
%var = OpVariable %ptr UniformConstant
%1 = OpFunction %void None %voidfn
%label = OpLabel
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1);
EXPECT_EQ(SPV_ERROR_INVALID_ID,
ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_1));
EXPECT_THAT(
getDiagnosticString(),
HasSubstr("UniformConstant id '2' is missing DescriptorSet decoration.\n"
"From Vulkan spec, section 14.5.2:\n"
"These variables must have DescriptorSet and Binding "
"decorations specified"));
}
TEST_F(ValidateDecorations, VulkanUniformConstantMissingBindingBad) {
std::string spirv = R"(
OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %1 "main"
OpExecutionMode %1 OriginUpperLeft
OpDecorate %var DescriptorSet 0
%void = OpTypeVoid
%voidfn = OpTypeFunction %void
%sampler = OpTypeSampler
%ptr = OpTypePointer UniformConstant %sampler
%var = OpVariable %ptr UniformConstant
%1 = OpFunction %void None %voidfn
%label = OpLabel
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1);
EXPECT_EQ(SPV_ERROR_INVALID_ID,
ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_1));
EXPECT_THAT(
getDiagnosticString(),
HasSubstr("UniformConstant id '2' is missing Binding decoration.\n"
"From Vulkan spec, section 14.5.2:\n"
"These variables must have DescriptorSet and Binding "
"decorations specified"));
}
TEST_F(ValidateDecorations, VulkanStorageBufferMissingDescriptorSetBad) {
std::string spirv = R"(
OpCapability Shader
OpExtension "SPV_KHR_storage_buffer_storage_class"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %1 "main"
OpExecutionMode %1 OriginUpperLeft
OpDecorate %struct BufferBlock
OpDecorate %var Binding 0
%void = OpTypeVoid
%voidfn = OpTypeFunction %void
%float = OpTypeFloat 32
%struct = OpTypeStruct %float
%ptr = OpTypePointer StorageBuffer %struct
%var = OpVariable %ptr StorageBuffer
%1 = OpFunction %void None %voidfn
%label = OpLabel
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1);
EXPECT_EQ(SPV_ERROR_INVALID_ID,
ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_1));
EXPECT_THAT(
getDiagnosticString(),
HasSubstr("StorageBuffer id '3' is missing DescriptorSet decoration.\n"
"From Vulkan spec, section 14.5.2:\n"
"These variables must have DescriptorSet and Binding "
"decorations specified"));
}
TEST_F(ValidateDecorations, VulkanStorageBufferMissingBindingBad) {
std::string spirv = R"(
OpCapability Shader
OpExtension "SPV_KHR_storage_buffer_storage_class"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %1 "main"
OpExecutionMode %1 OriginUpperLeft
OpDecorate %struct BufferBlock
OpDecorate %var DescriptorSet 0
%void = OpTypeVoid
%voidfn = OpTypeFunction %void
%float = OpTypeFloat 32
%struct = OpTypeStruct %float
%ptr = OpTypePointer StorageBuffer %struct
%var = OpVariable %ptr StorageBuffer
%1 = OpFunction %void None %voidfn
%label = OpLabel
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1);
EXPECT_EQ(SPV_ERROR_INVALID_ID,
ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_1));
EXPECT_THAT(getDiagnosticString(),
HasSubstr("StorageBuffer id '3' is missing Binding decoration.\n"
"From Vulkan spec, section 14.5.2:\n"
"These variables must have DescriptorSet and Binding "
"decorations specified"));
}
TEST_F(ValidateDecorations, UniformMissingDescriptorSetGood) {
std::string spirv = R"(
OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %1 "main"
OpExecutionMode %1 OriginUpperLeft
OpDecorate %struct Block
OpMemberDecorate %struct 0 Offset 0
OpDecorate %var Binding 0
%void = OpTypeVoid
%voidfn = OpTypeFunction %void
%float = OpTypeFloat 32
%struct = OpTypeStruct %float
%ptr = OpTypePointer Uniform %struct
%var = OpVariable %ptr Uniform
%1 = OpFunction %void None %voidfn
%label = OpLabel
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(spirv);
EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState())
<< getDiagnosticString();
}
TEST_F(ValidateDecorations, UniformMissingBindingGood) {
std::string spirv = R"(
OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %1 "main"
OpExecutionMode %1 OriginUpperLeft
OpDecorate %struct Block
OpMemberDecorate %struct 0 Offset 0
OpDecorate %var DescriptorSet 0
%void = OpTypeVoid
%voidfn = OpTypeFunction %void
%float = OpTypeFloat 32
%struct = OpTypeStruct %float
%ptr = OpTypePointer Uniform %struct
%var = OpVariable %ptr Uniform
%1 = OpFunction %void None %voidfn
%label = OpLabel
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(spirv);
EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState())
<< getDiagnosticString();
}
TEST_F(ValidateDecorations, UniformConstantMissingDescriptorSetGood) {
std::string spirv = R"(
OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %1 "main"
OpExecutionMode %1 OriginUpperLeft
OpDecorate %var Binding 0
%void = OpTypeVoid
%voidfn = OpTypeFunction %void
%sampler = OpTypeSampler
%ptr = OpTypePointer UniformConstant %sampler
%var = OpVariable %ptr UniformConstant
%1 = OpFunction %void None %voidfn
%label = OpLabel
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(spirv);
EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState())
<< getDiagnosticString();
}
TEST_F(ValidateDecorations, UniformConstantMissingBindingGood) {
std::string spirv = R"(
OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %1 "main"
OpExecutionMode %1 OriginUpperLeft
OpDecorate %var DescriptorSet 0
%void = OpTypeVoid
%voidfn = OpTypeFunction %void
%sampler = OpTypeSampler
%ptr = OpTypePointer UniformConstant %sampler
%var = OpVariable %ptr UniformConstant
%1 = OpFunction %void None %voidfn
%label = OpLabel
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(spirv);
EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState())
<< getDiagnosticString();
}
TEST_F(ValidateDecorations, StorageBufferMissingDescriptorSetGood) {
std::string spirv = R"(
OpCapability Shader
OpExtension "SPV_KHR_storage_buffer_storage_class"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %1 "main"
OpExecutionMode %1 OriginUpperLeft
OpDecorate %struct BufferBlock
OpDecorate %var Binding 0
%void = OpTypeVoid
%voidfn = OpTypeFunction %void
%float = OpTypeFloat 32
%struct = OpTypeStruct %float
%ptr = OpTypePointer StorageBuffer %struct
%var = OpVariable %ptr StorageBuffer
%1 = OpFunction %void None %voidfn
%label = OpLabel
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(spirv);
EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState())
<< getDiagnosticString();
}
TEST_F(ValidateDecorations, StorageBufferMissingBindingGood) {
std::string spirv = R"(
OpCapability Shader
OpExtension "SPV_KHR_storage_buffer_storage_class"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %1 "main"
OpExecutionMode %1 OriginUpperLeft
OpDecorate %struct BufferBlock
OpDecorate %var DescriptorSet 0
%void = OpTypeVoid
%voidfn = OpTypeFunction %void
%float = OpTypeFloat 32
%struct = OpTypeStruct %float
%ptr = OpTypePointer StorageBuffer %struct
%var = OpVariable %ptr StorageBuffer
%1 = OpFunction %void None %voidfn
%label = OpLabel
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(spirv);
EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState())
<< getDiagnosticString();
}
TEST_F(ValidateDecorations, StorageBufferStorageClassArrayBaseAlignmentGood) {
// Spot check buffer rules when using StorageBuffer storage class with Block
// decoration.

View File

@ -61,6 +61,37 @@ OpCapability ImageBuffer
ss << "OpExecutionMode %main OriginUpperLeft\n";
}
if (env == SPV_ENV_VULKAN_1_0) {
ss << R"(
OpDecorate %uniform_image_f32_1d_0001 DescriptorSet 0
OpDecorate %uniform_image_f32_1d_0001 Binding 0
OpDecorate %uniform_image_f32_1d_0002_rgba32f DescriptorSet 0
OpDecorate %uniform_image_f32_1d_0002_rgba32f Binding 1
OpDecorate %uniform_image_f32_2d_0001 DescriptorSet 0
OpDecorate %uniform_image_f32_2d_0001 Binding 2
OpDecorate %uniform_image_f32_2d_0010 DescriptorSet 0
OpDecorate %uniform_image_f32_2d_0010 Binding 3
OpDecorate %uniform_image_u32_2d_0001 DescriptorSet 1
OpDecorate %uniform_image_u32_2d_0001 Binding 0
OpDecorate %uniform_image_u32_2d_0000 DescriptorSet 1
OpDecorate %uniform_image_u32_2d_0000 Binding 1
OpDecorate %uniform_image_s32_3d_0001 DescriptorSet 1
OpDecorate %uniform_image_s32_3d_0001 Binding 2
OpDecorate %uniform_image_f32_2d_0002 DescriptorSet 1
OpDecorate %uniform_image_f32_2d_0002 Binding 3
OpDecorate %uniform_image_f32_spd_0002 DescriptorSet 2
OpDecorate %uniform_image_f32_spd_0002 Binding 0
OpDecorate %uniform_image_f32_3d_0111 DescriptorSet 2
OpDecorate %uniform_image_f32_3d_0111 Binding 1
OpDecorate %uniform_image_f32_cube_0101 DescriptorSet 2
OpDecorate %uniform_image_f32_cube_0101 Binding 2
OpDecorate %uniform_image_f32_cube_0102_rgba32f DescriptorSet 2
OpDecorate %uniform_image_f32_cube_0102_rgba32f Binding 3
OpDecorate %uniform_sampler DescriptorSet 3
OpDecorate %uniform_sampler Binding 0
)";
}
ss << R"(
%void = OpTypeVoid
%func = OpTypeFunction %void

View File

@ -64,6 +64,8 @@ OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %func "func"
OpExecutionMode %func OriginUpperLeft
OpDecorate %2 DescriptorSet 0
OpDecorate %2 Binding 0
%sampler = OpTypeSampler
%sampler_ptr = OpTypePointer UniformConstant %sampler
%2 = OpVariable %sampler_ptr UniformConstant
@ -115,6 +117,8 @@ OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %func "func"
OpExecutionMode %func OriginUpperLeft
OpDecorate %2 DescriptorSet 0
OpDecorate %2 Binding 0
%sampler = OpTypeSampler
%uint = OpTypeInt 32 0
%array_size = OpConstant %uint 5
@ -138,6 +142,8 @@ OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %func "func"
OpExecutionMode %func OriginUpperLeft
OpDecorate %2 DescriptorSet 0
OpDecorate %2 Binding 0
%sampler = OpTypeSampler
%uint = OpTypeInt 32 0
%array = OpTypeRuntimeArray %sampler