Skip to content

windows: fix VK_ERROR_DEVICE_LOST - give the D3D-interop blit its own VkQueue#174

Open
nobandegani wants to merge 1 commit into
nmfisher:developfrom
nobandegani:fix/windows-blit-queue-race
Open

windows: fix VK_ERROR_DEVICE_LOST - give the D3D-interop blit its own VkQueue#174
nobandegani wants to merge 1 commit into
nmfisher:developfrom
nobandegani:fix/windows-blit-queue-race

Conversation

@nobandegani

Copy link
Copy Markdown

Problem

On Windows with newer NVIDIA hardware/drivers, any Thermion app dies as soon as rendering starts:

Initializing WindowsVulkanContext
vkQueueSubmit failed: -4          <- VK_ERROR_DEVICE_LOST
vkWaitForFences failed: -4        <- repeats every frame afterwards

sometimes followed by a native abort through Filament's timer-query postcondition (vkGetQueryPoolResults error=-4). Reproduced with the unmodified examples/flutter/quickstart at 89c135b on:

  • GPU: NVIDIA GeForce RTX 5090 (Blackwell)
  • Driver: 610.62 (32.0.16.1062, 2026-06), Vulkan API 1.4.341
  • OS: Windows 11 Pro 26200

The device is lost on the first frame; the viewport stays blank (or shows the app's clear color) and the app eventually aborts.

Root cause

createLogicalDevice requests a single queue (queueCount = 1) in the graphics family. Two independent code paths then fetch the same VkQueue:

  1. Filament, via VulkanSharedContext (graphicsQueueFamilyIndex, graphicsQueueIndex = 0), submitting from its render thread;
  2. createCommandResources (vkGetDeviceQueue(device, family, 0)), used by the D3D-interop Blit() submitting from the Flutter platform thread.

Per the Vulkan spec, queue access is externally synchronizedvkQueueSubmit on the same queue from two threads without host synchronization is invalid. Many driver generations happen to tolerate it, which is why this hasn't bitten on existing hardware; the current NVIDIA driver for Blackwell rejects the first concurrent submission with VK_ERROR_DEVICE_LOST.

Fix

Request two queues from the graphics family when the hardware exposes more than one (NVIDIA exposes 16; the request is clamped to the family's queueCount):

  • queue 0 remains exclusively Filament's, exactly as before;
  • queue 1 is used by createCommandResources for the blit/command path.

Both queues are in the same family, so no queue-family ownership transfers are needed and the existing barriers/fences are unchanged. On hardware exposing a single graphics queue (queueCount == 1) the behavior is byte-for-byte the old one, so nothing regresses on platforms where the shared queue happened to work.

Validation

  • Before: examples/flutter/quickstart reproduces the device loss on the first frame (hardware above).
  • After: quickstart and a full app (113 MB character glTF with skin + morph targets) render correctly; no Vulkan errors across repeated runs, window resizes, and viewer re-creation.
  • A diagnostic line logs the chosen queue at startup: [INFO] Blit/command queue: family 0, queue index 1.

Happy to adjust if you'd prefer a different split (e.g. a dedicated transfer-family queue) — this is the minimal change that makes the existing design valid.

🤖 Generated with Claude Code

…queue

The logical device was created with a single queue in the graphics
family. Filament receives that queue (index 0) through
VulkanSharedContext and submits from its render thread, while the
D3D-interop Blit fetched the same VkQueue and submitted from the
Flutter platform thread. VkQueue access is externally synchronized, so
this is a data race; older drivers tolerate it, but newer NVIDIA
drivers (observed: RTX 5090, driver 610.62) fail the first concurrent
submission with VK_ERROR_DEVICE_LOST, followed by a native abort via
a vkGetQueryPoolResults postcondition in Filament timer queries.

Request two queues from the family when the hardware exposes more than
one (NVIDIA exposes 16) and use queue index 1 for the blit/command
path, keeping queue 0 exclusively Filament's. Falls back to the old
single-queue behavior on hardware with queueCount == 1.

Reproduced with examples/flutter/quickstart before the fix; renders
correctly after.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant