@@ -80,33 +80,41 @@ void CDirect3DEvents9::OnInvalidate(IDirect3DDevice9* pDevice)
8080{
8181 WriteDebugEvent (" CDirect3DEvents9::OnInvalidate" );
8282
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);
9087
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)
9792 {
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)
10598 {
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));
107102 }
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 );
108113 }
109-
114+
115+ g_bInMTAScene = false ;
116+ g_bInGTAScene = false ;
117+
110118 // Invalidate the VMR9 Manager
111119 // CVideoManager::GetSingleton ().OnLostDevice ();
112120
@@ -589,26 +597,43 @@ HRESULT CDirect3DEvents9::DrawIndexedPrimitiveShader(IDirect3DDevice9* pDevice,
589597// Finish the active shader if there is one
590598//
591599// ///////////////////////////////////////////////////////////
592- void CDirect3DEvents9::CloseActiveShader ()
600+ void CDirect3DEvents9::CloseActiveShader (bool bDeviceOperational )
593601{
594602 if (!g_pActiveShader)
595603 return ;
596604
597605 ID3DXEffect* pD3DEffect = g_pActiveShader->m_pShaderInstance ->m_pEffectWrap ->m_pD3DEffect ;
606+ IDirect3DDevice9* pDevice = g_pGraphics ? g_pGraphics->GetDevice () : nullptr ;
607+ HRESULT hrCooperativeLevel = D3D_OK;
608+ if (pDevice)
609+ hrCooperativeLevel = pDevice->TestCooperativeLevel ();
610+
611+ bool bAllowDeviceWork = bDeviceOperational;
612+ if (hrCooperativeLevel == D3D_OK)
613+ bAllowDeviceWork = true ;
614+ else if (hrCooperativeLevel == D3DERR_DEVICELOST || hrCooperativeLevel == D3DERR_DEVICENOTRESET)
615+ bAllowDeviceWork = false ;
616+
617+ if (pD3DEffect)
618+ {
619+ HRESULT hrEndPass = pD3DEffect->EndPass ();
620+ if (FAILED (hrEndPass) && hrEndPass != D3DERR_DEVICELOST && hrEndPass != D3DERR_DEVICENOTRESET)
621+ WriteDebugEvent (SString (" CloseActiveShader: EndPass failed: %08x" , hrEndPass));
622+ }
598623
599- pD3DEffect->EndPass ();
600-
601- g_pActiveShader->m_pShaderInstance ->m_pEffectWrap ->End ();
602- g_pActiveShader = NULL ;
603-
604- // We didn't get the effect to save the shader state, clear some things here
605- IDirect3DDevice9* pDevice = g_pGraphics->GetDevice ();
606- pDevice->SetVertexShader (NULL );
607- pDevice->SetPixelShader (NULL );
624+ // When the device is lost we intentionally skip touching the GPU beyond the required End call; the effect will be reset later.
625+ g_pActiveShader->m_pShaderInstance ->m_pEffectWrap ->End (bAllowDeviceWork);
626+ g_pActiveShader = nullptr ;
608627
609- // Unset additional vertex stream
610628 if (CAdditionalVertexStreamManager* pAdditionalStreamManager = CAdditionalVertexStreamManager::GetExistingSingleton ())
611629 pAdditionalStreamManager->MaybeUnsetAdditionalVertexStream ();
630+
631+ if (bAllowDeviceWork && pDevice)
632+ {
633+ // We didn't get the effect to save the shader state, clear some things here
634+ pDevice->SetVertexShader (nullptr );
635+ pDevice->SetPixelShader (nullptr );
636+ }
612637}
613638
614639// ///////////////////////////////////////////////////////////
0 commit comments