Closed charles-r-earp closed 11 months ago
With #2402, "push_uint_before_struct" passes, however "multiple_entry_points" does not, after adding an additional assertion that the push constant range is correct.
It looks like the push constant range is reflected globally, not per entry point, which in this test "main1" uses a push constant with 2 u32s (size = 8), and "main2" doesn't. But the test shows that both get the same range of 8.
There's a TODO Rua left in the PR, I'm not sure how to go about that myself.
Related to #756.
shader::reflect::entry_points
callsshader::reflect::push_constant_requirements
, which looks like this:This algorithm assumes that the first pointer of StorageClass::PushConstant will point to a TypeStruct, and that there is only one of these in the module.
I created tests to demonstrate:
Details
``` running 2 tests test push_uint_before_struct ... FAILED test multiple_entry_points ... FAILED failures: ---- push_uint_before_struct stdout ---- ; SPIR-V ; Version: 1.5 ; Generator: rspirv ; Bound: 13 OpMemoryModel Logical Vulkan OpEntryPoint GLCompute %9 "main1" %1 = OpTypeVoid %2 = OpTypeFunction %1 %1 %3 = OpTypeInt 32 0 %4 = OpTypePointer PushConstant %3 %5 = OpConstant %3 0 %6 = OpTypeStruct %3 %3 %7 = OpTypePointer PushConstant %6 %8 = OpVariable %7 PushConstant %9 = OpFunction %1 DontInline|Const %2 %10 = OpLabel %11 = OpAccessChain %4 %8 %5 %12 = OpLoad %3 %11 OpReturn OpFunctionEnd [reflect-push-constants-issue/src/lib.rs:40] &spirv = Spirv { version: 1.5.0, bound: 13, ids: { Id( 9, ): IdInfo { instruction: Function { result_type_id: Id( 1, ), result_id: Id( 9, ), function_control: FunctionControl { inline: false, dont_inline: true, pure: false, constant: true, opt_none_intel: false, }, function_type: Id( 2, ), }, names: [], decorations: [], members: [], }, Id( 4, ): IdInfo { instruction: TypePointer { result_id: Id( 4, ), storage_class: PushConstant, ty: Id( 3, ), }, names: [], decorations: [], members: [], }, Id( 7, ): IdInfo { instruction: TypePointer { result_id: Id( 7, ), storage_class: PushConstant, ty: Id( 6, ), }, names: [], decorations: [], members: [], }, Id( 3, ): IdInfo { instruction: TypeInt { result_id: Id( 3, ), width: 32, signedness: 0, }, names: [], decorations: [], members: [], }, Id( 1, ): IdInfo { instruction: TypeVoid { result_id: Id( 1, ), }, names: [], decorations: [], members: [], }, Id( 10, ): IdInfo { instruction: Label { result_id: Id( 10, ), }, names: [], decorations: [], members: [], }, Id( 6, ): IdInfo { instruction: TypeStruct { result_id: Id( 6, ), member_types: [ Id( 3, ), Id( 3, ), ], }, names: [], decorations: [], members: [ StructMemberInfo { names: [], decorations: [], }, StructMemberInfo { names: [], decorations: [], }, ], }, Id( 5, ): IdInfo { instruction: Constant { result_type_id: Id( 3, ), result_id: Id( 5, ), value: [ 0, ], }, names: [], decorations: [], members: [], }, Id( 12, ): IdInfo { instruction: Load { result_type_id: Id( 3, ), result_id: Id( 12, ), pointer: Id( 11, ), memory_access: None, }, names: [], decorations: [], members: [], }, Id( 8, ): IdInfo { instruction: Variable { result_type_id: Id( 7, ), result_id: Id( 8, ), storage_class: PushConstant, initializer: None, }, names: [], decorations: [], members: [], }, Id( 11, ): IdInfo { instruction: AccessChain { result_type_id: Id( 4, ), result_id: Id( 11, ), base: Id( 8, ), indexes: [ Id( 5, ), ], }, names: [], decorations: [], members: [], }, Id( 2, ): IdInfo { instruction: TypeFunction { result_id: Id( 2, ), return_type: Id( 1, ), parameter_types: [ Id( 1, ), ], }, names: [], decorations: [], members: [], }, }, instructions_capability: [], instructions_extension: [], instructions_ext_inst_import: [], instruction_memory_model: MemoryModel { addressing_model: Logical, memory_model: Vulkan, }, instructions_entry_point: [ EntryPoint { execution_model: GLCompute, entry_point: Id( 9, ), name: "main1", interface: [], }, ], instructions_execution_mode: [], instructions_name: [], instructions_decoration: [], instructions_global: [ TypeVoid { result_id: Id( 1, ), }, TypeFunction { result_id: Id( 2, ), return_type: Id( 1, ), parameter_types: [ Id( 1, ), ], }, TypeInt { result_id: Id( 3, ), width: 32, signedness: 0, }, TypePointer { result_id: Id( 4, ), storage_class: PushConstant, ty: Id( 3, ), }, Constant { result_type_id: Id( 3, ), result_id: Id( 5, ), value: [ 0, ], }, TypeStruct { result_id: Id( 6, ), member_types: [ Id( 3, ), Id( 3, ), ], }, TypePointer { result_id: Id( 7, ), storage_class: PushConstant, ty: Id( 6, ), }, Variable { result_type_id: Id( 7, ), result_id: Id( 8, ), storage_class: PushConstant, initializer: None, }, ], functions: { Id( 9, ): FunctionInfo { instructions: [ Function { result_type_id: Id( 1, ), result_id: Id( 9, ), function_control: FunctionControl { inline: false, dont_inline: true, pure: false, constant: true, opt_none_intel: false, }, function_type: Id( 2, ), }, Label { result_id: Id( 10, ), }, AccessChain { result_type_id: Id( 4, ), result_id: Id( 11, ), base: Id( 8, ), indexes: [ Id( 5, ), ], }, Load { result_type_id: Id( 3, ), result_id: Id( 12, ), pointer: Id( 11, ), memory_access: None, }, Return, FunctionEnd, ], entry_point: Some( EntryPoint { execution_model: GLCompute, entry_point: Id( 9, ), name: "main1", interface: [], }, ), execution_modes: [], }, }, } thread 'push_uint_before_struct' panicked at 'assertion failed: matches!(id_info.instruction(), Instruction :: TypeStruct { .. })', vulkano/src/shader/reflect.rs:864:17 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace ---- multiple_entry_points stdout ---- ; SPIR-V ; Version: 1.5 ; Generator: rspirv ; Bound: 15 OpMemoryModel Logical Vulkan OpEntryPoint GLCompute %9 "main1" OpEntryPoint GLCompute %13 "main2" %1 = OpTypeVoid %2 = OpTypeFunction %1 %1 %3 = OpTypeInt 32 0 %4 = OpConstant %3 0 %5 = OpTypeStruct %3 %3 %6 = OpTypePointer PushConstant %5 %7 = OpTypePointer PushConstant %3 %8 = OpVariable %6 PushConstant %9 = OpFunction %1 None %2 %10 = OpLabel %11 = OpAccessChain %7 %8 %4 %12 = OpLoad %3 %11 OpReturn OpFunctionEnd %13 = OpFunction %1 None %2 %14 = OpLabel OpReturn OpFunctionEnd [reflect-push-constants-issue/src/lib.rs:78] &spirv = Spirv { version: 1.5.0, bound: 15, ids: { Id( 8, ): IdInfo { instruction: Variable { result_type_id: Id( 6, ), result_id: Id( 8, ), storage_class: PushConstant, initializer: None, }, names: [], decorations: [], members: [], }, Id( 1, ): IdInfo { instruction: TypeVoid { result_id: Id( 1, ), }, names: [], decorations: [], members: [], }, Id( 10, ): IdInfo { instruction: Label { result_id: Id( 10, ), }, names: [], decorations: [], members: [], }, Id( 6, ): IdInfo { instruction: TypePointer { result_id: Id( 6, ), storage_class: PushConstant, ty: Id( 5, ), }, names: [], decorations: [], members: [], }, Id( 3, ): IdInfo { instruction: TypeInt { result_id: Id( 3, ), width: 32, signedness: 0, }, names: [], decorations: [], members: [], }, Id( 5, ): IdInfo { instruction: TypeStruct { result_id: Id( 5, ), member_types: [ Id( 3, ), Id( 3, ), ], }, names: [], decorations: [], members: [ StructMemberInfo { names: [], decorations: [], }, StructMemberInfo { names: [], decorations: [], }, ], }, Id( 9, ): IdInfo { instruction: Function { result_type_id: Id( 1, ), result_id: Id( 9, ), function_control: FunctionControl { inline: false, dont_inline: false, pure: false, constant: false, opt_none_intel: false, }, function_type: Id( 2, ), }, names: [], decorations: [], members: [], }, Id( 2, ): IdInfo { instruction: TypeFunction { result_id: Id( 2, ), return_type: Id( 1, ), parameter_types: [ Id( 1, ), ], }, names: [], decorations: [], members: [], }, Id( 12, ): IdInfo { instruction: Load { result_type_id: Id( 3, ), result_id: Id( 12, ), pointer: Id( 11, ), memory_access: None, }, names: [], decorations: [], members: [], }, Id( 11, ): IdInfo { instruction: AccessChain { result_type_id: Id( 7, ), result_id: Id( 11, ), base: Id( 8, ), indexes: [ Id( 4, ), ], }, names: [], decorations: [], members: [], }, Id( 13, ): IdInfo { instruction: Function { result_type_id: Id( 1, ), result_id: Id( 13, ), function_control: FunctionControl { inline: false, dont_inline: false, pure: false, constant: false, opt_none_intel: false, }, function_type: Id( 2, ), }, names: [], decorations: [], members: [], }, Id( 14, ): IdInfo { instruction: Label { result_id: Id( 14, ), }, names: [], decorations: [], members: [], }, Id( 4, ): IdInfo { instruction: Constant { result_type_id: Id( 3, ), result_id: Id( 4, ), value: [ 0, ], }, names: [], decorations: [], members: [], }, Id( 7, ): IdInfo { instruction: TypePointer { result_id: Id( 7, ), storage_class: PushConstant, ty: Id( 3, ), }, names: [], decorations: [], members: [], }, }, instructions_capability: [], instructions_extension: [], instructions_ext_inst_import: [], instruction_memory_model: MemoryModel { addressing_model: Logical, memory_model: Vulkan, }, instructions_entry_point: [ EntryPoint { execution_model: GLCompute, entry_point: Id( 9, ), name: "main1", interface: [], }, EntryPoint { execution_model: GLCompute, entry_point: Id( 13, ), name: "main2", interface: [], }, ], instructions_execution_mode: [], instructions_name: [], instructions_decoration: [], instructions_global: [ TypeVoid { result_id: Id( 1, ), }, TypeFunction { result_id: Id( 2, ), return_type: Id( 1, ), parameter_types: [ Id( 1, ), ], }, TypeInt { result_id: Id( 3, ), width: 32, signedness: 0, }, Constant { result_type_id: Id( 3, ), result_id: Id( 4, ), value: [ 0, ], }, TypeStruct { result_id: Id( 5, ), member_types: [ Id( 3, ), Id( 3, ), ], }, TypePointer { result_id: Id( 6, ), storage_class: PushConstant, ty: Id( 5, ), }, TypePointer { result_id: Id( 7, ), storage_class: PushConstant, ty: Id( 3, ), }, Variable { result_type_id: Id( 6, ), result_id: Id( 8, ), storage_class: PushConstant, initializer: None, }, ], functions: { Id( 9, ): FunctionInfo { instructions: [ Function { result_type_id: Id( 1, ), result_id: Id( 9, ), function_control: FunctionControl { inline: false, dont_inline: false, pure: false, constant: false, opt_none_intel: false, }, function_type: Id( 2, ), }, Label { result_id: Id( 10, ), }, AccessChain { result_type_id: Id( 7, ), result_id: Id( 11, ), base: Id( 8, ), indexes: [ Id( 4, ), ], }, Load { result_type_id: Id( 3, ), result_id: Id( 12, ), pointer: Id( 11, ), memory_access: None, }, Return, FunctionEnd, ], entry_point: Some( EntryPoint { execution_model: GLCompute, entry_point: Id( 9, ), name: "main1", interface: [], }, ), execution_modes: [], }, Id( 13, ): FunctionInfo { instructions: [ Function { result_type_id: Id( 1, ), result_id: Id( 13, ), function_control: FunctionControl { inline: false, dont_inline: false, pure: false, constant: false, opt_none_intel: false, }, function_type: Id( 2, ), }, Label { result_id: Id( 14, ), }, Return, FunctionEnd, ], entry_point: Some( EntryPoint { execution_model: GLCompute, entry_point: Id( 13, ), name: "main2", interface: [], }, ), execution_modes: [], }, }, } thread 'multiple_entry_points' panicked at 'Found runtime-sized push constants', vulkano/src/shader/reflect.rs:870:45 failures: multiple_entry_points push_uint_before_struct test result: FAILED. 0 passed; 2 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s ```This issue might not occur if using glsl or another language that only supports 1 entry point per module, and which might order the PushConstant struct pointer declarations first before the pointers to the fields.
Solution
A more robust implementation would look for a Variable of StorageClass::PushConstant used in an EntryPoint function, and then trace the variable to its pointer type, then lookup what struct that points to.