@@ -80,33 +80,41 @@ void CDirect3DEvents9::OnInvalidate(IDirect3DDevice9* pDevice)
80
80
{
81
81
WriteDebugEvent (" CDirect3DEvents9::OnInvalidate" );
82
82
83
- // Ensure device is in a valid state before invalidation
84
- // For example, Nvidia drivers can hang if device operations are attempted during invalid states
85
- if (pDevice->TestCooperativeLevel () == D3DERR_DEVICELOST)
86
- {
87
- WriteDebugEvent (" OnInvalidate: Device already lost, skipping operations" );
88
- return ;
89
- }
83
+ const HRESULT hrCooperativeLevel = pDevice->TestCooperativeLevel ();
84
+ const bool bDeviceOperational = (hrCooperativeLevel == D3D_OK);
85
+ const bool bDeviceTemporarilyLost =
86
+ (hrCooperativeLevel == D3DERR_DEVICELOST || hrCooperativeLevel == D3DERR_DEVICENOTRESET);
90
87
91
- // Flush any pending operations before invalidation
92
- g_pCore->GetGraphics ()->GetRenderItemManager ()->SaveReadableDepthBuffer ();
93
- g_pCore->GetGraphics ()->GetRenderItemManager ()->FlushNonAARenderTarget ();
94
-
95
- // Force completion of all GPU operations if a scene is currently active
96
- if (g_bInMTAScene || g_bInGTAScene)
88
+ if (!bDeviceOperational && !bDeviceTemporarilyLost)
89
+ WriteDebugEvent (SString (" OnInvalidate: unexpected cooperative level %08x" , hrCooperativeLevel));
90
+
91
+ if (bDeviceOperational)
97
92
{
98
- const HRESULT hrEndScene = pDevice->EndScene ();
99
- if (SUCCEEDED (hrEndScene))
100
- {
101
- g_bInMTAScene = false ;
102
- g_bInGTAScene = false ;
103
- }
104
- else
93
+ // Flush any pending operations before invalidation while the device still accepts work
94
+ g_pCore->GetGraphics ()->GetRenderItemManager ()->SaveReadableDepthBuffer ();
95
+ g_pCore->GetGraphics ()->GetRenderItemManager ()->FlushNonAARenderTarget ();
96
+
97
+ if (g_bInMTAScene || g_bInGTAScene)
105
98
{
106
- WriteDebugEvent (SString (" OnInvalidate: EndScene failed: %08x" , hrEndScene));
99
+ const HRESULT hrEndScene = pDevice->EndScene ();
100
+ if (FAILED (hrEndScene))
101
+ WriteDebugEvent (SString (" OnInvalidate: EndScene failed: %08x" , hrEndScene));
107
102
}
103
+
104
+ CloseActiveShader ();
105
+ }
106
+ else
107
+ {
108
+ if (g_bInMTAScene || g_bInGTAScene)
109
+ WriteDebugEvent (" OnInvalidate: device lost, skipping EndScene and pending GPU work" );
110
+
111
+ // Prevent reuse of partially configured shader state across device resets without touching the lost device
112
+ CloseActiveShader (false );
108
113
}
109
-
114
+
115
+ g_bInMTAScene = false ;
116
+ g_bInGTAScene = false ;
117
+
110
118
// Invalidate the VMR9 Manager
111
119
// CVideoManager::GetSingleton ().OnLostDevice ();
112
120
@@ -591,26 +599,43 @@ HRESULT CDirect3DEvents9::DrawIndexedPrimitiveShader(IDirect3DDevice9* pDevice,
591
599
// Finish the active shader if there is one
592
600
//
593
601
// ///////////////////////////////////////////////////////////
594
- void CDirect3DEvents9::CloseActiveShader ()
602
+ void CDirect3DEvents9::CloseActiveShader (bool bDeviceOperational )
595
603
{
596
604
if (!g_pActiveShader)
597
605
return ;
598
606
599
607
ID3DXEffect* pD3DEffect = g_pActiveShader->m_pShaderInstance ->m_pEffectWrap ->m_pD3DEffect ;
608
+ IDirect3DDevice9* pDevice = g_pGraphics ? g_pGraphics->GetDevice () : nullptr ;
609
+ HRESULT hrCooperativeLevel = D3D_OK;
610
+ if (pDevice)
611
+ hrCooperativeLevel = pDevice->TestCooperativeLevel ();
612
+
613
+ bool bAllowDeviceWork = bDeviceOperational;
614
+ if (hrCooperativeLevel == D3D_OK)
615
+ bAllowDeviceWork = true ;
616
+ else if (hrCooperativeLevel == D3DERR_DEVICELOST || hrCooperativeLevel == D3DERR_DEVICENOTRESET)
617
+ bAllowDeviceWork = false ;
618
+
619
+ if (pD3DEffect)
620
+ {
621
+ HRESULT hrEndPass = pD3DEffect->EndPass ();
622
+ if (FAILED (hrEndPass) && hrEndPass != D3DERR_DEVICELOST && hrEndPass != D3DERR_DEVICENOTRESET)
623
+ WriteDebugEvent (SString (" CloseActiveShader: EndPass failed: %08x" , hrEndPass));
624
+ }
600
625
601
- pD3DEffect->EndPass ();
602
-
603
- g_pActiveShader->m_pShaderInstance ->m_pEffectWrap ->End ();
604
- g_pActiveShader = NULL ;
605
-
606
- // We didn't get the effect to save the shader state, clear some things here
607
- IDirect3DDevice9* pDevice = g_pGraphics->GetDevice ();
608
- pDevice->SetVertexShader (NULL );
609
- pDevice->SetPixelShader (NULL );
626
+ // When the device is lost we intentionally skip touching the GPU beyond the required End call; the effect will be reset later.
627
+ g_pActiveShader->m_pShaderInstance ->m_pEffectWrap ->End (bAllowDeviceWork);
628
+ g_pActiveShader = nullptr ;
610
629
611
- // Unset additional vertex stream
612
630
if (CAdditionalVertexStreamManager* pAdditionalStreamManager = CAdditionalVertexStreamManager::GetExistingSingleton ())
613
631
pAdditionalStreamManager->MaybeUnsetAdditionalVertexStream ();
632
+
633
+ if (bAllowDeviceWork && pDevice)
634
+ {
635
+ // We didn't get the effect to save the shader state, clear some things here
636
+ pDevice->SetVertexShader (nullptr );
637
+ pDevice->SetPixelShader (nullptr );
638
+ }
614
639
}
615
640
616
641
// ///////////////////////////////////////////////////////////
0 commit comments