@@ -31,6 +31,34 @@ static EDiagnosticDebugType ms_DiagnosticDebug = EDiagnosticDebug::NONE;
3131// To reuse shader setups between calls to DrawIndexedPrimitive
3232CShaderItem* g_pActiveShader = NULL ;
3333
34+ namespace
35+ {
36+ bool IsDeviceOperational (IDirect3DDevice9* pDevice, bool * pbTemporarilyLost = nullptr )
37+ {
38+ if (pbTemporarilyLost)
39+ *pbTemporarilyLost = false ;
40+
41+ if (!pDevice)
42+ return false ;
43+
44+ const HRESULT hr = pDevice->TestCooperativeLevel ();
45+ if (hr == D3D_OK)
46+ return true ;
47+
48+ if (hr == D3DERR_DEVICELOST || hr == D3DERR_DEVICENOTRESET)
49+ {
50+ if (pbTemporarilyLost)
51+ *pbTemporarilyLost = true ;
52+ }
53+ else
54+ {
55+ WriteDebugEvent (SString (" IsDeviceOperational: unexpected cooperative level %08x" , hr));
56+ }
57+
58+ return false ;
59+ }
60+ }
61+
3462void CDirect3DEvents9::OnDirect3DDeviceCreate (IDirect3DDevice9* pDevice)
3563{
3664 WriteDebugEvent (" CDirect3DEvents9::OnDirect3DDeviceCreate" );
@@ -356,23 +384,79 @@ HRESULT CDirect3DEvents9::DrawPrimitiveShader(IDirect3DDevice9* pDevice, D3DPRIM
356384
357385 // Do shader passes
358386 ID3DXEffect* pD3DEffect = pShaderInstance->m_pEffectWrap ->m_pD3DEffect ;
387+ bool bEffectDeviceTemporarilyLost = false ;
388+ bool bEffectDeviceOperational = true ;
389+ if (pD3DEffect)
390+ {
391+ IDirect3DDevice9* pEffectDevice = nullptr ;
392+ if (SUCCEEDED (pD3DEffect->GetDevice (&pEffectDevice)) && pEffectDevice)
393+ {
394+ bEffectDeviceOperational = IsDeviceOperational (pEffectDevice, &bEffectDeviceTemporarilyLost);
395+ SAFE_RELEASE (pEffectDevice);
396+ }
397+ }
398+
399+ if (!bEffectDeviceOperational)
400+ {
401+ SAFE_RELEASE (pOriginalVertexShader);
402+ if (!bIsLayer && !bEffectDeviceTemporarilyLost)
403+ return DrawPrimitiveGuarded (pDevice, PrimitiveType, StartVertex, PrimitiveCount);
404+ return D3D_OK;
405+ }
359406
360407 DWORD dwFlags = D3DXFX_DONOTSAVESHADERSTATE; // D3DXFX_DONOTSAVE(SHADER|SAMPLER)STATE
361408 uint uiNumPasses = 0 ;
362- pShaderInstance->m_pEffectWrap ->Begin (&uiNumPasses, dwFlags);
409+ HRESULT hrBegin = pShaderInstance->m_pEffectWrap ->Begin (&uiNumPasses, dwFlags);
410+ if (FAILED (hrBegin) || uiNumPasses == 0 )
411+ {
412+ if (FAILED (hrBegin) && hrBegin != D3DERR_DEVICELOST && hrBegin != D3DERR_DEVICENOTRESET)
413+ WriteDebugEvent (SString (" DrawPrimitiveShader: Begin failed %08x" , hrBegin));
414+
415+ SAFE_RELEASE (pOriginalVertexShader);
416+ if (!bIsLayer && hrBegin != D3DERR_DEVICELOST && hrBegin != D3DERR_DEVICENOTRESET)
417+ return DrawPrimitiveGuarded (pDevice, PrimitiveType, StartVertex, PrimitiveCount);
418+ return D3D_OK;
419+ }
363420
421+ bool bCompletedAnyPass = false ;
422+ bool bEncounteredDeviceLoss = false ;
364423 for (uint uiPass = 0 ; uiPass < uiNumPasses; uiPass++)
365424 {
366- pD3DEffect->BeginPass (uiPass);
425+ HRESULT hrBeginPass = pD3DEffect->BeginPass (uiPass);
426+ if (FAILED (hrBeginPass))
427+ {
428+ if (hrBeginPass != D3DERR_DEVICELOST && hrBeginPass != D3DERR_DEVICENOTRESET)
429+ WriteDebugEvent (SString (" DrawPrimitiveShader: BeginPass %u failed %08x" , uiPass, hrBeginPass));
430+ else
431+ bEncounteredDeviceLoss = true ;
432+ break ;
433+ }
367434
368435 // Apply original vertex shader if original draw was using it (i.e. for ped animation)
369436 if (pOriginalVertexShader)
370437 pDevice->SetVertexShader (pOriginalVertexShader);
371438
372- DrawPrimitiveGuarded (pDevice, PrimitiveType, StartVertex, PrimitiveCount);
373- pD3DEffect->EndPass ();
439+ HRESULT hrDraw = DrawPrimitiveGuarded (pDevice, PrimitiveType, StartVertex, PrimitiveCount);
440+ if (hrDraw == D3DERR_DEVICELOST || hrDraw == D3DERR_DEVICENOTRESET)
441+ bEncounteredDeviceLoss = true ;
442+
443+ HRESULT hrEndPass = pD3DEffect->EndPass ();
444+ if (FAILED (hrEndPass))
445+ {
446+ if (hrEndPass != D3DERR_DEVICELOST && hrEndPass != D3DERR_DEVICENOTRESET)
447+ WriteDebugEvent (SString (" DrawPrimitiveShader: EndPass %u failed %08x" , uiPass, hrEndPass));
448+ else
449+ bEncounteredDeviceLoss = true ;
450+ break ;
451+ }
452+
453+ if (SUCCEEDED (hrDraw))
454+ bCompletedAnyPass = true ;
374455 }
375- pShaderInstance->m_pEffectWrap ->End ();
456+
457+ HRESULT hrEnd = pShaderInstance->m_pEffectWrap ->End (bEffectDeviceOperational && !bEncounteredDeviceLoss);
458+ if (FAILED (hrEnd) && hrEnd != D3DERR_DEVICELOST && hrEnd != D3DERR_DEVICENOTRESET)
459+ WriteDebugEvent (SString (" DrawPrimitiveShader: End failed %08x" , hrEnd));
376460
377461 // If we didn't get the effect to save the shader state, clear some things here
378462 if (dwFlags & D3DXFX_DONOTSAVESHADERSTATE)
@@ -382,6 +466,9 @@ HRESULT CDirect3DEvents9::DrawPrimitiveShader(IDirect3DDevice9* pDevice, D3DPRIM
382466 }
383467
384468 SAFE_RELEASE (pOriginalVertexShader);
469+
470+ if (!bCompletedAnyPass && !bIsLayer && !bEncounteredDeviceLoss)
471+ return DrawPrimitiveGuarded (pDevice, PrimitiveType, StartVertex, PrimitiveCount);
385472 }
386473
387474 return D3D_OK;
@@ -511,12 +598,41 @@ HRESULT CDirect3DEvents9::DrawIndexedPrimitiveShader(IDirect3DDevice9* pDevice,
511598 dassert (pShaderItem == g_pActiveShader);
512599 g_pDeviceState->FrameStats .iNumShadersReuseSetup ++;
513600
514- // Transfer any state changes to the active shader
601+ // Transfer any state changes to the active shader, but ensure the device still accepts work
515602 CShaderInstance* pShaderInstance = g_pActiveShader->m_pShaderInstance ;
516- bool bChanged = pShaderInstance->m_pEffectWrap ->ApplyCommonHandles ();
603+ ID3DXEffect* pActiveEffect = pShaderInstance->m_pEffectWrap ->m_pD3DEffect ;
604+
605+ bool bDeviceTemporarilyLost = false ;
606+ bool bDeviceOperational = true ;
607+ if (pActiveEffect)
608+ {
609+ IDirect3DDevice9* pEffectDevice = nullptr ;
610+ if (SUCCEEDED (pActiveEffect->GetDevice (&pEffectDevice)) && pEffectDevice)
611+ {
612+ bDeviceOperational = IsDeviceOperational (pEffectDevice, &bDeviceTemporarilyLost);
613+ SAFE_RELEASE (pEffectDevice);
614+ }
615+ }
616+
617+ if (!bDeviceOperational)
618+ {
619+ CloseActiveShader (false );
620+ return D3D_OK;
621+ }
622+
623+ bool bChanged = pShaderInstance->m_pEffectWrap ->ApplyCommonHandles ();
517624 bChanged |= pShaderInstance->m_pEffectWrap ->ApplyMappedHandles ();
518625 if (bChanged)
519- pShaderInstance->m_pEffectWrap ->m_pD3DEffect ->CommitChanges ();
626+ {
627+ HRESULT hrCommit = pShaderInstance->m_pEffectWrap ->m_pD3DEffect ->CommitChanges ();
628+ if (FAILED (hrCommit))
629+ {
630+ if (hrCommit != D3DERR_DEVICELOST && hrCommit != D3DERR_DEVICENOTRESET)
631+ WriteDebugEvent (SString (" DrawIndexedPrimitiveShader: CommitChanges failed %08x" , hrCommit));
632+ CloseActiveShader (false );
633+ return D3D_OK;
634+ }
635+ }
520636
521637 return DrawIndexedPrimitiveGuarded (pDevice, PrimitiveType, BaseVertexIndex, MinVertexIndex, NumVertices, startIndex, primCount);
522638 }
@@ -526,14 +642,11 @@ HRESULT CDirect3DEvents9::DrawIndexedPrimitiveShader(IDirect3DDevice9* pDevice,
526642 CShaderInstance* pShaderInstance = pShaderItem->m_pShaderInstance ;
527643
528644 // Add normal stream if shader wants it
529- if (pShaderInstance->m_pEffectWrap ->m_pEffectTemplate ->m_bRequiresNormals )
645+ CAdditionalVertexStreamManager* pAdditionalStreamManager = CAdditionalVertexStreamManager::GetExistingSingleton ();
646+ if (pShaderInstance->m_pEffectWrap ->m_pEffectTemplate ->m_bRequiresNormals && pAdditionalStreamManager)
530647 {
531648 // Find/create/set additional vertex stream
532- if (CAdditionalVertexStreamManager* pAdditionalStreamManager = CAdditionalVertexStreamManager::GetExistingSingleton ())
533- {
534- pAdditionalStreamManager->MaybeSetAdditionalVertexStream (PrimitiveType, BaseVertexIndex, MinVertexIndex, NumVertices, startIndex,
535- primCount);
536- }
649+ pAdditionalStreamManager->MaybeSetAdditionalVertexStream (PrimitiveType, BaseVertexIndex, MinVertexIndex, NumVertices, startIndex, primCount);
537650 }
538651
539652 // Apply custom parameters
@@ -549,22 +662,68 @@ HRESULT CDirect3DEvents9::DrawIndexedPrimitiveShader(IDirect3DDevice9* pDevice,
549662
550663 // Do shader passes
551664 ID3DXEffect* pD3DEffect = pShaderInstance->m_pEffectWrap ->m_pD3DEffect ;
665+ bool bEffectDeviceTemporarilyLost = false ;
666+ bool bEffectDeviceOperational = true ;
667+ if (pD3DEffect)
668+ {
669+ IDirect3DDevice9* pEffectDevice = nullptr ;
670+ if (SUCCEEDED (pD3DEffect->GetDevice (&pEffectDevice)) && pEffectDevice)
671+ {
672+ bEffectDeviceOperational = IsDeviceOperational (pEffectDevice, &bEffectDeviceTemporarilyLost);
673+ SAFE_RELEASE (pEffectDevice);
674+ }
675+ }
676+
677+ if (!bEffectDeviceOperational)
678+ {
679+ SAFE_RELEASE (pOriginalVertexShader);
680+ if (pAdditionalStreamManager)
681+ pAdditionalStreamManager->MaybeUnsetAdditionalVertexStream ();
682+ if (!bEffectDeviceTemporarilyLost && !bIsLayer)
683+ return DrawIndexedPrimitiveGuarded (pDevice, PrimitiveType, BaseVertexIndex, MinVertexIndex, NumVertices, startIndex, primCount);
684+ return D3D_OK;
685+ }
552686
553687 DWORD dwFlags = D3DXFX_DONOTSAVESHADERSTATE; // D3DXFX_DONOTSAVE(SHADER|SAMPLER)STATE
554688 uint uiNumPasses = 0 ;
555- pShaderInstance->m_pEffectWrap ->Begin (&uiNumPasses, dwFlags);
689+ HRESULT hrBegin = pShaderInstance->m_pEffectWrap ->Begin (&uiNumPasses, dwFlags);
690+ if (FAILED (hrBegin) || uiNumPasses == 0 )
691+ {
692+ if (FAILED (hrBegin) && hrBegin != D3DERR_DEVICELOST && hrBegin != D3DERR_DEVICENOTRESET)
693+ WriteDebugEvent (SString (" DrawIndexedPrimitiveShader: Begin failed %08x" , hrBegin));
556694
695+ SAFE_RELEASE (pOriginalVertexShader);
696+ if (pAdditionalStreamManager)
697+ pAdditionalStreamManager->MaybeUnsetAdditionalVertexStream ();
698+
699+ if (hrBegin != D3DERR_DEVICELOST && hrBegin != D3DERR_DEVICENOTRESET && !bIsLayer)
700+ return DrawIndexedPrimitiveGuarded (pDevice, PrimitiveType, BaseVertexIndex, MinVertexIndex, NumVertices, startIndex, primCount);
701+ return D3D_OK;
702+ }
703+
704+ bool bCompletedAnyPass = false ;
705+ bool bEncounteredDeviceLoss = false ;
557706 for (uint uiPass = 0 ; uiPass < uiNumPasses; uiPass++)
558707 {
559- pD3DEffect->BeginPass (uiPass);
708+ HRESULT hrBeginPass = pD3DEffect->BeginPass (uiPass);
709+ if (FAILED (hrBeginPass))
710+ {
711+ if (hrBeginPass != D3DERR_DEVICELOST && hrBeginPass != D3DERR_DEVICENOTRESET)
712+ WriteDebugEvent (SString (" DrawIndexedPrimitiveShader: BeginPass %u failed %08x" , uiPass, hrBeginPass));
713+ else
714+ bEncounteredDeviceLoss = true ;
715+ break ;
716+ }
560717
561718 // Apply original vertex shader if original draw was using it (i.e. for ped animation)
562719 if (pOriginalVertexShader)
563720 pDevice->SetVertexShader (pOriginalVertexShader);
564721
565- DrawIndexedPrimitiveGuarded (pDevice, PrimitiveType, BaseVertexIndex, MinVertexIndex, NumVertices, startIndex, primCount);
722+ HRESULT hrDraw = DrawIndexedPrimitiveGuarded (pDevice, PrimitiveType, BaseVertexIndex, MinVertexIndex, NumVertices, startIndex, primCount);
723+ if (hrDraw == D3DERR_DEVICELOST || hrDraw == D3DERR_DEVICENOTRESET)
724+ bEncounteredDeviceLoss = true ;
566725
567- if (uiNumPasses == 1 && bCanBecomeActiveShader && pOriginalVertexShader == NULL && g_pCore->IsRenderingGrass ())
726+ if (uiNumPasses == 1 && bCanBecomeActiveShader && pOriginalVertexShader == NULL && g_pCore->IsRenderingGrass () && SUCCEEDED (hrDraw) )
568727 {
569728 // Make this the active shader for possible reuse
570729 dassert (dwFlags == D3DXFX_DONOTSAVESHADERSTATE);
@@ -573,9 +732,23 @@ HRESULT CDirect3DEvents9::DrawIndexedPrimitiveShader(IDirect3DDevice9* pDevice,
573732 return D3D_OK;
574733 }
575734
576- pD3DEffect->EndPass ();
735+ HRESULT hrEndPass = pD3DEffect->EndPass ();
736+ if (FAILED (hrEndPass))
737+ {
738+ if (hrEndPass != D3DERR_DEVICELOST && hrEndPass != D3DERR_DEVICENOTRESET)
739+ WriteDebugEvent (SString (" DrawIndexedPrimitiveShader: EndPass %u failed %08x" , uiPass, hrEndPass));
740+ else
741+ bEncounteredDeviceLoss = true ;
742+ break ;
743+ }
744+
745+ if (SUCCEEDED (hrDraw))
746+ bCompletedAnyPass = true ;
577747 }
578- pShaderInstance->m_pEffectWrap ->End ();
748+
749+ HRESULT hrEnd = pShaderInstance->m_pEffectWrap ->End (bEffectDeviceOperational && !bEncounteredDeviceLoss);
750+ if (FAILED (hrEnd) && hrEnd != D3DERR_DEVICELOST && hrEnd != D3DERR_DEVICENOTRESET)
751+ WriteDebugEvent (SString (" DrawIndexedPrimitiveShader: End failed %08x" , hrEnd));
579752
580753 // If we didn't get the effect to save the shader state, clear some things here
581754 if (dwFlags & D3DXFX_DONOTSAVESHADERSTATE)
@@ -585,10 +758,13 @@ HRESULT CDirect3DEvents9::DrawIndexedPrimitiveShader(IDirect3DDevice9* pDevice,
585758 }
586759
587760 // Unset additional vertex stream
588- if (CAdditionalVertexStreamManager* pAdditionalStreamManager = CAdditionalVertexStreamManager::GetExistingSingleton () )
761+ if (pAdditionalStreamManager)
589762 pAdditionalStreamManager->MaybeUnsetAdditionalVertexStream ();
590763
591764 SAFE_RELEASE (pOriginalVertexShader);
765+
766+ if (!bCompletedAnyPass && !bEncounteredDeviceLoss && !bIsLayer)
767+ return DrawIndexedPrimitiveGuarded (pDevice, PrimitiveType, BaseVertexIndex, MinVertexIndex, NumVertices, startIndex, primCount);
592768 }
593769
594770 return D3D_OK;
@@ -608,15 +784,14 @@ void CDirect3DEvents9::CloseActiveShader(bool bDeviceOperational)
608784
609785 ID3DXEffect* pD3DEffect = g_pActiveShader->m_pShaderInstance ->m_pEffectWrap ->m_pD3DEffect ;
610786 IDirect3DDevice9* pDevice = g_pGraphics ? g_pGraphics->GetDevice () : nullptr ;
611- HRESULT hrCooperativeLevel = D3D_OK;
612- if (pDevice)
613- hrCooperativeLevel = pDevice->TestCooperativeLevel ();
614787
615788 bool bAllowDeviceWork = bDeviceOperational;
616- if (hrCooperativeLevel == D3D_OK)
617- bAllowDeviceWork = true ;
618- else if (hrCooperativeLevel == D3DERR_DEVICELOST || hrCooperativeLevel == D3DERR_DEVICENOTRESET)
619- bAllowDeviceWork = false ;
789+ if (pDevice)
790+ {
791+ bool bDeviceTemporarilyLost = false ;
792+ if (!IsDeviceOperational (pDevice, &bDeviceTemporarilyLost))
793+ bAllowDeviceWork = !bDeviceTemporarilyLost && bDeviceOperational;
794+ }
620795
621796 if (pD3DEffect)
622797 {
0 commit comments