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.

1047 lines
28 KiB

  1. //===== Copyright (c) 1996-2005, Valve Corporation, All rights reserved. ======//
  2. //
  3. // Purpose:
  4. //
  5. //=============================================================================//
  6. #ifndef DYNAMICIB_H
  7. #define DYNAMICIB_H
  8. #ifdef _WIN32
  9. #pragma once
  10. #endif
  11. #include "locald3dtypes.h"
  12. #include "recording.h"
  13. #include "shaderapidx8_global.h"
  14. #include "shaderapidx8.h"
  15. #include "shaderapi/ishaderutil.h"
  16. #include "materialsystem/ivballoctracker.h"
  17. #include "tier1/memstack.h"
  18. /////////////////////////////
  19. // D. Sim Dietrich Jr.
  20. // [email protected]
  21. /////////////////////////////
  22. #ifdef _WIN32
  23. #pragma warning (disable:4189)
  24. #endif
  25. #include "locald3dtypes.h"
  26. #include "tier1/strtools.h"
  27. #include "tier1/utlqueue.h"
  28. #include "tier0/memdbgon.h"
  29. // Helper function to unbind an index buffer
  30. void Unbind( IDirect3DIndexBuffer9 *pIndexBuffer );
  31. #define X360_INDEX_BUFFER_SIZE_MULTIPLIER 2.0 //minimum of 1, only affects dynamic buffers
  32. //#define X360_BLOCK_ON_IB_FLUSH //uncomment to block until all data is consumed when a flush is requested. Otherwise we only block when absolutely necessary
  33. #ifndef _CERT
  34. #define SPEW_INDEX_BUFFER_STALLS //uncomment to allow buffer stall spewing.
  35. #endif
  36. class CIndexBuffer
  37. {
  38. public:
  39. CIndexBuffer( D3DDeviceWrapper *pD3D, int count, bool bSoftwareVertexProcessing, bool dynamic = false, MeshBuffersAllocationSettings_t *pSettings = 0 );
  40. #ifdef _GAMECONSOLE
  41. CIndexBuffer();
  42. void Init( D3DDeviceWrapper *pD3D, uint16 *pIndexMemory, int count );
  43. #endif
  44. ~CIndexBuffer();
  45. LPDIRECT3DINDEXBUFFER GetInterface() const
  46. {
  47. // If this buffer still exists, then Late Creation didn't happen. Best case: we'll render the wrong image. Worst case: Crash.
  48. Assert( !m_pSysmemBuffer );
  49. return m_pIB;
  50. }
  51. // Use at beginning of frame to force a flush of VB contents on first draw
  52. void FlushAtFrameStart() { m_bFlush = true; }
  53. // lock, unlock
  54. unsigned short *Lock( bool bReadOnly, int numIndices, int &startIndex, int startPosition = -1 );
  55. void Unlock( int numIndices );
  56. void HandleLateCreation( );
  57. // Index position
  58. int IndexPosition() const { return m_Position; }
  59. // Index size
  60. int IndexSize() const { return sizeof(unsigned short); }
  61. // Index count
  62. int IndexCount() const { return m_IndexCount; }
  63. // Do we have enough room without discarding?
  64. bool HasEnoughRoom( int numIndices ) const;
  65. int GetIndicesLeft() const;
  66. bool IsDynamic() const { return m_bDynamic; }
  67. bool IsExternal() const { return m_bExternalMemory; }
  68. // Block until there's a free portion of the buffer of this size, m_Position will be updated to point at where this section starts
  69. void BlockUntilUnused( int nAllocationSize );
  70. #ifdef CHECK_INDICES
  71. void UpdateShadowIndices( unsigned short *pData )
  72. {
  73. Assert( m_LockedStartIndex + m_LockedNumIndices <= m_NumIndices );
  74. memcpy( m_pShadowIndices + m_LockedStartIndex, pData, m_LockedNumIndices * IndexSize() );
  75. }
  76. unsigned short GetShadowIndex( int i )
  77. {
  78. Assert( i >= 0 && i < (int)m_NumIndices );
  79. return m_pShadowIndices[i];
  80. }
  81. #endif
  82. // UID
  83. unsigned int UID() const
  84. {
  85. #ifdef RECORDING
  86. return m_UID;
  87. #else
  88. return 0;
  89. #endif
  90. }
  91. void HandlePerFrameTextureStats( int frame )
  92. {
  93. #ifdef VPROF_ENABLED
  94. if ( m_Frame != frame && !m_bDynamic )
  95. {
  96. m_Frame = frame;
  97. VPROF_INCREMENT_GROUP_COUNTER( "TexGroup_frame_" TEXTURE_GROUP_STATIC_INDEX_BUFFER,
  98. COUNTER_GROUP_TEXTURE_PER_FRAME, IndexCount() * IndexSize() );
  99. }
  100. #endif
  101. }
  102. static int BufferCount()
  103. {
  104. #ifdef _DEBUG
  105. return s_BufferCount;
  106. #else
  107. return 0;
  108. #endif
  109. }
  110. inline int AllocationSize() const;
  111. inline int AllocationCount() const;
  112. // Marks a fence indicating when this buffer was used
  113. void MarkUsedInRendering()
  114. {
  115. #ifdef _X360
  116. if ( m_bDynamic && m_pIB )
  117. {
  118. Assert( m_AllocationRing.Count() > 0 );
  119. m_AllocationRing[m_AllocationRing.Tail()].m_Fence = Dx9Device()->GetCurrentFence();
  120. }
  121. #endif
  122. }
  123. private :
  124. void Create( D3DDeviceWrapper *pD3D );
  125. inline void ReallyUnlock( int unlockBytes )
  126. {
  127. #if DX_TO_GL_ABSTRACTION
  128. // Knowing how much data was actually written is critical for performance under OpenGL.
  129. #if SHADERAPI_NO_D3DDeviceWrapper
  130. m_pIB->UnlockActualSize( unlockBytes );
  131. #else
  132. Dx9Device()->UnlockActualSize( m_pIB, unlockBytes );
  133. #endif
  134. #else
  135. unlockBytes; // Unused here
  136. #if SHADERAPI_NO_D3DDeviceWrapper
  137. m_pIB->Unlock();
  138. #else
  139. Dx9Device()->Unlock( m_pIB );
  140. #endif
  141. #endif
  142. }
  143. enum LOCK_FLAGS
  144. {
  145. LOCKFLAGS_FLUSH = D3DLOCK_NOSYSLOCK | D3DLOCK_DISCARD,
  146. #if !defined( _X360 )
  147. LOCKFLAGS_APPEND = D3DLOCK_NOSYSLOCK | D3DLOCK_NOOVERWRITE
  148. #else
  149. // X360BUG: forcing all locks to gpu flush, otherwise bizarre mesh corruption on decals
  150. // Currently iterating with microsoft 360 support to track source of gpu corruption
  151. LOCKFLAGS_APPEND = D3DLOCK_NOSYSLOCK
  152. #endif
  153. };
  154. LPDIRECT3DINDEXBUFFER m_pIB;
  155. #ifdef _X360
  156. struct DynamicBufferAllocation_t
  157. {
  158. DWORD m_Fence; //track whether this memory is safe to use again.
  159. int m_iStartOffset;
  160. int m_iEndOffset;
  161. unsigned int m_iZPassIdx; // The zpass during which this allocation was made
  162. };
  163. int m_iNextBlockingPosition; // m_iNextBlockingPosition >= m_Position where another allocation is still in use.
  164. unsigned char *m_pAllocatedMemory;
  165. int m_iAllocationCount; //The total number of indices the buffer we allocated can hold. Usually greater than the number of indices asked for
  166. IDirect3DIndexBuffer9 m_D3DIndexBuffer; //Only need one shared D3D header for our usage patterns.
  167. CUtlLinkedList<DynamicBufferAllocation_t> m_AllocationRing; //tracks what chunks of our memory are potentially still in use by D3D
  168. #endif
  169. int m_IndexCount;
  170. int m_Position;
  171. byte *m_pSysmemBuffer;
  172. int m_nSysmemBufferStartBytes;
  173. unsigned char m_bLocked : 1;
  174. unsigned char m_bFlush : 1;
  175. unsigned char m_bDynamic : 1;
  176. unsigned char m_bExternalMemory : 1;
  177. unsigned char m_bSoftwareVertexProcessing : 1;
  178. unsigned char m_bLateCreateShouldDiscard : 1;
  179. #ifdef VPROF_ENABLED
  180. int m_Frame;
  181. #endif
  182. #ifdef _DEBUG
  183. static int s_BufferCount;
  184. #endif
  185. #ifdef RECORDING
  186. unsigned int m_UID;
  187. #endif
  188. #if SHADERAPI_NO_D3DDeviceWrapper
  189. #else
  190. LockedBufferContext m_LockData;
  191. #endif
  192. protected:
  193. #ifdef CHECK_INDICES
  194. unsigned short *m_pShadowIndices;
  195. unsigned int m_NumIndices;
  196. #endif
  197. unsigned int m_LockedStartIndex;
  198. unsigned int m_LockedNumIndices;
  199. };
  200. #if defined( _X360 )
  201. #include "UtlMap.h"
  202. MEMALLOC_DEFINE_EXTERNAL_TRACKING( XMem_CIndexBuffer );
  203. #endif
  204. //-----------------------------------------------------------------------------
  205. // constructor, destructor
  206. //-----------------------------------------------------------------------------
  207. inline CIndexBuffer::CIndexBuffer( D3DDeviceWrapper *pD3D, int count,
  208. bool bSoftwareVertexProcessing, bool dynamic, MeshBuffersAllocationSettings_t *pSettings ) :
  209. m_pIB(0),
  210. m_Position(0),
  211. m_bFlush(true),
  212. m_bLocked(false),
  213. m_bExternalMemory(false),
  214. m_bDynamic(dynamic),
  215. m_bSoftwareVertexProcessing( bSoftwareVertexProcessing ),
  216. m_bLateCreateShouldDiscard( false )
  217. #ifdef _X360
  218. ,m_pAllocatedMemory(NULL)
  219. ,m_iNextBlockingPosition(0)
  220. ,m_iAllocationCount(0)
  221. #endif
  222. #ifdef VPROF_ENABLED
  223. ,m_Frame( -1 )
  224. #endif
  225. {
  226. // For write-combining, ensure we always have locked memory aligned to 4-byte boundaries
  227. count = ALIGN_VALUE( count, 2 );
  228. m_IndexCount = count;
  229. MEM_ALLOC_CREDIT_( m_bDynamic ? ( "D3D: " TEXTURE_GROUP_DYNAMIC_INDEX_BUFFER ) : ( "D3D: " TEXTURE_GROUP_STATIC_INDEX_BUFFER ) );
  230. #ifdef CHECK_INDICES
  231. m_pShadowIndices = NULL;
  232. #endif
  233. #ifdef RECORDING
  234. // assign a UID
  235. static unsigned int uid = 0;
  236. m_UID = uid++;
  237. #endif
  238. #ifdef _DEBUG
  239. ++s_BufferCount;
  240. #endif
  241. #ifdef CHECK_INDICES
  242. m_pShadowIndices = new unsigned short[ m_IndexCount ];
  243. m_NumIndices = m_IndexCount;
  244. #endif
  245. #if !defined(_X360)
  246. if ( !g_pShaderUtil->IsRenderThreadSafe() )
  247. {
  248. m_pSysmemBuffer = ( byte * )malloc( count * IndexSize() );
  249. m_nSysmemBufferStartBytes = 0;
  250. }
  251. else
  252. {
  253. m_pSysmemBuffer = NULL;
  254. Create( pD3D );
  255. }
  256. #else
  257. // _X360
  258. int nBufferSize = (count * IndexSize());
  259. if ( m_bDynamic )
  260. {
  261. m_iAllocationCount = count * X360_INDEX_BUFFER_SIZE_MULTIPLIER;
  262. Assert( m_iAllocationCount >= count );
  263. m_iAllocationCount = ALIGN_VALUE( m_iAllocationCount, 2 );
  264. m_pAllocatedMemory = (unsigned char*)XPhysicalAlloc( m_iAllocationCount * IndexSize(), MAXULONG_PTR, 0, PAGE_READWRITE | MEM_LARGE_PAGES | PAGE_WRITECOMBINE );
  265. }
  266. else
  267. {
  268. // Fall back to allocating a standalone IB
  269. // NOTE: write-combining (PAGE_WRITECOMBINE) is deliberately not used, since it slows down CPU access to the data (decals+defragmentation)
  270. m_iAllocationCount = count;
  271. m_pAllocatedMemory = (unsigned char*)XPhysicalAlloc( nBufferSize, MAXULONG_PTR, 0, PAGE_READWRITE );
  272. }
  273. if ( m_pAllocatedMemory )
  274. {
  275. MemAlloc_RegisterExternalAllocation( XMem_CIndexBuffer, m_pAllocatedMemory, XPhysicalSize( m_pAllocatedMemory ) );
  276. }
  277. m_iNextBlockingPosition = m_iAllocationCount;
  278. #endif // _X360
  279. #ifdef VPROF_ENABLED
  280. if ( !m_bDynamic )
  281. {
  282. VPROF_INCREMENT_GROUP_COUNTER( "TexGroup_global_" TEXTURE_GROUP_STATIC_INDEX_BUFFER,
  283. COUNTER_GROUP_TEXTURE_GLOBAL, IndexCount() * IndexSize() );
  284. }
  285. else if ( IsX360() )
  286. {
  287. VPROF_INCREMENT_GROUP_COUNTER( "TexGroup_global_" TEXTURE_GROUP_DYNAMIC_INDEX_BUFFER,
  288. COUNTER_GROUP_TEXTURE_GLOBAL, IndexCount() * IndexSize() );
  289. }
  290. #endif
  291. }
  292. void CIndexBuffer::Create( D3DDeviceWrapper *pD3D )
  293. {
  294. D3DINDEXBUFFER_DESC desc;
  295. memset( &desc, 0x00, sizeof( desc ) );
  296. desc.Format = D3DFMT_INDEX16;
  297. desc.Size = sizeof(unsigned short) * m_IndexCount;
  298. desc.Type = D3DRTYPE_INDEXBUFFER;
  299. desc.Pool = D3DPOOL_DEFAULT;
  300. desc.Usage = D3DUSAGE_WRITEONLY;
  301. if ( m_bDynamic )
  302. {
  303. desc.Usage |= D3DUSAGE_DYNAMIC;
  304. }
  305. if ( m_bSoftwareVertexProcessing )
  306. {
  307. desc.Usage |= D3DUSAGE_SOFTWAREPROCESSING;
  308. }
  309. RECORD_COMMAND( DX8_CREATE_INDEX_BUFFER, 6 );
  310. RECORD_INT( m_UID );
  311. RECORD_INT( m_IndexCount * IndexSize() );
  312. RECORD_INT( desc.Usage );
  313. RECORD_INT( desc.Format );
  314. RECORD_INT( desc.Pool );
  315. RECORD_INT( m_bDynamic );
  316. #if !defined( _X360 )
  317. HRESULT hr = pD3D->CreateIndexBuffer(
  318. m_IndexCount * IndexSize(),
  319. desc.Usage,
  320. desc.Format,
  321. desc.Pool,
  322. &m_pIB,
  323. NULL );
  324. if ( hr != D3D_OK )
  325. {
  326. Warning( "CreateIndexBuffer failed!\n" );
  327. }
  328. if ( ( hr == D3DERR_OUTOFVIDEOMEMORY ) || ( hr == E_OUTOFMEMORY ) )
  329. {
  330. // Don't have the memory for this. Try flushing all managed resources
  331. // out of vid mem and try again.
  332. // FIXME: need to record this
  333. pD3D->EvictManagedResources();
  334. hr = pD3D->CreateIndexBuffer( m_IndexCount * IndexSize(),
  335. desc.Usage, desc.Format, desc.Pool, &m_pIB, NULL );
  336. }
  337. Assert( m_pIB );
  338. Assert( hr == D3D_OK );
  339. #ifdef MEASURE_DRIVER_ALLOCATIONS
  340. int nMemUsed = 1024;
  341. VPROF_INCREMENT_GROUP_COUNTER( "ib count", COUNTER_GROUP_NO_RESET, 1 );
  342. VPROF_INCREMENT_GROUP_COUNTER( "ib driver mem", COUNTER_GROUP_NO_RESET, nMemUsed );
  343. VPROF_INCREMENT_GROUP_COUNTER( "total driver mem", COUNTER_GROUP_NO_RESET, nMemUsed );
  344. #endif
  345. #if defined( _DEBUG )
  346. if ( IsPC() && m_pIB && !m_pSysmemBuffer )
  347. {
  348. D3DINDEXBUFFER_DESC aDesc;
  349. m_pIB->GetDesc( &aDesc );
  350. Assert( memcmp( &aDesc, &desc, sizeof( desc ) ) == 0 );
  351. }
  352. #endif
  353. }
  354. #ifdef _GAMECONSOLE
  355. void *AllocateTempBuffer( size_t nSizeInBytes );
  356. inline CIndexBuffer::CIndexBuffer() :
  357. m_pIB(0),
  358. m_Position(0),
  359. m_bFlush(false),
  360. m_bLocked(false),
  361. m_bExternalMemory( true ),
  362. m_bDynamic( false )
  363. #ifdef VPROF_ENABLED
  364. ,m_Frame( -1 )
  365. #endif
  366. {
  367. m_IndexCount = 0;
  368. #ifdef CHECK_INDICES
  369. m_pShadowIndices = NULL;
  370. #endif
  371. #ifdef _X360
  372. m_iAllocationCount = 0;
  373. m_pAllocatedMemory = NULL;
  374. m_iNextBlockingPosition = 0;
  375. #endif
  376. }
  377. #include "tier0/memdbgoff.h"
  378. inline IDirect3DIndexBuffer9 *CreateExternalDynamicIB( uint16 *pIndexMemory, int count )
  379. {
  380. int nBufferSize = count * sizeof(uint16);
  381. IDirect3DIndexBuffer9 *pIB = new( AllocateTempBuffer( sizeof( IDirect3DIndexBuffer9 ) ) ) IDirect3DIndexBuffer9;
  382. #ifdef _X360
  383. XGSetIndexBufferHeader( nBufferSize, 0, D3DFMT_INDEX16, 0, 0, pIB );
  384. XGOffsetResourceAddress( pIB, pIndexMemory );
  385. #elif defined( _PS3 )
  386. pIB->m_idxDesc.Format = D3DFMT_INDEX16;
  387. pIB->m_idxDesc.Type = D3DRTYPE_INDEXBUFFER;
  388. pIB->m_idxDesc.Usage = D3DUSAGE_WRITEONLY | D3DUSAGE_DYNAMIC;
  389. pIB->m_idxDesc.Pool = D3DPOOL_DEFAULT;
  390. pIB->m_idxDesc.Size = nBufferSize;
  391. pIB->m_pBuffer = new( AllocateTempBuffer( sizeof( CPs3gcmBuffer ) ) ) CPs3gcmBuffer;
  392. pIB->m_pBuffer->m_lmBlock.AttachToExternalMemory( kAllocPs3GcmIndexBufferDynamic,
  393. ( uintp )pIndexMemory - ( uintp )g_ps3gcmGlobalState.m_pLocalBaseAddress, nBufferSize );
  394. #endif
  395. return pIB;
  396. }
  397. inline void CIndexBuffer::Init( D3DDeviceWrapper *pD3D, uint16 *pIndexMemory, int count )
  398. {
  399. m_IndexCount = count;
  400. m_Position = count;
  401. #ifdef _X360
  402. m_iAllocationCount = count;
  403. m_pAllocatedMemory = (uint8*)pIndexMemory;
  404. m_iNextBlockingPosition = m_iAllocationCount;
  405. #endif
  406. m_pIB = CreateExternalDynamicIB( pIndexMemory, count );
  407. }
  408. #include "tier0/memdbgon.h"
  409. #endif // _GAMECONSOLE
  410. inline CIndexBuffer::~CIndexBuffer()
  411. {
  412. #ifdef _DEBUG
  413. if ( !m_bExternalMemory )
  414. {
  415. --s_BufferCount;
  416. }
  417. #endif
  418. Unlock(0);
  419. #ifdef CHECK_INDICES
  420. if ( m_pShadowIndices )
  421. {
  422. delete [] m_pShadowIndices;
  423. m_pShadowIndices = NULL;
  424. }
  425. #endif
  426. if ( m_pSysmemBuffer )
  427. {
  428. free( m_pSysmemBuffer );
  429. m_pSysmemBuffer = NULL;
  430. }
  431. #ifdef MEASURE_DRIVER_ALLOCATIONS
  432. if ( !m_bExternalMemory )
  433. {
  434. int nMemUsed = 1024;
  435. VPROF_INCREMENT_GROUP_COUNTER( "ib count", COUNTER_GROUP_NO_RESET, -1 );
  436. VPROF_INCREMENT_GROUP_COUNTER( "ib driver mem", COUNTER_GROUP_NO_RESET, -nMemUsed );
  437. VPROF_INCREMENT_GROUP_COUNTER( "total driver mem", COUNTER_GROUP_NO_RESET, -nMemUsed );
  438. }
  439. #endif
  440. #if !defined( _X360 )
  441. if ( m_pIB && !m_bExternalMemory )
  442. {
  443. RECORD_COMMAND( DX8_DESTROY_INDEX_BUFFER, 1 );
  444. RECORD_INT( m_UID );
  445. #if SHADERAPI_NO_D3DDeviceWrapper
  446. m_pIB->Release();
  447. #else
  448. Dx9Device()->Release( m_pIB );
  449. #endif
  450. }
  451. #else
  452. if ( m_pIB && m_pIB->IsSet( Dx9Device() ) )
  453. {
  454. Unbind( m_pIB );
  455. }
  456. if ( m_pAllocatedMemory && !m_bExternalMemory )
  457. {
  458. MemAlloc_RegisterExternalDeallocation( XMem_CIndexBuffer, m_pAllocatedMemory, XPhysicalSize( m_pAllocatedMemory ) );
  459. XPhysicalFree( m_pAllocatedMemory );
  460. }
  461. m_pAllocatedMemory = NULL;
  462. m_pIB = NULL;
  463. #endif
  464. #ifdef VPROF_ENABLED
  465. if ( !m_bExternalMemory )
  466. {
  467. if ( !m_bDynamic )
  468. {
  469. VPROF_INCREMENT_GROUP_COUNTER( "TexGroup_global_" TEXTURE_GROUP_STATIC_INDEX_BUFFER,
  470. COUNTER_GROUP_TEXTURE_GLOBAL, - IndexCount() * IndexSize() );
  471. }
  472. else if ( IsX360() )
  473. {
  474. VPROF_INCREMENT_GROUP_COUNTER( "TexGroup_global_" TEXTURE_GROUP_DYNAMIC_INDEX_BUFFER,
  475. COUNTER_GROUP_TEXTURE_GLOBAL, - IndexCount() * IndexSize() );
  476. }
  477. }
  478. #endif
  479. }
  480. //-----------------------------------------------------------------------------
  481. // Do we have enough room without discarding?
  482. //-----------------------------------------------------------------------------
  483. inline bool CIndexBuffer::HasEnoughRoom( int numIndices ) const
  484. {
  485. #if !defined( _X360 )
  486. return ( numIndices + m_Position ) <= m_IndexCount;
  487. #else
  488. return numIndices <= m_IndexCount; //the ring buffer will free room as needed
  489. #endif
  490. }
  491. inline int CIndexBuffer::GetIndicesLeft() const
  492. {
  493. #if !defined( _X360 )
  494. return m_IndexCount - m_Position ;
  495. #else
  496. return m_IndexCount; //the ring buffer will free room as needed
  497. #endif
  498. }
  499. //-----------------------------------------------------------------------------
  500. // Block until this part of the index buffer is free
  501. //-----------------------------------------------------------------------------
  502. inline void CIndexBuffer::BlockUntilUnused( int nAllocationSize )
  503. {
  504. Assert( nAllocationSize <= m_IndexCount );
  505. #ifdef _X360
  506. Assert( (m_AllocationRing.Count() != 0) || ((m_Position == 0) && (m_iNextBlockingPosition == m_iAllocationCount)) );
  507. if ( (m_iNextBlockingPosition - m_Position) >= nAllocationSize )
  508. return;
  509. Assert( (m_AllocationRing[m_AllocationRing.Head()].m_iStartOffset == 0) || ((m_iNextBlockingPosition == m_AllocationRing[m_AllocationRing.Head()].m_iStartOffset) && (m_Position <= m_iNextBlockingPosition)) );
  510. int iMinBlockPosition = m_Position + nAllocationSize;
  511. if( iMinBlockPosition > m_iAllocationCount )
  512. {
  513. //Allocation requires us to wrap
  514. iMinBlockPosition = nAllocationSize;
  515. m_Position = 0;
  516. //modify the last allocation so that it uses up the whole tail end of the buffer. Makes other code simpler
  517. Assert( m_AllocationRing.Count() != 0 );
  518. m_AllocationRing[m_AllocationRing.Tail()].m_iEndOffset = m_iAllocationCount;
  519. //treat all allocations between the current position and the tail end of the ring as freed since they will be before we unblock
  520. while( m_AllocationRing.Count() )
  521. {
  522. unsigned int head = m_AllocationRing.Head();
  523. if( m_AllocationRing[head].m_iStartOffset == 0 )
  524. break;
  525. m_AllocationRing.Remove( head );
  526. }
  527. }
  528. //now we go through the allocations until we find the last fence we care about. Treat everything up until that fence as freed.
  529. DWORD FinalFence = 0;
  530. unsigned int iFinalAllocationZPassIdx = 0;
  531. while( m_AllocationRing.Count() )
  532. {
  533. unsigned int head = m_AllocationRing.Head();
  534. if( m_AllocationRing[head].m_iEndOffset >= iMinBlockPosition )
  535. {
  536. //When this frees, we'll finally have enough space for the allocation
  537. FinalFence = m_AllocationRing[head].m_Fence;
  538. iFinalAllocationZPassIdx = m_AllocationRing[head].m_iZPassIdx;
  539. m_iNextBlockingPosition = m_AllocationRing[head].m_iEndOffset;
  540. m_AllocationRing.Remove( head );
  541. break;
  542. }
  543. m_AllocationRing.Remove( head );
  544. }
  545. Assert( FinalFence != 0 );
  546. if( Dx9Device()->IsFencePending( FinalFence ) )
  547. {
  548. #ifdef SPEW_INDEX_BUFFER_STALLS
  549. float st = Plat_FloatTime();
  550. #endif
  551. if ( ( Dx9Device()->GetDeviceState() & D3DDEVICESTATE_ZPASS_BRACKET ) &&
  552. ( iFinalAllocationZPassIdx == ShaderAPI()->GetConsoleZPassCounter() ) )
  553. {
  554. // We're about to overrun our IB ringbuffer in a single Z prepass. To avoid rendering corruption, close out the
  555. // Z prepass and continue. This will reduce early-Z rejection efficiency and could cause a momentary framerate drop,
  556. // but it's better than rendering corruption.
  557. Warning( "Dynamic IB ring buffer overrun in Z Prepass. Tell Thorsten.\n" );
  558. ShaderAPI()->EndConsoleZPass();
  559. }
  560. Dx9Device()->BlockOnFence( FinalFence );
  561. #ifdef SPEW_INDEX_BUFFER_STALLS
  562. float dt = Plat_FloatTime() - st;
  563. Warning( "Blocked locking dynamic index buffer for %f ms!\n", 1000.0 * dt );
  564. #endif
  565. }
  566. #endif
  567. }
  568. //-----------------------------------------------------------------------------
  569. // lock, unlock
  570. //-----------------------------------------------------------------------------
  571. inline unsigned short* CIndexBuffer::Lock( bool bReadOnly, int numIndices, int& startIndex, int startPosition )
  572. {
  573. Assert( !m_bLocked );
  574. #if defined( _X360 )
  575. if ( m_pIB && m_pIB->IsSet( Dx9Device() ) )
  576. {
  577. Unbind( m_pIB );
  578. }
  579. #endif
  580. unsigned short* pLockedData = NULL;
  581. // For write-combining, ensure we always have locked memory aligned to 4-byte boundaries
  582. if( m_bDynamic )
  583. numIndices = ALIGN_VALUE( numIndices, 2 );
  584. // Ensure there is enough space in the IB for this data
  585. if ( numIndices > m_IndexCount )
  586. {
  587. Error( "too many indices for index buffer. . tell a programmer (%d>%d)\n", ( int )numIndices, ( int )m_IndexCount );
  588. Assert( false );
  589. return 0;
  590. }
  591. if ( !IsX360() && !m_pIB && !m_pSysmemBuffer )
  592. return 0;
  593. DWORD dwFlags;
  594. if ( m_bDynamic )
  595. {
  596. // startPosition now can be != -1, when calling in here with a static (staging) buffer.
  597. #if !defined( _X360 )
  598. dwFlags = LOCKFLAGS_APPEND;
  599. // If either user forced us to flush,
  600. // or there is not enough space for the vertex data,
  601. // then flush the buffer contents
  602. // xbox must not append at position 0 because nooverwrite cannot be guaranteed
  603. if ( !m_Position || m_bFlush || !HasEnoughRoom(numIndices) )
  604. {
  605. if ( m_pSysmemBuffer || !g_pShaderUtil->IsRenderThreadSafe() )
  606. m_bLateCreateShouldDiscard = true;
  607. m_bFlush = false;
  608. m_Position = 0;
  609. dwFlags = LOCKFLAGS_FLUSH;
  610. }
  611. #else
  612. if ( m_bFlush )
  613. {
  614. # if ( defined( X360_BLOCK_ON_IB_FLUSH ) )
  615. {
  616. if( m_AllocationRing.Count() )
  617. {
  618. DWORD FinalFence = m_AllocationRing[m_AllocationRing.Tail()].m_Fence;
  619. m_AllocationRing.RemoveAll();
  620. m_Position = 0;
  621. m_iNextBlockingPosition = m_iAllocationCount;
  622. # if ( defined( SPEW_VERTEX_BUFFER_STALLS ) )
  623. if( Dx9Device()->IsFencePending( FinalFence ) )
  624. {
  625. float st = Plat_FloatTime();
  626. # endif
  627. Dx9Device()->BlockOnFence( FinalFence );
  628. # if ( defined ( SPEW_VERTEX_BUFFER_STALLS ) )
  629. float dt = Plat_FloatTime() - st;
  630. Warning( "Blocked FLUSHING dynamic index buffer for %f ms!\n", 1000.0 * dt );
  631. }
  632. # endif
  633. }
  634. }
  635. # endif
  636. m_bFlush = false;
  637. }
  638. #endif
  639. }
  640. else
  641. {
  642. dwFlags = D3DLOCK_NOSYSLOCK;
  643. }
  644. if ( bReadOnly )
  645. {
  646. dwFlags |= D3DLOCK_READONLY;
  647. }
  648. int position = m_Position;
  649. if( startPosition >= 0 )
  650. {
  651. position = startPosition;
  652. }
  653. RECORD_COMMAND( DX8_LOCK_INDEX_BUFFER, 4 );
  654. RECORD_INT( m_UID );
  655. RECORD_INT( position * IndexSize() );
  656. RECORD_INT( numIndices * IndexSize() );
  657. RECORD_INT( dwFlags );
  658. m_LockedStartIndex = position;
  659. m_LockedNumIndices = numIndices;
  660. HRESULT hr = D3D_OK;
  661. #if !defined( _X360 )
  662. // 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
  663. // the non-current thread.
  664. if ( !m_pSysmemBuffer && !g_pShaderUtil->IsRenderThreadSafe() )
  665. {
  666. m_pSysmemBuffer = ( byte * )malloc( m_IndexCount * IndexSize() );
  667. m_nSysmemBufferStartBytes = position * IndexSize();
  668. }
  669. if ( m_pSysmemBuffer != NULL )
  670. {
  671. // Ensure that we're never moving backwards in a buffer--this code would need to be rewritten if so.
  672. // We theorize this can happen if you hit the end of a buffer and then wrap before drawing--but
  673. // this would probably break in other places as well.
  674. // if ( position * IndexSize() < m_nSysmemBufferStartBytes )
  675. // {
  676. // while(1) printf("wrapped\n");
  677. // }
  678. pLockedData = ( unsigned short * )( m_pSysmemBuffer + ( position * IndexSize() ) );
  679. }
  680. else
  681. {
  682. #if SHADERAPI_NO_D3DDeviceWrapper
  683. hr = m_pIB->Lock(
  684. #else
  685. hr = Dx9Device()->Lock( m_pIB,
  686. #endif
  687. position * IndexSize(), numIndices * IndexSize(),
  688. reinterpret_cast< void** >( &pLockedData ), dwFlags );
  689. }
  690. #else
  691. if ( m_bDynamic )
  692. {
  693. // Block until earlier parts of the buffer are free
  694. BlockUntilUnused( numIndices );
  695. position = m_Position;
  696. m_pIB = NULL;
  697. Assert( (m_Position + numIndices) <= m_iAllocationCount );
  698. }
  699. else
  700. {
  701. //static, block until last lock finished?
  702. m_Position = position;
  703. }
  704. pLockedData = (unsigned short *)(m_pAllocatedMemory + (position * IndexSize()));
  705. #endif
  706. switch ( hr )
  707. {
  708. case D3DERR_INVALIDCALL:
  709. Msg( "D3DERR_INVALIDCALL - Index Buffer Lock Failed in %s on line %d(offset %d, size %d, flags 0x%x)\n", V_UnqualifiedFileName(__FILE__), __LINE__, position * IndexSize(), numIndices * IndexSize(), dwFlags );
  710. break;
  711. case D3DERR_DRIVERINTERNALERROR:
  712. Msg( "D3DERR_DRIVERINTERNALERROR - Index Buffer Lock Failed in %s on line %d (offset %d, size %d, flags 0x%x)\n", V_UnqualifiedFileName(__FILE__), __LINE__, position * IndexSize(), numIndices * IndexSize(), dwFlags );
  713. break;
  714. case D3DERR_OUTOFVIDEOMEMORY:
  715. Msg( "D3DERR_OUTOFVIDEOMEMORY - Index Buffer Lock Failed in %s on line %d (offset %d, size %d, flags 0x%x)\n", V_UnqualifiedFileName(__FILE__), __LINE__, position * IndexSize(), numIndices * IndexSize(), dwFlags );
  716. break;
  717. }
  718. Assert( pLockedData != NULL );
  719. if ( !IsX360() )
  720. {
  721. startIndex = position;
  722. }
  723. else
  724. {
  725. startIndex = 0;
  726. }
  727. Assert( m_bLocked == false );
  728. m_bLocked = true;
  729. return pLockedData;
  730. }
  731. inline void CIndexBuffer::Unlock( int numIndices )
  732. {
  733. #if defined( _X360 )
  734. Assert( (m_Position + numIndices) <= m_iAllocationCount );
  735. #else
  736. Assert( (m_Position + numIndices) <= m_IndexCount );
  737. #endif
  738. if ( !m_bLocked )
  739. return;
  740. // For write-combining, ensure we always have locked memory aligned to 4-byte boundaries
  741. if( m_bDynamic )
  742. numIndices = ALIGN_VALUE( numIndices, 2 );
  743. if ( !IsX360() && !m_pIB && !m_pSysmemBuffer )
  744. return;
  745. RECORD_COMMAND( DX8_UNLOCK_INDEX_BUFFER, 1 );
  746. RECORD_INT( m_UID );
  747. #if !defined( _X360 )
  748. if ( m_pSysmemBuffer )
  749. {
  750. }
  751. else
  752. {
  753. #if DX_TO_GL_ABSTRACTION
  754. // Knowing how much data was actually written is critical for performance under OpenGL.
  755. Assert( numIndices <= (int)m_LockedNumIndices );
  756. int unlockBytes = ( m_bDynamic ? numIndices : m_LockedNumIndices ) * IndexSize();
  757. #else
  758. int unlockBytes = 0;
  759. #endif
  760. ReallyUnlock( unlockBytes );
  761. }
  762. #endif
  763. #else
  764. if ( m_bDynamic )
  765. {
  766. Assert( (m_Position == 0) || (m_AllocationRing[m_AllocationRing.Tail()].m_iEndOffset == m_Position) );
  767. DynamicBufferAllocation_t LockData;
  768. 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.
  769. LockData.m_iStartOffset = m_Position;
  770. LockData.m_iEndOffset = LockData.m_iStartOffset + numIndices;
  771. LockData.m_iZPassIdx = ( Dx9Device()->GetDeviceState() & D3DDEVICESTATE_ZPASS_BRACKET ) ? ShaderAPI()->GetConsoleZPassCounter() : 0;
  772. Assert( (LockData.m_iStartOffset == 0) || (LockData.m_iStartOffset == m_AllocationRing[m_AllocationRing.Tail()].m_iEndOffset) );
  773. m_AllocationRing.AddToTail( LockData );
  774. void* pLockedData = m_pAllocatedMemory + (LockData.m_iStartOffset * IndexSize());
  775. //Always re-use the same index buffer header based on the assumption that D3D copies it off in the draw calls.
  776. m_pIB = &m_D3DIndexBuffer;
  777. XGSetIndexBufferHeader( numIndices * IndexSize(), 0, D3DFMT_INDEX16, 0, 0, m_pIB );
  778. XGOffsetResourceAddress( m_pIB, pLockedData );
  779. // Invalidate the GPU caches for this memory.
  780. // FIXME: Should dynamic allocations be 4k aligned?
  781. Dx9Device()->InvalidateGpuCache( pLockedData, numIndices * IndexSize(), 0 );
  782. }
  783. else
  784. {
  785. if ( !m_pIB )
  786. {
  787. int nBufferSize = m_IndexCount * IndexSize();
  788. XGSetIndexBufferHeader( nBufferSize, 0, D3DFMT_INDEX16, 0, 0, &m_D3DIndexBuffer );
  789. XGOffsetResourceAddress( &m_D3DIndexBuffer, m_pAllocatedMemory );
  790. m_pIB = &m_D3DIndexBuffer;
  791. }
  792. // Invalidate the GPU caches for this memory.
  793. Dx9Device()->InvalidateGpuCache( m_pAllocatedMemory, m_IndexCount * IndexSize(), 0 );
  794. }
  795. #endif
  796. m_Position += numIndices;
  797. m_bLocked = false;
  798. #ifdef CHECK_INDICES
  799. m_LockedStartIndex = 0;
  800. m_LockedNumIndices = 0;
  801. #endif
  802. }
  803. inline void CIndexBuffer::HandleLateCreation( )
  804. {
  805. if ( !m_pSysmemBuffer )
  806. {
  807. return;
  808. }
  809. if( !m_pIB )
  810. {
  811. bool bPrior = g_VBAllocTracker->TrackMeshAllocations( "HandleLateCreation" );
  812. Create( Dx9Device() );
  813. if ( !bPrior )
  814. {
  815. g_VBAllocTracker->TrackMeshAllocations( NULL );
  816. }
  817. }
  818. void* pWritePtr = NULL;
  819. int dataToWriteBytes = ( m_Position * IndexSize() ) - m_nSysmemBufferStartBytes;
  820. DWORD dwFlags = D3DLOCK_NOSYSLOCK;
  821. if ( m_bDynamic )
  822. dwFlags |= ( m_bLateCreateShouldDiscard ? D3DLOCK_DISCARD : D3DLOCK_NOOVERWRITE );
  823. // Always clear this.
  824. m_bLateCreateShouldDiscard = false;
  825. // Don't use the Lock function, it does a bunch of stuff we don't want.
  826. HRESULT hr = 0;
  827. // If we've wrapped might as well transfer the whole IB
  828. if (dataToWriteBytes < 1)
  829. {
  830. dataToWriteBytes = m_IndexCount * IndexSize();
  831. m_nSysmemBufferStartBytes = 0;
  832. }
  833. #if SHADERAPI_NO_D3DDeviceWrapper
  834. hr = m_pIB->Lock(
  835. #else
  836. hr = Dx9Device()->Lock( m_pIB,
  837. #endif
  838. m_nSysmemBufferStartBytes,
  839. dataToWriteBytes,
  840. &pWritePtr,
  841. dwFlags );
  842. // If this fails we're about to crash. Consider skipping the update and leaving
  843. // m_pSysmemBuffer around to try again later. (For example in case of device loss)
  844. Assert( SUCCEEDED( hr ) ); hr;
  845. memcpy( pWritePtr, m_pSysmemBuffer + m_nSysmemBufferStartBytes, dataToWriteBytes );
  846. ReallyUnlock( dataToWriteBytes );
  847. free( m_pSysmemBuffer );
  848. m_pSysmemBuffer = NULL;
  849. }
  850. // Returns the allocated size
  851. inline int CIndexBuffer::AllocationSize() const
  852. {
  853. #ifdef _X360
  854. return m_iAllocationCount * IndexSize();
  855. #else
  856. return m_IndexCount * IndexSize();
  857. #endif
  858. }
  859. inline int CIndexBuffer::AllocationCount() const
  860. {
  861. #ifdef _X360
  862. return m_iAllocationCount;
  863. #else
  864. return m_IndexCount;
  865. #endif
  866. }
  867. #ifdef _WIN32
  868. #pragma warning (default:4189)
  869. #endif
  870. #include "tier0/memdbgoff.h"
  871. #endif // DYNAMICIB_H