Skip to content

Commit 1e09b1e

Browse files
Synchronize changes from 1.6 branch [ci skip]
fa742c1 Fix memory leak in case screenshot fails d8db960 Fix potential regressions from recent refactors
2 parents df5af1a + fa742c1 commit 1e09b1e

File tree

6 files changed

+160
-35
lines changed

6 files changed

+160
-35
lines changed

Client/core/CScreenGrabber.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,7 @@ bool CScreenGrabber::GetBackBufferPixels(uint uiSizeX, uint uiSizeY, CBuffer& bu
242242
if (!m_pScreenShotTemp)
243243
{
244244
strOutError = "No ScreenShotTemp";
245+
SAFE_RELEASE(pD3DBackBufferSurface);
245246
return false;
246247
}
247248

@@ -251,6 +252,7 @@ bool CScreenGrabber::GetBackBufferPixels(uint uiSizeX, uint uiSizeY, CBuffer& bu
251252
if (FAILED(hr))
252253
{
253254
strOutError = SString("StretchRect failed (0x%08x)", hr);
255+
SAFE_RELEASE(pD3DBackBufferSurface);
254256
return false;
255257
}
256258

@@ -260,6 +262,7 @@ bool CScreenGrabber::GetBackBufferPixels(uint uiSizeX, uint uiSizeY, CBuffer& bu
260262
if (!m_pScreenShotTemp->ReadPixels(buffer, strOutError))
261263
{
262264
dassert(!strOutError.empty());
265+
SAFE_RELEASE(pD3DBackBufferSurface);
263266
return false;
264267
}
265268

Client/core/DXHook/CDirect3DEvents9.cpp

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -86,10 +86,15 @@ bool CDirect3DEvents9::IsDeviceOperational(IDirect3DDevice9* pDevice, bool* pbTe
8686
{
8787
if (pbTemporarilyLost)
8888
*pbTemporarilyLost = true;
89+
return false;
8990
}
90-
else
91+
92+
const bool bSceneActive = g_bInMTAScene.load(std::memory_order_acquire) || g_bInGTAScene.load(std::memory_order_acquire);
93+
if (hr == D3DERR_INVALIDCALL && bSceneActive)
9194
{
92-
WriteDebugEvent(SString("IsDeviceOperational: unexpected cooperative level %08x", hr));
95+
if (pHrCooperativeLevel)
96+
*pHrCooperativeLevel = D3D_OK;
97+
return true;
9398
}
9499

95100
return false;

Client/core/DXHook/CProxyDirect3DDevice9.cpp

Lines changed: 132 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,12 @@
1212
#include "StdInc.h"
1313
#include <algorithm>
1414
#include <atomic>
15+
#include <cmath>
1516
#include <mutex>
1617
#include <game/CSettings.h>
1718

1819
class CProxyDirect3DDevice9;
20+
extern std::atomic<bool> g_bInMTAScene;
1921

2022
namespace
2123
{
@@ -46,6 +48,7 @@ std::mutex g_deviceStateMutex;
4648
std::atomic<uint64_t> g_proxyRegistrationCounter{0};
4749
std::mutex g_sceneStateMutex;
4850
uint32_t g_gtaSceneActiveCount = 0;
51+
uint64_t g_activeProxyRegistrationId = 0;
4952

5053
uint64_t RegisterProxyDevice(CProxyDirect3DDevice9* instance);
5154
bool 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+
477518
HRESULT 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+
14661568
CScopedActiveProxyDevice::CScopedActiveProxyDevice()
14671569
: m_pProxy(AcquireActiveProxyDevice())
14681570
{

Client/core/DXHook/CProxyDirect3DDevice9.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -565,6 +565,16 @@ extern CProxyDirect3DDevice9::SD3DDeviceState* g_pDeviceState;
565565
// GTA scene tracking helpers
566566
void ResetGTASceneState();
567567

568+
enum class ESceneOwner
569+
{
570+
None,
571+
GTA,
572+
MTA,
573+
};
574+
575+
bool BeginSceneWithoutProxy(IDirect3DDevice9* pDevice, ESceneOwner owner);
576+
bool EndSceneWithoutProxy(IDirect3DDevice9* pDevice, ESceneOwner owner);
577+
568578
CProxyDirect3DDevice9* AcquireActiveProxyDevice();
569579
void ReleaseActiveProxyDevice(CProxyDirect3DDevice9* pProxyDevice);
570580

Client/core/Graphics/CGraphics.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
#include "StdInc.h"
1313
#include <game/CSettings.h>
14+
#include "DXHook/CProxyDirect3DDevice9.h"
1415
#include "CTileBatcher.h"
1516
#include "CLine3DBatcher.h"
1617
#include "CMaterialLine3DBatcher.h"

Client/core/Graphics/CRenderItemManager.cpp

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include <game/CGame.h>
1212
#include <game/CRenderWare.h>
1313
#include <game/CSettings.h>
14+
#include "DXHook/CProxyDirect3DDevice9.h"
1415
#include "CRenderItem.EffectCloner.h"
1516

1617
extern std::atomic<bool> g_bInMTAScene;
@@ -1366,10 +1367,13 @@ void CRenderItemManager::SaveReadableDepthBuffer()
13661367
if (bDeviceReady && !g_bInMTAScene.load(std::memory_order_acquire) &&
13671368
!g_bInGTAScene.load(std::memory_order_acquire))
13681369
{
1369-
const HRESULT hBeginScene = m_pDevice->BeginScene();
1370-
if (SUCCEEDED(hBeginScene))
1370+
if (!BeginSceneWithoutProxy(m_pDevice, ESceneOwner::MTA))
13711371
{
1372-
m_pDevice->EndScene();
1372+
WriteDebugEvent("CRenderItemManager::SaveReadableDepthBuffer - BeginSceneWithoutProxy failed");
1373+
}
1374+
else if (!EndSceneWithoutProxy(m_pDevice, ESceneOwner::MTA))
1375+
{
1376+
WriteDebugEvent("CRenderItemManager::SaveReadableDepthBuffer - EndSceneWithoutProxy failed");
13731377
}
13741378
}
13751379
}

0 commit comments

Comments
 (0)