Team Fortress 2 Source Code as on 22/4/2020
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1215 lines
35 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. // TOGL CODE LICENSE
  3. //
  4. // Copyright 2011-2014 Valve Corporation
  5. // All Rights Reserved.
  6. //
  7. // Permission is hereby granted, free of charge, to any person obtaining a copy
  8. // of this software and associated documentation files (the "Software"), to deal
  9. // in the Software without restriction, including without limitation the rights
  10. // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  11. // copies of the Software, and to permit persons to whom the Software is
  12. // furnished to do so, subject to the following conditions:
  13. //
  14. // The above copyright notice and this permission notice shall be included in
  15. // all copies or substantial portions of the Software.
  16. //
  17. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  18. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  19. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  20. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  21. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  22. // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  23. // THE SOFTWARE.
  24. //
  25. // cglmbuffer.cpp
  26. //
  27. //===============================================================================
  28. #include "togl/rendermechanism.h"
  29. // memdbgon -must- be the last include file in a .cpp file.
  30. #include "tier0/memdbgon.h"
  31. // 7LS TODO : took out cmdline here
  32. bool g_bUsePseudoBufs = false; //( Plat_GetCommandLineA() ) ? ( strstr( Plat_GetCommandLineA(), "-gl_enable_pseudobufs" ) != NULL ) : false;
  33. #ifdef OSX
  34. // Significant perf degradation on some OSX parts if static buffers not disabled
  35. bool g_bDisableStaticBuffer = true;
  36. #else
  37. bool g_bDisableStaticBuffer = false; //( Plat_GetCommandLineA() ) ? ( strstr( Plat_GetCommandLineA(), "-gl_disable_static_buffer" ) != NULL ) : false;
  38. #endif
  39. // http://www.opengl.org/registry/specs/ARB/vertex_buffer_object.txt
  40. // http://www.opengl.org/registry/specs/ARB/pixel_buffer_object.txt
  41. // gl_bufmode: zero means we mark all vertex/index buffers static
  42. // non zero means buffers are initially marked static..
  43. // ->but can shift to dynamic upon first 'discard' (orphaning)
  44. // #define REPORT_LOCK_TIME 0
  45. ConVar gl_bufmode( "gl_bufmode", "1" );
  46. char ALIGN16 CGLMBuffer::m_StaticBuffers[ GL_MAX_STATIC_BUFFERS ][ GL_STATIC_BUFFER_SIZE ] ALIGN16_POST;
  47. bool CGLMBuffer::m_bStaticBufferUsed[ GL_MAX_STATIC_BUFFERS ];
  48. extern bool g_bNullD3DDevice;
  49. //===========================================================================//
  50. static uint gMaxPersistentOffset[kGLMNumBufferTypes] =
  51. {
  52. 0,
  53. 0,
  54. 0,
  55. 0
  56. };
  57. CON_COMMAND( gl_persistent_buffer_max_offset, "" )
  58. {
  59. ConMsg( "OpenGL Persistent buffer max offset :\n" );
  60. ConMsg( " Vertex buffer : %d bytes (%f MB) \n", gMaxPersistentOffset[kGLMVertexBuffer], gMaxPersistentOffset[kGLMVertexBuffer] / (1024.0f*1024.0f) );
  61. ConMsg( " Index buffer : %d bytes (%f MB) \n", gMaxPersistentOffset[kGLMIndexBuffer], gMaxPersistentOffset[kGLMIndexBuffer] / (1024.0f*1024.0f) );
  62. ConMsg( " Uniform buffer : %d bytes (%f MB) \n", gMaxPersistentOffset[kGLMUniformBuffer], gMaxPersistentOffset[kGLMUniformBuffer] / (1024.0f*1024.0f) );
  63. ConMsg( " Pixel buffer : %d bytes (%f MB) \n", gMaxPersistentOffset[kGLMPixelBuffer], gMaxPersistentOffset[kGLMPixelBuffer] / (1024.0f*1024.0f) );
  64. }
  65. CPersistentBuffer::CPersistentBuffer()
  66. :
  67. m_nSize( 0 )
  68. , m_nHandle( 0 )
  69. , m_pImmutablePersistentBuf( NULL )
  70. , m_nOffset( 0 )
  71. #ifdef HAVE_GL_ARB_SYNC
  72. , m_nSyncObj( 0 )
  73. #endif
  74. {}
  75. CPersistentBuffer::~CPersistentBuffer()
  76. {
  77. Deinit();
  78. }
  79. void CPersistentBuffer::Init( EGLMBufferType type,uint nSize )
  80. {
  81. Assert( gGL->m_bHave_GL_ARB_buffer_storage );
  82. Assert( gGL->m_bHave_GL_ARB_map_buffer_range );
  83. m_nSize = nSize;
  84. m_nOffset = 0;
  85. m_type = type;
  86. switch ( type )
  87. {
  88. case kGLMVertexBuffer: m_buffGLTarget = GL_ARRAY_BUFFER_ARB; break;
  89. case kGLMIndexBuffer: m_buffGLTarget = GL_ELEMENT_ARRAY_BUFFER_ARB; break;
  90. default: Assert( nSize == 0 );
  91. }
  92. if ( m_nSize > 0 )
  93. {
  94. gGL->glGenBuffersARB( 1, &m_nHandle );
  95. gGL->glBindBufferARB( m_buffGLTarget, m_nHandle );
  96. // Create persistent immutable buffer that we will permanently map. This buffer can be written from any thread (not just
  97. // the renderthread)
  98. gGL->glBufferStorage( m_buffGLTarget, m_nSize, (const GLvoid *)NULL, GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT | GL_MAP_COHERENT_BIT ); // V_GL_REQ: GL_ARB_buffer_storage, GL_ARB_map_buffer_range, GL_VERSION_4_4
  99. // Map the buffer for all of eternity. Pointer can be used from multiple threads.
  100. m_pImmutablePersistentBuf = gGL->glMapBufferRange( m_buffGLTarget, 0, m_nSize, GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT | GL_MAP_COHERENT_BIT ); // V_GL_REQ: GL_ARB_map_buffer_range, GL_ARB_buffer_storage, GL_VERSION_4_4
  101. Assert( m_pImmutablePersistentBuf != NULL );
  102. }
  103. }
  104. void CPersistentBuffer::Deinit()
  105. {
  106. if ( !m_pImmutablePersistentBuf )
  107. {
  108. return;
  109. }
  110. BlockUntilNotBusy();
  111. gGL->glBindBufferARB( m_buffGLTarget, m_nHandle );
  112. gGL->glUnmapBuffer( m_buffGLTarget );
  113. gGL->glBindBufferARB( m_buffGLTarget, 0 );
  114. gGL->glDeleteBuffersARB( 1, &m_nHandle );
  115. m_nSize = 0;
  116. m_nHandle = 0;
  117. m_nOffset = 0;
  118. m_pImmutablePersistentBuf = NULL;
  119. }
  120. void CPersistentBuffer::InsertFence()
  121. {
  122. #ifdef HAVE_GL_ARB_SYNC
  123. if (m_nSyncObj)
  124. {
  125. gGL->glDeleteSync( m_nSyncObj );
  126. }
  127. m_nSyncObj = gGL->glFenceSync( GL_SYNC_GPU_COMMANDS_COMPLETE, 0 );
  128. #endif
  129. }
  130. void CPersistentBuffer::BlockUntilNotBusy()
  131. {
  132. #ifdef HAVE_GL_ARB_SYNC
  133. if (m_nSyncObj)
  134. {
  135. gGL->glClientWaitSync( m_nSyncObj, GL_SYNC_FLUSH_COMMANDS_BIT, 3000000000000ULL );
  136. gGL->glDeleteSync( m_nSyncObj );
  137. m_nSyncObj = 0;
  138. }
  139. #endif
  140. m_nOffset = 0;
  141. }
  142. void CPersistentBuffer::Append( uint nSize )
  143. {
  144. m_nOffset += nSize;
  145. Assert( m_nOffset <= m_nSize );
  146. gMaxPersistentOffset[m_type] = Max( m_nOffset, gMaxPersistentOffset[m_type] );
  147. }
  148. //===========================================================================//
  149. #if GL_ENABLE_INDEX_VERIFICATION
  150. CGLMBufferSpanManager::CGLMBufferSpanManager() :
  151. m_pCtx( NULL ),
  152. m_nBufType( kGLMVertexBuffer ),
  153. m_nBufSize( 0 ),
  154. m_bDynamic( false ),
  155. m_nSpanEndMax( -1 ),
  156. m_nNumAllocatedBufs( 0 ),
  157. m_nTotalBytesAllocated( 0 )
  158. {
  159. }
  160. CGLMBufferSpanManager::~CGLMBufferSpanManager()
  161. {
  162. Deinit();
  163. }
  164. void CGLMBufferSpanManager::Init( GLMContext *pContext, EGLMBufferType nBufType, uint nInitialCapacity, uint nBufSize, bool bDynamic )
  165. {
  166. Assert( ( nBufType == kGLMIndexBuffer ) || ( nBufType == kGLMVertexBuffer ) );
  167. m_pCtx = pContext;
  168. m_nBufType = nBufType;
  169. m_nBufSize = nBufSize;
  170. m_bDynamic = bDynamic;
  171. m_ActiveSpans.EnsureCapacity( nInitialCapacity );
  172. m_DeletedSpans.EnsureCapacity( nInitialCapacity );
  173. m_nSpanEndMax = -1;
  174. m_nNumAllocatedBufs = 0;
  175. m_nTotalBytesAllocated = 0;
  176. }
  177. bool CGLMBufferSpanManager::AllocDynamicBuf( uint nSize, GLDynamicBuf_t &buf )
  178. {
  179. buf.m_nGLType = GetGLBufType();
  180. buf.m_nActualBufSize = nSize;
  181. buf.m_nHandle = 0;
  182. buf.m_nSize = nSize;
  183. m_nNumAllocatedBufs++;
  184. m_nTotalBytesAllocated += buf.m_nActualBufSize;
  185. return true;
  186. }
  187. void CGLMBufferSpanManager::ReleaseDynamicBuf( GLDynamicBuf_t &buf )
  188. {
  189. Assert( m_nNumAllocatedBufs > 0 );
  190. m_nNumAllocatedBufs--;
  191. Assert( m_nTotalBytesAllocated >= (int)buf.m_nActualBufSize );
  192. m_nTotalBytesAllocated -= buf.m_nActualBufSize;
  193. }
  194. void CGLMBufferSpanManager::Deinit()
  195. {
  196. if ( !m_pCtx )
  197. return;
  198. for ( int i = 0; i < m_ActiveSpans.Count(); i++ )
  199. {
  200. if ( m_ActiveSpans[i].m_bOriginalAlloc )
  201. ReleaseDynamicBuf( m_ActiveSpans[i].m_buf );
  202. }
  203. m_ActiveSpans.SetCountNonDestructively( 0 );
  204. for ( int i = 0; i < m_DeletedSpans.Count(); i++ )
  205. ReleaseDynamicBuf( m_DeletedSpans[i].m_buf );
  206. m_DeletedSpans.SetCountNonDestructively( 0 );
  207. m_pCtx->BindGLBufferToCtx( GetGLBufType(), NULL, true );
  208. m_nSpanEndMax = -1;
  209. m_pCtx = NULL;
  210. Assert( !m_nNumAllocatedBufs );
  211. Assert( !m_nTotalBytesAllocated );
  212. }
  213. void CGLMBufferSpanManager::DiscardAllSpans()
  214. {
  215. for ( int i = 0; i < m_ActiveSpans.Count(); i++ )
  216. {
  217. if ( m_ActiveSpans[i].m_bOriginalAlloc )
  218. ReleaseDynamicBuf( m_ActiveSpans[i].m_buf );
  219. }
  220. m_ActiveSpans.SetCountNonDestructively( 0 );
  221. for ( int i = 0; i < m_DeletedSpans.Count(); i++ )
  222. ReleaseDynamicBuf( m_DeletedSpans[i].m_buf );
  223. m_DeletedSpans.SetCountNonDestructively( 0 );
  224. m_nSpanEndMax = -1;
  225. Assert( !m_nNumAllocatedBufs );
  226. Assert( !m_nTotalBytesAllocated );
  227. }
  228. // TODO: Add logic to detect incorrect usage of bNoOverwrite.
  229. CGLMBufferSpanManager::ActiveSpan_t *CGLMBufferSpanManager::AddSpan( uint nOffset, uint nMaxSize, uint nActualSize, bool bDiscard, bool bNoOverwrite )
  230. {
  231. (void)bDiscard;
  232. (void)bNoOverwrite;
  233. const uint nStart = nOffset;
  234. const uint nSize = nActualSize;
  235. const uint nEnd = nStart + nSize;
  236. GLDynamicBuf_t newDynamicBuf;
  237. if ( !AllocDynamicBuf( nSize, newDynamicBuf ) )
  238. {
  239. DXABSTRACT_BREAK_ON_ERROR();
  240. return NULL;
  241. }
  242. if ( (int)nStart < m_nSpanEndMax )
  243. {
  244. // Lock region potentially overlaps another previously locked region (since the last discard) - this is a very rarely (if ever) taken path in Source1 games.
  245. int i = 0;
  246. while ( i < m_ActiveSpans.Count() )
  247. {
  248. ActiveSpan_t &existingSpan = m_ActiveSpans[i];
  249. if ( ( nEnd <= existingSpan.m_nStart ) || ( nStart >= existingSpan.m_nEnd ) )
  250. {
  251. i++;
  252. continue;
  253. }
  254. Warning( "GL performance warning: AddSpan() at offset %u max size %u actual size %u, on a %s %s buffer of total size %u, overwrites an existing active lock span at offset %u size %u!\n",
  255. nOffset, nMaxSize, nActualSize,
  256. m_bDynamic ? "dynamic" : "static", ( m_nBufType == kGLMVertexBuffer ) ? "vertex" : "index", m_nBufSize,
  257. existingSpan.m_nStart, existingSpan.m_nEnd - existingSpan.m_nStart );
  258. if ( ( nStart <= existingSpan.m_nStart ) && ( nEnd >= existingSpan.m_nEnd ) )
  259. {
  260. if ( existingSpan.m_bOriginalAlloc )
  261. {
  262. // New span totally covers existing span
  263. // Can't immediately delete the span's buffer because it could be referred to by another (child) span.
  264. m_DeletedSpans.AddToTail( existingSpan );
  265. }
  266. // Delete span
  267. m_ActiveSpans[i] = m_ActiveSpans[ m_ActiveSpans.Count() - 1 ];
  268. m_ActiveSpans.SetCountNonDestructively( m_ActiveSpans.Count() - 1 );
  269. continue;
  270. }
  271. // New span does NOT fully cover the existing span (partial overlap)
  272. if ( nStart < existingSpan.m_nStart )
  273. {
  274. // New span starts before existing span, but ends somewhere inside, so shrink it (start moves "right")
  275. existingSpan.m_nStart = nEnd;
  276. }
  277. else if ( nEnd > existingSpan.m_nEnd )
  278. {
  279. // New span ends after existing span, but starts somewhere inside (end moves "left")
  280. existingSpan.m_nEnd = nStart;
  281. }
  282. else //if ( ( nStart >= existingSpan.m_nStart ) && ( nEnd <= existingSpan.m_nEnd ) )
  283. {
  284. // New span lies inside of existing span
  285. if ( nStart == existingSpan.m_nStart )
  286. {
  287. // New span begins inside the existing span (start moves "right")
  288. existingSpan.m_nStart = nEnd;
  289. }
  290. else
  291. {
  292. if ( nEnd < existingSpan.m_nEnd )
  293. {
  294. // New span is completely inside existing span
  295. m_ActiveSpans.AddToTail( ActiveSpan_t( nEnd, existingSpan.m_nEnd, existingSpan.m_buf, false ) );
  296. }
  297. existingSpan.m_nEnd = nStart;
  298. }
  299. }
  300. Assert( existingSpan.m_nStart < existingSpan.m_nEnd );
  301. i++;
  302. }
  303. }
  304. newDynamicBuf.m_nLockOffset = nStart;
  305. newDynamicBuf.m_nLockSize = nSize;
  306. m_ActiveSpans.AddToTail( ActiveSpan_t( nStart, nEnd, newDynamicBuf, true ) );
  307. m_nSpanEndMax = MAX( m_nSpanEndMax, (int)nEnd );
  308. return &m_ActiveSpans.Tail();
  309. }
  310. bool CGLMBufferSpanManager::IsValid( uint nOffset, uint nSize ) const
  311. {
  312. const uint nEnd = nOffset + nSize;
  313. int nTotalBytesRemaining = nSize;
  314. for ( int i = m_ActiveSpans.Count() - 1; i >= 0; --i )
  315. {
  316. const ActiveSpan_t &span = m_ActiveSpans[i];
  317. if ( span.m_nEnd <= nOffset )
  318. continue;
  319. if ( span.m_nStart >= nEnd )
  320. continue;
  321. uint nIntersectStart = MAX( span.m_nStart, nOffset );
  322. uint nIntersectEnd = MIN( span.m_nEnd, nEnd );
  323. Assert( nIntersectStart <= nIntersectEnd );
  324. nTotalBytesRemaining -= ( nIntersectEnd - nIntersectStart );
  325. Assert( nTotalBytesRemaining >= 0 );
  326. if ( nTotalBytesRemaining <= 0 )
  327. break;
  328. }
  329. return nTotalBytesRemaining == 0;
  330. }
  331. #endif // GL_ENABLE_INDEX_VERIFICATION
  332. // glBufferSubData() with a max size limit, to work around NVidia's threaded driver limits (anything > than roughly 256KB triggers a sync with the server thread).
  333. void glBufferSubDataMaxSize( GLenum target, GLintptr offset, GLsizeiptr size, const GLvoid *data, uint nMaxSizePerCall )
  334. {
  335. #if TOGL_SUPPORT_NULL_DEVICE
  336. if ( g_bNullD3DDevice ) return;
  337. #endif
  338. uint nBytesLeft = size;
  339. uint nOfs = 0;
  340. while ( nBytesLeft )
  341. {
  342. uint nBytesToCopy = MIN( nMaxSizePerCall, nBytesLeft );
  343. gGL->glBufferSubData( target, offset + nOfs, nBytesToCopy, static_cast<const unsigned char *>( data ) + nOfs );
  344. nBytesLeft -= nBytesToCopy;
  345. nOfs += nBytesToCopy;
  346. }
  347. }
  348. CGLMBuffer::CGLMBuffer( GLMContext *pCtx, EGLMBufferType type, uint size, uint options )
  349. {
  350. m_pCtx = pCtx;
  351. m_type = type;
  352. m_bDynamic = ( options & GLMBufferOptionDynamic ) != 0;
  353. switch ( m_type )
  354. {
  355. case kGLMVertexBuffer: m_buffGLTarget = GL_ARRAY_BUFFER_ARB; break;
  356. case kGLMIndexBuffer: m_buffGLTarget = GL_ELEMENT_ARRAY_BUFFER_ARB; break;
  357. case kGLMUniformBuffer: m_buffGLTarget = GL_UNIFORM_BUFFER_EXT; break;
  358. case kGLMPixelBuffer: m_buffGLTarget = GL_PIXEL_UNPACK_BUFFER_ARB; break;
  359. default: Assert(!"Unknown buffer type" ); DXABSTRACT_BREAK_ON_ERROR();
  360. }
  361. m_nSize = size;
  362. m_nActualSize = size;
  363. m_bMapped = false;
  364. m_pLastMappedAddress = NULL;
  365. m_pStaticBuffer = NULL;
  366. m_nPinnedMemoryOfs = -1;
  367. m_nPersistentBufferStartOffset = 0;
  368. m_bUsingPersistentBuffer = false;
  369. m_bEnableAsyncMap = false;
  370. m_bEnableExplicitFlush = false;
  371. m_dirtyMinOffset = m_dirtyMaxOffset = 0; // adjust/grow on lock, clear on unlock
  372. m_pCtx->CheckCurrent();
  373. m_nRevision = rand();
  374. m_pPseudoBuf = NULL;
  375. m_pActualPseudoBuf = NULL;
  376. m_bPseudo = false;
  377. #if GL_ENABLE_UNLOCK_BUFFER_OVERWRITE_DETECTION
  378. m_bPseudo = true;
  379. #endif
  380. #if GL_ENABLE_INDEX_VERIFICATION
  381. m_BufferSpanManager.Init( m_pCtx, m_type, 512, m_nSize, m_bDynamic );
  382. if ( m_type == kGLMIndexBuffer )
  383. m_bPseudo = true;
  384. #endif
  385. if ( g_bUsePseudoBufs && m_bDynamic )
  386. {
  387. m_bPseudo = true;
  388. }
  389. if ( m_bPseudo )
  390. {
  391. m_nHandle = 0;
  392. #if GL_ENABLE_UNLOCK_BUFFER_OVERWRITE_DETECTION
  393. m_nDirtyRangeStart = 0xFFFFFFFF;
  394. m_nDirtyRangeEnd = 0;
  395. m_nActualSize = ALIGN_VALUE( ( m_nSize + sizeof( uint32 ) ), 4096 );
  396. m_pPseudoBuf = m_pActualPseudoBuf = (char *)VirtualAlloc( NULL, m_nActualSize, MEM_COMMIT, PAGE_READWRITE );
  397. if ( !m_pPseudoBuf )
  398. {
  399. Error( "VirtualAlloc() failed!\n" );
  400. }
  401. for ( uint i = 0; i < m_nActualSize / sizeof( uint32 ); i++ )
  402. {
  403. reinterpret_cast< uint32 * >( m_pPseudoBuf )[i] = 0xDEADBEEF;
  404. }
  405. DWORD nOldProtect;
  406. BOOL bResult = VirtualProtect( m_pActualPseudoBuf, m_nActualSize, PAGE_READONLY, &nOldProtect );
  407. if ( !bResult )
  408. {
  409. Error( "VirtualProtect() failed!\n" );
  410. }
  411. #else
  412. m_nActualSize = size + 15;
  413. m_pActualPseudoBuf = (char*)malloc( m_nActualSize );
  414. m_pPseudoBuf = (char*)(((intp)m_pActualPseudoBuf + 15) & ~15);
  415. #endif
  416. m_pCtx->BindBufferToCtx( m_type, NULL ); // exit with no buffer bound
  417. }
  418. else
  419. {
  420. gGL->glGenBuffersARB( 1, &m_nHandle );
  421. m_pCtx->BindBufferToCtx( m_type, this ); // causes glBindBufferARB
  422. // buffers start out static, but if they get orphaned and gl_bufmode is non zero,
  423. // then they will get flipped to dynamic.
  424. GLenum hint = GL_STATIC_DRAW_ARB;
  425. switch (m_type)
  426. {
  427. case kGLMVertexBuffer: hint = m_bDynamic ? GL_DYNAMIC_DRAW_ARB : GL_STATIC_DRAW_ARB; break;
  428. case kGLMIndexBuffer: hint = m_bDynamic ? GL_DYNAMIC_DRAW_ARB : GL_STATIC_DRAW_ARB; break;
  429. case kGLMUniformBuffer: hint = GL_DYNAMIC_DRAW_ARB; break;
  430. case kGLMPixelBuffer: hint = m_bDynamic ? GL_DYNAMIC_DRAW_ARB : GL_STATIC_DRAW_ARB; break;
  431. default: Assert(!"Unknown buffer type" ); DXABSTRACT_BREAK_ON_ERROR();
  432. }
  433. gGL->glBufferDataARB( m_buffGLTarget, m_nSize, (const GLvoid*)NULL, hint ); // may ultimately need more hints to set the usage correctly (esp for streaming)
  434. SetModes( false, true, true );
  435. m_pCtx->BindBufferToCtx( m_type, NULL ); // unbind me
  436. }
  437. }
  438. CGLMBuffer::~CGLMBuffer( )
  439. {
  440. m_pCtx->CheckCurrent();
  441. if ( m_bPseudo )
  442. {
  443. #if GL_ENABLE_UNLOCK_BUFFER_OVERWRITE_DETECTION
  444. BOOL bResult = VirtualFree( m_pActualPseudoBuf, 0, MEM_RELEASE );
  445. if ( !bResult )
  446. {
  447. Error( "VirtualFree() failed!\n" );
  448. }
  449. #else
  450. free( m_pActualPseudoBuf );
  451. #endif
  452. m_pActualPseudoBuf = NULL;
  453. m_pPseudoBuf = NULL;
  454. }
  455. else
  456. {
  457. gGL->glDeleteBuffersARB( 1, &m_nHandle );
  458. }
  459. m_pCtx = NULL;
  460. m_nHandle = 0;
  461. m_pLastMappedAddress = NULL;
  462. #if GL_ENABLE_INDEX_VERIFICATION
  463. m_BufferSpanManager.Deinit();
  464. #endif
  465. }
  466. void CGLMBuffer::SetModes( bool bAsyncMap, bool bExplicitFlush, bool bForce )
  467. {
  468. // assumes buffer is bound. called by constructor and by Lock.
  469. if ( m_bPseudo )
  470. {
  471. // ignore it...
  472. }
  473. else
  474. {
  475. if ( bForce || ( m_bEnableAsyncMap != bAsyncMap ) )
  476. {
  477. // note the sense of the parameter, it's TRUE if you *want* serialization, so for async you turn it to false.
  478. if ( ( gGL->m_bHave_GL_APPLE_flush_buffer_range ) && ( !gGL->m_bHave_GL_ARB_map_buffer_range ) )
  479. {
  480. gGL->glBufferParameteriAPPLE( m_buffGLTarget, GL_BUFFER_SERIALIZED_MODIFY_APPLE, bAsyncMap == false );
  481. }
  482. m_bEnableAsyncMap = bAsyncMap;
  483. }
  484. if ( bForce || ( m_bEnableExplicitFlush != bExplicitFlush ) )
  485. {
  486. // Note that the GL_ARB_map_buffer_range path handles this in the glMapBufferRange() call in Lock().
  487. // note the sense of the parameter, it's TRUE if you *want* auto-flush-on-unmap, so for explicit-flush, you turn it to false.
  488. if ( ( gGL->m_bHave_GL_APPLE_flush_buffer_range ) && ( !gGL->m_bHave_GL_ARB_map_buffer_range ) )
  489. {
  490. gGL->glBufferParameteriAPPLE( m_buffGLTarget, GL_BUFFER_FLUSHING_UNMAP_APPLE, bExplicitFlush == false );
  491. }
  492. m_bEnableExplicitFlush = bExplicitFlush;
  493. }
  494. }
  495. }
  496. #if GL_ENABLE_INDEX_VERIFICATION
  497. bool CGLMBuffer::IsSpanValid( uint nOffset, uint nSize ) const
  498. {
  499. return m_BufferSpanManager.IsValid( nOffset, nSize );
  500. }
  501. #endif
  502. void CGLMBuffer::FlushRange( uint offset, uint size )
  503. {
  504. if ( m_pStaticBuffer )
  505. {
  506. }
  507. else if ( m_bPseudo )
  508. {
  509. // nothing to do
  510. }
  511. else
  512. {
  513. #ifdef REPORT_LOCK_TIME
  514. double flStart = Plat_FloatTime();
  515. #endif
  516. // assumes buffer is bound.
  517. if ( gGL->m_bHave_GL_ARB_map_buffer_range )
  518. {
  519. gGL->glFlushMappedBufferRange( m_buffGLTarget, (GLintptr)( offset - m_dirtyMinOffset ), (GLsizeiptr)size );
  520. }
  521. else if ( gGL->m_bHave_GL_APPLE_flush_buffer_range )
  522. {
  523. gGL->glFlushMappedBufferRangeAPPLE( m_buffGLTarget, (GLintptr)offset, (GLsizeiptr)size );
  524. }
  525. #ifdef REPORT_LOCK_TIME
  526. double flEnd = Plat_FloatTime();
  527. if ( flEnd - flStart > 5.0 / 1000.0 )
  528. {
  529. int nDelta = ( int )( ( flEnd - flStart ) * 1000 );
  530. if ( nDelta > 2 )
  531. {
  532. Msg( "**** " );
  533. }
  534. Msg( "glFlushMappedBufferRange Time %d: ( Name=%d BufSize=%d ) Target=%p Offset=%d FlushSize=%d\n", nDelta, m_nHandle, m_nSize, m_buffGLTarget, offset - m_dirtyMinOffset, size );
  535. }
  536. #endif
  537. // If you don't have any extension support here, you'll flush the whole buffer on unmap. Performance loss, but it's still safe and correct.
  538. }
  539. }
  540. void CGLMBuffer::Lock( GLMBuffLockParams *pParams, char **pAddressOut )
  541. {
  542. #if GL_TELEMETRY_GPU_ZONES
  543. CScopedGLMPIXEvent glmPIXEvent( "CGLMBuffer::Lock" );
  544. g_TelemetryGPUStats.m_nTotalBufferLocksAndUnlocks++;
  545. #endif
  546. char *resultPtr = NULL;
  547. if ( m_bMapped )
  548. {
  549. DXABSTRACT_BREAK_ON_ERROR();
  550. return;
  551. }
  552. m_pCtx->CheckCurrent();
  553. Assert( pParams->m_nSize );
  554. m_LockParams = *pParams;
  555. if ( pParams->m_nOffset >= m_nSize )
  556. {
  557. DXABSTRACT_BREAK_ON_ERROR();
  558. return;
  559. }
  560. if ( ( pParams->m_nOffset + pParams->m_nSize ) > m_nSize)
  561. {
  562. DXABSTRACT_BREAK_ON_ERROR();
  563. return;
  564. }
  565. #if GL_ENABLE_INDEX_VERIFICATION
  566. if ( pParams->m_bDiscard )
  567. {
  568. m_BufferSpanManager.DiscardAllSpans();
  569. }
  570. #endif
  571. m_pStaticBuffer = NULL;
  572. bool bUsingPersistentBuffer = false;
  573. uint padding = 0;
  574. if ( m_bDynamic && gGL->m_bHave_GL_ARB_buffer_storage )
  575. {
  576. // Compute padding to add to make sure the start offset is valid
  577. CPersistentBuffer *pTempBuffer = m_pCtx->GetCurPersistentBuffer( m_type );
  578. uint persistentBufferOffset = pTempBuffer->GetOffset();
  579. if (pParams->m_nOffset > persistentBufferOffset)
  580. {
  581. // Make sure the start offset if valid (adding padding to the persistent buffer)
  582. padding = pParams->m_nOffset - persistentBufferOffset;
  583. }
  584. }
  585. if ( m_bPseudo )
  586. {
  587. if ( pParams->m_bDiscard )
  588. {
  589. m_nRevision++;
  590. }
  591. // async map modes are a no-op
  592. // calc lock address
  593. resultPtr = m_pPseudoBuf + pParams->m_nOffset;
  594. #if GL_ENABLE_UNLOCK_BUFFER_OVERWRITE_DETECTION
  595. BOOL bResult;
  596. DWORD nOldProtect;
  597. if ( pParams->m_bDiscard )
  598. {
  599. bResult = VirtualProtect( m_pActualPseudoBuf, m_nSize, PAGE_READWRITE, &nOldProtect );
  600. if ( !bResult )
  601. {
  602. Error( "VirtualProtect() failed!\n" );
  603. }
  604. m_nDirtyRangeStart = 0xFFFFFFFF;
  605. m_nDirtyRangeEnd = 0;
  606. for ( uint i = 0; i < m_nSize / sizeof( uint32 ); i++ )
  607. {
  608. reinterpret_cast< uint32 * >( m_pPseudoBuf )[i] = 0xDEADBEEF;
  609. }
  610. bResult = VirtualProtect( m_pActualPseudoBuf, m_nSize, PAGE_READONLY, &nOldProtect );
  611. if ( !bResult )
  612. {
  613. Error( "VirtualProtect() failed!\n" );
  614. }
  615. }
  616. uint nProtectOfs = m_LockParams.m_nOffset & 4095;
  617. uint nProtectEnd = ( m_LockParams.m_nOffset + m_LockParams.m_nSize + 4095 ) & ~4095;
  618. uint nProtectSize = nProtectEnd - nProtectOfs;
  619. bResult = VirtualProtect( m_pActualPseudoBuf + nProtectOfs, nProtectSize, PAGE_READWRITE, &nOldProtect );
  620. if ( !bResult )
  621. {
  622. Error( "VirtualProtect() failed!\n" );
  623. }
  624. #endif
  625. }
  626. else if ( m_bDynamic && gGL->m_bHave_GL_ARB_buffer_storage && ( m_pCtx->GetCurPersistentBuffer( m_type )->GetBytesRemaining() >= ( pParams->m_nSize + padding ) ) )
  627. {
  628. CPersistentBuffer *pTempBuffer = m_pCtx->GetCurPersistentBuffer( m_type );
  629. // Make sure the start offset if valid (adding padding to the persistent buffer)
  630. pTempBuffer->Append( padding );
  631. uint persistentBufferOffset = pTempBuffer->GetOffset();
  632. uint startOffset = persistentBufferOffset - pParams->m_nOffset;
  633. if ( pParams->m_bDiscard || ( startOffset != m_nPersistentBufferStartOffset ) )
  634. {
  635. m_nRevision++;
  636. // Offset to be added to the vertex and index buffer when setting the vertex and index buffer (before drawing)
  637. // Since we are using a immutable buffer storage, the persistent buffer is actually bigger than
  638. // buffer size requested upon creation. We keep appending to the end of the persistent buffer
  639. // and therefore need to keep track of the start of the actual buffer (in the persistent one)
  640. m_nPersistentBufferStartOffset = startOffset;
  641. //DevMsg( "Discard (%s): startOffset = %d\n", pParams->m_bDiscard ? "true" : "false", m_nPersistentBufferStartOffset );
  642. }
  643. resultPtr = static_cast<char*>(pTempBuffer->GetPtr()) + persistentBufferOffset;
  644. bUsingPersistentBuffer = true;
  645. //DevMsg( " --> buff=%x, startOffset=%d, paramsOffset=%d, persistOffset = %d\n", this, m_nPersistentBufferStartOffset, pParams->m_nOffset, persistentBufferOffset );
  646. }
  647. #ifndef OSX
  648. else if ( m_bDynamic && gGL->m_bHave_GL_AMD_pinned_memory && ( m_pCtx->GetCurPinnedMemoryBuffer()->GetBytesRemaining() >= pParams->m_nSize ) )
  649. {
  650. if ( pParams->m_bDiscard )
  651. {
  652. m_nRevision++;
  653. }
  654. m_dirtyMinOffset = pParams->m_nOffset;
  655. m_dirtyMaxOffset = pParams->m_nOffset + pParams->m_nSize;
  656. CPinnedMemoryBuffer *pTempBuffer = m_pCtx->GetCurPinnedMemoryBuffer();
  657. m_nPinnedMemoryOfs = pTempBuffer->GetOfs();
  658. resultPtr = static_cast<char*>( pTempBuffer->GetPtr() ) + m_nPinnedMemoryOfs;
  659. pTempBuffer->Append( pParams->m_nSize );
  660. }
  661. #endif // OSX
  662. else if ( !g_bDisableStaticBuffer && ( pParams->m_bDiscard || pParams->m_bNoOverwrite ) && ( pParams->m_nSize <= GL_STATIC_BUFFER_SIZE ) )
  663. {
  664. #if TOGL_SUPPORT_NULL_DEVICE
  665. if ( !g_bNullD3DDevice )
  666. #endif
  667. {
  668. if ( pParams->m_bDiscard )
  669. {
  670. m_pCtx->BindBufferToCtx( m_type, this );
  671. // observe gl_bufmode on any orphan event.
  672. // if orphaned and bufmode is nonzero, flip it to dynamic.
  673. GLenum hint = gl_bufmode.GetInt() ? GL_DYNAMIC_DRAW_ARB : GL_STATIC_DRAW_ARB;
  674. gGL->glBufferDataARB( m_buffGLTarget, m_nSize, (const GLvoid*)NULL, hint );
  675. m_nRevision++; // revision grows on orphan event
  676. }
  677. }
  678. m_dirtyMinOffset = pParams->m_nOffset;
  679. m_dirtyMaxOffset = pParams->m_nOffset + pParams->m_nSize;
  680. switch ( m_type )
  681. {
  682. case kGLMVertexBuffer:
  683. {
  684. m_pStaticBuffer = m_StaticBuffers[ 0 ];
  685. break;
  686. }
  687. case kGLMIndexBuffer:
  688. {
  689. m_pStaticBuffer = m_StaticBuffers[ 1 ];
  690. break;
  691. }
  692. default:
  693. {
  694. DXABSTRACT_BREAK_ON_ERROR();
  695. return;
  696. }
  697. }
  698. resultPtr = m_pStaticBuffer;
  699. }
  700. else
  701. {
  702. // bind (yes, even for pseudo - this binds name 0)
  703. m_pCtx->BindBufferToCtx( m_type, this );
  704. // perform discard if requested
  705. if ( pParams->m_bDiscard )
  706. {
  707. // observe gl_bufmode on any orphan event.
  708. // if orphaned and bufmode is nonzero, flip it to dynamic.
  709. // We always want to call glBufferData( ..., NULL ) on discards, even though we're using the GL_MAP_INVALIDATE_BUFFER_BIT flag, because this flag is actually only a hint according to AMD.
  710. GLenum hint = gl_bufmode.GetInt() ? GL_DYNAMIC_DRAW_ARB : GL_STATIC_DRAW_ARB;
  711. gGL->glBufferDataARB( m_buffGLTarget, m_nSize, (const GLvoid*)NULL, hint );
  712. m_nRevision++; // revision grows on orphan event
  713. }
  714. // adjust async map option appropriately, leave explicit flush unchanged
  715. SetModes( pParams->m_bNoOverwrite, m_bEnableExplicitFlush );
  716. // map
  717. char *mapPtr;
  718. if ( gGL->m_bHave_GL_ARB_map_buffer_range )
  719. {
  720. // m_bEnableAsyncMap is actually pParams->m_bNoOverwrite
  721. GLbitfield parms = GL_MAP_WRITE_BIT | ( m_bEnableAsyncMap ? GL_MAP_UNSYNCHRONIZED_BIT : 0 ) | ( pParams->m_bDiscard ? GL_MAP_INVALIDATE_BUFFER_BIT : 0 ) | ( m_bEnableExplicitFlush ? GL_MAP_FLUSH_EXPLICIT_BIT : 0 );
  722. #ifdef REPORT_LOCK_TIME
  723. double flStart = Plat_FloatTime();
  724. #endif
  725. mapPtr = (char*)gGL->glMapBufferRange( m_buffGLTarget, pParams->m_nOffset, pParams->m_nSize, parms);
  726. #ifdef REPORT_LOCK_TIME
  727. double flEnd = Plat_FloatTime();
  728. if ( flEnd - flStart > 5.0 / 1000.0 )
  729. {
  730. int nDelta = ( int )( ( flEnd - flStart ) * 1000 );
  731. if ( nDelta > 2 )
  732. {
  733. Msg( "**** " );
  734. }
  735. Msg( "glMapBufferRange Time=%d: ( Name=%d BufSize=%d ) Target=%p Offset=%d LockSize=%d ", nDelta, m_nHandle, m_nSize, m_buffGLTarget, pParams->m_nOffset, pParams->m_nSize );
  736. if ( parms & GL_MAP_WRITE_BIT )
  737. {
  738. Msg( "GL_MAP_WRITE_BIT ");
  739. }
  740. if ( parms & GL_MAP_UNSYNCHRONIZED_BIT )
  741. {
  742. Msg( "GL_MAP_UNSYNCHRONIZED_BIT ");
  743. }
  744. if ( parms & GL_MAP_INVALIDATE_BUFFER_BIT )
  745. {
  746. Msg( "GL_MAP_INVALIDATE_BUFFER_BIT ");
  747. }
  748. if ( parms & GL_MAP_INVALIDATE_RANGE_BIT )
  749. {
  750. Msg( "GL_MAP_INVALIDATE_RANGE_BIT ");
  751. }
  752. if ( parms & GL_MAP_FLUSH_EXPLICIT_BIT )
  753. {
  754. Msg( "GL_MAP_FLUSH_EXPLICIT_BIT ");
  755. }
  756. Msg( "\n" );
  757. }
  758. #endif
  759. }
  760. else
  761. {
  762. mapPtr = (char*)gGL->glMapBufferARB( m_buffGLTarget, GL_WRITE_ONLY_ARB );
  763. }
  764. Assert( mapPtr );
  765. // calculate offset location
  766. resultPtr = mapPtr;
  767. if ( !gGL->m_bHave_GL_ARB_map_buffer_range )
  768. {
  769. resultPtr += pParams->m_nOffset;
  770. }
  771. // set range
  772. m_dirtyMinOffset = pParams->m_nOffset;
  773. m_dirtyMaxOffset = pParams->m_nOffset + pParams->m_nSize;
  774. }
  775. if ( m_bUsingPersistentBuffer != bUsingPersistentBuffer )
  776. {
  777. // Up the revision number when switching from a persistent to a non persistent buffer (or vice versa)
  778. // Ensure the right GL buffer is bound before drawing (and vertex attribs properly set)
  779. m_nRevision++;
  780. m_bUsingPersistentBuffer = bUsingPersistentBuffer;
  781. }
  782. m_bMapped = true;
  783. m_pLastMappedAddress = (float*)resultPtr;
  784. *pAddressOut = resultPtr;
  785. }
  786. void CGLMBuffer::Unlock( int nActualSize, const void *pActualData )
  787. {
  788. #if GL_TELEMETRY_GPU_ZONES
  789. CScopedGLMPIXEvent glmPIXEvent( "CGLMBuffer::Unlock" );
  790. g_TelemetryGPUStats.m_nTotalBufferLocksAndUnlocks++;
  791. #endif
  792. m_pCtx->CheckCurrent();
  793. if ( !m_bMapped )
  794. {
  795. DXABSTRACT_BREAK_ON_ERROR();
  796. return;
  797. }
  798. if ( nActualSize < 0 )
  799. {
  800. nActualSize = m_LockParams.m_nSize;
  801. }
  802. if ( nActualSize > (int)m_LockParams.m_nSize )
  803. {
  804. DXABSTRACT_BREAK_ON_ERROR();
  805. return;
  806. }
  807. #if GL_ENABLE_UNLOCK_BUFFER_OVERWRITE_DETECTION
  808. if ( m_bPseudo )
  809. {
  810. // Check guard DWORD to detect buffer overruns (but are still within the last 4KB page so they don't get caught via pagefaults)
  811. if ( *reinterpret_cast< const uint32 * >( m_pPseudoBuf + m_nSize ) != 0xDEADBEEF )
  812. {
  813. // If this fires the client app has overwritten the guard DWORD beyond the end of the buffer.
  814. DXABSTRACT_BREAK_ON_ERROR();
  815. }
  816. static const uint s_nInitialValues[4] = { 0xEF, 0xBE, 0xAD, 0xDE };
  817. int nActualModifiedStart, nActualModifiedEnd;
  818. for ( nActualModifiedStart = 0; nActualModifiedStart < (int)m_LockParams.m_nSize; ++nActualModifiedStart )
  819. if ( reinterpret_cast< const uint8 * >( m_pLastMappedAddress )[nActualModifiedStart] != s_nInitialValues[ ( m_LockParams.m_nOffset + nActualModifiedStart ) & 3 ] )
  820. break;
  821. for ( nActualModifiedEnd = m_LockParams.m_nSize - 1; nActualModifiedEnd > nActualModifiedStart; --nActualModifiedEnd )
  822. if ( reinterpret_cast< const uint8 * >( m_pLastMappedAddress )[nActualModifiedEnd] != s_nInitialValues[ ( m_LockParams.m_nOffset + nActualModifiedEnd ) & 3 ] )
  823. break;
  824. int nNumActualBytesModified = 0;
  825. if ( nActualModifiedEnd >= nActualModifiedStart )
  826. {
  827. // The modified check is conservative (i.e. it should always err on the side of detecting <= actual bytes than where actually modified, never more).
  828. // We primarily care about the case where the user lies about the actual # of modified bytes, which can lead to difficult to debug/inconsistent problems with some drivers.
  829. // Round up/down the modified range, because the user's data may alias with the initial buffer values (0xDEADBEEF) so we may miss some bytes that where written.
  830. if ( m_type == kGLMIndexBuffer )
  831. {
  832. nActualModifiedStart &= ~1;
  833. nActualModifiedEnd = MIN( (int)m_LockParams.m_nSize, ( ( nActualModifiedEnd + 1 ) + 1 ) & ~1 ) - 1;
  834. }
  835. else
  836. {
  837. nActualModifiedStart &= ~3;
  838. nActualModifiedEnd = MIN( (int)m_LockParams.m_nSize, ( ( nActualModifiedEnd + 1 ) + 3 ) & ~3 ) - 1;
  839. }
  840. nNumActualBytesModified = nActualModifiedEnd + 1;
  841. if ( nActualSize < nNumActualBytesModified )
  842. {
  843. // The caller may be lying about the # of actually modified bytes in this lock.
  844. // Has this lock region been previously locked? If so, it may have been previously overwritten before. Otherwise, the region had to be the 0xDEADBEEF fill DWORD at lock time.
  845. if ( ( m_nDirtyRangeStart > m_nDirtyRangeEnd ) ||
  846. ( m_LockParams.m_nOffset > m_nDirtyRangeEnd ) || ( ( m_LockParams.m_nOffset + m_LockParams.m_nSize ) <= m_nDirtyRangeStart ) )
  847. {
  848. // If this fires the client has lied about the actual # of bytes they've modified in the buffer - this will cause unreliable rendering on AMD drivers (because AMD actually pays attention to the actual # of flushed bytes).
  849. DXABSTRACT_BREAK_ON_ERROR();
  850. }
  851. }
  852. m_nDirtyRangeStart = MIN( m_nDirtyRangeStart, m_LockParams.m_nOffset + nActualModifiedStart );
  853. m_nDirtyRangeEnd = MAX( m_nDirtyRangeEnd, m_LockParams.m_nOffset + nActualModifiedEnd );
  854. }
  855. #if GL_ENABLE_INDEX_VERIFICATION
  856. if ( nActualModifiedEnd >= nActualModifiedStart )
  857. {
  858. int n = nActualModifiedEnd + 1;
  859. if ( n != nActualSize )
  860. {
  861. // The actual detected modified size is < than the reported size, which is common because the last few DWORD's of the vertex format may not actually be used/written (or read by the vertex shader). So just fudge it so the batch consumption checks work.
  862. if ( ( (int)nActualSize - n ) <= 32 )
  863. {
  864. n = nActualSize;
  865. }
  866. }
  867. m_BufferSpanManager.AddSpan( m_LockParams.m_nOffset + nActualModifiedStart, m_LockParams.m_nSize, n - nActualModifiedStart, m_LockParams.m_bDiscard, m_LockParams.m_bNoOverwrite );
  868. }
  869. #endif
  870. }
  871. #elif GL_ENABLE_INDEX_VERIFICATION
  872. if ( nActualSize > 0 )
  873. {
  874. m_BufferSpanManager.AddSpan( m_LockParams.m_nOffset, m_LockParams.m_nSize, nActualSize, m_LockParams.m_bDiscard, m_LockParams.m_bNoOverwrite );
  875. }
  876. #endif
  877. #if GL_BATCH_PERF_ANALYSIS
  878. if ( m_type == kGLMIndexBuffer )
  879. g_nTotalIBLockBytes += nActualSize;
  880. else if ( m_type == kGLMVertexBuffer )
  881. g_nTotalVBLockBytes += nActualSize;
  882. #endif
  883. #ifndef OSX
  884. if ( m_nPinnedMemoryOfs >= 0 )
  885. {
  886. #if TOGL_SUPPORT_NULL_DEVICE
  887. if ( !g_bNullD3DDevice )
  888. {
  889. #endif
  890. if ( nActualSize )
  891. {
  892. m_pCtx->BindBufferToCtx( m_type, this );
  893. gGL->glCopyBufferSubData(
  894. GL_EXTERNAL_VIRTUAL_MEMORY_BUFFER_AMD,
  895. m_buffGLTarget,
  896. m_nPinnedMemoryOfs,
  897. m_dirtyMinOffset,
  898. nActualSize );
  899. }
  900. #if TOGL_SUPPORT_NULL_DEVICE
  901. }
  902. #endif
  903. m_nPinnedMemoryOfs = -1;
  904. }
  905. else
  906. #endif // !OSX
  907. if ( m_bUsingPersistentBuffer )
  908. {
  909. if ( nActualSize )
  910. {
  911. CPersistentBuffer *pTempBuffer = m_pCtx->GetCurPersistentBuffer( m_type );
  912. pTempBuffer->Append( nActualSize );
  913. //DevMsg( " <-- actualSize=%d, persistOffset = %d\n", nActualSize, pTempBuffer->GetOffset() );
  914. }
  915. }
  916. else if ( m_pStaticBuffer )
  917. {
  918. #if TOGL_SUPPORT_NULL_DEVICE
  919. if ( !g_bNullD3DDevice )
  920. #endif
  921. {
  922. if ( nActualSize )
  923. {
  924. tmZone( TELEMETRY_LEVEL2, TMZF_NONE, "UnlockSubData" );
  925. #ifdef REPORT_LOCK_TIME
  926. double flStart = Plat_FloatTime();
  927. #endif
  928. m_pCtx->BindBufferToCtx( m_type, this );
  929. Assert( nActualSize <= (int)( m_dirtyMaxOffset - m_dirtyMinOffset ) );
  930. glBufferSubDataMaxSize( m_buffGLTarget, m_dirtyMinOffset, nActualSize, pActualData ? pActualData : m_pStaticBuffer );
  931. #ifdef REPORT_LOCK_TIME
  932. double flEnd = Plat_FloatTime();
  933. if ( flEnd - flStart > 5.0 / 1000.0 )
  934. {
  935. int nDelta = ( int )( ( flEnd - flStart ) * 1000 );
  936. if ( nDelta > 2 )
  937. {
  938. Msg( "**** " );
  939. }
  940. // Msg( "glBufferSubData Time=%d: ( Name=%d BufSize=%d ) Target=%p Offset=%d Size=%d\n", nDelta, m_nHandle, m_nSize, m_buffGLTarget, m_dirtyMinOffset, m_dirtyMaxOffset - m_dirtyMinOffset );
  941. }
  942. #endif
  943. }
  944. }
  945. m_pStaticBuffer = NULL;
  946. }
  947. else if ( m_bPseudo )
  948. {
  949. if ( pActualData )
  950. {
  951. memcpy( m_pLastMappedAddress, pActualData, nActualSize );
  952. }
  953. #if GL_ENABLE_UNLOCK_BUFFER_OVERWRITE_DETECTION
  954. uint nProtectOfs = m_LockParams.m_nOffset & 4095;
  955. uint nProtectEnd = ( m_LockParams.m_nOffset + m_LockParams.m_nSize + 4095 ) & ~4095;
  956. uint nProtectSize = nProtectEnd - nProtectOfs;
  957. DWORD nOldProtect;
  958. BOOL bResult = VirtualProtect( m_pActualPseudoBuf + nProtectOfs, nProtectSize, PAGE_READONLY, &nOldProtect );
  959. if ( !bResult )
  960. {
  961. Error( "VirtualProtect() failed!\n" );
  962. }
  963. #endif
  964. }
  965. else
  966. {
  967. tmZone( TELEMETRY_LEVEL2, TMZF_NONE, "UnlockUnmap" );
  968. if ( pActualData )
  969. {
  970. memcpy( m_pLastMappedAddress, pActualData, nActualSize );
  971. }
  972. m_pCtx->BindBufferToCtx( m_type, this );
  973. Assert( nActualSize <= (int)( m_dirtyMaxOffset - m_dirtyMinOffset ) );
  974. // time to do explicit flush (currently m_bEnableExplicitFlush is always true)
  975. if ( m_bEnableExplicitFlush )
  976. {
  977. FlushRange( m_dirtyMinOffset, nActualSize );
  978. }
  979. // clear dirty range no matter what
  980. m_dirtyMinOffset = m_dirtyMaxOffset = 0; // adjust/grow on lock, clear on unlock
  981. #ifdef REPORT_LOCK_TIME
  982. double flStart = Plat_FloatTime();
  983. #endif
  984. gGL->glUnmapBuffer( m_buffGLTarget );
  985. #ifdef REPORT_LOCK_TIME
  986. double flEnd = Plat_FloatTime();
  987. if ( flEnd - flStart > 5.0 / 1000.0 )
  988. {
  989. int nDelta = ( int )( ( flEnd - flStart ) * 1000 );
  990. if ( nDelta > 2 )
  991. {
  992. Msg( "**** " );
  993. }
  994. Msg( "glUnmapBuffer Time=%d: ( Name=%d BufSize=%d ) Target=%p\n", nDelta, m_nHandle, m_nSize, m_buffGLTarget );
  995. }
  996. #endif
  997. }
  998. m_bMapped = false;
  999. }
  1000. GLuint CGLMBuffer::GetHandle() const
  1001. {
  1002. return ( m_bUsingPersistentBuffer ? m_pCtx->GetCurPersistentBuffer( m_type )->GetHandle() : m_nHandle );
  1003. }