1414#include " CAdditionalVertexStreamManager.h"
1515#include " CVertexStreamBoundingBoxManager.h"
1616
17+ #include < algorithm>
18+ #include < array>
19+ #include < cstring>
20+ #include < new>
21+
1722// ///////////////////////////////////////////////////////////
1823//
1924// CProxyDirect3DVertexBuffer::CProxyDirect3DVertexBuffer
@@ -109,34 +114,50 @@ HRESULT CProxyDirect3DVertexBuffer::Lock(UINT OffsetToLock, UINT SizeToLock, voi
109114 CVertexStreamBoundingBoxManager::GetSingleton ()->OnVertexBufferRangeInvalidated (m_pOriginal, OffsetToLock, SizeToLock);
110115 }
111116
117+ ClearFallbackLock ();
118+
112119 *ppbData = NULL ;
113- HRESULT hr = DoLock (OffsetToLock, SizeToLock, ppbData, Flags);
114120
115- // Report problems
116- if (FAILED (hr) || *ppbData == NULL )
121+ const UINT clampedSize = ClampLockSize (OffsetToLock, SizeToLock);
122+
123+ HRESULT hr = DoLock (OffsetToLock, clampedSize, ppbData, Flags);
124+ if (SUCCEEDED (hr) && *ppbData != NULL )
125+ return hr;
126+
127+ HRESULT recoveryHr = TryRecoverLock (OffsetToLock, clampedSize, ppbData, Flags, hr);
128+ if (SUCCEEDED (recoveryHr) && *ppbData != NULL )
129+ return recoveryHr;
130+
131+ HRESULT finalHr = FAILED (recoveryHr) ? recoveryHr : hr;
132+ HRESULT fallbackHr = ActivateFallbackLock (OffsetToLock, clampedSize, ppbData, Flags, finalHr);
133+ if (SUCCEEDED (fallbackHr) && *ppbData != NULL )
134+ return fallbackHr;
135+
136+ if (FAILED (fallbackHr))
137+ finalHr = fallbackHr;
138+
139+ struct
117140 {
118- struct
119- {
120- const char * szText;
121- uint uiReportId;
122- uint uiLogEventId;
123- } info;
124- if (hr == D3D_OK)
125- info = {" result NULL" , 8621 , 621 };
126- else if (hr == STATUS_ARRAY_BOUNDS_EXCEEDED)
127- info = {" offset out of range" , 8622 , 622 };
128- else if (hr == STATUS_ACCESS_VIOLATION)
129- info = {" access violation" , 8623 , 623 };
130- else
131- info = {" fail" , 8620 , 620 };
141+ const char * szText;
142+ uint uiReportId;
143+ uint uiLogEventId;
144+ } info;
145+ if (finalHr == D3D_OK)
146+ info = {" result NULL" , 8621 , 621 };
147+ else if (finalHr == STATUS_ARRAY_BOUNDS_EXCEEDED)
148+ info = {" offset out of range" , 8622 , 622 };
149+ else if (finalHr == STATUS_ACCESS_VIOLATION)
150+ info = {" access violation" , 8623 , 623 };
151+ else
152+ info = {" fail" , 8620 , 620 };
132153
133- SString strMessage (" Lock VertexBuffer [%s] hr:%x Length:%x Usage:%x FVF:%x Pool:%x OffsetToLock:%x SizeToLock:%x Flags:%x" , info.szText , hr , m_iMemUsed,
134- m_dwUsage, m_dwFVF, m_pool, OffsetToLock, SizeToLock , Flags);
135- WriteDebugEvent (strMessage);
136- AddReportLog (info.uiReportId , strMessage);
137- CCore::GetSingleton ().LogEvent (info.uiLogEventId , " Lock VertexBuffer" , " " , strMessage);
138- }
139- return hr ;
154+ SString strMessage (" Lock VertexBuffer [%s] hr:%x Length:%x Usage:%x FVF:%x Pool:%x OffsetToLock:%x SizeToLock:%x Flags:%x" , info.szText , finalHr , m_iMemUsed,
155+ m_dwUsage, m_dwFVF, m_pool, OffsetToLock, clampedSize , Flags);
156+ WriteDebugEvent (strMessage);
157+ AddReportLog (info.uiReportId , strMessage);
158+ CCore::GetSingleton ().LogEvent (info.uiLogEventId , " Lock VertexBuffer" , " " , strMessage);
159+
160+ return finalHr ;
140161}
141162
142163// ///////////////////////////////////////////////////////////
@@ -148,22 +169,197 @@ HRESULT CProxyDirect3DVertexBuffer::Lock(UINT OffsetToLock, UINT SizeToLock, voi
148169// ///////////////////////////////////////////////////////////
149170HRESULT CProxyDirect3DVertexBuffer::DoLock (UINT OffsetToLock, UINT SizeToLock, void ** ppbData, DWORD Flags)
150171{
151- // Validate sizes because gta can give invalid values (reason unknown)
152- if (OffsetToLock + SizeToLock > m_iMemUsed)
172+ if (OffsetToLock >= m_iMemUsed)
173+ return STATUS_ARRAY_BOUNDS_EXCEEDED;
174+
175+ UINT adjustedSize = ClampLockSize (OffsetToLock, SizeToLock);
176+ if (adjustedSize == 0 )
177+ return STATUS_ARRAY_BOUNDS_EXCEEDED;
178+
179+ return LockInternal (OffsetToLock, adjustedSize, ppbData, Flags);
180+ }
181+
182+ HRESULT CProxyDirect3DVertexBuffer::Unlock ()
183+ {
184+ if (!m_fallbackLock.active )
185+ return m_pOriginal->Unlock ();
186+
187+ HRESULT copyHr = D3D_OK;
188+
189+ if ((m_fallbackLock.flags & D3DLOCK_READONLY) == 0 && m_fallbackLock.size > 0 && !m_fallbackLock.buffer .empty ())
153190 {
154- if (OffsetToLock > m_iMemUsed)
191+ void * pDestination = NULL ;
192+ copyHr = LockInternal (m_fallbackLock.offset , m_fallbackLock.size , &pDestination, 0 );
193+ if (SUCCEEDED (copyHr) && pDestination != NULL )
155194 {
156- return STATUS_ARRAY_BOUNDS_EXCEEDED;
195+ std::memcpy (pDestination, m_fallbackLock.buffer .data (), m_fallbackLock.size );
196+ HRESULT unlockHr = m_pOriginal->Unlock ();
197+ if (FAILED (unlockHr))
198+ {
199+ copyHr = unlockHr;
200+ SString strMessage (" Unlock VertexBuffer [fallback unlock failed] hr:%x Length:%x Usage:%x FVF:%x Pool:%x Offset:%x Size:%x" , unlockHr, m_iMemUsed,
201+ m_dwUsage, m_dwFVF, m_pool, m_fallbackLock.offset , m_fallbackLock.size );
202+ WriteDebugEvent (strMessage);
203+ AddReportLog (8627 , strMessage);
204+ CCore::GetSingleton ().LogEvent (627 , " Unlock VertexBuffer" , " " , strMessage);
205+ }
206+ }
207+ else
208+ {
209+ if (SUCCEEDED (copyHr) && pDestination == NULL )
210+ copyHr = D3DERR_INVALIDCALL;
211+
212+ SString strMessage (" Unlock VertexBuffer [fallback copy failed] hr:%x Length:%x Usage:%x FVF:%x Pool:%x Offset:%x Size:%x" , copyHr, m_iMemUsed, m_dwUsage,
213+ m_dwFVF, m_pool, m_fallbackLock.offset , m_fallbackLock.size );
214+ WriteDebugEvent (strMessage);
215+ AddReportLog (8626 , strMessage);
216+ CCore::GetSingleton ().LogEvent (626 , " Unlock VertexBuffer" , " " , strMessage);
157217 }
158- SizeToLock = m_iMemUsed - OffsetToLock;
159218 }
160219
220+ ClearFallbackLock ();
221+ return D3D_OK;
222+ }
223+
224+ UINT CProxyDirect3DVertexBuffer::ClampLockSize (UINT OffsetToLock, UINT SizeToLock) const
225+ {
226+ if (OffsetToLock >= m_iMemUsed)
227+ return 0 ;
228+
229+ const UINT available = static_cast <UINT>(m_iMemUsed - OffsetToLock);
230+ if (available == 0 )
231+ return 0 ;
232+
233+ if (SizeToLock == 0 )
234+ return available;
235+
236+ return std::min (SizeToLock, available);
237+ }
238+
239+ HRESULT CProxyDirect3DVertexBuffer::LockInternal (UINT OffsetToLock, UINT SizeToLock, void ** ppbData, DWORD Flags)
240+ {
241+ if (ppbData == NULL )
242+ return E_INVALIDARG;
243+
244+ *ppbData = NULL ;
245+
161246 __try
162247 {
163248 return m_pOriginal->Lock (OffsetToLock, SizeToLock, ppbData, Flags);
164249 }
165250 __except (GetExceptionCode () == EXCEPTION_ACCESS_VIOLATION)
166251 {
252+ *ppbData = NULL ;
167253 return STATUS_ACCESS_VIOLATION;
168254 }
169255}
256+
257+ HRESULT CProxyDirect3DVertexBuffer::TryRecoverLock (UINT OffsetToLock, UINT SizeToLock, void ** ppbData, DWORD Flags, HRESULT originalHr)
258+ {
259+ HRESULT lastHr = originalHr;
260+
261+ struct SLockAttempt
262+ {
263+ DWORD flags;
264+ UINT offset;
265+ UINT size;
266+ bool adjustPointer;
267+ };
268+
269+ std::array<SLockAttempt, 7 > attempts = {
270+ SLockAttempt{Flags & ~D3DLOCK_DONOTWAIT, OffsetToLock, SizeToLock, false },
271+ SLockAttempt{Flags & ~D3DLOCK_NOOVERWRITE, OffsetToLock, SizeToLock, false },
272+ SLockAttempt{Flags & ~D3DLOCK_DISCARD, OffsetToLock, SizeToLock, false },
273+ SLockAttempt{Flags & ~(D3DLOCK_DONOTWAIT | D3DLOCK_NOOVERWRITE), OffsetToLock, SizeToLock, false },
274+ SLockAttempt{Flags & ~(D3DLOCK_DONOTWAIT | D3DLOCK_NOOVERWRITE | D3DLOCK_NO_DIRTY_UPDATE), OffsetToLock, SizeToLock, false },
275+ SLockAttempt{0u , OffsetToLock, SizeToLock, false },
276+ SLockAttempt{0u , 0u , static_cast <UINT>(m_iMemUsed), true },
277+ };
278+
279+ for (const SLockAttempt& attempt : attempts)
280+ {
281+ if (attempt.flags == Flags && attempt.offset == OffsetToLock && attempt.size == SizeToLock)
282+ continue ;
283+
284+ if (attempt.size > m_iMemUsed)
285+ continue ;
286+
287+ UINT clampedSize = ClampLockSize (attempt.offset , attempt.size );
288+ if (clampedSize == 0 )
289+ continue ;
290+
291+ void * pRecovered = NULL ;
292+ HRESULT hr = LockInternal (attempt.offset , clampedSize, &pRecovered, attempt.flags );
293+ if (SUCCEEDED (hr) && pRecovered != NULL )
294+ {
295+ if (attempt.adjustPointer )
296+ {
297+ UINT pointerOffset = 0 ;
298+ if (OffsetToLock > attempt.offset )
299+ pointerOffset = OffsetToLock - attempt.offset ;
300+ if (pointerOffset >= clampedSize)
301+ {
302+ lastHr = hr;
303+ continue ;
304+ }
305+ pRecovered = static_cast <void *>(static_cast <std::uint8_t *>(pRecovered) + pointerOffset);
306+ }
307+
308+ *ppbData = pRecovered;
309+
310+ SString strMessage (" Lock VertexBuffer [retry] hr:%x->%x Length:%x Usage:%x FVF:%x Pool:%x Offset:%x Size:%x Flags:%x->%x" , originalHr, hr, m_iMemUsed,
311+ m_dwUsage, m_dwFVF, m_pool, OffsetToLock, SizeToLock, Flags, attempt.flags );
312+ WriteDebugEvent (strMessage);
313+ AddReportLog (8625 , strMessage);
314+ CCore::GetSingleton ().LogEvent (625 , " Lock VertexBuffer" , " " , strMessage);
315+
316+ return hr;
317+ }
318+
319+ lastHr = hr;
320+ }
321+
322+ return lastHr;
323+ }
324+
325+ HRESULT CProxyDirect3DVertexBuffer::ActivateFallbackLock (UINT OffsetToLock, UINT SizeToLock, void ** ppbData, DWORD Flags, HRESULT originalHr)
326+ {
327+ if (ppbData == NULL )
328+ return originalHr;
329+
330+ UINT clampedSize = ClampLockSize (OffsetToLock, SizeToLock);
331+ if (clampedSize == 0 )
332+ return originalHr;
333+
334+ try
335+ {
336+ m_fallbackLock.buffer .resize (clampedSize);
337+ }
338+ catch (const std::bad_alloc&)
339+ {
340+ return E_OUTOFMEMORY;
341+ }
342+
343+ m_fallbackLock.active = true ;
344+ m_fallbackLock.offset = OffsetToLock;
345+ m_fallbackLock.size = clampedSize;
346+ m_fallbackLock.flags = Flags;
347+
348+ *ppbData = m_fallbackLock.buffer .data ();
349+
350+ SString strMessage (" Lock VertexBuffer [fallback] hr:%x Length:%x Usage:%x FVF:%x Pool:%x OffsetToLock:%x SizeToLock:%x Flags:%x" , originalHr, m_iMemUsed,
351+ m_dwUsage, m_dwFVF, m_pool, OffsetToLock, clampedSize, Flags);
352+ WriteDebugEvent (strMessage);
353+ AddReportLog (8624 , strMessage);
354+ CCore::GetSingleton ().LogEvent (624 , " Lock VertexBuffer" , " " , strMessage);
355+
356+ return D3D_OK;
357+ }
358+
359+ void CProxyDirect3DVertexBuffer::ClearFallbackLock ()
360+ {
361+ m_fallbackLock.active = false ;
362+ m_fallbackLock.offset = 0 ;
363+ m_fallbackLock.size = 0 ;
364+ m_fallbackLock.flags = 0 ;
365+ }
0 commit comments