diff --git a/source/val/validate_interfaces.cpp b/source/val/validate_interfaces.cpp index 23e18570c2..42685369a4 100644 --- a/source/val/validate_interfaces.cpp +++ b/source/val/validate_interfaces.cpp @@ -529,6 +529,61 @@ spv_result_t GetLocationsForVariable( return SPV_SUCCESS; } +bool IsInterpolationDecoration(spv::Decoration dec) { + return dec == spv::Decoration::PerVertexKHR || dec == spv::Decoration::Flat || + dec == spv::Decoration::NoPerspective || + dec == spv::Decoration::Sample || dec == spv::Decoration::Centroid; +} + +// Need to check Variables (or struct members) with explicit Locaiton decoration +// don't mix interpolation decorations +spv_result_t ValidateLocationInterpolation( + ValidationState_t& _, bool is_input, const Instruction* variable, + std::unordered_map>& + interpolation_map) { + uint32_t location = 0; + bool has_location = false; + std::vector interpolation_decs; + for (auto& dec : _.id_decorations(variable->id())) { + spv::Decoration decoration = dec.dec_type(); + if (decoration == spv::Decoration::Location) { + location = dec.params()[0]; + has_location = true; + } else if (IsInterpolationDecoration(decoration)) { + interpolation_decs.push_back(decoration); + } + } + + // Look for Location in a Block decorated Struct + if (!has_location) { + auto ptr_type_id = variable->GetOperandAs(0); + auto ptr_type = _.FindDef(ptr_type_id); + auto type_id = ptr_type->GetOperandAs(2); + for (auto& dec : _.id_decorations(type_id)) { + spv::Decoration decoration = dec.dec_type(); + if (decoration == spv::Decoration::Location) { + location = dec.params()[0]; + has_location = true; + } else if (IsInterpolationDecoration(decoration)) { + interpolation_decs.push_back(decoration); + } + } + } + + if (has_location) { + auto it = interpolation_map.find(location); + if (it == interpolation_map.end()) { + interpolation_map[location] = interpolation_decs; + } else if (interpolation_map[location] != interpolation_decs) { + return _.diag(SPV_ERROR_INVALID_DATA, variable) + << _.VkErrorID(10604) << (is_input ? "input" : "output") + << " Location " << location + << " has conflicting Interpolation decorations"; + } + } + return SPV_SUCCESS; +} + spv_result_t ValidateLocations(ValidationState_t& _, const Instruction* entry_point) { // According to Vulkan 14.1 only the following execution models have @@ -554,6 +609,12 @@ spv_result_t ValidateLocations(ValidationState_t& _, std::unordered_set patch_locations_index0; std::unordered_set patch_locations_index1; std::unordered_set seen; + + std::unordered_map> + input_interpolation; + std::unordered_map> + output_interpolation; + for (uint32_t i = 3; i < entry_point->operands().size(); ++i) { auto interface_id = entry_point->GetOperandAs(i); auto interface_var = _.FindDef(interface_id); @@ -590,12 +651,17 @@ spv_result_t ValidateLocations(ValidationState_t& _, continue; } - auto locations = (storage_class == spv::StorageClass::Input) - ? &input_locations - : &output_locations_index0; + bool is_input = storage_class == spv::StorageClass::Input; + auto locations = is_input ? &input_locations : &output_locations_index0; if (auto error = GetLocationsForVariable( _, entry_point, interface_var, locations, &output_locations_index1)) return error; + + auto& interpolation_map = + is_input ? input_interpolation : output_interpolation; + if (auto error = ValidateLocationInterpolation(_, is_input, interface_var, + interpolation_map)) + return error; } return SPV_SUCCESS; diff --git a/source/val/validation_state.cpp b/source/val/validation_state.cpp index 01d299842b..fc37050eb6 100644 --- a/source/val/validation_state.cpp +++ b/source/val/validation_state.cpp @@ -2536,6 +2536,8 @@ std::string ValidationState_t::VkErrorID(uint32_t id, return VUID_WRAP(VUID-RuntimeSpirv-Offset-10213); case 10583: return VUID_WRAP(VUID-StandaloneSpirv-Component-10583); + case 10604: + return VUID_WRAP(VUID-StandaloneSpirv-Input-10604); default: return ""; // unknown id } diff --git a/test/val/val_interfaces_test.cpp b/test/val/val_interfaces_test.cpp index c3a1c7ccb2..ee4e7ff1b3 100644 --- a/test/val/val_interfaces_test.cpp +++ b/test/val/val_interfaces_test.cpp @@ -1939,6 +1939,321 @@ OpFunctionEnd EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_3)); } +TEST_F(ValidateInterfacesTest, InterpolationOverlapVariableSuccess) { + const std::string text = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %cFlat %cFlat2 %cSmooth + OpExecutionMode %main OriginUpperLeft + OpDecorate %cFlat Flat + OpDecorate %cFlat Location 0 + OpDecorate %cFlat Component 0 + OpDecorate %cFlat2 Flat + OpDecorate %cFlat2 Location 0 + OpDecorate %cFlat2 Component 1 + OpDecorate %cSmooth Centroid + OpDecorate %cSmooth Location 1 + OpDecorate %cSmooth Component 0 +%void = OpTypeVoid +%4 = OpTypeFunction %void +%float = OpTypeFloat 32 +%in_ptr = OpTypePointer Input %float +%cFlat = OpVariable %in_ptr Input +%cFlat2 = OpVariable %in_ptr Input +%cSmooth = OpVariable %in_ptr Input +%main = OpFunction %void None %4 +%6 = OpLabel + OpReturn + OpFunctionEnd + )"; + + CompileSuccessfully(text, SPV_ENV_VULKAN_1_0); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0)); +} + +TEST_F(ValidateInterfacesTest, InterpolationOverlapVariableBad) { + const std::string text = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %cFlat %cSmooth + OpExecutionMode %main OriginUpperLeft + OpDecorate %cFlat Flat + OpDecorate %cFlat Location 0 + OpDecorate %cFlat Component 0 + OpDecorate %cSmooth Centroid + OpDecorate %cSmooth Location 0 + OpDecorate %cSmooth Component 1 +%void = OpTypeVoid +%4 = OpTypeFunction %void +%float = OpTypeFloat 32 +%in_ptr = OpTypePointer Input %float +%cFlat = OpVariable %in_ptr Input +%cSmooth = OpVariable %in_ptr Input +%main = OpFunction %void None %4 +%6 = OpLabel + OpReturn + OpFunctionEnd + )"; + + CompileSuccessfully(text, SPV_ENV_VULKAN_1_0); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-Input-10604")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("input Location 0 has conflicting Interpolation decorations")); +} + +TEST_F(ValidateInterfacesTest, InterpolationOverlapBlockSuccess) { + const std::string text = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %in +OpExecutionMode %main OriginUpperLeft +OpDecorate %struct Block +OpMemberDecorate %struct 0 Flat +OpMemberDecorate %struct 0 Location 0 +OpMemberDecorate %struct 0 Component 0 +OpMemberDecorate %struct 2 Flat +OpMemberDecorate %struct 2 Location 0 +OpMemberDecorate %struct 2 Component 1 +OpMemberDecorate %struct 1 Centroid +OpMemberDecorate %struct 1 Location 1 +OpMemberDecorate %struct 1 Component 0 +%void = OpTypeVoid +%float = OpTypeFloat 32 +%struct = OpTypeStruct %float %float %float +%in_ptr = OpTypePointer Input %struct +%in = OpVariable %in_ptr Input +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd + )"; + + CompileSuccessfully(text, SPV_ENV_VULKAN_1_0); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0)); +} + +TEST_F(ValidateInterfacesTest, InterpolationOverlapBlockMixMembers) { + const std::string text = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %in +OpExecutionMode %main OriginUpperLeft +OpDecorate %struct Block +OpMemberDecorate %struct 0 Flat +OpMemberDecorate %struct 0 Location 0 +OpMemberDecorate %struct 0 Component 0 +OpMemberDecorate %struct 1 Centroid +OpMemberDecorate %struct 1 Location 0 +OpMemberDecorate %struct 1 Component 1 +%void = OpTypeVoid +%float = OpTypeFloat 32 +%struct = OpTypeStruct %float %float +%in_ptr = OpTypePointer Input %struct +%in = OpVariable %in_ptr Input +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd + )"; + + CompileSuccessfully(text, SPV_ENV_VULKAN_1_0); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0)); +} + +TEST_F(ValidateInterfacesTest, InterpolationOverlapBlockAndVariableSuccess) { + const std::string text = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %in_s %in_f + OpExecutionMode %main OriginUpperLeft + OpDecorate %struct Block + OpMemberDecorate %struct 0 Flat + OpMemberDecorate %struct 0 Location 0 + OpMemberDecorate %struct 0 Component 0 + OpDecorate %in_f Flat + OpDecorate %in_f Location 0 + OpDecorate %in_f Component 1 + %void = OpTypeVoid + %float = OpTypeFloat 32 + %struct = OpTypeStruct %float + %s_ptr = OpTypePointer Input %struct + %f_ptr = OpTypePointer Input %float + %in_s = OpVariable %s_ptr Input + %in_f = OpVariable %f_ptr Input + %void_fn = OpTypeFunction %void + %main = OpFunction %void None %void_fn + %entry = OpLabel + OpReturn + OpFunctionEnd + )"; + + CompileSuccessfully(text, SPV_ENV_VULKAN_1_0); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0)); +} + +TEST_F(ValidateInterfacesTest, InterpolationOverlapBlockAndVariableBad) { + const std::string text = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %in_s %in_f +OpExecutionMode %main OriginUpperLeft +OpDecorate %struct Block +OpMemberDecorate %struct 0 Flat +OpMemberDecorate %struct 0 Location 0 +OpMemberDecorate %struct 0 Component 0 +OpDecorate %in_f Centroid +OpDecorate %in_f Location 0 +OpDecorate %in_f Component 1 +%void = OpTypeVoid +%float = OpTypeFloat 32 +%struct = OpTypeStruct %float +%s_ptr = OpTypePointer Input %struct +%f_ptr = OpTypePointer Input %float +%in_s = OpVariable %s_ptr Input +%in_f = OpVariable %f_ptr Input +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd + )"; + + CompileSuccessfully(text, SPV_ENV_VULKAN_1_0); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-Input-10604")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("input Location 0 has conflicting Interpolation decorations")); +} + +TEST_F(ValidateInterfacesTest, InterpolationOverlapSingleVariable) { + const std::string text = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %in_f +OpExecutionMode %main OriginUpperLeft +OpDecorate %in_f Flat +OpDecorate %in_f Centroid +OpDecorate %in_f Location 0 +%void = OpTypeVoid +%float = OpTypeFloat 32 +%f_ptr = OpTypePointer Input %float +%in_f = OpVariable %f_ptr Input +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd + )"; + + CompileSuccessfully(text, SPV_ENV_VULKAN_1_0); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0)); +} + +TEST_F(ValidateInterfacesTest, InterpolationOverlapVariableWithoutDecoration) { + const std::string text = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %in_a %in_b +OpExecutionMode %main OriginUpperLeft +OpDecorate %in_a Flat +OpDecorate %in_a Location 0 +OpDecorate %in_a Component 0 +OpDecorate %in_b Location 0 +OpDecorate %in_b Component 1 +%void = OpTypeVoid +%float = OpTypeFloat 32 +%f_ptr = OpTypePointer Input %float +%in_a = OpVariable %f_ptr Input +%in_b = OpVariable %f_ptr Input +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd + )"; + + CompileSuccessfully(text, SPV_ENV_VULKAN_1_0); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-Input-10604")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("input Location 0 has conflicting Interpolation decorations")); +} + +TEST_F(ValidateInterfacesTest, + InterpolationOverlapVariableMultipleDecorationSuccess) { + const std::string text = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %in_a %in_b +OpExecutionMode %main OriginUpperLeft +OpDecorate %in_a Flat +OpDecorate %in_a Centroid +OpDecorate %in_a Location 0 +OpDecorate %in_a Component 0 +OpDecorate %in_b Flat +OpDecorate %in_b Centroid +OpDecorate %in_b Location 0 +OpDecorate %in_b Component 1 +%void = OpTypeVoid +%float = OpTypeFloat 32 +%f_ptr = OpTypePointer Input %float +%in_a = OpVariable %f_ptr Input +%in_b = OpVariable %f_ptr Input +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd + )"; + + CompileSuccessfully(text, SPV_ENV_VULKAN_1_0); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0)); +} + +TEST_F(ValidateInterfacesTest, + InterpolationOverlapVariableMultipleDecorationBad) { + const std::string text = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %in_a %in_b + OpExecutionMode %main OriginUpperLeft + OpDecorate %in_a Flat + OpDecorate %in_a Centroid + OpDecorate %in_a Location 0 + OpDecorate %in_a Component 0 + OpDecorate %in_b Flat + OpDecorate %in_b Location 0 + OpDecorate %in_b Component 1 + %void = OpTypeVoid + %float = OpTypeFloat 32 + %f_ptr = OpTypePointer Input %float + %in_a = OpVariable %f_ptr Input + %in_b = OpVariable %f_ptr Input + %void_fn = OpTypeFunction %void + %main = OpFunction %void None %void_fn + %entry = OpLabel + OpReturn + OpFunctionEnd + )"; + + CompileSuccessfully(text, SPV_ENV_VULKAN_1_0); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-StandaloneSpirv-Input-10604")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("input Location 0 has conflicting Interpolation decorations")); +} + } // namespace } // namespace val } // namespace spvtools