From ae4285856038a80431c0ce30ed6ef98d8a9d99ed Mon Sep 17 00:00:00 2001
From: CasualPokePlayer <50538166+CasualPokePlayer@users.noreply.github.com>
Date: Sun, 13 Aug 2023 14:20:48 -0700
Subject: [PATCH] fix D3D9 crashing on minimize

---
 .../Controls/D3D9Control.cs                   | 17 +++---
 .../D3D9/D3D9SwapChain.cs                     | 37 ++++++++-----
 src/BizHawk.Bizware.Graphics/D3D9/IGL_D3D9.cs | 53 ++++++++++++-------
 3 files changed, 65 insertions(+), 42 deletions(-)

diff --git a/src/BizHawk.Bizware.Graphics.Controls/Controls/D3D9Control.cs b/src/BizHawk.Bizware.Graphics.Controls/Controls/D3D9Control.cs
index 597b46a47e..73e66e89b7 100644
--- a/src/BizHawk.Bizware.Graphics.Controls/Controls/D3D9Control.cs
+++ b/src/BizHawk.Bizware.Graphics.Controls/Controls/D3D9Control.cs
@@ -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);
 	}
 }
\ No newline at end of file
diff --git a/src/BizHawk.Bizware.Graphics/D3D9/D3D9SwapChain.cs b/src/BizHawk.Bizware.Graphics/D3D9/D3D9SwapChain.cs
index 938fef167a..77e494317f 100644
--- a/src/BizHawk.Bizware.Graphics/D3D9/D3D9SwapChain.cs
+++ b/src/BizHawk.Bizware.Graphics/D3D9/D3D9SwapChain.cs
@@ -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);
 	}
 }
diff --git a/src/BizHawk.Bizware.Graphics/D3D9/IGL_D3D9.cs b/src/BizHawk.Bizware.Graphics/D3D9/IGL_D3D9.cs
index cdc2743a51..4cbe12bc6b 100644
--- a/src/BizHawk.Bizware.Graphics/D3D9/IGL_D3D9.cs
+++ b/src/BizHawk.Bizware.Graphics/D3D9/IGL_D3D9.cs
@@ -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);
 		}