@@ -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
@@ -591,26 +599,43 @@ HRESULT CDirect3DEvents9::DrawIndexedPrimitiveShader(IDirect3DDevice9* pDevice,
591599// Finish the active shader if there is one
592600//
593601// ///////////////////////////////////////////////////////////
594- void CDirect3DEvents9::CloseActiveShader ()
602+ void CDirect3DEvents9::CloseActiveShader (bool bDeviceOperational )
595603{
596604 if (!g_pActiveShader)
597605 return ;
598606
599607 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+ }
600625
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 ;
610629
611- // Unset additional vertex stream
612630 if (CAdditionalVertexStreamManager* pAdditionalStreamManager = CAdditionalVertexStreamManager::GetExistingSingleton ())
613631 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+ }
614639}
615640
616641// ///////////////////////////////////////////////////////////
0 commit comments