fix D3D9 crashing on minimize

This commit is contained in:
CasualPokePlayer 2023-08-13 14:20:48 -07:00
parent 56ac05f9a3
commit ae42858560
3 changed files with 65 additions and 42 deletions

View File

@ -1,16 +1,17 @@
using System;
using System.Drawing;
using System.Windows.Forms;
namespace BizHawk.Bizware.Graphics.Controls
{
internal sealed class D3D9Control : GraphicsControl
{
private readonly Func<IntPtr, D3D9SwapChain> _createSwapChain;
private readonly Func<D3D9SwapChain.ControlParameters, D3D9SwapChain> _createSwapChain;
private D3D9SwapChain _swapChain;
private bool Vsync;
public D3D9Control(Func<IntPtr, D3D9SwapChain> createSwapChain)
private D3D9SwapChain.ControlParameters ControlParameters => new(Handle, Width, Height, Vsync);
public D3D9Control(Func<D3D9SwapChain.ControlParameters, D3D9SwapChain> createSwapChain)
{
_createSwapChain = createSwapChain;
@ -21,12 +22,10 @@ namespace BizHawk.Bizware.Graphics.Controls
DoubleBuffered = false;
}
protected override Size DefaultSize => new(1, 1);
protected override void OnHandleCreated(EventArgs e)
{
base.OnHandleCreated(e);
_swapChain = _createSwapChain(Handle);
_swapChain = _createSwapChain(ControlParameters);
}
protected override void OnHandleDestroyed(EventArgs e)
@ -39,7 +38,7 @@ namespace BizHawk.Bizware.Graphics.Controls
protected override void OnResize(EventArgs e)
{
base.OnResize(e);
_swapChain.Refresh(Vsync);
_swapChain.Refresh(ControlParameters);
}
public override void SetVsync(bool state)
@ -47,7 +46,7 @@ namespace BizHawk.Bizware.Graphics.Controls
if (Vsync != state)
{
Vsync = state;
_swapChain.Refresh(Vsync);
_swapChain.Refresh(ControlParameters);
}
}
@ -58,6 +57,6 @@ namespace BizHawk.Bizware.Graphics.Controls
=> _swapChain.SetBackBuffer();
public override void SwapBuffers()
=> _swapChain.PresentBuffer();
=> _swapChain.PresentBuffer(ControlParameters);
}
}

View File

@ -7,16 +7,32 @@ namespace BizHawk.Bizware.Graphics
{
public sealed class D3D9SwapChain : IDisposable
{
public readonly struct ControlParameters
{
public readonly IntPtr Handle;
public readonly int Width;
public readonly int Height;
public readonly bool Vsync;
public ControlParameters(IntPtr handle, int width, int height, bool vsync)
{
Handle = handle;
Width = Math.Max(width, 1);
Height = Math.Max(height, 1);
Vsync = vsync;
}
}
private const int D3DERR_DEVICELOST = unchecked((int)0x88760868);
private readonly Device _device;
private readonly Func<PresentParameters, SwapChain> _resetDeviceCallback;
private readonly Func<PresentParameters, SwapChain> _resetSwapChainCallback;
private readonly Func<ControlParameters, SwapChain> _resetDeviceCallback;
private readonly Func<ControlParameters, SwapChain> _resetSwapChainCallback;
private SwapChain _swapChain;
internal D3D9SwapChain(Device device, SwapChain swapChain,
Func<PresentParameters, SwapChain> resetDeviceCallback, Func<PresentParameters, SwapChain> resetSwapChainCallback)
Func<ControlParameters, SwapChain> resetDeviceCallback, Func<ControlParameters, SwapChain> resetSwapChainCallback)
{
_device = device;
_swapChain = swapChain;
@ -37,7 +53,7 @@ namespace BizHawk.Bizware.Graphics
_device.DepthStencilSurface = null;
}
public void PresentBuffer()
public void PresentBuffer(ControlParameters cp)
{
SetBackBuffer();
@ -49,19 +65,12 @@ namespace BizHawk.Bizware.Graphics
{
if (ex.ResultCode.Code == D3DERR_DEVICELOST)
{
var pp = _swapChain.PresentParameters;
pp.BackBufferWidth = pp.BackBufferHeight = 0;
_swapChain = _resetDeviceCallback(pp);
_swapChain = _resetDeviceCallback(cp);
}
}
}
public void Refresh(bool vsync)
{
var pp = _swapChain.PresentParameters;
pp.BackBufferWidth = pp.BackBufferHeight = 0;
pp.PresentationInterval = vsync ? PresentInterval.One : PresentInterval.Immediate;
_swapChain = _resetSwapChainCallback(pp);
}
public void Refresh(ControlParameters cp)
=> _swapChain = _resetSwapChainCallback(cp);
}
}

View File

@ -29,6 +29,7 @@ namespace BizHawk.Bizware.Graphics
private const int D3DERR_DEVICENOTRESET = unchecked((int)0x88760869);
private Device _device;
private Size _maxWindowSize;
private SwapChain _controlSwapchain;
private IntPtr _offscreenSdl2Window;
@ -105,6 +106,11 @@ namespace BizHawk.Bizware.Graphics
flags |= CreateFlags.FpuPreserve;
_device = new(d3d9, 0, DeviceType.Hardware, pp.DeviceWindowHandle, flags, pp);
// save the maximum size a window backbuffer can be
// this allows us to avoid resetting the swapchain on resizing the window
var displayMode = d3d9.Adapters[0].CurrentDisplayMode;
_maxWindowSize = new(displayMode.Width, displayMode.Height);
}
private PresentParameters MakePresentParameters()
@ -119,7 +125,22 @@ namespace BizHawk.Bizware.Graphics
};
}
private SwapChain ResetDevice(PresentParameters swapChainPresentParameters)
private static PresentParameters MakePresentParameters(D3D9SwapChain.ControlParameters cp)
{
return new()
{
BackBufferWidth = cp.Width,
BackBufferHeight = cp.Height,
BackBufferFormat = Format.X8R8G8B8,
BackBufferCount = 2,
SwapEffect = SwapEffect.Discard,
DeviceWindowHandle = cp.Handle,
Windowed = true,
PresentationInterval = cp.Vsync ? PresentInterval.One : PresentInterval.Immediate
};
}
private SwapChain ResetDevice(D3D9SwapChain.ControlParameters cp)
{
SuspendRenderTargets();
_controlSwapchain.Dispose();
@ -151,35 +172,29 @@ namespace BizHawk.Bizware.Graphics
}
ResumeRenderTargets();
_controlSwapchain = new(_device, swapChainPresentParameters);
return _controlSwapchain;
}
private SwapChain ResetSwapChain(PresentParameters pp)
{
_controlSwapchain.Dispose();
pp = MakePresentParameters(cp);
_controlSwapchain = new(_device, pp);
return _controlSwapchain;
}
public D3D9SwapChain CreateSwapChain(IntPtr handle)
private SwapChain ResetSwapChain(D3D9SwapChain.ControlParameters cp)
{
_controlSwapchain.Dispose();
var pp = MakePresentParameters(cp);
_controlSwapchain = new(_device, pp);
return _controlSwapchain;
}
public D3D9SwapChain CreateSwapChain(D3D9SwapChain.ControlParameters cp)
{
if (_controlSwapchain != null)
{
throw new InvalidOperationException($"{nameof(IGL_D3D9)} can only have 1 control swap chain");
}
var pp = new PresentParameters
{
BackBufferCount = 1,
BackBufferFormat = Format.X8R8G8B8,
SwapEffect = SwapEffect.Discard,
DeviceWindowHandle = handle,
Windowed = true,
PresentationInterval = PresentInterval.Immediate
};
var pp = MakePresentParameters(cp);
_controlSwapchain = new(_device, pp);
return new(_device, _controlSwapchain, ResetDevice, ResetSwapChain);
}