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.

210 lines
6.8 KiB

  1. //========== Copyright � Valve Corporation, All rights reserved. ========
  2. #include "tier0/memalloc.h"
  3. #include "ps3/ps3_gcm_config.h"
  4. #include "spudrawqueue.h"
  5. #include "ps3gcmstate.h"
  6. void SpuDrawQueue::Init( uint nBufferSize, uint32 * pSignal, FnFlushCallback_t fnFlushCallback, FnStallCallback_t fnStallCallback )
  7. {
  8. if( nBufferSize < 2 * DRAWQUEUE_LSRING_SIZE )
  9. {
  10. Warning("SpuDrawQueue requested size (%d bytes) is too small (must be at least %d), auto-adjusting\n", nBufferSize, 2 * DRAWQUEUE_LSRING_SIZE );
  11. nBufferSize = 2 * DRAWQUEUE_LSRING_SIZE;
  12. }
  13. m_pBuffer = ( uint32* ) g_ps3gcmGlobalState.IoSlackAlloc( 128, nBufferSize );
  14. m_pBufferEnd = AddBytes( m_pBuffer, nBufferSize & -16 );
  15. m_pPut = m_pGet = m_pBuffer;
  16. *pSignal = GetSignal();
  17. m_pSignal = pSignal;
  18. m_fnFlushCallback = fnFlushCallback;
  19. m_fnStallCallback = fnStallCallback;
  20. m_fnFlushCallbackStack = NULL;
  21. #ifdef _DEBUG
  22. m_nAllocBreakAddress = NULL;
  23. m_nAllocCount = m_nCollectCount = 0;
  24. m_nAllocBreak = m_nCollectBreak = 0;
  25. #endif
  26. m_nAllocWords = 0;
  27. m_pFlushWatermark = AddBytes( m_pBuffer, DRAWQUEUE_LSRING_SIZE );
  28. if( m_pFlushWatermark + 8 >= m_pBufferEnd )
  29. {
  30. Error( "SpuDrawQueue misconfiguration: allocated buffer of %d bytes, but LS watermark size is %d bytes. Increase the main memory buffer size to avoid PPU deadlocks\n", nBufferSize, DRAWQUEUE_LSRING_SIZE );
  31. }
  32. }
  33. void SpuDrawQueue::PushFlushCallback( FnFlushCallback_t fnNewCallback )
  34. {
  35. Assert( !m_fnFlushCallbackStack );
  36. m_fnFlushCallbackStack = m_fnFlushCallback;
  37. m_fnFlushCallback = fnNewCallback;
  38. }
  39. void SpuDrawQueue::PopFlushCallback()
  40. {
  41. Assert( m_fnFlushCallbackStack );
  42. m_fnFlushCallback = m_fnFlushCallbackStack;
  43. m_fnFlushCallbackStack = NULL;
  44. }
  45. void SpuDrawQueue::Shutdown()
  46. {
  47. g_ps3gcmGlobalState.IoSlackFree( m_pBuffer );
  48. }
  49. void SpuDrawQueue::UnallocToAlign()
  50. {
  51. m_pPut = ( uint32* )( uintp( m_pPut ) & -16 );
  52. }
  53. //////////////////////////////////////////////////////////////////////////
  54. // REENTRANT: m_fnFlushCallback can in turn call AllocWords with a small number of words
  55. //
  56. uint32 *SpuDrawQueue::AllocWords( uint nWords /*, uint nAlignMask, uint nAlignValue*/ )
  57. {
  58. #ifdef _DEBUG
  59. uint32 * pSavePut = m_pPut, *pSaveGet = m_pGet;(void)(pSavePut, pSaveGet);
  60. m_nAllocCount++;
  61. if( m_nAllocCount == m_nAllocBreak )
  62. DebuggerBreak();
  63. #endif
  64. Assert( nWords * sizeof( uint32 ) <= SPUDRAWQUEUE_NOPCOUNT_MASK );
  65. uint32 * pOldPut = m_pPut, * pAllocation = pOldPut;//( uint32* )( uintp( pOldPut ) + ( ( nAlignValue - uintp( pOldPut ) ) & nAlignMask ) );
  66. uint32 * pNewPut = pAllocation + nWords;
  67. bool bWrap = false;
  68. if( pNewPut > m_pBufferEnd ) // do we need to wrap?
  69. {
  70. //we have to wrap...
  71. if( m_pPut < m_pBufferEnd )
  72. *m_pPut = SPUDRAWQUEUE_NOPCOUNT_METHOD | ( m_pBufferEnd - m_pPut - 1 );
  73. pNewPut = m_pBuffer + nWords;
  74. bWrap = true;
  75. pAllocation = m_pBuffer;
  76. }
  77. // since this put may be the last, we need to make sure that even after alignment, put != get
  78. // so we wait for the space to free up for aligned put
  79. uint32 * pNewAlignedPut = ( uint32* )AlignValue( uintp( pNewPut ), DMA_ALIGNMENT );
  80. if( bWrap ? pOldPut <= m_pFlushWatermark || m_pFlushWatermark < pNewAlignedPut:
  81. pOldPut <= m_pFlushWatermark && m_pFlushWatermark < pNewAlignedPut )
  82. {
  83. // collects , aligns and submits commands to SPU
  84. m_fnFlushCallback( this );
  85. // m_pPut may have changed slightly for alignment or EndZPass(), so we need to reconsider wrapping and recompute all pointers
  86. pOldPut = m_pPut; pAllocation = pOldPut;
  87. pNewPut = pOldPut + nWords;
  88. bWrap = false;
  89. if( pNewPut > m_pBufferEnd ) // do we need to wrap?
  90. {
  91. //we have to wrap...
  92. if( m_pPut < m_pBufferEnd )
  93. *m_pPut = SPUDRAWQUEUE_NOPCOUNT_METHOD | ( m_pBufferEnd - m_pPut - 1 );
  94. pNewPut = m_pBuffer + nWords;
  95. bWrap = true;
  96. pAllocation = m_pBuffer;
  97. }
  98. // since this put may be the last, we need to make sure that even after alignment, put != get
  99. // so we wait for the space to free up for aligned put
  100. pNewAlignedPut = ( uint32* )AlignValue( uintp( pNewPut ), DMA_ALIGNMENT );
  101. }
  102. // we must not allow new put == get, because it will cause the whole ring to suddenly be marked as empty
  103. uint nSpins = 0;
  104. while( bWrap ? pOldPut < m_pGet || m_pGet <= pNewAlignedPut : pOldPut < m_pGet && m_pGet <= pNewAlignedPut )
  105. {
  106. if( nSpins++ > 2 )
  107. {
  108. m_fnStallCallback( this, m_pGet, nWords );
  109. }
  110. SetSignal( *m_pSignal );
  111. }
  112. Assert( pNewPut >= m_pBuffer && pNewPut <= m_pBufferEnd );
  113. Assert( pAllocation >= m_pBuffer && pAllocation <= m_pBufferEnd );
  114. Assert( pAllocation + nWords >= m_pBuffer && pAllocation + nWords <= m_pBufferEnd );
  115. m_pPut = pNewPut; // we don't need to use up the whole aligned buffer
  116. #ifdef _DEBUG
  117. if( pAllocation == m_nAllocBreakAddress )
  118. DebuggerBreak();
  119. #endif
  120. m_nAllocWords += nWords;
  121. return pAllocation;
  122. }
  123. // This is called within the Flush callback. May change m_pPut
  124. // returns the number of bytes written from UNaligned start to UNaligned end
  125. uint SpuDrawQueue::Collect( uint32 * pStartBatch, uint32 * pEndBatch, CDmaListConstructor & dmac )
  126. {
  127. #ifdef _DEBUG
  128. CDmaListConstructor saveDmac = dmac;(void)saveDmac;
  129. m_nCollectCount++;
  130. Assert( m_nCollectCount != m_nCollectBreak );
  131. #endif
  132. Assert( pStartBatch >= m_pBuffer && pStartBatch <= m_pBufferEnd && pEndBatch >= m_pBuffer && pEndBatch <= m_pBufferEnd );
  133. uint nSize = 0;
  134. if( pEndBatch != pStartBatch ) // or else it's an empty transaction, nothing to upload
  135. {
  136. // align the put pointer for DMA - always safe because SPUs can't be processing the remainder of 16-byte block
  137. // while we're writing into its beginning.
  138. // while( uintp( pEndBatch ) & ( DMA_ALIGNMENT - 1 ) )
  139. // {
  140. // *( pEndBatch++ ) = 0;
  141. // }
  142. if( pEndBatch > pStartBatch )
  143. {
  144. // it wraps
  145. dmac.AddInputDmaLargeUnalignedRegion( pStartBatch, pEndBatch );
  146. nSize += uintp( pEndBatch ) - uintp( pStartBatch );
  147. }
  148. else
  149. {
  150. if( pStartBatch != m_pBufferEnd )
  151. {
  152. dmac.AddInputDmaLargeUnalignedRegion( pStartBatch, m_pBufferEnd );
  153. nSize += uintp( m_pBufferEnd ) - uintp( pStartBatch );
  154. }
  155. dmac.AddInputDmaLargeUnalignedRegion( m_pBuffer, pEndBatch );
  156. nSize += uintp( pEndBatch ) - uintp( m_pBuffer );
  157. }
  158. }
  159. SetFlushWatermarkFrom( pEndBatch );
  160. return nSize;
  161. }
  162. void SpuDrawQueue::SetFlushWatermarkFrom( uint32 *pPut )
  163. {
  164. m_pFlushWatermark = ( uint32* )( ( uintp( pPut ) + DRAWQUEUE_LSRING_SIZE ) & -16 );
  165. while( m_pFlushWatermark >= m_pBufferEnd )
  166. {
  167. m_pFlushWatermark -= m_pBufferEnd - m_pBuffer;
  168. }
  169. }
  170. uint SpuDrawQueue::Length( uint32 * pBegin, uint32 * pEnd )const
  171. {
  172. Assert( IsValidCursor( pBegin ) && IsValidCursor( pEnd ) );
  173. if( pBegin < pEnd )
  174. return uintp( pEnd ) - uintp( pBegin );
  175. else
  176. return ( uintp( m_pBufferEnd ) - uintp( pBegin ) ) +
  177. ( uintp( pEnd ) - uintp( m_pBuffer ) );
  178. }