[HwRender] WPF crashes on ARM64 when GPU is suspended during frame presentation
Summary
On ARM64 devices, WPF crashes when the GPU is suspended (e.g. during power management transitions) while the compositor thread is in the middle of calling IDirect3DSwapChain9::Present. The process terminates with a fatal exception rather than recovering gracefully.
Observed behavior
- WPF application crashes on ARM64 hardware when the GPU is suspended or power-cycled during rendering.
- Fault bucket signature:
XAML_887a0005_wpfgfx_cor3.dll!VS_FatalError
IDirect3DSwapChain9::Present returns 0x887A0005 (DXGI_ERROR_DEVICE_REMOVED).
- The crash is ARM64-specific; x64 is not affected.
Repro notes
- Run a WPF application on ARM64 hardware (e.g. Snapdragon-based Windows device) with continuous rendering activity (animations, video, or rapid UI updates).
- Trigger a GPU power-management event: sleep/resume the device, lock the screen for an extended period, or physically remove/disable the display adapter.
- Observe the WPF application crash rather than recovering and repainting after the GPU comes back.
Impact
- Affects any WPF application running on ARM64 hardware (Snapdragon X Elite/Plus, other Windows-on-ARM devices).
- Crash is deterministic when a GPU suspension event occurs during an active Present call.
- Hit count indicates this affects a significant number of ARM64 users in production.
- Issue originally observed in Visual Studio integration scenario.
Expected behavior
When IDirect3DSwapChain9::Present returns an error indicating the GPU was lost or removed, WPF should:
- Recognize the error as a device-lost condition.
- Release GPU resources via the existing
MarkUnusable() path.
- Skip the current frame and notify the UI thread (
RENDERING_STATUS_DEVICE_LOST).
- Automatically recreate GPU resources and repaint the window when the GPU resumes.
No crash, no zombie window.
Actual behavior
PresentWithD3D only checks for S_OK, S_PRESENT_MODE_CHANGED, and S_PRESENT_OCCLUDED. Any other HRESULT falls through to MIL_THR, which treats 0x887A0005 as a fatal error, triggering an unrecoverable exception and terminating the process.
Suspected root cause
On ARM64, the D3D9-over-DXGI compatibility layer surfaces DXGI_ERROR_DEVICE_REMOVED (0x887A0005) directly when the GPU is suspended or removed, instead of translating it to the legacy D3D9 equivalent D3DERR_DEVICELOST. WPF's PresentWithD3D does not recognize this DXGI code and passes it unchanged to MIL_THR, which classifies it as fatal. HandlePresentFailure — which contains the correct device-lost recovery logic — is never reached.
This is an ARM64-specific behavior difference in the D3D9-over-DXGI translation layer.
Proposed fix direction
- Define
DXGI_ERROR_DEVICE_REMOVED locally in wgx_error.h (as a stable #define) to avoid pulling in dxgi.h.
- In
PresentWithD3D (d3ddevice.cpp): add an else if (hr == DXGI_ERROR_DEVICE_REMOVED) branch — placed before the MIL_THR call — that converts the error to D3DERR_DEVICELOST. This feeds the existing device-lost recovery path.
- In
HandlePresentFailure (d3ddevice.cpp): add DXGI_ERROR_DEVICE_REMOVED to the device-lost condition block as defense-in-depth for other callers (e.g. PresentWithGDI).
[HwRender] WPF crashes on ARM64 when GPU is suspended during frame presentation
Summary
On ARM64 devices, WPF crashes when the GPU is suspended (e.g. during power management transitions) while the compositor thread is in the middle of calling
IDirect3DSwapChain9::Present. The process terminates with a fatal exception rather than recovering gracefully.Observed behavior
XAML_887a0005_wpfgfx_cor3.dll!VS_FatalErrorIDirect3DSwapChain9::Presentreturns0x887A0005(DXGI_ERROR_DEVICE_REMOVED).Repro notes
Impact
Expected behavior
When
IDirect3DSwapChain9::Presentreturns an error indicating the GPU was lost or removed, WPF should:MarkUnusable()path.RENDERING_STATUS_DEVICE_LOST).No crash, no zombie window.
Actual behavior
PresentWithD3Donly checks forS_OK,S_PRESENT_MODE_CHANGED, andS_PRESENT_OCCLUDED. Any other HRESULT falls through toMIL_THR, which treats0x887A0005as a fatal error, triggering an unrecoverable exception and terminating the process.Suspected root cause
On ARM64, the D3D9-over-DXGI compatibility layer surfaces
DXGI_ERROR_DEVICE_REMOVED(0x887A0005) directly when the GPU is suspended or removed, instead of translating it to the legacy D3D9 equivalentD3DERR_DEVICELOST. WPF'sPresentWithD3Ddoes not recognize this DXGI code and passes it unchanged toMIL_THR, which classifies it as fatal.HandlePresentFailure— which contains the correct device-lost recovery logic — is never reached.This is an ARM64-specific behavior difference in the D3D9-over-DXGI translation layer.
Proposed fix direction
DXGI_ERROR_DEVICE_REMOVEDlocally inwgx_error.h(as a stable#define) to avoid pulling indxgi.h.PresentWithD3D(d3ddevice.cpp): add anelse if (hr == DXGI_ERROR_DEVICE_REMOVED)branch — placed before theMIL_THRcall — that converts the error toD3DERR_DEVICELOST. This feeds the existing device-lost recovery path.HandlePresentFailure(d3ddevice.cpp): addDXGI_ERROR_DEVICE_REMOVEDto the device-lost condition block as defense-in-depth for other callers (e.g.PresentWithGDI).