Validate that there is at most one push constant block (#2163)

Fixes #2006

Validates that there is at most one PushConstant interface per entry point for Vulkan environment.
This commit is contained in:
Alejandro Lopez 2018-12-05 13:30:04 -05:00 committed by alan-baker
parent 3e645b9d67
commit 2f5f5308b6
2 changed files with 286 additions and 2 deletions

View File

@ -781,6 +781,8 @@ void ComputeMemberConstraintsForArray(MemberConstraints* constraints,
}
spv_result_t CheckDecorationsOfBuffers(ValidationState_t& vstate) {
// Set of entry points that are known to use a push constant.
std::unordered_set<uint32_t> uses_push_constant;
for (const auto& inst : vstate.ordered_instructions()) {
const auto& words = inst.words();
if (SpvOpVariable == inst.opcode()) {
@ -794,9 +796,25 @@ spv_result_t CheckDecorationsOfBuffers(ValidationState_t& vstate) {
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)) {
// Vulkan 14.5.1: There must be no more than one PushConstant block
// per entry point.
if (push_constant) {
auto entry_points = vstate.EntryPointReferences(var_id);
for (auto ep_id : entry_points) {
const bool already_used = !uses_push_constant.insert(ep_id).second;
if (already_used) {
return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(var_id))
<< "Entry point id '" << ep_id
<< "' uses more than one PushConstant interface.\n"
<< "From Vulkan spec, section 14.5.1:\n"
<< "There must be no more than one push constant block "
<< "statically used per shader entry point.";
}
}
}
// Vulkan 14.5.2: Check DescriptorSet and Binding decoration for
// UniformConstant which cannot be a struct.
if (uniform_constant) {
auto entry_points = vstate.EntryPointReferences(var_id);
if (!entry_points.empty() &&

View File

@ -2283,6 +2283,272 @@ TEST_F(ValidateDecorations, VulkanPushConstantMissingBlockBad) {
"decoration"));
}
TEST_F(ValidateDecorations, MultiplePushConstantsSingleEntryPointGood) {
std::string spirv = R"(
OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %1 "main"
OpExecutionMode %1 OriginUpperLeft
OpDecorate %struct Block
OpMemberDecorate %struct 0 Offset 0
%void = OpTypeVoid
%voidfn = OpTypeFunction %void
%float = OpTypeFloat 32
%int = OpTypeInt 32 0
%int_0 = OpConstant %int 0
%struct = OpTypeStruct %float
%ptr = OpTypePointer PushConstant %struct
%ptr_float = OpTypePointer PushConstant %float
%pc1 = OpVariable %ptr PushConstant
%pc2 = OpVariable %ptr PushConstant
%1 = OpFunction %void None %voidfn
%label = OpLabel
%2 = OpAccessChain %ptr_float %pc1 %int_0
%3 = OpLoad %float %2
%4 = OpAccessChain %ptr_float %pc2 %int_0
%5 = OpLoad %float %4
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(spirv);
EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState())
<< getDiagnosticString();
}
TEST_F(ValidateDecorations,
VulkanMultiplePushConstantsDifferentEntryPointGood) {
std::string spirv = R"(
OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint Vertex %1 "func1"
OpEntryPoint Fragment %2 "func2"
OpExecutionMode %2 OriginUpperLeft
OpDecorate %struct Block
OpMemberDecorate %struct 0 Offset 0
%void = OpTypeVoid
%voidfn = OpTypeFunction %void
%float = OpTypeFloat 32
%int = OpTypeInt 32 0
%int_0 = OpConstant %int 0
%struct = OpTypeStruct %float
%ptr = OpTypePointer PushConstant %struct
%ptr_float = OpTypePointer PushConstant %float
%pc1 = OpVariable %ptr PushConstant
%pc2 = OpVariable %ptr PushConstant
%1 = OpFunction %void None %voidfn
%label1 = OpLabel
%3 = OpAccessChain %ptr_float %pc1 %int_0
%4 = OpLoad %float %3
OpReturn
OpFunctionEnd
%2 = OpFunction %void None %voidfn
%label2 = OpLabel
%5 = OpAccessChain %ptr_float %pc2 %int_0
%6 = OpLoad %float %5
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1);
EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_1))
<< getDiagnosticString();
}
TEST_F(ValidateDecorations,
VulkanMultiplePushConstantsUnusedSingleEntryPointGood) {
std::string spirv = R"(
OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %1 "main"
OpExecutionMode %1 OriginUpperLeft
OpDecorate %struct Block
OpMemberDecorate %struct 0 Offset 0
%void = OpTypeVoid
%voidfn = OpTypeFunction %void
%float = OpTypeFloat 32
%int = OpTypeInt 32 0
%int_0 = OpConstant %int 0
%struct = OpTypeStruct %float
%ptr = OpTypePointer PushConstant %struct
%ptr_float = OpTypePointer PushConstant %float
%pc1 = OpVariable %ptr PushConstant
%pc2 = OpVariable %ptr PushConstant
%1 = OpFunction %void None %voidfn
%label = OpLabel
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1);
EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_1))
<< getDiagnosticString();
}
TEST_F(ValidateDecorations, VulkanMultiplePushConstantsSingleEntryPointBad) {
std::string spirv = R"(
OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %1 "main"
OpExecutionMode %1 OriginUpperLeft
OpDecorate %struct Block
OpMemberDecorate %struct 0 Offset 0
%void = OpTypeVoid
%voidfn = OpTypeFunction %void
%float = OpTypeFloat 32
%int = OpTypeInt 32 0
%int_0 = OpConstant %int 0
%struct = OpTypeStruct %float
%ptr = OpTypePointer PushConstant %struct
%ptr_float = OpTypePointer PushConstant %float
%pc1 = OpVariable %ptr PushConstant
%pc2 = OpVariable %ptr PushConstant
%1 = OpFunction %void None %voidfn
%label = OpLabel
%2 = OpAccessChain %ptr_float %pc1 %int_0
%3 = OpLoad %float %2
%4 = OpAccessChain %ptr_float %pc2 %int_0
%5 = OpLoad %float %4
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(
"Entry point id '1' uses more than one PushConstant interface.\n"
"From Vulkan spec, section 14.5.1:\n"
"There must be no more than one push constant block "
"statically used per shader entry point."));
}
TEST_F(ValidateDecorations,
VulkanMultiplePushConstantsDifferentEntryPointSubFunctionGood) {
std::string spirv = R"(
OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint Vertex %1 "func1"
OpEntryPoint Fragment %2 "func2"
OpExecutionMode %2 OriginUpperLeft
OpDecorate %struct Block
OpMemberDecorate %struct 0 Offset 0
%void = OpTypeVoid
%voidfn = OpTypeFunction %void
%float = OpTypeFloat 32
%int = OpTypeInt 32 0
%int_0 = OpConstant %int 0
%struct = OpTypeStruct %float
%ptr = OpTypePointer PushConstant %struct
%ptr_float = OpTypePointer PushConstant %float
%pc1 = OpVariable %ptr PushConstant
%pc2 = OpVariable %ptr PushConstant
%sub1 = OpFunction %void None %voidfn
%label_sub1 = OpLabel
%3 = OpAccessChain %ptr_float %pc1 %int_0
%4 = OpLoad %float %3
OpReturn
OpFunctionEnd
%sub2 = OpFunction %void None %voidfn
%label_sub2 = OpLabel
%5 = OpAccessChain %ptr_float %pc2 %int_0
%6 = OpLoad %float %5
OpReturn
OpFunctionEnd
%1 = OpFunction %void None %voidfn
%label1 = OpLabel
%call1 = OpFunctionCall %void %sub1
OpReturn
OpFunctionEnd
%2 = OpFunction %void None %voidfn
%label2 = OpLabel
%call2 = OpFunctionCall %void %sub2
OpReturn
OpFunctionEnd
)";
CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1);
EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_1))
<< getDiagnosticString();
}
TEST_F(ValidateDecorations,
VulkanMultiplePushConstantsSingleEntryPointSubFunctionBad) {
std::string spirv = R"(
OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %1 "main"
OpExecutionMode %1 OriginUpperLeft
OpDecorate %struct Block
OpMemberDecorate %struct 0 Offset 0
%void = OpTypeVoid
%voidfn = OpTypeFunction %void
%float = OpTypeFloat 32
%int = OpTypeInt 32 0
%int_0 = OpConstant %int 0
%struct = OpTypeStruct %float
%ptr = OpTypePointer PushConstant %struct
%ptr_float = OpTypePointer PushConstant %float
%pc1 = OpVariable %ptr PushConstant
%pc2 = OpVariable %ptr PushConstant
%sub1 = OpFunction %void None %voidfn
%label_sub1 = OpLabel
%3 = OpAccessChain %ptr_float %pc1 %int_0
%4 = OpLoad %float %3
OpReturn
OpFunctionEnd
%sub2 = OpFunction %void None %voidfn
%label_sub2 = OpLabel
%5 = OpAccessChain %ptr_float %pc2 %int_0
%6 = OpLoad %float %5
OpReturn
OpFunctionEnd
%1 = OpFunction %void None %voidfn
%label1 = OpLabel
%call1 = OpFunctionCall %void %sub1
%call2 = OpFunctionCall %void %sub2
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(
"Entry point id '1' uses more than one PushConstant interface.\n"
"From Vulkan spec, section 14.5.1:\n"
"There must be no more than one push constant block "
"statically used per shader entry point."));
}
TEST_F(ValidateDecorations, VulkanUniformMissingDescriptorSetBad) {
std::string spirv = R"(
OpCapability Shader