From b9802946eda0df8204cc2c82a2c11946df666b64 Mon Sep 17 00:00:00 2001 From: spencer-lunarg Date: Sun, 6 Sep 2026 13:11:04 -0400 Subject: [PATCH] spirv-val: Component of array-of-arrays --- source/val/validate_interfaces.cpp | 12 ++++++ source/val/validation_state.cpp | 4 ++ test/val/val_interfaces_test.cpp | 66 ++++++++++++++++++++++++++++++ 3 files changed, 82 insertions(+) diff --git a/source/val/validate_interfaces.cpp b/source/val/validate_interfaces.cpp index 4a442ff972..ffa10afda1 100644 --- a/source/val/validate_interfaces.cpp +++ b/source/val/validate_interfaces.cpp @@ -290,6 +290,7 @@ spv_result_t GetLocationsForVariable( // validator allows duplicate decorations if the location/component/index are // equal. Also track Patch and PerTaskNV decorations. bool has_location = false; + bool has_component = false; uint32_t location = 0; uint32_t component = 0; bool has_index = false; @@ -304,6 +305,7 @@ spv_result_t GetLocationsForVariable( location = dec.params()[0]; } else if (dec.dec_type() == spv::Decoration::Component) { component = dec.params()[0]; + has_component = true; } else if (dec.dec_type() == spv::Decoration::Index) { if (!is_output || !is_fragment) { return _.diag(SPV_ERROR_INVALID_DATA, variable) @@ -376,6 +378,16 @@ spv_result_t GetLocationsForVariable( type = _.FindDef(type_id); } + if (has_component && type->opcode() == spv::Op::OpTypeArray && + _.GetIdOpcode(type->GetOperandAs(1)) == spv::Op::OpTypeArray) { + return _.diag(SPV_ERROR_INVALID_DATA, variable) + << _.VkErrorID(is_output ? 10585 : 10584) + << "Entry-point interface variable in the " + << (is_output ? "Output" : "Input") + << " storage class is decorated with Component and is an array of " + "arrays of scalars or vectors"; + } + if (type->opcode() == spv::Op::OpTypeStruct) { // Don't check built-ins. if (_.HasDecoration(type_id, spv::Decoration::BuiltIn)) return SPV_SUCCESS; diff --git a/source/val/validation_state.cpp b/source/val/validation_state.cpp index 4f7c7d00a8..d52c487b84 100644 --- a/source/val/validation_state.cpp +++ b/source/val/validation_state.cpp @@ -3652,6 +3652,10 @@ std::string ValidationState_t::VkErrorID(uint32_t id, return VUID_WRAP(VUID-StandaloneSpirv-OpTypeFloat-10370); case 10583: return VUID_WRAP(VUID-StandaloneSpirv-Component-10583); + case 10584: + return VUID_WRAP(VUID-StandaloneSpirv-Execution-10584); + case 10585: + return VUID_WRAP(VUID-StandaloneSpirv-Execution-10585); case 10589: return VUID_WRAP(VUID-CullPrimitiveEXT-CullPrimitiveEXT-10589); case 10590: diff --git a/test/val/val_interfaces_test.cpp b/test/val/val_interfaces_test.cpp index 3894ca347f..da865ca899 100644 --- a/test/val/val_interfaces_test.cpp +++ b/test/val/val_interfaces_test.cpp @@ -2310,6 +2310,72 @@ OpFunctionEnd ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_3)); } +TEST_F(ValidateInterfacesTest, VulkanComponentArrayOfArraysInput) { + const std::string text = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %var + OpExecutionMode %main OriginUpperLeft + OpDecorate %var Location 0 + OpDecorate %var Component 0 + %void = OpTypeVoid + %void_fn = OpTypeFunction %void + %float = OpTypeFloat 32 + %uint = OpTypeInt 32 0 + %uint_2 = OpConstant %uint 2 + %arr_float = OpTypeArray %float %uint_2 + %arr_arr_float = OpTypeArray %arr_float %uint_2 + %ptr_input_arr_arr = OpTypePointer Input %arr_arr_float + %var = OpVariable %ptr_input_arr_arr Input + %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-Execution-10584")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Entry-point interface variable in the Input storage " + "class is decorated with Component and is an array of " + "arrays of scalars or vectors")); +} + +TEST_F(ValidateInterfacesTest, VulkanComponentArrayOfArraysOutput) { + const std::string text = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %var + OpExecutionMode %main OriginUpperLeft + OpDecorate %var Location 0 + OpDecorate %var Component 0 + %void = OpTypeVoid + %void_fn = OpTypeFunction %void + %float = OpTypeFloat 32 + %uint = OpTypeInt 32 0 + %uint_2 = OpConstant %uint 2 + %arr_float = OpTypeArray %float %uint_2 + %arr_arr_float = OpTypeArray %arr_float %uint_2 + %ptr_output_arr_arr = OpTypePointer Output %arr_arr_float + %var = OpVariable %ptr_output_arr_arr Output + %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-Execution-10585")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Entry-point interface variable in the Output storage " + "class is decorated with Component and is an array of " + "arrays of scalars or vectors")); +} + } // namespace } // namespace val } // namespace spvtools