From 9efe66509d9923c8dedb5948808a4d2c0c7d802e Mon Sep 17 00:00:00 2001
From: Stenzek <stenzek@gmail.com>
Date: Sun, 21 Feb 2016 18:55:55 +1000
Subject: [PATCH] D3D12: Fix crash/errors when switching MSAA modes while
 running

---
 Source/Core/VideoBackends/D3D12/D3DState.cpp | 12 ++++++++++++
 Source/Core/VideoBackends/D3D12/D3DState.h   |  9 +++++++--
 Source/Core/VideoBackends/D3D12/Render.cpp   | 11 +++++++++--
 3 files changed, 28 insertions(+), 4 deletions(-)

diff --git a/Source/Core/VideoBackends/D3D12/D3DState.cpp b/Source/Core/VideoBackends/D3D12/D3DState.cpp
index d0df14371f..5810a74536 100644
--- a/Source/Core/VideoBackends/D3D12/D3DState.cpp
+++ b/Source/Core/VideoBackends/D3D12/D3DState.cpp
@@ -461,6 +461,18 @@ HRESULT StateCache::GetPipelineStateObjectFromCache(SmallPsoDesc* pso_desc, ID3D
 	return S_OK;
 }
 
+void StateCache::OnMSAASettingsChanged()
+{
+	for (auto& it : m_small_pso_map)
+	{
+		SAFE_RELEASE(it.second);
+	}
+	m_small_pso_map.clear();
+
+	// Update sample count for new PSOs being created
+	gx_state_cache.m_current_pso_desc.SampleDesc.Count = g_ActiveConfig.iMultisamples;
+}
+
 void StateCache::Clear()
 {
 	for (auto& it : m_pso_map)
diff --git a/Source/Core/VideoBackends/D3D12/D3DState.h b/Source/Core/VideoBackends/D3D12/D3DState.h
index 85e83592d0..49d4fab157 100644
--- a/Source/Core/VideoBackends/D3D12/D3DState.h
+++ b/Source/Core/VideoBackends/D3D12/D3DState.h
@@ -95,6 +95,9 @@ public:
 	HRESULT GetPipelineStateObjectFromCache(D3D12_GRAPHICS_PIPELINE_STATE_DESC* pso_desc, ID3D12PipelineState** pso);
 	HRESULT GetPipelineStateObjectFromCache(SmallPsoDesc* pso_desc, ID3D12PipelineState** pso, D3D12_PRIMITIVE_TOPOLOGY_TYPE topology, const GeometryShaderUid* gs_uid, const PixelShaderUid* ps_uid, const VertexShaderUid* vs_uid);
 
+	// Called when the MSAA count/quality changes. Invalidates all small PSOs.
+	void OnMSAASettingsChanged();
+
 	// Release all cached states and clear hash tables.
 	void Clear();
 
@@ -126,7 +129,8 @@ private:
 				            lhs.BlendState.RenderTarget[0].DestBlend,
 				            lhs.BlendState.RenderTarget[0].SrcBlend,
 				            lhs.BlendState.RenderTarget[0].RenderTargetWriteMask,
-				            lhs.RTVFormats[0]) ==
+				            lhs.RTVFormats[0],
+				            lhs.SampleDesc.Count) ==
 				   std::tie(rhs.PS.pShaderBytecode, rhs.VS.pShaderBytecode, rhs.GS.pShaderBytecode,
 				            rhs.RasterizerState.CullMode,
 				            rhs.DepthStencilState.DepthEnable,
@@ -137,7 +141,8 @@ private:
 				            rhs.BlendState.RenderTarget[0].DestBlend,
 				            rhs.BlendState.RenderTarget[0].SrcBlend,
 				            rhs.BlendState.RenderTarget[0].RenderTargetWriteMask,
-				            rhs.RTVFormats[0]);
+				            rhs.RTVFormats[0],
+				            rhs.SampleDesc.Count);
 		}
 	};
 
diff --git a/Source/Core/VideoBackends/D3D12/Render.cpp b/Source/Core/VideoBackends/D3D12/Render.cpp
index 4f763a68cb..bf80081ff5 100644
--- a/Source/Core/VideoBackends/D3D12/Render.cpp
+++ b/Source/Core/VideoBackends/D3D12/Render.cpp
@@ -984,9 +984,16 @@ void Renderer::SwapImpl(u32 xfb_addr, u32 fb_width, u32 fb_stride, u32 fb_height
 		s_last_stereo_mode != (g_ActiveConfig.iStereoMode > 0))
 	{
 		s_last_xfb_mode = g_ActiveConfig.bUseRealXFB;
-		s_last_multisamples = g_ActiveConfig.iMultisamples;
 
-		StaticShaderCache::InvalidateMSAAShaders();
+		// Block on any changes until the GPU catches up, so we can free resources safely.
+		D3D::command_list_mgr->ExecuteQueuedWork(true);
+
+		if (s_last_multisamples != g_ActiveConfig.iMultisamples)
+		{
+			s_last_multisamples = g_ActiveConfig.iMultisamples;
+			StaticShaderCache::InvalidateMSAAShaders();
+			gx_state_cache.OnMSAASettingsChanged();
+		}
 
 		if (window_resized)
 		{