Counter Strike : Global Offensive Source Code
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.

1096 lines
30 KiB

  1. //===== Copyright (c) 1996-2005, Valve Corporation, All rights reserved. ======//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //
  7. //===========================================================================//
  8. #ifndef DYNAMICVB_H
  9. #define DYNAMICVB_H
  10. #ifdef _WIN32
  11. #pragma once
  12. #endif
  13. #include "shaderapidx8_global.h"
  14. /////////////////////////////
  15. // D. Sim Dietrich Jr.
  16. // [email protected]
  17. //////////////////////
  18. // Helper function to unbind an vertex buffer
  19. void Unbind( IDirect3DVertexBuffer9 *pVertexBuffer );
  20. #define X360_VERTEX_BUFFER_SIZE_MULTIPLIER 2.0 //minimum of 1, only affects dynamic buffers
  21. //#define X360_BLOCK_ON_VB_FLUSH //uncomment to block until all data is consumed when a flush is requested. Otherwise we only block when absolutely necessary
  22. //#define SPEW_VERTEX_BUFFER_STALLS //uncomment to allow buffer stall spewing.
  23. #define MB (1024.0f*1024.0f)
  24. class CVertexBuffer
  25. {
  26. public:
  27. CVertexBuffer( D3DDeviceWrapper * pD3D, VertexFormat_t fmt, DWORD theFVF, int vertexSize,
  28. int theVertexCount, const char *pTextureBudgetName, bool bSoftwareVertexProcessing, bool dynamic = false );
  29. #ifdef _GAMECONSOLE
  30. CVertexBuffer();
  31. void Init( D3DDeviceWrapper * pD3D, VertexFormat_t fmt, DWORD theFVF, uint8 *pVertexData, int vertexSize, int theVertexCount );
  32. #endif
  33. ~CVertexBuffer();
  34. LPDIRECT3DVERTEXBUFFER GetInterface() const
  35. {
  36. // If this buffer still exists, then Late Creation didn't happen. Best case: we'll render the wrong image. Worst case: Crash.
  37. Assert( !m_pSysmemBuffer );
  38. return m_pVB;
  39. }
  40. // Use at beginning of frame to force a flush of VB contents on first draw
  41. void FlushAtFrameStart() { m_bFlush = true; }
  42. // lock, unlock
  43. unsigned char* Lock( int numVerts, int& baseVertexIndex );
  44. unsigned char* Modify( bool bReadOnly, int firstVertex, int numVerts );
  45. void Unlock( int numVerts );
  46. void HandleLateCreation( );
  47. // Vertex size
  48. int VertexSize() const { return m_VertexSize; }
  49. // Vertex count
  50. int VertexCount() const { return m_VertexCount; }
  51. #ifdef _X360
  52. // For some VBs, memory allocation is managed by CGPUBufferAllocator, via ShaderAPI
  53. const GPUBufferHandle_t *GetBufferAllocationHandle( void );
  54. void SetBufferAllocationHandle( const GPUBufferHandle_t &bufferAllocationHandle );
  55. bool IsPooled( void ) { creturn m_GPUBufferHandle.IsValid(); }
  56. // Expose the data pointer for read-only CPU access to the data
  57. // (double-indirection supports relocation of the data by CGPUBufferAllocator)
  58. const byte **GetBufferDataPointerAddress( void );
  59. #endif // _X360
  60. static int BufferCount()
  61. {
  62. #ifdef _DEBUG
  63. return s_BufferCount;
  64. #else
  65. return 0;
  66. #endif
  67. }
  68. // UID
  69. unsigned int UID() const
  70. {
  71. #ifdef RECORDING
  72. return m_UID;
  73. #else
  74. return 0;
  75. #endif
  76. }
  77. void HandlePerFrameTextureStats( int frame )
  78. {
  79. #ifdef VPROF_ENABLED
  80. if ( m_Frame != frame && !m_bDynamic )
  81. {
  82. m_Frame = frame;
  83. m_pFrameCounter += m_nBufferSize;
  84. }
  85. #endif
  86. }
  87. // Do we have enough room without discarding?
  88. bool HasEnoughRoom( int numVertices ) const;
  89. // Is this dynamic?
  90. bool IsDynamic() const { return m_bDynamic; }
  91. bool IsExternal() const { return m_bExternalMemory; }
  92. // Block until this part of the vertex buffer is free
  93. void BlockUntilUnused( int nBufferSize );
  94. // used to alter the characteristics after creation
  95. // allows one dynamic vb to be shared for multiple formats
  96. void ChangeConfiguration( int vertexSize, int totalSize )
  97. {
  98. Assert( m_bDynamic && !m_bLocked && vertexSize );
  99. m_VertexSize = vertexSize;
  100. m_VertexCount = m_nBufferSize / vertexSize;
  101. }
  102. // Compute the next offset for the next lock
  103. int NextLockOffset( ) const;
  104. // Returns the allocated size
  105. int AllocationSize() const;
  106. // Returns the number of vertices we have enough room for
  107. int NumVerticesUntilFlush() const
  108. {
  109. #if defined( _X360 )
  110. if( m_AllocationRing.Count() )
  111. {
  112. //Cycle through the ring buffer and see what memory is free now
  113. int iNode = m_AllocationRing.Head();
  114. while( m_AllocationRing.IsValidIndex( iNode ) )
  115. {
  116. if( Dx9Device()->IsFencePending( m_AllocationRing[iNode].m_Fence ) )
  117. break;
  118. iNode = m_AllocationRing.Next( iNode );
  119. }
  120. if( m_AllocationRing.IsValidIndex( iNode ) )
  121. {
  122. int iEndFreeOffset = m_AllocationRing[iNode].m_iEndOffset;
  123. if( iEndFreeOffset < m_Position )
  124. {
  125. //Wrapped. Making the arbitrary decision that the return value for this function *should* handle the singe giant allocation case which requires contiguous memory
  126. if( iEndFreeOffset > (m_iNextBlockingPosition - m_Position) )
  127. return iEndFreeOffset / m_VertexSize;
  128. else
  129. return (m_iNextBlockingPosition - m_Position) / m_VertexSize;
  130. }
  131. }
  132. else
  133. {
  134. //we didn't block on any fence
  135. return m_VertexCount;
  136. }
  137. }
  138. return m_VertexCount;
  139. #else
  140. return (m_nBufferSize - NextLockOffset()) / m_VertexSize;
  141. #endif
  142. }
  143. // Marks a fence indicating when this buffer was used
  144. void MarkUsedInRendering()
  145. {
  146. #ifdef _X360
  147. if ( m_bDynamic && m_pVB )
  148. {
  149. Assert( m_AllocationRing.Count() > 0 );
  150. m_AllocationRing[m_AllocationRing.Tail()].m_Fence = Dx9Device()->GetCurrentFence();
  151. }
  152. #endif
  153. }
  154. private:
  155. void Create( D3DDeviceWrapper *pD3D );
  156. inline void ReallyUnlock( int unlockBytes )
  157. {
  158. #if DX_TO_GL_ABSTRACTION
  159. // Knowing how much data was actually written is critical for performance under OpenGL.
  160. #if SHADERAPI_NO_D3DDeviceWrapper
  161. m_pVB->UnlockActualSize( unlockBytes );
  162. #else
  163. Dx9Device()->UnlockActualSize( m_pVB, unlockBytes );
  164. #endif
  165. #else
  166. unlockBytes; // Unused here
  167. #if SHADERAPI_NO_D3DDeviceWrapper
  168. m_pVB->Unlock();
  169. #else
  170. Dx9Device()->Unlock( m_pVB );
  171. #endif
  172. #endif
  173. }
  174. enum LOCK_FLAGS
  175. {
  176. LOCKFLAGS_FLUSH = D3DLOCK_NOSYSLOCK | D3DLOCK_DISCARD,
  177. #if !defined( _X360 )
  178. LOCKFLAGS_APPEND = D3DLOCK_NOSYSLOCK | D3DLOCK_NOOVERWRITE
  179. #else
  180. // X360BUG: forcing all locks to gpu flush, otherwise bizarre mesh corruption on decals
  181. // Currently iterating with microsoft 360 support to track source of gpu corruption
  182. LOCKFLAGS_APPEND = D3DLOCK_NOSYSLOCK
  183. #endif
  184. };
  185. LPDIRECT3DVERTEXBUFFER m_pVB;
  186. #ifdef _X360
  187. struct DynamicBufferAllocation_t
  188. {
  189. DWORD m_Fence; //track whether this memory is safe to use again.
  190. int m_iStartOffset;
  191. int m_iEndOffset;
  192. unsigned int m_iZPassIdx; // The zpass during which this allocation was made
  193. };
  194. int m_iNextBlockingPosition; // m_iNextBlockingPosition >= m_Position where another allocation is still in use.
  195. unsigned char *m_pAllocatedMemory;
  196. int m_iAllocationSize; //Total size of the ring buffer, usually more than what was asked for
  197. IDirect3DVertexBuffer9 m_D3DVertexBuffer; //Only need one shared D3D header for our usage patterns.
  198. CUtlLinkedList<DynamicBufferAllocation_t> m_AllocationRing; //tracks what chunks of our memory are potentially still in use by D3D
  199. #endif
  200. VertexFormat_t m_VertexBufferFormat; // yes, Vertex, only used for allocation tracking
  201. int m_nBufferSize;
  202. int m_Position;
  203. int m_VertexCount;
  204. int m_VertexSize;
  205. DWORD m_TheFVF;
  206. byte *m_pSysmemBuffer;
  207. int m_nSysmemBufferStartBytes;
  208. uint m_nLockCount;
  209. unsigned char m_bDynamic : 1;
  210. unsigned char m_bLocked : 1;
  211. unsigned char m_bFlush : 1;
  212. unsigned char m_bExternalMemory : 1;
  213. unsigned char m_bSoftwareVertexProcessing : 1;
  214. unsigned char m_bLateCreateShouldDiscard : 1;
  215. #ifdef VPROF_ENABLED
  216. int m_Frame;
  217. int *m_pFrameCounter;
  218. int *m_pGlobalCounter;
  219. #endif
  220. #ifdef _DEBUG
  221. static int s_BufferCount;
  222. #endif
  223. #ifdef RECORDING
  224. unsigned int m_UID;
  225. #endif
  226. };
  227. #if defined( _X360 )
  228. #include "UtlMap.h"
  229. MEMALLOC_DEFINE_EXTERNAL_TRACKING( XMem_CVertexBuffer );
  230. #endif
  231. //-----------------------------------------------------------------------------
  232. // constructor, destructor
  233. //-----------------------------------------------------------------------------
  234. inline CVertexBuffer::CVertexBuffer(D3DDeviceWrapper * pD3D, VertexFormat_t fmt, DWORD theFVF,
  235. int vertexSize, int vertexCount, const char *pTextureBudgetName,
  236. bool bSoftwareVertexProcessing, bool dynamic ) :
  237. m_pVB(0),
  238. m_Position(0),
  239. m_VertexSize(vertexSize),
  240. m_VertexCount(vertexCount),
  241. m_bFlush(true),
  242. m_bLocked(false),
  243. m_bExternalMemory( false ),
  244. m_nBufferSize(vertexSize * vertexCount),
  245. m_TheFVF( theFVF ),
  246. m_bSoftwareVertexProcessing( bSoftwareVertexProcessing ),
  247. m_bDynamic(dynamic),
  248. m_VertexBufferFormat( fmt ),
  249. m_bLateCreateShouldDiscard( false )
  250. #ifdef _X360
  251. ,m_pAllocatedMemory(NULL)
  252. ,m_iNextBlockingPosition(0)
  253. ,m_iAllocationSize(0)
  254. #endif
  255. #ifdef VPROF_ENABLED
  256. ,m_Frame( -1 )
  257. #endif
  258. {
  259. MEM_ALLOC_CREDIT_( pTextureBudgetName );
  260. #ifdef RECORDING
  261. // assign a UID
  262. static unsigned int uid = 0;
  263. m_UID = uid++;
  264. #endif
  265. #ifdef _DEBUG
  266. ++s_BufferCount;
  267. #endif
  268. #ifdef VPROF_ENABLED
  269. if ( !m_bDynamic )
  270. {
  271. char name[256];
  272. V_strcpy_safe( name, "TexGroup_global_" );
  273. V_strcat_safe( name, pTextureBudgetName, sizeof(name) );
  274. m_pGlobalCounter = g_VProfCurrentProfile.FindOrCreateCounter( name, COUNTER_GROUP_TEXTURE_GLOBAL );
  275. V_strcpy_safe( name, "TexGroup_frame_" );
  276. V_strcat_safe( name, pTextureBudgetName, sizeof(name) );
  277. m_pFrameCounter = g_VProfCurrentProfile.FindOrCreateCounter( name, COUNTER_GROUP_TEXTURE_PER_FRAME );
  278. }
  279. else
  280. {
  281. m_pGlobalCounter = g_VProfCurrentProfile.FindOrCreateCounter( "TexGroup_global_" TEXTURE_GROUP_DYNAMIC_VERTEX_BUFFER, COUNTER_GROUP_TEXTURE_GLOBAL );
  282. }
  283. #endif
  284. if ( !g_pShaderUtil->IsRenderThreadSafe() )
  285. {
  286. m_pSysmemBuffer = ( byte * )malloc( m_nBufferSize );
  287. m_nSysmemBufferStartBytes = 0;
  288. }
  289. else
  290. {
  291. m_pSysmemBuffer = NULL;
  292. Create( pD3D );
  293. }
  294. #ifdef VPROF_ENABLED
  295. if ( IsX360() || !m_bDynamic )
  296. {
  297. Assert( m_pGlobalCounter );
  298. *m_pGlobalCounter += m_nBufferSize;
  299. }
  300. #endif
  301. }
  302. void CVertexBuffer::Create( D3DDeviceWrapper *pD3D )
  303. {
  304. D3DVERTEXBUFFER_DESC desc;
  305. memset( &desc, 0x00, sizeof( desc ) );
  306. desc.Format = D3DFMT_VERTEXDATA;
  307. desc.Size = m_nBufferSize;
  308. desc.Type = D3DRTYPE_VERTEXBUFFER;
  309. desc.Pool = m_bDynamic ? D3DPOOL_DEFAULT : D3DPOOL_MANAGED;
  310. desc.FVF = m_TheFVF;
  311. #if defined( IS_WINDOWS_PC ) && defined( SHADERAPIDX9 ) && 0 // this may not be supported on all platforms
  312. extern bool g_ShaderDeviceUsingD3D9Ex;
  313. if ( g_ShaderDeviceUsingD3D9Ex )
  314. {
  315. desc.Pool = D3DPOOL_DEFAULT;
  316. }
  317. #endif
  318. desc.Usage = D3DUSAGE_WRITEONLY;
  319. if ( m_bDynamic )
  320. {
  321. desc.Usage |= D3DUSAGE_DYNAMIC;
  322. // Dynamic meshes should never be compressed (slows down writing to them)
  323. Assert( CompressionType( m_TheFVF ) == VERTEX_COMPRESSION_NONE );
  324. }
  325. if ( m_bSoftwareVertexProcessing )
  326. {
  327. desc.Usage |= D3DUSAGE_SOFTWAREPROCESSING;
  328. }
  329. #if !defined( _X360 )
  330. RECORD_COMMAND( DX8_CREATE_VERTEX_BUFFER, 6 );
  331. RECORD_INT( m_UID );
  332. RECORD_INT( m_nBufferSize );
  333. RECORD_INT( desc.Usage );
  334. RECORD_INT( desc.FVF );
  335. RECORD_INT( desc.Pool );
  336. RECORD_INT( m_bDynamic );
  337. HRESULT hr = pD3D->CreateVertexBuffer( m_nBufferSize, desc.Usage, desc.FVF, desc.Pool, &m_pVB, NULL );
  338. if ( hr == D3DERR_OUTOFVIDEOMEMORY || hr == E_OUTOFMEMORY )
  339. {
  340. // Don't have the memory for this. Try flushing all managed resources
  341. // out of vid mem and try again.
  342. // FIXME: need to record this
  343. pD3D->EvictManagedResources();
  344. pD3D->CreateVertexBuffer( m_nBufferSize, desc.Usage, desc.FVF, desc.Pool, &m_pVB, NULL );
  345. }
  346. #ifdef _DEBUG
  347. if ( hr != D3D_OK )
  348. {
  349. switch ( hr )
  350. {
  351. case D3DERR_INVALIDCALL:
  352. Assert( !"D3DERR_INVALIDCALL" );
  353. break;
  354. case D3DERR_OUTOFVIDEOMEMORY:
  355. Assert( !"D3DERR_OUTOFVIDEOMEMORY" );
  356. break;
  357. case E_OUTOFMEMORY:
  358. Assert( !"E_OUTOFMEMORY" );
  359. break;
  360. default:
  361. Assert( 0 );
  362. break;
  363. }
  364. }
  365. #endif
  366. Assert( m_pVB );
  367. #else
  368. // _X360
  369. if ( m_bDynamic )
  370. {
  371. m_iAllocationSize = m_nBufferSize * X360_VERTEX_BUFFER_SIZE_MULTIPLIER;
  372. Assert( m_iAllocationSize >= m_nBufferSize );
  373. m_pAllocatedMemory = (unsigned char*)XPhysicalAlloc( m_iAllocationSize, MAXULONG_PTR, 0, PAGE_READWRITE | MEM_LARGE_PAGES | PAGE_WRITECOMBINE );
  374. }
  375. else
  376. {
  377. // Fall back to allocating a standalone VB
  378. // NOTE: write-combining (PAGE_WRITECOMBINE) is deliberately not used, since it slows down CPU access to the data (decals+defragmentation)
  379. m_iAllocationSize = m_nBufferSize;
  380. m_pAllocatedMemory = (unsigned char*)XPhysicalAlloc( m_iAllocationSize, MAXULONG_PTR, 0, PAGE_READWRITE );
  381. }
  382. if ( m_pAllocatedMemory )
  383. {
  384. MemAlloc_RegisterExternalAllocation( XMem_CVertexBuffer, m_pAllocatedMemory, XPhysicalSize( m_pAllocatedMemory ) );
  385. }
  386. else
  387. {
  388. size_t nUsedMemory, nFreeMemory;
  389. g_pMemAlloc->GlobalMemoryStatus( &nUsedMemory, &nFreeMemory );
  390. Warning( "Failed to XPhysicalAlloc %.2fmb %s vertex buffer, with %.2fmb total memory free - memory is either exhausted or fragmented!\n", m_iAllocationSize/MB, m_bDynamic?"dynamic":"static", nFreeMemory/MB );
  391. Assert( m_pAllocatedMemory ); // If this assert fires, we're probably out of memory
  392. }
  393. m_iNextBlockingPosition = m_iAllocationSize;
  394. #endif
  395. #ifdef MEASURE_DRIVER_ALLOCATIONS
  396. int nMemUsed = 1024;
  397. VPROF_INCREMENT_GROUP_COUNTER( "vb count", COUNTER_GROUP_NO_RESET, 1 );
  398. VPROF_INCREMENT_GROUP_COUNTER( "vb driver mem", COUNTER_GROUP_NO_RESET, nMemUsed );
  399. VPROF_INCREMENT_GROUP_COUNTER( "total driver mem", COUNTER_GROUP_NO_RESET, nMemUsed );
  400. #endif
  401. // Track VB allocations
  402. #if !defined( _X360 )
  403. g_VBAllocTracker->CountVB( m_pVB, m_bDynamic, m_nBufferSize, m_VertexSize, m_VertexBufferFormat );
  404. #else
  405. g_VBAllocTracker->CountVB( this, m_bDynamic, m_iAllocationSize, m_VertexSize, m_VertexBufferFormat );
  406. #endif
  407. }
  408. #ifdef _GAMECONSOLE
  409. void *AllocateTempBuffer( size_t nSizeInBytes );
  410. //-----------------------------------------------------------------------------
  411. // This variant is for when we already have the data in physical memory
  412. //-----------------------------------------------------------------------------
  413. inline CVertexBuffer::CVertexBuffer( ) :
  414. m_pVB( 0 ),
  415. m_Position( 0 ),
  416. m_VertexSize( 0 ),
  417. m_VertexCount( 0 ),
  418. m_bFlush( false ),
  419. m_bLocked( false ),
  420. m_bExternalMemory( true ),
  421. m_nBufferSize( 0 ),
  422. m_bDynamic( false )
  423. #ifdef VPROF_ENABLED
  424. ,m_Frame( -1 )
  425. #endif
  426. {
  427. #ifdef _X360
  428. m_iAllocationSize = 0;
  429. m_pAllocatedMemory = 0;
  430. m_iNextBlockingPosition = 0;
  431. #endif
  432. }
  433. #include "tier0/memdbgoff.h"
  434. inline void CVertexBuffer::Init( D3DDeviceWrapper *pD3D, VertexFormat_t fmt, DWORD theFVF, uint8 *pVertexData, int vertexSize, int vertexCount )
  435. {
  436. m_nBufferSize = vertexSize * vertexCount;
  437. m_Position = m_nBufferSize;
  438. m_VertexSize = vertexSize;
  439. m_VertexCount = vertexCount;
  440. #ifdef _X360
  441. m_iAllocationSize = m_nBufferSize;
  442. m_pAllocatedMemory = pVertexData;
  443. m_iNextBlockingPosition = m_iAllocationSize;
  444. #endif
  445. m_pVB = new( AllocateTempBuffer( sizeof( IDirect3DVertexBuffer9 ) ) ) IDirect3DVertexBuffer9;
  446. #ifdef _X360
  447. XGSetVertexBufferHeader( m_nBufferSize, 0, 0, 0, m_pVB );
  448. XGOffsetResourceAddress( m_pVB, pVertexData );
  449. #elif _PS3
  450. memset( &m_pVB->m_vtxDesc, 0, sizeof(m_pVB->m_vtxDesc) );
  451. // m_pVB->m_vtxDesc.Format = ?;
  452. m_pVB->m_vtxDesc.Type = D3DRTYPE_VERTEXBUFFER;
  453. m_pVB->m_vtxDesc.Usage = D3DUSAGE_WRITEONLY | D3DUSAGE_DYNAMIC;
  454. m_pVB->m_vtxDesc.Pool = D3DPOOL_DEFAULT;
  455. m_pVB->m_vtxDesc.Size = m_nBufferSize;
  456. // m_pVB->m_vtxDesc.FVF = theFVF;
  457. m_pVB->m_pBuffer = new( AllocateTempBuffer( sizeof( CPs3gcmBuffer ) ) ) CPs3gcmBuffer;
  458. m_pVB->m_pBuffer->m_lmBlock.AttachToExternalMemory( kAllocPs3GcmVertexBufferDynamic,
  459. ( uintp )pVertexData - ( uintp )g_ps3gcmGlobalState.m_pLocalBaseAddress, m_nBufferSize );
  460. #endif
  461. }
  462. #include "tier0/memdbgon.h"
  463. #endif // _X360
  464. inline CVertexBuffer::~CVertexBuffer()
  465. {
  466. // Track VB allocations
  467. #if !defined( _X360 )
  468. if ( m_pVB != NULL )
  469. {
  470. g_VBAllocTracker->UnCountVB( m_pVB );
  471. }
  472. #else
  473. if ( m_pVB && m_pVB->IsSet( Dx9Device() ) )
  474. {
  475. Unbind( m_pVB );
  476. }
  477. if ( !m_bExternalMemory )
  478. {
  479. g_VBAllocTracker->UnCountVB( m_pAllocatedMemory );
  480. }
  481. #endif
  482. if ( !m_bExternalMemory )
  483. {
  484. #ifdef MEASURE_DRIVER_ALLOCATIONS
  485. int nMemUsed = 1024;
  486. VPROF_INCREMENT_GROUP_COUNTER( "vb count", COUNTER_GROUP_NO_RESET, -1 );
  487. VPROF_INCREMENT_GROUP_COUNTER( "vb driver mem", COUNTER_GROUP_NO_RESET, -nMemUsed );
  488. VPROF_INCREMENT_GROUP_COUNTER( "total driver mem", COUNTER_GROUP_NO_RESET, -nMemUsed );
  489. #endif
  490. #ifdef VPROF_ENABLED
  491. if ( IsX360() || !m_bDynamic )
  492. {
  493. Assert( m_pGlobalCounter );
  494. *m_pGlobalCounter -= m_nBufferSize;
  495. }
  496. #endif
  497. #ifdef _DEBUG
  498. --s_BufferCount;
  499. #endif
  500. }
  501. Unlock( 0 );
  502. if ( m_pSysmemBuffer )
  503. {
  504. free( m_pSysmemBuffer );
  505. m_pSysmemBuffer = NULL;
  506. }
  507. #if !defined( _X360 )
  508. if ( m_pVB && !m_bExternalMemory )
  509. {
  510. RECORD_COMMAND( DX8_DESTROY_VERTEX_BUFFER, 1 );
  511. RECORD_INT( m_UID );
  512. #if SHADERAPI_NO_D3DDeviceWrapper
  513. m_pVB->Release();
  514. #else
  515. Dx9Device()->Release( m_pVB );
  516. #endif
  517. }
  518. #else
  519. if ( m_pAllocatedMemory && !m_bExternalMemory )
  520. {
  521. MemAlloc_RegisterExternalDeallocation( XMem_CVertexBuffer, m_pAllocatedMemory, XPhysicalSize( m_pAllocatedMemory ) );
  522. XPhysicalFree( m_pAllocatedMemory );
  523. }
  524. m_pAllocatedMemory = NULL;
  525. m_pVB = NULL;
  526. #endif
  527. }
  528. //-----------------------------------------------------------------------------
  529. // Compute the next offset for the next lock
  530. //-----------------------------------------------------------------------------
  531. inline int CVertexBuffer::NextLockOffset( ) const
  532. {
  533. #if !defined( _X360 )
  534. int nNextOffset = ( m_Position + m_VertexSize - 1 ) / m_VertexSize;
  535. nNextOffset *= m_VertexSize;
  536. return nNextOffset;
  537. #else
  538. return m_Position; //position is already aligned properly on unlocks for 360.
  539. #endif
  540. }
  541. //-----------------------------------------------------------------------------
  542. // Do we have enough room without discarding?
  543. //-----------------------------------------------------------------------------
  544. inline bool CVertexBuffer::HasEnoughRoom( int numVertices ) const
  545. {
  546. #if defined( _X360 )
  547. return numVertices <= m_VertexCount; //the ring buffer will free room as needed
  548. #elif defined( _PS3 )
  549. // Dynamic VBs are used with dynamic IBs that use 16-bit indices, so do not ever let this number exceed 65536
  550. int nMaxVertexCount = m_bDynamic ? MIN( 65536, m_VertexCount ) : m_VertexCount;
  551. return ( NextLockOffset() / m_VertexSize + numVertices ) <= nMaxVertexCount;
  552. #else
  553. return (NextLockOffset() + (numVertices * m_VertexSize)) <= m_nBufferSize;
  554. #endif
  555. }
  556. //-----------------------------------------------------------------------------
  557. // Block until this part of the index buffer is free
  558. //-----------------------------------------------------------------------------
  559. inline void CVertexBuffer::BlockUntilUnused( int nBufferSize )
  560. {
  561. Assert( nBufferSize <= m_nBufferSize );
  562. #ifdef _X360
  563. int nLockOffset = NextLockOffset();
  564. Assert( (m_AllocationRing.Count() != 0) || ((m_Position == 0) && (m_iNextBlockingPosition == m_iAllocationSize)) );
  565. if ( (m_iNextBlockingPosition - nLockOffset) >= nBufferSize )
  566. return;
  567. Assert( (m_AllocationRing[m_AllocationRing.Head()].m_iStartOffset == 0) || ((m_iNextBlockingPosition == m_AllocationRing[m_AllocationRing.Head()].m_iStartOffset) && (m_Position <= m_iNextBlockingPosition)) );
  568. int iMinBlockPosition = nLockOffset + nBufferSize;
  569. if( iMinBlockPosition > m_iAllocationSize )
  570. {
  571. //Allocation requires us to wrap
  572. iMinBlockPosition = nBufferSize;
  573. m_Position = 0;
  574. //modify the last allocation so that it uses up the whole tail end of the buffer. Makes other code simpler
  575. Assert( m_AllocationRing.Count() != 0 );
  576. m_AllocationRing[m_AllocationRing.Tail()].m_iEndOffset = m_iAllocationSize;
  577. //treat all allocations between the current position and the tail end of the ring as freed since they will be before we unblock
  578. while( m_AllocationRing.Count() )
  579. {
  580. unsigned int head = m_AllocationRing.Head();
  581. if( m_AllocationRing[head].m_iStartOffset == 0 )
  582. break;
  583. m_AllocationRing.Remove( head );
  584. }
  585. }
  586. //now we go through the allocations until we find the last fence we care about. Treat everything up until that fence as freed.
  587. DWORD FinalFence = 0;
  588. unsigned int iFinalAllocationZPassIdx = 0;
  589. while( m_AllocationRing.Count() )
  590. {
  591. unsigned int head = m_AllocationRing.Head();
  592. if( m_AllocationRing[head].m_iEndOffset >= iMinBlockPosition )
  593. {
  594. //When this frees, we'll finally have enough space for the allocation
  595. FinalFence = m_AllocationRing[head].m_Fence;
  596. iFinalAllocationZPassIdx = m_AllocationRing[head].m_iZPassIdx;
  597. m_iNextBlockingPosition = m_AllocationRing[head].m_iEndOffset;
  598. m_AllocationRing.Remove( head );
  599. break;
  600. }
  601. m_AllocationRing.Remove( head );
  602. }
  603. Assert( FinalFence != 0 );
  604. if( Dx9Device()->IsFencePending( FinalFence ) )
  605. {
  606. #ifdef SPEW_VERTEX_BUFFER_STALLS
  607. float st = Plat_FloatTime();
  608. #endif
  609. if ( ( Dx9Device()->GetDeviceState() & D3DDEVICESTATE_ZPASS_BRACKET ) &&
  610. ( iFinalAllocationZPassIdx == ShaderAPI()->GetConsoleZPassCounter() ) )
  611. {
  612. // We're about to overrun our VB ringbuffer in a single Z prepass. To avoid rendering corruption, close out the
  613. // Z prepass and continue. This will reduce early-Z rejection efficiency and could cause a momentary framerate drop,
  614. // but it's better than rendering corruption.
  615. Warning( "Dynamic VB ring buffer overrun in Z Prepass. Tell Thorsten.\n" );
  616. ShaderAPI()->EndConsoleZPass();
  617. }
  618. Dx9Device()->BlockOnFence( FinalFence );
  619. #ifdef SPEW_VERTEX_BUFFER_STALLS
  620. float dt = Plat_FloatTime() - st;
  621. Warning( "Blocked locking dynamic vertex buffer for %f ms!\n", 1000.0 * dt );
  622. #endif
  623. }
  624. #endif
  625. }
  626. //-----------------------------------------------------------------------------
  627. // lock, unlock
  628. //-----------------------------------------------------------------------------
  629. inline unsigned char* CVertexBuffer::Lock( int numVerts, int& baseVertexIndex )
  630. {
  631. #if defined( _X360 )
  632. if ( m_pVB && m_pVB->IsSet( Dx9Device() ) )
  633. {
  634. Unbind( m_pVB );
  635. }
  636. #endif
  637. m_nLockCount = numVerts;
  638. unsigned char* pLockedData = 0;
  639. baseVertexIndex = 0;
  640. int nBufferSize = numVerts * m_VertexSize;
  641. Assert( IsPC() || ( IsGameConsole() && !m_bLocked ) );
  642. // Ensure there is enough space in the VB for this data
  643. if ( numVerts > m_VertexCount )
  644. {
  645. Assert( 0 );
  646. return 0;
  647. }
  648. if ( !IsX360() && !m_pVB && !m_pSysmemBuffer )
  649. return 0;
  650. DWORD dwFlags;
  651. if ( m_bDynamic )
  652. {
  653. dwFlags = LOCKFLAGS_APPEND;
  654. #if !defined( _X360 )
  655. // If either the user forced us to flush,
  656. // or there is not enough space for the vertex data,
  657. // then flush the buffer contents
  658. if ( !m_Position || m_bFlush || !HasEnoughRoom(numVerts) )
  659. {
  660. if ( m_pSysmemBuffer || !g_pShaderUtil->IsRenderThreadSafe() )
  661. m_bLateCreateShouldDiscard = true;
  662. m_bFlush = false;
  663. m_Position = 0;
  664. dwFlags = LOCKFLAGS_FLUSH;
  665. }
  666. #else
  667. if( m_bFlush )
  668. {
  669. # if ( defined( X360_BLOCK_ON_VB_FLUSH ) )
  670. {
  671. if( m_AllocationRing.Count() )
  672. {
  673. DWORD FinalFence = m_AllocationRing[m_AllocationRing.Tail()].m_Fence;
  674. m_AllocationRing.RemoveAll();
  675. m_Position = 0;
  676. m_iNextBlockingPosition = m_iAllocationSize;
  677. # if ( defined( SPEW_VERTEX_BUFFER_STALLS ) )
  678. if( Dx9Device()->IsFencePending( FinalFence ) )
  679. {
  680. float st = Plat_FloatTime();
  681. # endif
  682. Dx9Device()->BlockOnFence( FinalFence );
  683. # if ( defined ( SPEW_VERTEX_BUFFER_STALLS ) )
  684. float dt = Plat_FloatTime() - st;
  685. Warning( "Blocked FLUSHING dynamic vertex buffer for %f ms!\n", 1000.0 * dt );
  686. }
  687. # endif
  688. }
  689. }
  690. # endif
  691. m_bFlush = false;
  692. }
  693. #endif
  694. }
  695. else
  696. {
  697. // Since we are a static VB, always lock the beginning of the buffer.
  698. dwFlags = D3DLOCK_NOSYSLOCK;
  699. m_Position = 0;
  700. }
  701. if ( IsX360() && m_bDynamic )
  702. {
  703. // Block until we have enough room in the buffer, this affects the result of NextLockOffset() in wrap conditions.
  704. BlockUntilUnused( nBufferSize );
  705. m_pVB = NULL;
  706. }
  707. int nLockOffset = NextLockOffset( );
  708. RECORD_COMMAND( DX8_LOCK_VERTEX_BUFFER, 4 );
  709. RECORD_INT( m_UID );
  710. RECORD_INT( nLockOffset );
  711. RECORD_INT( nBufferSize );
  712. RECORD_INT( dwFlags );
  713. #if !defined( _X360 )
  714. // If the caller isn't in the thread that owns the render lock, need to return a system memory pointer--cannot talk to GL from
  715. // the non-current thread.
  716. if ( !m_pSysmemBuffer && !g_pShaderUtil->IsRenderThreadSafe() )
  717. {
  718. m_pSysmemBuffer = ( byte * )malloc( m_nBufferSize );
  719. m_nSysmemBufferStartBytes = nLockOffset;
  720. Assert( ( m_nSysmemBufferStartBytes % m_VertexSize ) == 0 );
  721. }
  722. if ( m_pSysmemBuffer != NULL )
  723. {
  724. // Ensure that we're never moving backwards in a buffer--this code would need to be rewritten if so.
  725. // We theorize this can happen if you hit the end of a buffer and then wrap before drawing--but
  726. // this would probably break in other places as well.
  727. Assert( nLockOffset >= m_nSysmemBufferStartBytes );
  728. pLockedData = m_pSysmemBuffer + nLockOffset;
  729. }
  730. else
  731. {
  732. #if SHADERAPI_NO_D3DDeviceWrapper
  733. m_pVB->Lock( nLockOffset,
  734. nBufferSize,
  735. reinterpret_cast< void** >( &pLockedData ),
  736. dwFlags );
  737. #else
  738. Dx9Device()->Lock( m_pVB, nLockOffset,
  739. nBufferSize,
  740. reinterpret_cast< void** >( &pLockedData ),
  741. dwFlags );
  742. #endif
  743. }
  744. #else
  745. pLockedData = m_pAllocatedMemory + nLockOffset;
  746. #endif
  747. Assert( pLockedData != 0 );
  748. m_bLocked = true;
  749. if ( !IsX360() )
  750. {
  751. baseVertexIndex = nLockOffset / m_VertexSize;
  752. }
  753. else
  754. {
  755. baseVertexIndex = 0;
  756. }
  757. return pLockedData;
  758. }
  759. inline unsigned char* CVertexBuffer::Modify( bool bReadOnly, int firstVertex, int numVerts )
  760. {
  761. unsigned char* pLockedData = 0;
  762. // D3D still returns a pointer when you call lock with 0 verts, so just in
  763. // case it's actually doing something, don't even try to lock the buffer with 0 verts.
  764. if ( numVerts == 0 )
  765. return NULL;
  766. m_nLockCount = numVerts;
  767. // If this hits, m_pSysmemBuffer logic needs to be added to this code.
  768. Assert( g_pShaderUtil->IsRenderThreadSafe() );
  769. Assert( !m_pSysmemBuffer ); // if this hits, then we need to add code to handle it
  770. Assert( m_pVB && !m_bDynamic );
  771. if ( firstVertex + numVerts > m_VertexCount )
  772. {
  773. Assert( 0 );
  774. return NULL;
  775. }
  776. DWORD dwFlags = D3DLOCK_NOSYSLOCK;
  777. if ( bReadOnly )
  778. {
  779. dwFlags |= D3DLOCK_READONLY;
  780. }
  781. RECORD_COMMAND( DX8_LOCK_VERTEX_BUFFER, 4 );
  782. RECORD_INT( m_UID );
  783. RECORD_INT( firstVertex * m_VertexSize );
  784. RECORD_INT( numVerts * m_VertexSize );
  785. RECORD_INT( dwFlags );
  786. // mmw: for forcing all dynamic... LOCKFLAGS_FLUSH );
  787. #if !defined( _X360 )
  788. #if SHADERAPI_NO_D3DDeviceWrapper
  789. m_pVB->Lock(
  790. firstVertex * m_VertexSize,
  791. numVerts * m_VertexSize,
  792. reinterpret_cast< void** >( &pLockedData ),
  793. dwFlags );
  794. #else
  795. Dx9Device()->Lock(
  796. m_pVB,
  797. firstVertex * m_VertexSize,
  798. numVerts * m_VertexSize,
  799. reinterpret_cast< void** >( &pLockedData ),
  800. dwFlags );
  801. #endif
  802. #else
  803. if ( m_pVB->IsSet( Dx9Device() ) )
  804. {
  805. Unbind( m_pVB );
  806. }
  807. pLockedData = m_pAllocatedMemory + (firstVertex * m_VertexSize);
  808. #endif
  809. m_Position = firstVertex * m_VertexSize;
  810. Assert( pLockedData != 0 );
  811. m_bLocked = true;
  812. return pLockedData;
  813. }
  814. inline void CVertexBuffer::Unlock( int numVerts )
  815. {
  816. if ( !m_bLocked )
  817. return;
  818. if ( !IsX360() && !m_pVB && !m_pSysmemBuffer )
  819. return;
  820. int nLockOffset = NextLockOffset();
  821. int nBufferSize = numVerts * m_VertexSize;
  822. RECORD_COMMAND( DX8_UNLOCK_VERTEX_BUFFER, 1 );
  823. RECORD_INT( m_UID );
  824. #if !defined( _X360 )
  825. if ( m_pSysmemBuffer != NULL )
  826. {
  827. }
  828. else
  829. {
  830. #if DX_TO_GL_ABSTRACTION
  831. Assert( numVerts <= (int)m_nLockCount );
  832. int unlockBytes = ( m_bDynamic ? nBufferSize : ( m_nLockCount * m_VertexSize ) );
  833. #else
  834. int unlockBytes = 0;
  835. #endif
  836. ReallyUnlock( unlockBytes );
  837. }
  838. m_Position = nLockOffset + nBufferSize;
  839. #else
  840. if ( m_bDynamic )
  841. {
  842. if ( numVerts > 0 )
  843. {
  844. DynamicBufferAllocation_t LockData;
  845. LockData.m_Fence = Dx9Device()->GetCurrentFence(); //This isn't the correct fence, but it's all we have access to for now and it'll provide marginal safety if something goes really wrong.
  846. LockData.m_iStartOffset = nLockOffset;
  847. LockData.m_iEndOffset = LockData.m_iStartOffset + nBufferSize;
  848. LockData.m_iZPassIdx = ( Dx9Device()->GetDeviceState() & D3DDEVICESTATE_ZPASS_BRACKET ) ? ShaderAPI()->GetConsoleZPassCounter() : 0;
  849. // Round dynamic locks to 4k boundaries for GPU cache reasons
  850. LockData.m_iEndOffset = ALIGN_VALUE( LockData.m_iEndOffset, 4096 );
  851. if( LockData.m_iEndOffset > m_iAllocationSize )
  852. LockData.m_iEndOffset = m_iAllocationSize;
  853. m_AllocationRing.AddToTail( LockData );
  854. m_Position = LockData.m_iEndOffset;
  855. void* pLockedData = m_pAllocatedMemory + LockData.m_iStartOffset;
  856. //Always re-use the same vertex buffer header based on the assumption that D3D copies it off in the draw calls.
  857. m_pVB = &m_D3DVertexBuffer;
  858. XGSetVertexBufferHeader( nBufferSize, 0, D3DPOOL_DEFAULT, 0, m_pVB );
  859. XGOffsetResourceAddress( m_pVB, pLockedData );
  860. // Invalidate the GPU caches for this memory.
  861. Dx9Device()->InvalidateGpuCache( pLockedData, nBufferSize, 0 );
  862. }
  863. }
  864. else
  865. {
  866. if ( !m_pVB )
  867. {
  868. m_pVB = &m_D3DVertexBuffer;
  869. XGSetVertexBufferHeader( m_nBufferSize, 0, D3DPOOL_DEFAULT, 0, m_pVB );
  870. XGOffsetResourceAddress( m_pVB, m_pAllocatedMemory );
  871. }
  872. m_Position = nLockOffset + nBufferSize;
  873. // Invalidate the GPU caches for this memory.
  874. Dx9Device()->InvalidateGpuCache( m_pAllocatedMemory, m_nBufferSize, 0 );
  875. }
  876. #endif
  877. m_bLocked = false;
  878. }
  879. inline void CVertexBuffer::HandleLateCreation( )
  880. {
  881. if ( !m_pSysmemBuffer )
  882. {
  883. return;
  884. }
  885. if( !m_pVB )
  886. {
  887. bool bPrior = g_VBAllocTracker->TrackMeshAllocations( "HandleLateCreation" );
  888. Create( Dx9Device() );
  889. if ( !bPrior )
  890. {
  891. g_VBAllocTracker->TrackMeshAllocations( NULL );
  892. }
  893. }
  894. void* pWritePtr = NULL;
  895. int dataToWriteBytes = m_bDynamic ? ( m_Position - m_nSysmemBufferStartBytes ) : ( m_nLockCount * m_VertexSize );
  896. DWORD dwFlags = D3DLOCK_NOSYSLOCK;
  897. if ( m_bDynamic )
  898. {
  899. dwFlags |= ( m_bLateCreateShouldDiscard ? D3DLOCK_DISCARD : D3DLOCK_NOOVERWRITE );
  900. }
  901. // Always clear this.
  902. m_bLateCreateShouldDiscard = false;
  903. // If we've wrapped might as well transfer the whole VB
  904. if (dataToWriteBytes < 1)
  905. {
  906. dataToWriteBytes = m_VertexCount * VertexSize();
  907. m_nSysmemBufferStartBytes = 0;
  908. }
  909. // Don't use the Lock function, it does a bunch of stuff we don't want.
  910. #if SHADERAPI_NO_D3DDeviceWrapper
  911. HRESULT hr = m_pVB->Lock( m_nSysmemBufferStartBytes,
  912. dataToWriteBytes,
  913. &pWritePtr,
  914. dwFlags);
  915. #else
  916. HRESULT hr = Dx9Device()->Lock( m_pVB, m_nSysmemBufferStartBytes,
  917. dataToWriteBytes,
  918. &pWritePtr,
  919. dwFlags );
  920. #endif
  921. // If this fails we're about to crash. Consider skipping the update and leaving
  922. // m_pSysmemBuffer around to try again later. (For example in case of device loss)
  923. Assert( SUCCEEDED( hr ) ); hr;
  924. memcpy( pWritePtr, m_pSysmemBuffer + m_nSysmemBufferStartBytes, dataToWriteBytes );
  925. ReallyUnlock( dataToWriteBytes );
  926. free( m_pSysmemBuffer );
  927. m_pSysmemBuffer = NULL;
  928. }
  929. // Returns the allocated size
  930. inline int CVertexBuffer::AllocationSize() const
  931. {
  932. #ifdef _X360
  933. return m_iAllocationSize;
  934. #else
  935. return m_VertexCount * m_VertexSize;
  936. #endif
  937. }
  938. #endif // DYNAMICVB_H