KhronosGroup / MoltenVK

MoltenVK is a Vulkan Portability implementation. It layers a subset of the high-performance, industry-standard Vulkan graphics and compute API over Apple's Metal graphics framework, enabling Vulkan applications to run on macOS, iOS and tvOS.
Apache License 2.0
4.76k stars 419 forks source link

Deadlock in `vkCreateSwapchainKHR()` called from non-main thread #2213

Closed RandomShaper closed 5 months ago

RandomShaper commented 5 months ago

In the Godot game engine, when rendering from a non-main thread, deadlock happen, similarly to what is described in #234.

This is the stack trace from the rendering thread at the point of deadlock (2024-04-23: updated with symbols):

* thread #5
    frame #0: 0x00007ff81713d222 libsystem_kernel.dylib`__ulock_wait + 10
    frame #1: 0x00007ff816fd3db2 libdispatch.dylib`_dlock_wait + 46
    frame #2: 0x00007ff816fd3c3a libdispatch.dylib`_dispatch_thread_event_wait_slow + 40
    frame #3: 0x00007ff816fe06c3 libdispatch.dylib`__DISPATCH_WAIT_FOR_QUEUE__ + 307
    frame #4: 0x00007ff816fe02ef libdispatch.dylib`_dispatch_sync_f_slow + 175
    frame #5: 0x00000001001f3597 godot.macos.editor.dev.x86_64`mvkDispatchToMainAndWait(block=0x0000000100217430) at MVKOSExtensions.mm:159:3
  * frame #6: 0x000000010021737b godot.macos.editor.dev.x86_64`-[CAMetalLayer(self=0x000060000122b7e0, _cmd="screenMVK") screenMVK] at CAMetalLayer+MoltenVK.mm:93:2
    frame #7: 0x00000001001c1462 godot.macos.editor.dev.x86_64`MVKSwapchain::initSurfaceImages(this=0x00007fee6f956400, pCreateInfo=0x0000700005e1eaa8, imgCnt=3) at MVKSwapchain.mm:618:27
    frame #8: 0x00000001001c0a2f godot.macos.editor.dev.x86_64`MVKSwapchain::MVKSwapchain(this=0x00007fee6f956400, device=0x00007fee6f826800, pCreateInfo=0x0000700005e1eaa8) at MVKSwapchain.mm:426:5
    frame #9: 0x00000001001c1615 godot.macos.editor.dev.x86_64`MVKSwapchain::MVKSwapchain(this=0x00007fee6f956400, device=0x00007fee6f826800, pCreateInfo=0x0000700005e1eaa8) at MVKSwapchain.mm:381:41
    frame #10: 0x000000010010606d godot.macos.editor.dev.x86_64`MVKDevice::createSwapchain(this=0x00007fee6f826800, pCreateInfo=0x0000700005e1eaa8, pAllocator=0x0000000000000000) at MVKDevice.mm:3877:13
    frame #11: 0x0000000100041460 godot.macos.editor.dev.x86_64`vkCreateSwapchainKHR(device=0x00007fee6f826818, pCreateInfo=0x0000700005e1eaa8, pAllocator=0x0000000000000000, pSwapchain=0x0000600002f60fd0) at vulkan.mm:3151:43
    [...]

At first, I thought it had been introduced by 90eb1af19f2351329d7025e8c121dda3fd7a15ca, but I get the same issue with 1.3.275.

The OS surface is being created via VK_EXT_METAL_SURFACE_EXTENSION.

cdavis5e commented 5 months ago

"Deadlock" suggests that the lock is already held. What do the other threads' backtraces look like?

RandomShaper commented 5 months ago

It's a deadlock in the sense that #234 describes:

Unfortunately if the main thread is currently blocking waiting for that surface create to finish you end up with a deadlock between the two threads.

Our main thread is waiting for the render thread (the one in the stack trace) to finish executing drawing, which can involve recreating swap chains. Only when that is done, message "pumping" will happen. Unfortunately, though, the rendering thread is waiting for the main thread to respond to the message. (Apologies if my Mac jargon is not very accurate since I'm not very familiar with its frameworks.)

RandomShaper commented 5 months ago

I've made #2214, that fixes the issue for Godot.