Conversation
…ries#52085) Co-authored-by: Chris Hynes <chris@programcsharp.com>
Contributor
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Objective
Closes #52085
Prevent a recoverable DirectX device removal or reset from aborting the process inside the Windows window procedure.
We reproduced the same fatal callback path without changing the installed driver: a debug-only fault injector enters the real vsync and
WM_GPUI_GPU_DEVICE_LOSTpath, then forces window recovery to fail. The existing handler panics after its recovery attempts; Windows terminates the process with0xC0000409.Solution
0, 100, 250, 500, 1000, 2000, 4000, 8000 ms) without sleeping the vsync thread. Exhaustion leaves the window suspended and the process alive.try_borrow_mutand return deferred; the vsync-owned coordinator retries the pending window after the active draw, present, or resize releases its borrow.gpui_device_lossprefix.This follows Microsoft's lost-device contract rather than inventing a new renderer lifecycle. Direct3D 11 requires replacing the device and every device-dependent resource, including swapchains. DirectComposition likewise requires a new DXGI device, DirectComposition device, and content; its pending commands are committed atomically. Win2D exposes the same transition as
DeviceLostfollowed byCreateResources(NewDevice). GPUI's extra machinery handles its multi-window and re-entrant Win32 shape: unpublished candidates, generation/size/lifetime validation, and a required fresh first frame keep old device state from crossing the recovery boundary.Sources: Microsoft's Direct3D 11 device-loss guidance, DirectComposition device-state contract, atomic DirectComposition commits, and Win2D device-loss handling.
Deterministic test seam
Debug builds accept:
GPUI_TEST_DEVICE_LOSS_AT_VSYNCS=<n>[,<n>...]GPUI_TEST_DEVICE_RECOVERY_FAILURE=<stage>:<count>The seam is inert outside debug builds. It injects at the real vsync and recovery boundaries; it does not install, reset, disable, or otherwise touch a display driver.
Unit tests cover trigger parsing, all seven candidate stages, a fresh failure budget per generation, deferred attempts, retry deadlines, exact eight-attempt exhaustion, active/destroyed terminal states, and recycled HWND registration. They are ordinary inline Rust tests next to the private recovery types and run in Zed's Windows
cargo nextestjob.script/test-gpui-windows-device-loss.ps1is the native integration test. It builds two existing GPUI examples, launches one owned process per scenario, waits for structuredgpui_device_loss_testrecords, asserts that the process is still alive at the expected terminal result, requires every recovered window's forced fresh frame to complete a successfulPresent(), and stops that exact child through its process handle. It is intentionally separate from nextest because it needs an interactive Windows desktop and a real DirectX/DirectComposition window.Related work and scope boundaries
ManuallyDropbookkeeping and deliberately panicked when recovery could not complete. This PR preserves the sound ownership model while replacing the panic with an explicit suspended state and bounded retries.DXGI_ERROR_DEVICE_HUNG/device-recovery family and a later atlas index panic. The atlas symptom was addressed by Fix DirectX atlas panic after GPU device recovery #55878; this PR addresses the remaining partial-recovery and fatal-exhaustion path.Testing
Before, against public injector-only baseline commit
16ce921398:Three consecutive runs entered the existing fatal handler and exited
0xC0000409with:After:
resources:8: eight failures, finalresult=exhausted; process remained alive with the window suspended.resources:7: attempt eight reachedresult=active.30,180plusresources:1: both generations failed once and recovered on attempt two, proving the failure budget resets.The submitted native integration test reproduces those checks with one command:
The reviewed implementation passed all twelve scenarios twice. After the final upstream rebase and squash, the exact final head passed all twelve again. Every recovered window completed a successful post-recovery
Present().Native recovery scenario coverage
resources:7: active on attempt eight.resources:8: exhausted on attempt eight and suspended.30,180plusresources:1: generations one and two independently active on attempt two.resources:1: active on attempt two.Tested Zed base
ef075910c99ce2c8fd07da4174e7c3bb71513f35, fork headec6123c7e9:cargo fmt -p gpui_windows --check: passcargo test -p gpui_windows --features test-support -- --skip test_clipboard: 18 passed, 1 clipboard test filteredscript/clippy.ps1 -p gpui_windows: passcargo build -p gpui --example hello_world --example on_window_close_quit: passscript/test-gpui-windows-device-loss.ps1: 12 scenarios passed at the final squashed head, after two earlier full passes of the same implementation; every recovered window completed a successful post-recoveryPresent(), every owned process was alive at its expected result, and each was terminated through its process handle afterward16ce921398,resources:8: three consecutive0xC0000409exits with the original fatal-handler messagecf61a7492eover the final squashed head: clean cherry-pick;present_starvationcheck, formatting, 18 focused tests, canonical release Clippy, and all 12 native recovery scenarios passedCoverage boundary: the Rust tests run in normal Windows CI. The native script requires an interactive Windows desktop, so it is submitted and repeatable but not part of the parallel nextest job. It exercises the real vsync, window-message, DirectX, and DirectComposition paths, but injects the loss rather than servicing a physical display driver. No driver was installed, reset, disabled, or otherwise changed. The seam proves the software recovery state machine; it does not claim coverage of every adapter, driver, or Windows build.
Self-Review Checklist:
Release Notes: