Summary
Vulkan RHI submits batches that wait on binary semaphores before the corresponding signal operations are submitted (violates VUID-vkQueueSubmit-pWaitSemaphores-03238); deadlocks on Mesa/RADV Linux
What Type of Bug are you experiencing?
Editor
Steps to Reproduce
- Linux with any Mesa-based Vulkan driver (e.g. RADV; tested on AMD RX 9070 XT), Mesa without the robustness fix from MR1.
(On RT-capable AMD hardware, getting past editor startup currently also requires MR2, which fixes an unrelated RADV descriptor bug.) - Open any UE 5.8 project in UnrealEditor with the Vulkan RHI.
- Wait ~10-60 seconds after the viewport appears.
- Alternatively, on any platform/driver: run with the Khronos validation layer enabled - VUID-vkQueueSubmit2-semaphore-03873 / VUID-vkQueueSubmit-pWaitSemaphores-03238 errors are reported.
Expected Result
The editor runs normally. Per the Vulkan spec
(VUID-vkQueueSubmit-pWaitSemaphores-03238), every batch that waits on a
binary semaphore must be submitted only after the corresponding signal
operation has been submitted for execution:
“All elements of the pWaitSemaphores member of all elements of pSubmits
created with a VkSemaphoreType of VK_SEMAPHORE_TYPE_BINARY must reference
a semaphore signal operation that has been submitted for execution and any
semaphore signal operations on which it depends (if any) must have also
been submitted for execution.”
Observed Result
The editor freezes, then the watchdog aborts with “GameThread timed out
waiting for RenderThread” about 40 seconds later.
The RHISubmission thread is deadlocked inside the driver’s vkQueueSubmit2:
Mesa’s shared Vulkan runtime performs a blocking wait for each binary wait
semaphore’s signal operation to exist (it is entitled to, per the VUID
above). The signal operation is produced later by the very thread that is
now blocked, so it never arrives.
I instrumented the driver to check, at vkQueueSubmit2 time, whether each
binary wait semaphore already had a pending signal operation. In a
75-second editor session, 36,757 waits were submitted before their signal
operation existed, on both the graphics and the async compute queue —
i.e. this is steady-state behavior of the parallel submission pipeline
(FVulkanPayload processing in VulkanSubmission.cpp / FVulkanQueue::Submit
in VulkanQueue.cpp), hundreds of violations per frame, not a rare race:
payloads are submitted to one queue while the payload that signals the
binary semaphores they wait on has not yet been submitted (typically to
another queue, e.g. graphics vs async compute).
On drivers that pass unmaterialized syncobj waits to the kernel this
happens to work, which is presumably why it went unnoticed.
Affects Versions
5.8
5.7
Platform(s)
Linux
For crash reports, include your callstack
Deadlocked RHISubmission thread (gdb, inside vkQueueSubmit2):
#0 ioctl
#1 drmIoctl
#2 drmSyncobjTimelineWait
#3 vk_drm_syncobj_wait_many (abs_timeout_ns=INT64_MAX)
#4 __vk_sync_wait (wait_flags=VK_SYNC_WAIT_PENDING, abs_timeout_ns=UINT64_MAX)
#5 vk_sync_wait
#6 vk_queue_submit_move_binary_waits_to_temps (vk_queue.c:1025)
#7 vk_queue_submit
#8 vk_queue_merge_submit
#9 vk_common_QueueSubmit2 (submitCount=15)
#10 FVulkanQueue::Submit (VulkanQueue.cpp:486)
#11 FVulkanRayTracingPipelineLibraryCache::GetOrAddLibrary (VulkanRayTracing.cpp:1498, via task graph)
Subsequent watchdog abort (game thread):
0x... libc.so.6!pthread_cond_timedwait
0x... FPThreadEvent::Wait (GenericPlatformProcess.cpp:519)
0x... FRenderCommandFence::Wait
0x... FFrameEndSync::Sync (RenderingThread.cpp:2509)
0x... FEngineLoop::Tick (LaunchEngineLoop.cpp:6084)
Additional Notes
- Mesa-side robustness fix (defer the blocking wait to the driver’s submit
thread instead of deadlocking):
MR1 — but the
API usage is invalid regardless and can bite on any driver that takes the
VUID at its word. - Related RADV fix needed to get UE 5.8 bindless past startup on RT-capable
AMD GPUs (mesa/mesa#15697):
MR2 - Suggested engine-side fix: in the RHI submission pipeline, do not submit
a payload until the payloads that signal its binary wait semaphores have
been submitted to their queues (cross-queue ordering), or use timeline
semaphores for cross-queue synchronization, which have no such
requirement. - Likely duplicates / corroborating community reports (no root cause
identified in them):- 5.8 Freezing on UI interaction
— AMD 7900 XT on Mesa, freezes seconds into an editor session, “not
present in 5.7.4”; matches this deadlock’s symptoms exactly. - 5.8 "Stable" - Vulkan crash on Linux
— RX 6900 XT GPUVM fault; consistent with the related RADV descriptor
issue above, which crashes earlier and masks this deadlock. - Possibly related (affects NVIDIA too, so not the Mesa blocking path,
but the same “wait submitted before its signal” design would explain
it): Editor hangs forever in Linux upon opening modal dialogs, with a message about Vulkan swapchains being outdated.
— hang in FVulkanDynamicRHI::WaitAndProcessInterruptQueue on
vkWaitSemaphoresKHR.
- 5.8 Freezing on UI interaction
- With both Mesa MRs applied locally, UnrealEditor 5.8 runs correctly on
RADV/GFX1201 (bindless + ray tracing enabled).