Skip to content

Commit ea1c086

Browse files
Synchronize changes from 1.6 branch [ci skip]
3640a2e Addendum #2 to vertex buffer lock fixes; 6f3369b Addendum to vertex buffer lock fixes (Return S_OK as GTA expects for return values)
2 parents dc3f5c7 + 3640a2e commit ea1c086

File tree

2 files changed

+178
-9
lines changed

2 files changed

+178
-9
lines changed

Client/core/DXHook/CProxyDirect3DVertexBuffer.cpp

Lines changed: 169 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
#include "CProxyDirect3DVertexBuffer.h"
1414
#include "CAdditionalVertexStreamManager.h"
1515
#include "CVertexStreamBoundingBoxManager.h"
16+
#include <algorithm>
17+
#include <cstring>
1618

1719
/////////////////////////////////////////////////////////////
1820
//
@@ -30,6 +32,11 @@ CProxyDirect3DVertexBuffer::CProxyDirect3DVertexBuffer(IDirect3DDevice9* InD3DDe
3032
m_dwUsage = Usage;
3133
m_dwFVF = FVF;
3234
m_pool = Pool;
35+
m_bFallbackActive = false;
36+
m_fallbackOffset = 0;
37+
m_fallbackSize = 0;
38+
m_fallbackFlags = 0;
39+
m_fallbackStorage.clear();
3340

3441
m_stats.iCurrentCount++;
3542
m_stats.iCurrentBytes += m_iMemUsed;
@@ -113,42 +120,196 @@ HRESULT CProxyDirect3DVertexBuffer::Lock(UINT OffsetToLock, UINT SizeToLock, voi
113120
pBoundingBoxManager->OnVertexBufferRangeInvalidated(m_pOriginal, OffsetToLock, SizeToLock);
114121
}
115122

123+
if (m_bFallbackActive)
124+
{
125+
m_bFallbackActive = false;
126+
m_fallbackOffset = 0;
127+
m_fallbackSize = 0;
128+
m_fallbackFlags = 0;
129+
}
130+
116131
*ppbData = nullptr;
117132
HRESULT hr = DoLock(OffsetToLock, SizeToLock, ppbData, Flags);
118133
HRESULT originalHr = hr;
119134

120-
if (SUCCEEDED(hr) && *ppbData == nullptr)
135+
bool bPointerNullEvent = SUCCEEDED(hr) && (*ppbData == nullptr);
136+
bool bRetryAttempted = false;
137+
bool bRetrySucceeded = false;
138+
bool bFallbackUsed = false;
139+
bool bUnlockedAfterNull = false;
140+
DWORD retryFlags = Flags;
141+
142+
if (bPointerNullEvent)
121143
{
122-
hr = D3DERR_INVALIDCALL;
144+
WriteDebugEvent(SString("Lock VertexBuffer: initial pointer null (Usage:%08x Flags:%08x Offset:%x Size:%x)", m_dwUsage, Flags, OffsetToLock,
145+
SizeToLock));
146+
147+
// Retry once for dynamic buffers using DISCARD
148+
if ((m_dwUsage & D3DUSAGE_DYNAMIC) && (Flags & D3DLOCK_READONLY) == 0)
149+
{
150+
bRetryAttempted = true;
151+
152+
retryFlags &= ~(DWORD)(D3DLOCK_NOOVERWRITE | D3DLOCK_DISCARD | D3DLOCK_READONLY);
153+
retryFlags |= D3DLOCK_DISCARD;
154+
155+
// Release the bogus lock result before retrying
156+
m_pOriginal->Unlock();
157+
bUnlockedAfterNull = true;
158+
159+
void* pRetryData = nullptr;
160+
hr = DoLock(OffsetToLock, SizeToLock, &pRetryData, retryFlags);
161+
162+
if (SUCCEEDED(hr) && pRetryData != nullptr)
163+
{
164+
*ppbData = pRetryData;
165+
bPointerNullEvent = false;
166+
bRetrySucceeded = true;
167+
WriteDebugEvent(SString("Lock VertexBuffer: retry with DISCARD succeeded (Flags:%08x)", retryFlags));
168+
}
169+
else
170+
{
171+
// Ensure we unlock on success with null pointer to avoid leaving the resource locked
172+
if (SUCCEEDED(hr))
173+
{
174+
m_pOriginal->Unlock();
175+
bUnlockedAfterNull = true;
176+
}
177+
}
178+
}
179+
180+
if (bPointerNullEvent)
181+
{
182+
if (!bUnlockedAfterNull && SUCCEEDED(hr))
183+
{
184+
m_pOriginal->Unlock();
185+
bUnlockedAfterNull = true;
186+
}
187+
// Fall back to artificial buffer
188+
UINT clampedOffset = std::min(OffsetToLock, m_iMemUsed);
189+
UINT clampedSize = SizeToLock;
190+
if (clampedOffset + clampedSize > m_iMemUsed)
191+
clampedSize = m_iMemUsed - clampedOffset;
192+
if (clampedSize == 0 && clampedOffset < m_iMemUsed)
193+
clampedSize = m_iMemUsed - clampedOffset;
194+
195+
// Ensure we have some storage to hand back (even zero-sized locks receive a valid pointer)
196+
size_t requiredStorage = std::max<size_t>(static_cast<size_t>(clampedSize), static_cast<size_t>(1));
197+
if (m_fallbackStorage.size() < requiredStorage)
198+
m_fallbackStorage.resize(requiredStorage);
199+
200+
if (clampedSize > 0 && (Flags & D3DLOCK_READONLY))
201+
memset(m_fallbackStorage.data(), 0, clampedSize);
202+
203+
*ppbData = m_fallbackStorage.data();
204+
205+
m_bFallbackActive = true;
206+
m_fallbackOffset = clampedOffset;
207+
m_fallbackSize = clampedSize;
208+
m_fallbackFlags = Flags;
209+
210+
bFallbackUsed = true;
211+
bPointerNullEvent = true;
212+
213+
hr = D3D_OK;
214+
215+
WriteDebugEvent(SString("Lock VertexBuffer: engaged fallback buffer (Offset:%x Size:%x Flags:%08x)", m_fallbackOffset, m_fallbackSize,
216+
m_fallbackFlags));
217+
}
123218
}
124219

125220
// Report problems
126-
if (FAILED(hr))
221+
if (FAILED(hr) || bPointerNullEvent)
127222
{
128223
struct
129224
{
130225
const char* szText;
131226
uint uiReportId;
132227
uint uiLogEventId;
133228
} info;
134-
if (hr == D3DERR_INVALIDCALL && originalHr == D3D_OK)
229+
HRESULT reportHr = FAILED(hr) ? hr : D3DERR_INVALIDCALL;
230+
if (bPointerNullEvent && originalHr == D3D_OK)
135231
info = {"result NULL", 8621, 621};
136-
else if (hr == STATUS_ARRAY_BOUNDS_EXCEEDED)
232+
else if (reportHr == STATUS_ARRAY_BOUNDS_EXCEEDED)
137233
info = {"offset out of range", 8622, 622};
138-
else if (hr == STATUS_ACCESS_VIOLATION)
234+
else if (reportHr == STATUS_ACCESS_VIOLATION)
139235
info = {"access violation", 8623, 623};
140236
else
141237
info = {"fail", 8620, 620};
142238

143-
SString strMessage("Lock VertexBuffer [%s] hr:%x origHr:%x Length:%x Usage:%x FVF:%x Pool:%x OffsetToLock:%x SizeToLock:%x Flags:%x", info.szText, hr,
144-
originalHr, m_iMemUsed, m_dwUsage, m_dwFVF, m_pool, OffsetToLock, SizeToLock, Flags);
239+
SString strMessage(
240+
"Lock VertexBuffer [%s] hr:%x origHr:%x returnHr:%x pointerNull:%u retryAttempted:%u retrySucceeded:%u fallback:%u fallbackSize:%x fallbackFlags:%x"
241+
" unlockedAfterNull:%u Length:%x Usage:%x FVF:%x Pool:%x OffsetToLock:%x SizeToLock:%x Flags:%x retryFlags:%x",
242+
info.szText, reportHr, originalHr, hr, static_cast<uint>(bPointerNullEvent), static_cast<uint>(bRetryAttempted),
243+
static_cast<uint>(bRetrySucceeded), static_cast<uint>(bFallbackUsed), m_fallbackSize, m_fallbackFlags, static_cast<uint>(bUnlockedAfterNull),
244+
m_iMemUsed, m_dwUsage, m_dwFVF, m_pool, OffsetToLock, SizeToLock, Flags, retryFlags);
145245
WriteDebugEvent(strMessage);
146246
AddReportLog(info.uiReportId, strMessage);
147247
CCore::GetSingleton().LogEvent(info.uiLogEventId, "Lock VertexBuffer", "", strMessage);
148248
}
149249
return hr;
150250
}
151251

252+
/////////////////////////////////////////////////////////////
253+
//
254+
// CProxyDirect3DVertexBuffer::Unlock
255+
//
256+
// Apply fallback data if we had to hand out an artificial buffer
257+
//
258+
/////////////////////////////////////////////////////////////
259+
HRESULT CProxyDirect3DVertexBuffer::Unlock()
260+
{
261+
if (!m_bFallbackActive)
262+
return m_pOriginal->Unlock();
263+
264+
HRESULT copyResult = D3D_OK;
265+
266+
if ((m_fallbackFlags & D3DLOCK_READONLY) == 0 && m_fallbackSize > 0)
267+
{
268+
UINT offset = std::min(m_fallbackOffset, m_iMemUsed);
269+
UINT size = m_fallbackSize;
270+
if (offset + size > m_iMemUsed)
271+
size = (offset < m_iMemUsed) ? (m_iMemUsed - offset) : 0;
272+
273+
if (size > 0)
274+
{
275+
DWORD writeFlags = m_fallbackFlags;
276+
writeFlags &= ~(DWORD)(D3DLOCK_NOOVERWRITE | D3DLOCK_READONLY);
277+
if (m_dwUsage & D3DUSAGE_DYNAMIC)
278+
writeFlags |= D3DLOCK_DISCARD;
279+
280+
void* pReal = nullptr;
281+
HRESULT lockHr = DoLock(offset, size, &pReal, writeFlags);
282+
283+
if (SUCCEEDED(lockHr) && pReal != nullptr)
284+
{
285+
memcpy(pReal, m_fallbackStorage.data(), size);
286+
HRESULT unlockHr = m_pOriginal->Unlock();
287+
if (FAILED(unlockHr))
288+
copyResult = unlockHr;
289+
}
290+
else
291+
{
292+
if (SUCCEEDED(lockHr))
293+
m_pOriginal->Unlock();
294+
295+
WriteDebugEvent(SString("Unlock VertexBuffer: failed to copy fallback data (lockHr:%x offset:%x size:%x flags:%08x)", lockHr, offset, size,
296+
writeFlags));
297+
copyResult = FAILED(lockHr) ? lockHr : D3DERR_INVALIDCALL;
298+
}
299+
}
300+
}
301+
302+
WriteDebugEvent(SString("Unlock VertexBuffer: fallback completed (offset:%x size:%x flags:%08x result:%x)", m_fallbackOffset, m_fallbackSize,
303+
m_fallbackFlags, copyResult));
304+
305+
m_bFallbackActive = false;
306+
m_fallbackOffset = 0;
307+
m_fallbackSize = 0;
308+
m_fallbackFlags = 0;
309+
310+
return copyResult;
311+
}
312+
152313
/////////////////////////////////////////////////////////////
153314
//
154315
// CProxyDirect3DVertexBuffer::DoLock

Client/core/DXHook/CProxyDirect3DVertexBuffer.h

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
#pragma once
1313

1414
#include <d3d9.h>
15+
#include <cstdint>
16+
#include <vector>
1517
#include "CProxyDirect3DDevice9.h" // Include full definition for SResourceMemory
1618

1719
DEFINE_GUID(CProxyDirect3DVertexBuffer_GUID, 0x128A025E, 0x0100, 0x04F1, 0x40, 0x60, 0x53, 0x19, 0x44, 0x56, 0x59, 0x42);
@@ -39,7 +41,7 @@ class CProxyDirect3DVertexBuffer : public IDirect3DVertexBuffer9
3941

4042
/*** IDirect3DVertexBuffer9 methods ***/
4143
HRESULT __stdcall Lock(UINT OffsetToLock, UINT SizeToLock, void** ppbData, DWORD Flags);
42-
HRESULT __stdcall Unlock() { return m_pOriginal->Unlock(); }
44+
HRESULT __stdcall Unlock();
4345
HRESULT __stdcall GetDesc(D3DVERTEXBUFFER_DESC* pDesc) { return m_pOriginal->GetDesc(pDesc); }
4446

4547
// CProxyDirect3DVertexBuffer
@@ -56,4 +58,10 @@ class CProxyDirect3DVertexBuffer : public IDirect3DVertexBuffer9
5658
DWORD m_dwFVF;
5759
D3DPOOL m_pool;
5860
CProxyDirect3DDevice9::SResourceMemory& m_stats;
61+
62+
bool m_bFallbackActive;
63+
UINT m_fallbackOffset;
64+
UINT m_fallbackSize;
65+
DWORD m_fallbackFlags;
66+
std::vector<uint8_t> m_fallbackStorage;
5967
};

0 commit comments

Comments
 (0)