Repository: godotengine/godot (language: cpp)

Issue:
iOS Metal driver crash with error in `shader_compile_binary_from_spirv`
### Tested versions

Godot 4.4/RC2

### System information

iOS 18.3, iPad M2

### Issue description

I was testing the new sample "Museum of all Things" on iOS, and ended up with a crash, I do not think the actual crash location is important, (but I included it), I think the root problem is with the SPIRV compiler.

<details>

```
ERROR: FATAL: Index p_index = 17 is out of bounds (size() = 0).
at: get (./core/templates/cowdata.h:219)
* 
* thread #1, name = 'Godot Main Thread [instance #1]', queue = 'com.apple.main-thread', stop reason = EXC_BREAKPOINT (code=1, subcode=0x1283a5ed4)
  * frame #0: 0x00000001283a5ed4 Xogot.debug.dylib`CowData<RID>::get(this=0x000000014eac4278, p_index=17) const at cowdata.h:219:3
    frame #1: 0x00000001283a5e04 Xogot.debug.dylib`Vector<RID>::operator[](this=0x000000014eac4270, p_index=17) const at vector.h:102:75
    frame #2: 0x000000012f905970 Xogot.debug.dylib`ShaderRD::version_get_shader(this=0x000000014e45a958, p_version=(_id = 52677773885443), p_variant=17) at shader_rd.h:193:10
    frame #3: 0x000000012fb395b8 Xogot.debug.dylib`RendererSceneRenderImplementation::SceneShaderForwardClustered::ShaderData::_get_shader_variant(this=0x00000001562ecc10, p_shader_version=17) const at scene_shader_forward_clustered.cpp:412:74
    frame #4: 0x000000012fb39680 Xogot.debug.dylib`RendererSceneRenderImplementation::SceneShaderForwardClustered::ShaderData::get_vertex_input_mask(this=0x00000001562ecc10, p_pipeline_version=PIPELINE_VERSION_COLOR_PASS, p_color_pass_flags=0, p_ubershader=true) at scene_shader_forward_clustered.cpp:433:20
    frame #5: 0x000000012fafe390 Xogot.debug.dylib`RendererSceneRenderImplementation::RenderForwardClustered::_mesh_compile_pipeline_for_surface(this=0x000000014e458010, p_shader=0x00000001562ecc10, p_mesh_surface=0x0000000168e1f110, p_ubershader=true, p_instanced_surface=false, p_source=PIPELINE_SOURCE_SURFACE, r_pipeline_key=0x000000016b5d8b28, r_pipeline_pairs=0x0000000000000000) at render_forward_clustered.cpp:4445:34
    frame #6: 0x000000012fafecf0 Xogot.debug.dylib`RendererSceneRenderImplementation::RenderForwardClustered::_mesh_compile_pipelines_for_surface(this=0x000000014e458010, p_surface=0x000000016b5d8be0, p_global=0x000000014e45b8cc, p_source=PIPELINE_SOURCE_SURFACE, r_pipeline_pairs=0x0000000000000000) at render_forward_clustered.cpp:4501:5
    frame #7: 0x000000012fb00400 Xogot.debug.dylib`RendererSceneRenderImplementation::RenderForwardClustered::_mesh_generate_all_pipelines_for_surface_cache(this=0x000000014e458010, p_surface_cache=0x000000012233c5a8, p_global=0x000000014e45b8cc) at render_forward_clustered.cpp:4617:2
    frame #8: 0x000000012faf1990 Xogot.debug.dylib`RendererSceneRenderImplementation::RenderForwardClustered::_update_dirty_geometry_pipelines(this=0x000000014e458010) at render_forward_clustered.cpp:4634:4
    frame #9: 0x000000012fae3934 Xogot.debug.dylib`RendererSceneRenderImplementation::RenderForwardClustered::_update_dirty_geometry_instances(this=0x000000014e458010) at render_forward_clustered.cpp:4625:2
    frame #10: 0x000000012fae22c8 Xogot.debug.dylib`RendererSceneRenderImplementation::RenderForwardClustered::_fill_render_list(this=0x000000014e458010, p_render_list=RENDER_LIST_OPAQUE, p_render_data=0x000000016b5db608, p_pass_mode=PASS_MODE_COLOR, p_using_sdfgi=false, p_using_opaque_gi=false, p_using_motion_pass=false, p_append=false) at render_forward_clustered.cpp:917:2
    frame #11: 0x000000012faebaf0 Xogot.debug.dylib`RendererSceneRenderImplementation::RenderForwardClustered::_render_scene(this=0x000000014e458010, p_render_data=0x000000016b5db608, p_default_bg_color=0x000000016b5db470) at render_forward_clustered.cpp:1870:2
    frame #12: 0x000000012f97b3ec Xogot.debug.dylib`RendererSceneRenderRD::render_scene(this=0x000000014e458010, p_render_buffers=0x000000016e54d3f8, p_camera_data=0x000000016b5dcc28, p_prev_camera_data=0x000000016e54d410, p_instances=0x00000001066546d0, p_lights=0x0000000106654730, p_reflection_probes=0x0000000106654790, p_voxel_gi_instances=0x00000001066547f0, p_decals=0x00000001066547c0, p_lightmaps=0x0000000106654760, p_fog_volumes=0x0000000106654850, p_environment=(_id = 219709052026883), p_camera_attributes=(_id = 0), p_compositor=(_id = 0), p_shadow_atlas=(_id = 196962905227866), p_occluder_debug_tex=(_id = 0), p_reflection_atlas=(_id = 4230542786560), p_reflection_probe=(_id = 0), p_reflection_probe_pass=-1, p_screen_mesh_lod_threshold=0.000418760465, p_render_shadows=0x0000000106655490, p_render_shadow_count=4, p_render_sdfgi_regions=0x000000010665d498, p_render_sdfgi_region_count=0, p_sdfgi_update_data=0x000000010665d9d8, r_render_info=0x000000016e54d650) at renderer_scene_render_rd.cpp:1260:2
    frame #13: 0x000000012f521e94 Xogot.debug.dylib`RendererSceneCull::_render_scene(this=0x0000000106654010, p_camera_data=0x000000016b5dcc28, p_render_buffers=0x000000016e54d3f8, p_environment=(_id = 219709052026883), p_force_camera_attributes=(_id = 0), p_compositor=(_id = 0), p_visible_layers=252706815, p_scenario=(_id = 4221952851968), p_viewport=(_id = 196941430391384), p_shadow_atlas=(_id = 196962905227866), p_reflection_probe=(_id = 0), p_reflection_probe_pass=-1, p_screen_mesh_lod_threshold=0.000418760465, p_using_shadows=true, r_render_info=0x000000016e54d650) at renderer_scene_cull.cpp:3557:16
    frame #14: 0x000000012f51fb2c Xogot.debug.dylib`RendererSceneCull::render_camera(this=0x0000000106654010, p_render_buffers=0x000000016e54d3f8, p_camera=(_id = 197005854900225), p_scenario=(_id = 4221952851968), p_viewport=(_id = 196941430391384), p_viewport_size=Size2 @ 0x000000016b5dcde8, p_jitter_phase_count=0, p_screen_mesh_lod_threshold=0.000418760465, p_shadow_atlas=(_id = 196962905227866), p_xr_interface=0x000000016b5dcfd8, r_render_info=0x000000016e54d650) at renderer_scene_cull.cpp:2778:2
    frame #15: 0x000000012f56ec6c Xogot.debug.dylib`RendererViewport::_draw_3d(this=0x0000000303b91910, p_viewport=0x000000016e54d390) at renderer_viewport.cpp:296:31
    frame #16: 0x000000012f56f2e4 Xogot.debug.dylib`RendererViewport::_draw_viewport(this=0x0000000303b91910, p_viewport=0x000000016e54d390) at renderer_viewport.cpp:363:3
    frame #17: 0x000000012f572664 Xogot.debug.dylib`RendererViewport::draw_viewports(this=0x0000000303b91910, p_swap_buffers=true) at renderer_viewport.cpp:870:4
    frame #18: 0x000000012f74a7ec Xogot.debug.dylib`RenderingServerDefault::_draw(this=0x000000011dcdf110, p_swap_buffers=true, frame_step=1.250828666666667) at rendering_server_default.cpp:86:34
    frame #19: 0x000000012f74cdf4 Xogot.debug.dylib`RenderingServerDefault::draw(this=0x000000011dcdf110, p_present=true, frame_step=1.250828666666667) at rendering_server_default.cpp:436:3
    frame #20: 0x00000001270eb9e4 Xogot.debug.dylib`Main::iteration() at main.cpp:4537:39
    frame #21: 0x000000012a7c03e0 Xogot.debug.dylib`ProgressDialog::_update_ui(this=0x0000000154a68010) at progress_dialog.cpp:151:3
    frame #22: 0x000000012a7c09b0 Xogot.debug.dylib`ProgressDialog::add_task(this=0x0000000154a68010, p_task=0x000000016b5deb60, p_label=0x000000016b5deb58, p_steps=5, p_can_cancel=false) at progress_dialog.cpp:214:2
    frame #23: 0x000000012a14f698 Xogot.debug.dylib`EditorNode::progress_add_task(p_task=0x000000016b5deb60, p_label=0x000000016b5deb58, p_steps=5, p_can_cancel=false) at editor_node.cpp:5077:48
    frame #24: 0x000000012a14f4a4 Xogot.debug.dylib`EditorProgress::EditorProgress(this=0x000000016b5deb68, p_task=0x000000016b5deb60, p_label=0x000000016b5deb58, p_amount=5, p_can_cancel=false, p_force_background=false) at editor_node.cpp:192:3
    frame #25: 0x000000012a14f774 Xogot.debug.dylib`EditorProgress::EditorProgress(this=0x000000016b5deb68, p_task=0x000000016b5deb60, p_label=0x000000016b5deb58, p_amount=5, p_can_cancel=false, p_force_background=false) at editor_node.cpp:190:135
    frame #26: 0x0000000129f0bb24 Xogot.debug.dylib`EditorFileSystem::_first_scan_filesystem(this=0xffffffffffffffff) at editor_file_system.cpp:305:22
    frame #27: 0x0000000129f15ad0 Xogot.debug.dylib`EditorFileSystem::scan(this=0xffffffffffffffff) at editor_file_system.cpp:1067:3
    frame #28: 0x000000012a154b60 Xogot.debug.dylib`EditorNode::_notification(this=0x00000001523fce10, p_what=17) at editor_node.cpp:665:40
    frame #29: 0x000000012a198624 Xogot.debug.dylib`EditorNode::_notificationv(this=0x00000001523fce10, p_notification=17, p_reversed=false) at editor_node.h:124:2
    frame #30: 0x0000000130cfd500 Xogot.debug.dylib`Object::notification(this=0x00000001523fce10, p_notification=17, p_reversed=false) at object.cpp:914:3
    frame #31: 0x000000012cae70d0 Xogot.debug.dylib`SceneTree::_process_group(this=0xffffffffffffffff, p_group=0x00000001523f5520, p_physics=false) at scene_tree.cpp:1066:8
    frame #32: 0x000000012cae460c Xogot.debug.dylib`SceneTree::_process(this=0xffffffffffffffff, p_physics=false) at scene_tree.cpp:1143:8
    frame #33: 0x000000012cae50d4 Xogot.debug.dylib`SceneTree::process(this=0xffffffffffffffff, p_time=0.14917133333333318) at scene_tree.cpp:584:2
    frame #34: 0x00000001270eb868 Xogot.debug.dylib`Main::iteration() at main.cpp:4521:44
    frame #35: 0x0000000130c9a604 Xogot.debug.dylib`GodotInstance::iteration(this=0xffffffffffffffff) at godot_instance.cpp:112:9
    frame #36: 0x0000000127077ec0 Xogot.debug.dylib`void call_with_ptr_args_ret_helper<__UnexistingClass, bool>(p_instance=0x000000014c6b24d0, p_method=(Xogot.debug.dylib`GodotInstance::iteration() at godot_instance.cpp:110), p_args=0x0000000000000000, r_ret=0x000000016b5dfeff, (null)=IndexSequence<> @ 0x000000016b5dfcaf) at binder_common.h:346:22
    frame #37: 0x0000000127077e1c Xogot.debug.dylib`void call_with_ptr_args_ret<__UnexistingClass, bool>(p_instance=0x000000014c6b24d0, p_method=(Xogot.debug.dylib`GodotInstance::iteration() at godot_instance.cpp:110), p_args=0x0000000000000000, r_ret=0x000000016b5dfeff) at binder_common.h:594:2
    frame #38: 0x0000000127077788 Xogot.debug.dylib`MethodBindTR<bool>::ptrcall(this=0x000000030290b510, p_object=0xffffffffffffffff, p_args=0x0000000000000000, r_ret=0x000000016b5dfeff) const at method_bind.h:547:3
    frame #39: 0x0000000130c59ad4 Xogot.debug.dylib`gdextension_object_method_bind_ptrcall(p_method_bind=0x000000030290b510, p_instance=0x000000014c6b24d1, p_args=0x0000000000000000, p_ret=0x000000016b5dfeff) at gdextension_interface.cpp:1419:6
    frame #40: 0x000000010e74a3d0 SwiftGodot`GodotInstance.iteration() at GodotInstance.swift:76:12
    frame #41: 0x00000001258b9e88 Xogot.debug.dylib`closure #1 in GodotApp.iterate(, instance=0x0000000300620460) at GodotAppView.swift:641:34
    frame #42: 0x00000001254f0dbc Xogot.debug.dylib`closure #1 in closure #1 in GodotInstance.async(args=SwiftGodot.Arguments @ 0x000000016b5e0578, callback=0x00000001258f36cc Xogot.debug.dylib`partial apply forwarder for closure #1 () -> () in Xogot.GodotApp.iterate() -> () at <compiler-generated>) at SwiftGodotKit.swift:354:17
    frame #43: 0x000000010d7931e8 SwiftGodot`CallableWrapper.invoke(arguments=SwiftGodot.Arguments @ 0x000000016b5e0698, retPtr=0x16b5e0b70, err=0x16b5e091c) at Wrapped.swift:803:28
    frame #44: 0x000000010d793118 SwiftGodot`closure #1 in invokeWrappedCallable(arguments=SwiftGodot.Arguments @ 0x000000016b5e0728, wrapperPtr=0x30058eb00, retPtr=0x16b5e0b70, err=0x16b5e091c) at Wrapped.swift:789:14
    frame #45: 0x000000010d793360 SwiftGodot`thunk for @callee_guaranteed (@guaranteed Arguments) -> () at <compiler-generated>:0
    frame #46: 0x000000010d75ce20 SwiftGodot`withArguments<()>(pargs=nil, argc=0, body=0x10d793ef8) at Arguments.swift:384:18
    frame #47: 0x000000010d793060 SwiftGodot`invokeWrappedCallable(wrapperPtr=0x30058eb00, pargs=nil, argc=0, retPtr=0x16b5e0b70, err=0x16b5e091c) at Wrapped.swift:785:5
    frame #48: 0x000000010d79391c SwiftGodot`@objc invokeWrappedCallable(wrapperPtr:pargs:argc:retPtr:err:) at <compiler-generated>:0
    frame #49: 0x0000000130c5e454 Xogot.debug.dylib`CallableCustomExtension::call(this=0x00000003029f9110, p_arguments=0x0000000000000000, p_argcount=0, r_return_value=0x000000016b5e0b70, r_call_error=0x000000016b5e0b04) const at gdextension_interface.cpp:172:3
    frame #50: 0x000000013081e1b8 Xogot.debug.dylib`Callable::callp(this=0x000000030076cca8, p_arguments=0x0000000000000000, p_argcount=0, r_return_value=0x000000016b5e0b70, r_call_error=0x000000016b5e0b04) const at callable.cpp:57:11
    frame #51: 0x000000012961c4c0 Xogot.debug.dylib`InstanceState<CoreStringNames*>::InstanceState(this=0xd47754701bb0007b, (null)=0x000000016b5e0bf7) - 18446744068720311103
```

</details>

The SPIRV translator produces a lot of these error messages:

```
ERROR: Failed to compile stage Vertex: BaseInstance requires Metal 1.1 and Mac or Apple A9+ hardware.
at: shader_compile_binary_from_spirv (drivers/metal/rendering_device_driver_metal.mm:2106)
ERROR: Condition "shader_data.is_empty()" is true.
at: _compile_variant (servers/rendering/renderer_rd/shader_rd.cpp:345)

```

Googling the message indicates that some users are setting this value in advance, this was the patch that introduced it to the C API:

https://github.com/KhronosGroup/SPIRV-Cross/pull/1615/files

The following one-line patch fixes it, but I suspect Godot needs a better way of catching the scenario where a shader fails to compile and report that, rather than continuing:

https://gist.github.com/migueldeicaza/b73592b4e10118cdc1aff9cb2667cc81

At that point, detecting whether the device supports the feature might be worth doing, but for now, this value won't be any worse, and will at least not crash.

### Steps to reproduce

Export this app for iOS:

https://github.com/m4ym4y/museum-of-all-things

### Minimal reproduction project (MRP)

https://github.com/m4ym4y/museum-of-all-things


Discussion / hints:

Fix patch:
diff --git a/drivers/metal/rendering_device_driver_metal.mm b/drivers/metal/rendering_device_driver_metal.mm
index 0a49dc1cb122..6c170034d908 100644
--- a/drivers/metal/rendering_device_driver_metal.mm
+++ b/drivers/metal/rendering_device_driver_metal.mm
@@ -2045,6 +2045,7 @@ void deserialize(BufReader &p_reader) {
 
 #if TARGET_OS_IPHONE
 	msl_options.ios_use_simdgroup_functions = (*device_properties).features.simdPermute;
+	msl_options.ios_support_base_vertex_instance = true;
 #endif
 
 	bool disable_argument_buffers = false;


Test patch: