@@ -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