1212#include " StdInc.h"
1313#include < algorithm>
1414#include < atomic>
15+ #include < cmath>
1516#include < mutex>
1617#include < game/CSettings.h>
1718
1819class CProxyDirect3DDevice9 ;
20+ extern std::atomic<bool > g_bInMTAScene;
1921
2022namespace
2123{
@@ -46,6 +48,7 @@ std::mutex g_deviceStateMutex;
4648std::atomic<uint64_t > g_proxyRegistrationCounter{0 };
4749std::mutex g_sceneStateMutex;
4850uint32_t g_gtaSceneActiveCount = 0 ;
51+ uint64_t g_activeProxyRegistrationId = 0 ;
4952
5053uint64_t RegisterProxyDevice (CProxyDirect3DDevice9* instance);
5154bool UnregisterProxyDevice (CProxyDirect3DDevice9* instance, uint64_t registrationId);
@@ -233,7 +236,10 @@ CProxyDirect3DDevice9::~CProxyDirect3DDevice9()
233236
234237 if (bRestoreGamma && m_pDevice)
235238 {
236- m_pDevice->SetGammaRamp (lastSwapChain, 0 , &originalGammaRamp);
239+ IDirect3DDevice9* pDevice = m_pDevice;
240+ pDevice->AddRef ();
241+ pDevice->SetGammaRamp (lastSwapChain, 0 , &originalGammaRamp);
242+ pDevice->Release ();
237243 WriteDebugEvent (" Restored original gamma ramp on device destruction" );
238244 }
239245
@@ -474,6 +480,41 @@ HRESULT DoResetDevice(IDirect3DDevice9* pDevice, D3DPRESENT_PARAMETERS* pPresent
474480 return hResult;
475481}
476482
483+ static bool WaitForGpuIdle (IDirect3DDevice9* pDevice)
484+ {
485+ if (!pDevice)
486+ return false ;
487+
488+ IDirect3DQuery9* pEventQuery = nullptr ;
489+ const HRESULT hrCreate = pDevice->CreateQuery (D3DQUERYTYPE_EVENT, &pEventQuery);
490+ if (FAILED (hrCreate) || !pEventQuery)
491+ return false ;
492+
493+ bool bCompleted = false ;
494+ if (SUCCEEDED (pEventQuery->Issue (D3DISSUE_END)))
495+ {
496+ constexpr int kMaxAttempts = 16 ;
497+ for (int attempt = 0 ; attempt < kMaxAttempts ; ++attempt)
498+ {
499+ const HRESULT hrData = pEventQuery->GetData (nullptr , 0 , D3DGETDATA_FLUSH);
500+ if (hrData == S_OK)
501+ {
502+ bCompleted = true ;
503+ break ;
504+ }
505+ if (hrData == D3DERR_DEVICELOST)
506+ {
507+ break ;
508+ }
509+
510+ Sleep (0 );
511+ }
512+ }
513+
514+ pEventQuery->Release ();
515+ return bCompleted;
516+ }
517+
477518HRESULT CProxyDirect3DDevice9::Reset (D3DPRESENT_PARAMETERS* pPresentationParameters)
478519{
479520 WriteDebugEvent (" CProxyDirect3DDevice9::Reset" );
@@ -528,7 +569,12 @@ HRESULT CProxyDirect3DDevice9::Reset(D3DPRESENT_PARAMETERS* pPresentationParamet
528569 // Release cached state so lingering references can't block Reset on default-pool resources
529570 ReleaseCachedResources ();
530571
531- // Give GPU driver time to complete any pending operations
572+ // Give GPU driver time to complete any pending operations. We keep the light Sleep as a
573+ // conservative fallback but first rely on an event query, as recommended in Microsoft's GPU
574+ // synchronization guidance, to ensure outstanding commands finish.
575+ if (!WaitForGpuIdle (m_pDevice))
576+ WriteDebugEvent (" CProxyDirect3DDevice9::Reset - WaitForGpuIdle timed out or failed" );
577+
532578 Sleep (1 );
533579
534580 // Call the real reset routine.
@@ -538,43 +584,37 @@ HRESULT CProxyDirect3DDevice9::Reset(D3DPRESENT_PARAMETERS* pPresentationParamet
538584 {
539585 // Store actual present parameters used
540586 IDirect3DSwapChain9* pSwapChain = nullptr ;
541- HRESULT hrSwapChain = m_pDevice->GetSwapChain (0 , &pSwapChain);
542- if (SUCCEEDED (hrSwapChain))
587+ HRESULT hrSwapChain = m_pDevice->GetSwapChain (0 , &pSwapChain);
588+ if (FAILED (hrSwapChain))
543589 {
544- if (pSwapChain)
545- {
546- std::lock_guard<std::mutex> stateGuard (g_deviceStateMutex);
547- if (g_pDeviceState)
548- {
549- pSwapChain->GetPresentParameters (&g_pDeviceState->CreationState .PresentationParameters );
550- }
551- }
552- else
553- {
554- WriteDebugEvent (" Warning: GetSwapChain succeeded but returned null swap chain" );
555- }
590+ WriteDebugEvent (SString (" Warning: Failed to get swap chain for parameter storage: %08x" , hrSwapChain));
556591 }
557- else
592+ else if (!pSwapChain)
558593 {
559- WriteDebugEvent (SString ( " Warning: Failed to get swap chain for parameter storage: %08x " , hrSwapChain) );
594+ WriteDebugEvent (" Warning: GetSwapChain succeeded but returned null swap chain " );
560595 }
561-
562- // Always release the swap chain if it was obtained, regardless of success/failure
563- ReleaseInterface (pSwapChain);
564596
565- // Store device creation parameters as well
597+ // Store device creation parameters as well, keeping both updates atomic relative to device state changes
566598 HRESULT hrCreationParams = D3D_OK;
567599 {
568600 std::lock_guard<std::mutex> stateGuard (g_deviceStateMutex);
569601 if (g_pDeviceState)
570602 {
603+ if (SUCCEEDED (hrSwapChain) && pSwapChain)
604+ {
605+ pSwapChain->GetPresentParameters (&g_pDeviceState->CreationState .PresentationParameters );
606+ }
571607 hrCreationParams = m_pDevice->GetCreationParameters (&g_pDeviceState->CreationState .CreationParameters );
572608 }
573609 else
574610 {
575611 hrCreationParams = D3DERR_INVALIDCALL;
576612 }
577613 }
614+
615+ // Always release the swap chain if it was obtained, regardless of success/failure
616+ ReleaseInterface (pSwapChain);
617+
578618 if (FAILED (hrCreationParams))
579619 {
580620 WriteDebugEvent (SString (" Warning: Failed to get creation parameters: %08x" , hrCreationParams));
@@ -599,8 +639,14 @@ HRESULT CProxyDirect3DDevice9::Reset(D3DPRESENT_PARAMETERS* pPresentationParamet
599639 CDirect3DEvents9::OnRestore (m_pDevice);
600640
601641 // Additional sync point for GPU driver
602- m_pDevice->BeginScene ();
603- m_pDevice->EndScene ();
642+ if (!BeginSceneWithoutProxy (m_pDevice, ESceneOwner::MTA))
643+ {
644+ WriteDebugEvent (" CProxyDirect3DDevice9::Reset - Failed to begin scene for driver sync" );
645+ }
646+ else if (!EndSceneWithoutProxy (m_pDevice, ESceneOwner::MTA))
647+ {
648+ WriteDebugEvent (" CProxyDirect3DDevice9::Reset - Failed to end scene for driver sync" );
649+ }
604650
605651 WriteDebugEvent (SString (" BackBufferWidth:%d Height:%d Format:%d Count:%d" , pPresentationParameters->BackBufferWidth ,
606652 pPresentationParameters->BackBufferHeight , pPresentationParameters->BackBufferFormat , pPresentationParameters->BackBufferCount ));
@@ -712,9 +758,9 @@ VOID CProxyDirect3DDevice9::SetGammaRamp(UINT iSwapChain, DWORD Flags, CONST D3D
712758 std::lock_guard<std::mutex> gammaLock (g_gammaStateMutex);
713759 if (!g_GammaState.bOriginalGammaStored || g_GammaState.lastSwapChain != iSwapChain || g_GammaState.bLastWasBorderless != bIsBorderlessMode)
714760 {
715- if (!bIsBorderlessMode || ! g_GammaState.bOriginalGammaStored )
761+ if (!g_GammaState.bOriginalGammaStored )
716762 {
717- // In fullscreen mode or first time - this is likely the original gamma
763+ // Capture the original gamma only once per session
718764 g_GammaState.originalGammaRamp = *pRamp;
719765 g_GammaState.bOriginalGammaStored = true ;
720766 g_GammaState.lastSwapChain = iSwapChain;
@@ -733,6 +779,12 @@ VOID CProxyDirect3DDevice9::SetGammaRamp(UINT iSwapChain, DWORD Flags, CONST D3D
733779 const float fGammaCorrection = 1 .0f / 1 .3f ;
734780 const float fBrightnessBoost = 1 .4f ;
735781
782+ const auto convertNormalizedToGammaWord = [](float normalized) -> WORD {
783+ const float scaled = std::clamp (normalized * 65535 .0f , 0 .0f , 65535 .0f );
784+ const float rounded = std::floor (scaled + 0 .5f );
785+ return static_cast <WORD>(rounded);
786+ };
787+
736788 for (int i = 0 ; i < 256 ; i++)
737789 {
738790 // Convert to normalized float (0.0 - 1.0)
@@ -756,9 +808,9 @@ VOID CProxyDirect3DDevice9::SetGammaRamp(UINT iSwapChain, DWORD Flags, CONST D3D
756808 fBlue = std::min (1 .0f , fBlue * fBrightnessBoost );
757809
758810 // Convert back to WORD values with proper rounding
759- adjustedRamp.red [i] = static_cast <WORD> (fRed * 65535 . 0f + 0 . 5f );
760- adjustedRamp.green [i] = static_cast <WORD> (fGreen * 65535 . 0f + 0 . 5f );
761- adjustedRamp.blue [i] = static_cast <WORD> (fBlue * 65535 . 0f + 0 . 5f );
811+ adjustedRamp.red [i] = convertNormalizedToGammaWord (fRed );
812+ adjustedRamp.green [i] = convertNormalizedToGammaWord (fGreen );
813+ adjustedRamp.blue [i] = convertNormalizedToGammaWord (fBlue );
762814 }
763815
764816 // Set the adjusted gamma ramp
@@ -1424,6 +1476,7 @@ uint64_t RegisterProxyDevice(CProxyDirect3DDevice9* instance)
14241476 std::lock_guard<std::mutex> guard (g_proxyDeviceMutex);
14251477 const uint64_t registrationId = g_proxyRegistrationCounter.fetch_add (1 , std::memory_order_relaxed) + 1 ;
14261478 g_pProxyDevice = instance;
1479+ g_activeProxyRegistrationId = registrationId;
14271480 {
14281481 std::lock_guard<std::mutex> stateGuard (g_deviceStateMutex);
14291482 g_pDeviceState = instance ? &instance->DeviceState : nullptr ;
@@ -1437,10 +1490,11 @@ bool UnregisterProxyDevice(CProxyDirect3DDevice9* instance, uint64_t registratio
14371490 if (g_pProxyDevice != instance)
14381491 return false ;
14391492
1440- if (g_proxyRegistrationCounter. load (std::memory_order_relaxed) != registrationId)
1493+ if (g_activeProxyRegistrationId != registrationId)
14411494 return false ;
14421495
14431496 g_pProxyDevice = nullptr ;
1497+ g_activeProxyRegistrationId = 0 ;
14441498 {
14451499 std::lock_guard<std::mutex> stateGuard (g_deviceStateMutex);
14461500 g_pDeviceState = nullptr ;
@@ -1463,6 +1517,54 @@ void ReleaseActiveProxyDevice(CProxyDirect3DDevice9* pProxyDevice)
14631517 pProxyDevice->Release ();
14641518}
14651519
1520+ bool BeginSceneWithoutProxy (IDirect3DDevice9* pDevice, ESceneOwner owner)
1521+ {
1522+ if (!pDevice || owner == ESceneOwner::None)
1523+ return false ;
1524+
1525+ const HRESULT hr = pDevice->BeginScene ();
1526+ if (FAILED (hr))
1527+ {
1528+ WriteDebugEvent (SString (" BeginSceneWithoutProxy failed: %08x" , hr));
1529+ return false ;
1530+ }
1531+
1532+ if (owner == ESceneOwner::GTA)
1533+ {
1534+ IncrementGTASceneState ();
1535+ }
1536+ else if (owner == ESceneOwner::MTA)
1537+ {
1538+ g_bInMTAScene.store (true , std::memory_order_release);
1539+ }
1540+
1541+ return true ;
1542+ }
1543+
1544+ bool EndSceneWithoutProxy (IDirect3DDevice9* pDevice, ESceneOwner owner)
1545+ {
1546+ if (!pDevice || owner == ESceneOwner::None)
1547+ return false ;
1548+
1549+ const HRESULT hr = pDevice->EndScene ();
1550+ if (FAILED (hr))
1551+ {
1552+ WriteDebugEvent (SString (" EndSceneWithoutProxy failed: %08x" , hr));
1553+ return false ;
1554+ }
1555+
1556+ if (owner == ESceneOwner::GTA)
1557+ {
1558+ DecrementGTASceneState ();
1559+ }
1560+ else if (owner == ESceneOwner::MTA)
1561+ {
1562+ g_bInMTAScene.store (false , std::memory_order_release);
1563+ }
1564+
1565+ return true ;
1566+ }
1567+
14661568CScopedActiveProxyDevice::CScopedActiveProxyDevice ()
14671569 : m_pProxy(AcquireActiveProxyDevice())
14681570{
0 commit comments