@@ -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" );
@@ -358,23 +386,79 @@ HRESULT CDirect3DEvents9::DrawPrimitiveShader(IDirect3DDevice9* pDevice, D3DPRIM
358386
359387 // Do shader passes
360388 ID3DXEffect* pD3DEffect = pShaderInstance->m_pEffectWrap ->m_pD3DEffect ;
389+ bool bEffectDeviceTemporarilyLost = false ;
390+ bool bEffectDeviceOperational = true ;
391+ if (pD3DEffect)
392+ {
393+ IDirect3DDevice9* pEffectDevice = nullptr ;
394+ if (SUCCEEDED (pD3DEffect->GetDevice (&pEffectDevice)) && pEffectDevice)
395+ {
396+ bEffectDeviceOperational = IsDeviceOperational (pEffectDevice, &bEffectDeviceTemporarilyLost);
397+ SAFE_RELEASE (pEffectDevice);
398+ }
399+ }
400+
401+ if (!bEffectDeviceOperational)
402+ {
403+ SAFE_RELEASE (pOriginalVertexShader);
404+ if (!bIsLayer && !bEffectDeviceTemporarilyLost)
405+ return DrawPrimitiveGuarded (pDevice, PrimitiveType, StartVertex, PrimitiveCount);
406+ return D3D_OK;
407+ }
361408
362409 DWORD dwFlags = D3DXFX_DONOTSAVESHADERSTATE; // D3DXFX_DONOTSAVE(SHADER|SAMPLER)STATE
363410 uint uiNumPasses = 0 ;
364- pShaderInstance->m_pEffectWrap ->Begin (&uiNumPasses, dwFlags);
411+ HRESULT hrBegin = pShaderInstance->m_pEffectWrap ->Begin (&uiNumPasses, dwFlags);
412+ if (FAILED (hrBegin) || uiNumPasses == 0 )
413+ {
414+ if (FAILED (hrBegin) && hrBegin != D3DERR_DEVICELOST && hrBegin != D3DERR_DEVICENOTRESET)
415+ WriteDebugEvent (SString (" DrawPrimitiveShader: Begin failed %08x" , hrBegin));
416+
417+ SAFE_RELEASE (pOriginalVertexShader);
418+ if (!bIsLayer && hrBegin != D3DERR_DEVICELOST && hrBegin != D3DERR_DEVICENOTRESET)
419+ return DrawPrimitiveGuarded (pDevice, PrimitiveType, StartVertex, PrimitiveCount);
420+ return D3D_OK;
421+ }
365422
423+ bool bCompletedAnyPass = false ;
424+ bool bEncounteredDeviceLoss = false ;
366425 for (uint uiPass = 0 ; uiPass < uiNumPasses; uiPass++)
367426 {
368- pD3DEffect->BeginPass (uiPass);
427+ HRESULT hrBeginPass = pD3DEffect->BeginPass (uiPass);
428+ if (FAILED (hrBeginPass))
429+ {
430+ if (hrBeginPass != D3DERR_DEVICELOST && hrBeginPass != D3DERR_DEVICENOTRESET)
431+ WriteDebugEvent (SString (" DrawPrimitiveShader: BeginPass %u failed %08x" , uiPass, hrBeginPass));
432+ else
433+ bEncounteredDeviceLoss = true ;
434+ break ;
435+ }
369436
370437 // Apply original vertex shader if original draw was using it (i.e. for ped animation)
371438 if (pOriginalVertexShader)
372439 pDevice->SetVertexShader (pOriginalVertexShader);
373440
374- DrawPrimitiveGuarded (pDevice, PrimitiveType, StartVertex, PrimitiveCount);
375- pD3DEffect->EndPass ();
441+ HRESULT hrDraw = DrawPrimitiveGuarded (pDevice, PrimitiveType, StartVertex, PrimitiveCount);
442+ if (hrDraw == D3DERR_DEVICELOST || hrDraw == D3DERR_DEVICENOTRESET)
443+ bEncounteredDeviceLoss = true ;
444+
445+ HRESULT hrEndPass = pD3DEffect->EndPass ();
446+ if (FAILED (hrEndPass))
447+ {
448+ if (hrEndPass != D3DERR_DEVICELOST && hrEndPass != D3DERR_DEVICENOTRESET)
449+ WriteDebugEvent (SString (" DrawPrimitiveShader: EndPass %u failed %08x" , uiPass, hrEndPass));
450+ else
451+ bEncounteredDeviceLoss = true ;
452+ break ;
453+ }
454+
455+ if (SUCCEEDED (hrDraw))
456+ bCompletedAnyPass = true ;
376457 }
377- pShaderInstance->m_pEffectWrap ->End ();
458+
459+ HRESULT hrEnd = pShaderInstance->m_pEffectWrap ->End (bEffectDeviceOperational && !bEncounteredDeviceLoss);
460+ if (FAILED (hrEnd) && hrEnd != D3DERR_DEVICELOST && hrEnd != D3DERR_DEVICENOTRESET)
461+ WriteDebugEvent (SString (" DrawPrimitiveShader: End failed %08x" , hrEnd));
378462
379463 // If we didn't get the effect to save the shader state, clear some things here
380464 if (dwFlags & D3DXFX_DONOTSAVESHADERSTATE)
@@ -384,6 +468,9 @@ HRESULT CDirect3DEvents9::DrawPrimitiveShader(IDirect3DDevice9* pDevice, D3DPRIM
384468 }
385469
386470 SAFE_RELEASE (pOriginalVertexShader);
471+
472+ if (!bCompletedAnyPass && !bIsLayer && !bEncounteredDeviceLoss)
473+ return DrawPrimitiveGuarded (pDevice, PrimitiveType, StartVertex, PrimitiveCount);
387474 }
388475
389476 return D3D_OK;
@@ -513,12 +600,41 @@ HRESULT CDirect3DEvents9::DrawIndexedPrimitiveShader(IDirect3DDevice9* pDevice,
513600 dassert (pShaderItem == g_pActiveShader);
514601 g_pDeviceState->FrameStats .iNumShadersReuseSetup ++;
515602
516- // Transfer any state changes to the active shader
603+ // Transfer any state changes to the active shader, but ensure the device still accepts work
517604 CShaderInstance* pShaderInstance = g_pActiveShader->m_pShaderInstance ;
518- bool bChanged = pShaderInstance->m_pEffectWrap ->ApplyCommonHandles ();
605+ ID3DXEffect* pActiveEffect = pShaderInstance->m_pEffectWrap ->m_pD3DEffect ;
606+
607+ bool bDeviceTemporarilyLost = false ;
608+ bool bDeviceOperational = true ;
609+ if (pActiveEffect)
610+ {
611+ IDirect3DDevice9* pEffectDevice = nullptr ;
612+ if (SUCCEEDED (pActiveEffect->GetDevice (&pEffectDevice)) && pEffectDevice)
613+ {
614+ bDeviceOperational = IsDeviceOperational (pEffectDevice, &bDeviceTemporarilyLost);
615+ SAFE_RELEASE (pEffectDevice);
616+ }
617+ }
618+
619+ if (!bDeviceOperational)
620+ {
621+ CloseActiveShader (false );
622+ return D3D_OK;
623+ }
624+
625+ bool bChanged = pShaderInstance->m_pEffectWrap ->ApplyCommonHandles ();
519626 bChanged |= pShaderInstance->m_pEffectWrap ->ApplyMappedHandles ();
520627 if (bChanged)
521- pShaderInstance->m_pEffectWrap ->m_pD3DEffect ->CommitChanges ();
628+ {
629+ HRESULT hrCommit = pShaderInstance->m_pEffectWrap ->m_pD3DEffect ->CommitChanges ();
630+ if (FAILED (hrCommit))
631+ {
632+ if (hrCommit != D3DERR_DEVICELOST && hrCommit != D3DERR_DEVICENOTRESET)
633+ WriteDebugEvent (SString (" DrawIndexedPrimitiveShader: CommitChanges failed %08x" , hrCommit));
634+ CloseActiveShader (false );
635+ return D3D_OK;
636+ }
637+ }
522638
523639 return DrawIndexedPrimitiveGuarded (pDevice, PrimitiveType, BaseVertexIndex, MinVertexIndex, NumVertices, startIndex, primCount);
524640 }
@@ -528,14 +644,11 @@ HRESULT CDirect3DEvents9::DrawIndexedPrimitiveShader(IDirect3DDevice9* pDevice,
528644 CShaderInstance* pShaderInstance = pShaderItem->m_pShaderInstance ;
529645
530646 // Add normal stream if shader wants it
531- if (pShaderInstance->m_pEffectWrap ->m_pEffectTemplate ->m_bRequiresNormals )
647+ CAdditionalVertexStreamManager* pAdditionalStreamManager = CAdditionalVertexStreamManager::GetExistingSingleton ();
648+ if (pShaderInstance->m_pEffectWrap ->m_pEffectTemplate ->m_bRequiresNormals && pAdditionalStreamManager)
532649 {
533650 // Find/create/set additional vertex stream
534- if (CAdditionalVertexStreamManager* pAdditionalStreamManager = CAdditionalVertexStreamManager::GetExistingSingleton ())
535- {
536- pAdditionalStreamManager->MaybeSetAdditionalVertexStream (PrimitiveType, BaseVertexIndex, MinVertexIndex, NumVertices, startIndex,
537- primCount);
538- }
651+ pAdditionalStreamManager->MaybeSetAdditionalVertexStream (PrimitiveType, BaseVertexIndex, MinVertexIndex, NumVertices, startIndex, primCount);
539652 }
540653
541654 // Apply custom parameters
@@ -551,22 +664,68 @@ HRESULT CDirect3DEvents9::DrawIndexedPrimitiveShader(IDirect3DDevice9* pDevice,
551664
552665 // Do shader passes
553666 ID3DXEffect* pD3DEffect = pShaderInstance->m_pEffectWrap ->m_pD3DEffect ;
667+ bool bEffectDeviceTemporarilyLost = false ;
668+ bool bEffectDeviceOperational = true ;
669+ if (pD3DEffect)
670+ {
671+ IDirect3DDevice9* pEffectDevice = nullptr ;
672+ if (SUCCEEDED (pD3DEffect->GetDevice (&pEffectDevice)) && pEffectDevice)
673+ {
674+ bEffectDeviceOperational = IsDeviceOperational (pEffectDevice, &bEffectDeviceTemporarilyLost);
675+ SAFE_RELEASE (pEffectDevice);
676+ }
677+ }
678+
679+ if (!bEffectDeviceOperational)
680+ {
681+ SAFE_RELEASE (pOriginalVertexShader);
682+ if (pAdditionalStreamManager)
683+ pAdditionalStreamManager->MaybeUnsetAdditionalVertexStream ();
684+ if (!bEffectDeviceTemporarilyLost && !bIsLayer)
685+ return DrawIndexedPrimitiveGuarded (pDevice, PrimitiveType, BaseVertexIndex, MinVertexIndex, NumVertices, startIndex, primCount);
686+ return D3D_OK;
687+ }
554688
555689 DWORD dwFlags = D3DXFX_DONOTSAVESHADERSTATE; // D3DXFX_DONOTSAVE(SHADER|SAMPLER)STATE
556690 uint uiNumPasses = 0 ;
557- pShaderInstance->m_pEffectWrap ->Begin (&uiNumPasses, dwFlags);
691+ HRESULT hrBegin = pShaderInstance->m_pEffectWrap ->Begin (&uiNumPasses, dwFlags);
692+ if (FAILED (hrBegin) || uiNumPasses == 0 )
693+ {
694+ if (FAILED (hrBegin) && hrBegin != D3DERR_DEVICELOST && hrBegin != D3DERR_DEVICENOTRESET)
695+ WriteDebugEvent (SString (" DrawIndexedPrimitiveShader: Begin failed %08x" , hrBegin));
558696
697+ SAFE_RELEASE (pOriginalVertexShader);
698+ if (pAdditionalStreamManager)
699+ pAdditionalStreamManager->MaybeUnsetAdditionalVertexStream ();
700+
701+ if (hrBegin != D3DERR_DEVICELOST && hrBegin != D3DERR_DEVICENOTRESET && !bIsLayer)
702+ return DrawIndexedPrimitiveGuarded (pDevice, PrimitiveType, BaseVertexIndex, MinVertexIndex, NumVertices, startIndex, primCount);
703+ return D3D_OK;
704+ }
705+
706+ bool bCompletedAnyPass = false ;
707+ bool bEncounteredDeviceLoss = false ;
559708 for (uint uiPass = 0 ; uiPass < uiNumPasses; uiPass++)
560709 {
561- pD3DEffect->BeginPass (uiPass);
710+ HRESULT hrBeginPass = pD3DEffect->BeginPass (uiPass);
711+ if (FAILED (hrBeginPass))
712+ {
713+ if (hrBeginPass != D3DERR_DEVICELOST && hrBeginPass != D3DERR_DEVICENOTRESET)
714+ WriteDebugEvent (SString (" DrawIndexedPrimitiveShader: BeginPass %u failed %08x" , uiPass, hrBeginPass));
715+ else
716+ bEncounteredDeviceLoss = true ;
717+ break ;
718+ }
562719
563720 // Apply original vertex shader if original draw was using it (i.e. for ped animation)
564721 if (pOriginalVertexShader)
565722 pDevice->SetVertexShader (pOriginalVertexShader);
566723
567- DrawIndexedPrimitiveGuarded (pDevice, PrimitiveType, BaseVertexIndex, MinVertexIndex, NumVertices, startIndex, primCount);
724+ HRESULT hrDraw = DrawIndexedPrimitiveGuarded (pDevice, PrimitiveType, BaseVertexIndex, MinVertexIndex, NumVertices, startIndex, primCount);
725+ if (hrDraw == D3DERR_DEVICELOST || hrDraw == D3DERR_DEVICENOTRESET)
726+ bEncounteredDeviceLoss = true ;
568727
569- if (uiNumPasses == 1 && bCanBecomeActiveShader && pOriginalVertexShader == NULL && g_pCore->IsRenderingGrass ())
728+ if (uiNumPasses == 1 && bCanBecomeActiveShader && pOriginalVertexShader == NULL && g_pCore->IsRenderingGrass () && SUCCEEDED (hrDraw) )
570729 {
571730 // Make this the active shader for possible reuse
572731 dassert (dwFlags == D3DXFX_DONOTSAVESHADERSTATE);
@@ -575,9 +734,23 @@ HRESULT CDirect3DEvents9::DrawIndexedPrimitiveShader(IDirect3DDevice9* pDevice,
575734 return D3D_OK;
576735 }
577736
578- pD3DEffect->EndPass ();
737+ HRESULT hrEndPass = pD3DEffect->EndPass ();
738+ if (FAILED (hrEndPass))
739+ {
740+ if (hrEndPass != D3DERR_DEVICELOST && hrEndPass != D3DERR_DEVICENOTRESET)
741+ WriteDebugEvent (SString (" DrawIndexedPrimitiveShader: EndPass %u failed %08x" , uiPass, hrEndPass));
742+ else
743+ bEncounteredDeviceLoss = true ;
744+ break ;
745+ }
746+
747+ if (SUCCEEDED (hrDraw))
748+ bCompletedAnyPass = true ;
579749 }
580- pShaderInstance->m_pEffectWrap ->End ();
750+
751+ HRESULT hrEnd = pShaderInstance->m_pEffectWrap ->End (bEffectDeviceOperational && !bEncounteredDeviceLoss);
752+ if (FAILED (hrEnd) && hrEnd != D3DERR_DEVICELOST && hrEnd != D3DERR_DEVICENOTRESET)
753+ WriteDebugEvent (SString (" DrawIndexedPrimitiveShader: End failed %08x" , hrEnd));
581754
582755 // If we didn't get the effect to save the shader state, clear some things here
583756 if (dwFlags & D3DXFX_DONOTSAVESHADERSTATE)
@@ -587,10 +760,13 @@ HRESULT CDirect3DEvents9::DrawIndexedPrimitiveShader(IDirect3DDevice9* pDevice,
587760 }
588761
589762 // Unset additional vertex stream
590- if (CAdditionalVertexStreamManager* pAdditionalStreamManager = CAdditionalVertexStreamManager::GetExistingSingleton () )
763+ if (pAdditionalStreamManager)
591764 pAdditionalStreamManager->MaybeUnsetAdditionalVertexStream ();
592765
593766 SAFE_RELEASE (pOriginalVertexShader);
767+
768+ if (!bCompletedAnyPass && !bEncounteredDeviceLoss && !bIsLayer)
769+ return DrawIndexedPrimitiveGuarded (pDevice, PrimitiveType, BaseVertexIndex, MinVertexIndex, NumVertices, startIndex, primCount);
594770 }
595771
596772 return D3D_OK;
@@ -610,15 +786,14 @@ void CDirect3DEvents9::CloseActiveShader(bool bDeviceOperational)
610786
611787 ID3DXEffect* pD3DEffect = g_pActiveShader->m_pShaderInstance ->m_pEffectWrap ->m_pD3DEffect ;
612788 IDirect3DDevice9* pDevice = g_pGraphics ? g_pGraphics->GetDevice () : nullptr ;
613- HRESULT hrCooperativeLevel = D3D_OK;
614- if (pDevice)
615- hrCooperativeLevel = pDevice->TestCooperativeLevel ();
616789
617790 bool bAllowDeviceWork = bDeviceOperational;
618- if (hrCooperativeLevel == D3D_OK)
619- bAllowDeviceWork = true ;
620- else if (hrCooperativeLevel == D3DERR_DEVICELOST || hrCooperativeLevel == D3DERR_DEVICENOTRESET)
621- bAllowDeviceWork = false ;
791+ if (pDevice)
792+ {
793+ bool bDeviceTemporarilyLost = false ;
794+ if (!IsDeviceOperational (pDevice, &bDeviceTemporarilyLost))
795+ bAllowDeviceWork = !bDeviceTemporarilyLost && bDeviceOperational;
796+ }
622797
623798 if (pD3DEffect)
624799 {
0 commit comments