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,18 +120,105 @@ 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;
119- bool bPointerNull = false ;
120134
121- if (SUCCEEDED (hr))
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)
122143 {
123- bPointerNull = (*ppbData == nullptr );
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+ }
124218 }
125219
126220 // Report problems
127- if (FAILED (hr) || bPointerNull )
221+ if (FAILED (hr) || bPointerNullEvent )
128222 {
129223 struct
130224 {
@@ -133,7 +227,7 @@ HRESULT CProxyDirect3DVertexBuffer::Lock(UINT OffsetToLock, UINT SizeToLock, voi
133227 uint uiLogEventId;
134228 } info;
135229 HRESULT reportHr = FAILED (hr) ? hr : D3DERR_INVALIDCALL;
136- if (bPointerNull && originalHr == D3D_OK)
230+ if (bPointerNullEvent && originalHr == D3D_OK)
137231 info = {" result NULL" , 8621 , 621 };
138232 else if (reportHr == STATUS_ARRAY_BOUNDS_EXCEEDED)
139233 info = {" offset out of range" , 8622 , 622 };
@@ -142,16 +236,80 @@ HRESULT CProxyDirect3DVertexBuffer::Lock(UINT OffsetToLock, UINT SizeToLock, voi
142236 else
143237 info = {" fail" , 8620 , 620 };
144238
145- SString strMessage (" Lock VertexBuffer [%s] hr:%x origHr:%x returnHr:%x pointerNull:%u Length:%x Usage:%x FVF:%x Pool:%x OffsetToLock:%x SizeToLock:%x Flags:%x" ,
146- info.szText , reportHr, originalHr, hr, static_cast <uint>(bPointerNull), m_iMemUsed, m_dwUsage, m_dwFVF, m_pool, OffsetToLock, SizeToLock,
147- 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);
148245 WriteDebugEvent (strMessage);
149246 AddReportLog (info.uiReportId , strMessage);
150247 CCore::GetSingleton ().LogEvent (info.uiLogEventId , " Lock VertexBuffer" , " " , strMessage);
151248 }
152249 return hr;
153250}
154251
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+
155313// ///////////////////////////////////////////////////////////
156314//
157315// CProxyDirect3DVertexBuffer::DoLock
0 commit comments