MonoGame/MonoGame

[WinDX] Handle "graphics device removed" scenario

Open

#6,265 opened on Mar 22, 2018

View on GitHub
 (20 comments) (1 reaction) (0 assignees)C# (13,886 stars) (3,071 forks)batch import
DirectXHelp Wanted

Description

Occasionally I get a crash report due to a SharpDX exception with error code DXGI_ERROR_DEVICE_REMOVED. I know this can happen normally due to a number of reasons (e.g. video card removed, driver upgrade, driver error, etc).

The reason I am raising this issue is because XNA seems to handle this scenario gracefully and recover from it automatically. Whereas MonoGame currently just silently swallows the exception when it first occurs in GraphicsDevice.Present(), which then results in it just being thrown in the next place which happens to try to do something relating to the graphics device. (N.B. I tested this using dxcap -forcetdr as described here)

So ideally, MonoGame should also be able to recover from this. I took a look into how to do this, and found the following article which describes how this scenario should be handled: https://docs.microsoft.com/en-us/windows/uwp/gaming/handling-device-lost-scenarios

Basically, the Direct3D device must be reinitialised which is straightforward enough to do. But, all device-dependent resources must also be recreated. So using a texture as an example, I imagine this means that the original data must be kept so that it can be used to later recreate the texture after the device has been reinitialised.

I started messing around with this and seemed to get the device reinitialised correctly, but got stuck on the resource recreation part. fwiw, here's my code from GraphicsDevice.DirectX.PlatformPresent():

try
{
    var syncInterval = PresentationParameters.PresentationInterval.GetSyncInterval();

    // The first argument instructs DXGI to block n VSyncs before presenting.
    lock (_d3dContext)
        _swapChain.Present(syncInterval, PresentFlags.None);
}
catch (SharpDX.SharpDXException ex)
{
    if ((ex.ResultCode == SharpDX.DXGI.ResultCode.DeviceRemoved) || (ex.ResultCode == SharpDX.DXGI.ResultCode.DeviceReset))
    {
        _swapChain.Dispose();
        _swapChain = null;
        CreateDeviceResources();
        Reset();
    }
    else
    {
        throw;
    }
}

So I'm not really too sure how to proceed with this. I'm hoping someone else may be able to help out here or offer some insight. But at the very least, I'd say that GraphicsDevice.DirectX.PlatformPresent() should stop swallowing the SharpDX exceptions as that just muddles the true source of the error.

Contributor guide