From 3096b13472abb6e7f0146ac300eac62717fb5414 Mon Sep 17 00:00:00 2001 From: ET Date: Fri, 20 Feb 2026 15:01:02 -0800 Subject: [PATCH] HwRender: fix ARM64 crash when GPU is suspended during Present On ARM64, the D3D9-over-DXGI compatibility layer surfaces DXGI_ERROR_DEVICE_REMOVED (0x887A0005) directly when the GPU is suspended or removed, instead of mapping it to D3DERR_DEVICELOST. PresentWithD3D only checked for S_OK, S_PRESENT_MODE_CHANGED, and S_PRESENT_OCCLUDED. Any unrecognised HRESULT fell through to MIL_THR, which treats 0x887A0005 as fatal, triggering VS_FatalError and a crash. HandlePresentFailure was never reached. Fix: - wgx_error.h: define DXGI_ERROR_DEVICE_REMOVED locally to avoid pulling in dxgi.h; the value is a stable DirectX ABI constant. - PresentWithD3D: add else-if branch that converts DXGI_ERROR_DEVICE_REMOVED to D3DERR_DEVICELOST before MIL_THR, so the existing device-lost recovery path handles it gracefully. - HandlePresentFailure: add DXGI_ERROR_DEVICE_REMOVED to the device-lost condition block as defense-in-depth for other call sites (PresentWithGDI). Recovery follows the existing TDR path: MarkUnusable() invalidates GPU resources, render thread skips the frame and fires RENDERING_STATUS_DEVICE_LOST, and when the GPU resumes UpdateDisplayState recreates resources and repaints the window. --- .../src/WpfGfx/core/hw/d3ddevice.cpp | 19 ++++++++++++++++--- .../src/WpfGfx/include/wgx_error.h | 10 ++++++++++ 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/src/Microsoft.DotNet.Wpf/src/WpfGfx/core/hw/d3ddevice.cpp b/src/Microsoft.DotNet.Wpf/src/WpfGfx/core/hw/d3ddevice.cpp index aad5b33c195..0c1716d8efb 100644 --- a/src/Microsoft.DotNet.Wpf/src/WpfGfx/core/hw/d3ddevice.cpp +++ b/src/Microsoft.DotNet.Wpf/src/WpfGfx/core/hw/d3ddevice.cpp @@ -3378,6 +3378,17 @@ CD3DDeviceLevel1::PresentWithD3D( hr = S_OK; } + else if (hr == DXGI_ERROR_DEVICE_REMOVED) + { + // + // On ARM64, the D3D9-over-DXGI compatibility layer surfaces + // DXGI_ERROR_DEVICE_REMOVED directly when the GPU is suspended or + // removed, instead of mapping it to D3DERR_DEVICELOST. + // Convert here so HandlePresentFailure can recognize and handle it + // gracefully rather than letting MIL_THR trigger a fatal crash. + // + hr = D3DERR_DEVICELOST; + } // // !!! Critical Note: After this point hr may not be S_OK. Make sure not @@ -3477,9 +3488,11 @@ CD3DDeviceLevel1::HandlePresentFailure( hr == D3DERR_DEVICEHUNG || // Hw Adapter timed out and has // been reset by the OS (LH Only) // - hr == D3DERR_DEVICEREMOVED // Hw Adapter has been removed (LH - // Only) - // + hr == D3DERR_DEVICEREMOVED || // Hw Adapter has been removed (LH + // Only) + // + hr == DXGI_ERROR_DEVICE_REMOVED // surfaced directly by D3D9-over-DXGI + // on ARM64 when GPU is suspended or removed ) { hr = WGXERR_DISPLAYSTATEINVALID; diff --git a/src/Microsoft.DotNet.Wpf/src/WpfGfx/include/wgx_error.h b/src/Microsoft.DotNet.Wpf/src/WpfGfx/include/wgx_error.h index 93628ea7acf..235d51d8799 100644 --- a/src/Microsoft.DotNet.Wpf/src/WpfGfx/include/wgx_error.h +++ b/src/Microsoft.DotNet.Wpf/src/WpfGfx/include/wgx_error.h @@ -11,6 +11,16 @@ #pragma once +/*=========================================================================*\ + DXGI error codes + Defined here to avoid a dependency on dxgi.h, which is not included + in WpfGfx. These values are stable ABI constants defined by DirectX. +\*=========================================================================*/ + +#ifndef DXGI_ERROR_DEVICE_REMOVED +#define DXGI_ERROR_DEVICE_REMOVED ((HRESULT)0x887A0005L) +#endif + /*=========================================================================*\ MIL Status Codes \*=========================================================================*/