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.

1098 lines
31 KiB

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