14
14
#include " CAdditionalVertexStreamManager.h"
15
15
#include " CVertexStreamBoundingBoxManager.h"
16
16
17
+ #include < algorithm>
18
+ #include < array>
19
+ #include < cstring>
20
+ #include < new>
21
+
17
22
// ///////////////////////////////////////////////////////////
18
23
//
19
24
// CProxyDirect3DVertexBuffer::CProxyDirect3DVertexBuffer
@@ -109,34 +114,50 @@ HRESULT CProxyDirect3DVertexBuffer::Lock(UINT OffsetToLock, UINT SizeToLock, voi
109
114
CVertexStreamBoundingBoxManager::GetSingleton ()->OnVertexBufferRangeInvalidated (m_pOriginal, OffsetToLock, SizeToLock);
110
115
}
111
116
117
+ ClearFallbackLock ();
118
+
112
119
*ppbData = NULL ;
113
- HRESULT hr = DoLock (OffsetToLock, SizeToLock, ppbData, Flags);
114
120
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
117
140
{
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 };
132
153
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 ;
140
161
}
141
162
142
163
// ///////////////////////////////////////////////////////////
@@ -148,22 +169,197 @@ HRESULT CProxyDirect3DVertexBuffer::Lock(UINT OffsetToLock, UINT SizeToLock, voi
148
169
// ///////////////////////////////////////////////////////////
149
170
HRESULT CProxyDirect3DVertexBuffer::DoLock (UINT OffsetToLock, UINT SizeToLock, void ** ppbData, DWORD Flags)
150
171
{
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 ())
153
190
{
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 )
155
194
{
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);
157
217
}
158
- SizeToLock = m_iMemUsed - OffsetToLock;
159
218
}
160
219
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
+
161
246
__try
162
247
{
163
248
return m_pOriginal->Lock (OffsetToLock, SizeToLock, ppbData, Flags);
164
249
}
165
250
__except (GetExceptionCode () == EXCEPTION_ACCESS_VIOLATION)
166
251
{
252
+ *ppbData = NULL ;
167
253
return STATUS_ACCESS_VIOLATION;
168
254
}
169
255
}
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