Skip to content

HwRender: WPF crashes on ARM64 when GPU is suspended during frame presentation (DXGI_ERROR_DEVICE_REMOVED) #11471

@etvorun

Description

@etvorun

[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

  1. Run a WPF application on ARM64 hardware (e.g. Snapdragon-based Windows device) with continuous rendering activity (animations, video, or rapid UI updates).
  2. Trigger a GPU power-management event: sleep/resume the device, lock the screen for an extended period, or physically remove/disable the display adapter.
  3. 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:

  1. Recognize the error as a device-lost condition.
  2. Release GPU resources via the existing MarkUnusable() path.
  3. Skip the current frame and notify the UI thread (RENDERING_STATUS_DEVICE_LOST).
  4. 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).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions