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.

308 lines
8.6 KiB

  1. //========== Copyright � Valve Corporation, All rights reserved. ========
  2. //
  3. // Producer-consumer FIFO ring buffers
  4. // THese are shared between SPU and PPU, and define some common misc functions
  5. //
  6. //
  7. #ifndef VJOBS_PCRING_HDR
  8. #define VJOBS_PCRING_HDR
  9. #include "ps3/ps3_gcm_config.h"
  10. #include "ps3/spu_job_shared.h"
  11. struct ALIGN16 SetLabelAlignedCommand_t
  12. {
  13. union CmdUnion_t
  14. {
  15. uint32 m_nCmd[4];
  16. vector unsigned int m_vuCmd;
  17. };
  18. CmdUnion_t m_cmd;
  19. // uint32 m_nMethodSetSemaphoreOffset;
  20. // uint32 m_nSemaphoreOffset;
  21. // uint32 m_nMethodSemaphoreRelease;
  22. // uint32 m_nSemaphoreValue;
  23. void SetWriteTextureLabel( uint nIndex, uint nValue )
  24. {
  25. uint offset = 0x10 * nIndex;
  26. #ifdef SPU
  27. m_cmd.m_vuCmd = ( vector unsigned int )
  28. {
  29. CELL_GCM_METHOD(CELL_GCM_NV4097_SET_SEMAPHORE_OFFSET, 1),
  30. (offset),
  31. CELL_GCM_METHOD(CELL_GCM_NV4097_TEXTURE_READ_SEMAPHORE_RELEASE, 1),
  32. (nValue)
  33. };
  34. #else
  35. uint32 * p = m_cmd.m_nCmd;
  36. CELL_GCM_METHOD_SET_SEMAPHORE_OFFSET(p, offset);
  37. CELL_GCM_METHOD_TEXTURE_READ_SEMAPHORE_RELEASE(p, nValue);
  38. #endif
  39. }
  40. void UpdateWriteTextureLabel( uint nValue )
  41. {
  42. m_cmd.m_nCmd[3] = nValue;
  43. }
  44. uint32 GetWriteTextureLabel( )
  45. {
  46. return m_cmd.m_nCmd[3];
  47. }
  48. }
  49. ALIGN16_POST;
  50. // read-only part of pcring
  51. struct ALIGN16 PcRingRo_t
  52. {
  53. SetLabelAlignedCommand_t m_head[2]; // the variants of the head sync
  54. void Init( uint nLabel );
  55. };
  56. //
  57. // producer-consumer FIFO ring buffer for command buffer.
  58. // Kept in main memory, controlled/produced by SPU, consumed by RSX
  59. //
  60. class ALIGN16 SysFifo
  61. {
  62. public:
  63. uint32 m_eaBuffer; // the buffer begin, EA
  64. uint32 m_nSize; // the whole buffer size
  65. // the important thing here is that put and end pointers are independent
  66. // and can be updated from different threads lock-free, wait-free
  67. // we're putting into Put segment; we can increment it until we hit "end", at which point we need to wait for RSX to eat up and move the "End" forward
  68. uint32 m_nPut, m_nEnd; // high bit means odd-even ring
  69. enum {ODD_BIT = 0x80000000};
  70. void Init( uintp eaBuffer, uint nSize, uint nPut = 0 )
  71. {
  72. // put may be anywhere (it must be the GCM control register PUT, realtively to eaBuffer), but it must be aligned and within the buffer
  73. Assert( nPut < nSize && !( 0xF & nPut ) );
  74. m_eaBuffer = ( uint32 )eaBuffer;
  75. m_nSize = nSize;
  76. m_nPut = nPut;
  77. m_nEnd = ODD_BIT;
  78. }
  79. void HardReset()
  80. {
  81. m_nPut = 0;
  82. m_nEnd = ODD_BIT;
  83. }
  84. // must wrap before put?
  85. bool MustWrap( uint nPutBytes ) const
  86. {
  87. return ( ( m_nPut + nPutBytes ) & ~ODD_BIT ) > m_nSize;
  88. }
  89. bool IsOrdered( uint nSignal0, uint nSignal1 )
  90. {
  91. if( ( nSignal0 ^ nSignal1 ) & ODD_BIT )
  92. {
  93. return ( nSignal1 & ~ODD_BIT ) <= ( nSignal0 & ~ODD_BIT );
  94. }
  95. else
  96. {
  97. return nSignal0 <= nSignal1;
  98. }
  99. }
  100. bool CanPutNoWrap( uint nPutBytes ) const
  101. {
  102. Assert( !MustWrap( nPutBytes ) );
  103. if ( ( m_nPut ^ m_nEnd ) & ODD_BIT ) // bits are different => we have enough space till the end of the buffer
  104. {
  105. Assert( ( m_nPut | ODD_BIT ) >= ( m_nEnd | ODD_BIT ) ); // the End must be trailing behind Put , only in the NEXT ring
  106. return true;
  107. }
  108. else
  109. {
  110. // bits are the same => we have continuous unsigned range between put, put+add, end
  111. // we don't want to put commands up to m_nEnd because theoretically we can get to the situation when put==get and RSX will skip the whole SYSring
  112. return ( m_nPut + nPutBytes < m_nEnd );
  113. }
  114. }
  115. bool CanWrapAndPut( uint nPutBytes )const
  116. {
  117. if ( ( m_nPut ^ m_nEnd ) & ODD_BIT ) // to wrap, "end" must be in the next ring
  118. {
  119. // Important: Assume that we'll reset Put to 0 when we put ... and "add" must be before "end"
  120. // we don't want to put commands up to m_nEnd because theoretically we can get to the situation when put==get and RSX will skip the whole SYSring
  121. return ( nPutBytes < ( m_nEnd & ~ODD_BIT ) );
  122. }
  123. else
  124. {
  125. Assert( m_nPut <= m_nEnd ); // the End must be in front of Put, since it's in the same ring
  126. return false;
  127. }
  128. }
  129. void Wrap( )
  130. {
  131. Assert( ( m_nPut ^ m_nEnd ) & ODD_BIT );
  132. m_nPut = ( ~m_nPut ) & ODD_BIT; // begin from the start, only in the next ring (invert odd/even)
  133. }
  134. // prepare to Put(nBytes); wrap if necessary; don't do anything unless subsequent Put(nBytes) is valid
  135. enum PreparePutEnum_t
  136. {
  137. PUT_PREPARED_WRAPPED,
  138. PUT_PREPARED_NOWRAP,
  139. PUT_PREPARE_FAILED
  140. };
  141. PreparePutEnum_t PreparePut( uint nBytes )
  142. {
  143. if( MustWrap( nBytes ) )
  144. {
  145. if( CanWrapAndPut( nBytes ) )
  146. {
  147. Wrap();
  148. Assert( CanPutNoWrap( nBytes ) );
  149. return PUT_PREPARED_WRAPPED;
  150. }
  151. }
  152. else
  153. {
  154. if( CanPutNoWrap( nBytes ) )
  155. {
  156. return PUT_PREPARED_NOWRAP;
  157. }
  158. }
  159. return PUT_PREPARE_FAILED;
  160. }
  161. // NOTE: the guarantee of this function is that multiple Puts are additive: Put(100) is equivalent to Put(25),Put(75) and such
  162. void Put( uint nPutBytes )
  163. {
  164. Assert( CanPutNoWrap( nPutBytes ) );
  165. m_nPut += nPutBytes;
  166. }
  167. uint EaPut( )const
  168. {
  169. return m_eaBuffer + ( m_nPut & ~ODD_BIT );
  170. }
  171. uint PutToEa( uint nPut )const
  172. {
  173. return m_eaBuffer + ( nPut & ~ODD_BIT );
  174. }
  175. uint EaWrapAndPut()const // EA of PUT after Wrap() is executed
  176. {
  177. return m_eaBuffer + sizeof( SetLabelAlignedCommand_t );
  178. }
  179. // how much memory is left in this ring, without Wrapping?
  180. int GetNoWrapCapacity()const
  181. {
  182. return m_nSize - ( m_nPut & ~ODD_BIT );
  183. }
  184. // returns a value that will signal that the buffer has been processed to m_nPut pointer
  185. uint GetSignal()const
  186. {
  187. return m_nPut ^ ODD_BIT;
  188. }
  189. // FFFFFFFF would imply a 2-Gb buffer, which we clearly can't have on PS3
  190. static uint GetInvalidSignal() { return 0xFFFFFFFF; }
  191. const SetLabelAlignedCommand_t * GetHead( PcRingRo_t &ro )const
  192. {
  193. return &ro.m_head[m_nPut >> 31];
  194. }
  195. const SetLabelAlignedCommand_t * GetNextHead( PcRingRo_t &ro )const
  196. {
  197. return &ro.m_head[( ~m_nPut ) >> 31];
  198. }
  199. // notify about a signal coming in asynchronously , must be a result of GetSignal() after Put()
  200. void NotifySignal( uint nSignal )
  201. {
  202. AssertSpuMsg( ( nSignal & ~ODD_BIT ) <= m_nSize, "{ea=0x%X,size=0x%X,put=0x%X,end=0x%X}.NotifySignal(0x%X)\n", m_eaBuffer, m_nSize, m_nPut, m_nEnd, nSignal );
  203. if( SPUGCM_ENABLE_NOTIFY_RSX_GET )
  204. {
  205. // we can artificially set the signal ahead sometimes, because we have 2 streams of signals from RSX :
  206. // THe control register GET and the cmd buffer label (GCM_LABEL_SYSRING_SIGNAL)
  207. // so we'll filter extra signals here: we may NOT step back
  208. if( !( ( nSignal ^ m_nEnd ) & ODD_BIT ) && nSignal < m_nEnd )
  209. {
  210. return;// skip this: signal and end are in the same ring and signal is earlier than end
  211. }
  212. }
  213. AssertSpuMsg( ( ( nSignal ^ m_nPut ) & ODD_BIT ) ?
  214. ( nSignal & ~ODD_BIT ) <= ( m_nPut & ~ODD_BIT )// signal and put are in different rings
  215. :
  216. nSignal >= m_nPut, // signal and put are in the same ring
  217. "{ea=0x%X,size=0x%X,put=0x%X,end=0x%X}.NotifySignal(0x%X)\n",
  218. m_eaBuffer, m_nSize, m_nPut, m_nEnd, nSignal
  219. );
  220. m_nEnd = nSignal;
  221. }
  222. // NotifySignal() version that can tolerate outdated signals due to different latencies between SPU and RSX
  223. void NotifySignalSafe( uint nSignal )
  224. {
  225. if( ( ( nSignal ^ m_nPut ) & ODD_BIT ) ?
  226. ( nSignal & ~ODD_BIT ) <= ( m_nPut & ~ODD_BIT )// signal and put are in different rings
  227. :
  228. nSignal >= m_nPut ) // signal and put are in the same ring
  229. {
  230. m_nEnd = nSignal;
  231. }
  232. }
  233. bool IsSignalDifferent( uint nSignal )
  234. {
  235. return m_nEnd != nSignal;
  236. }
  237. // WARNING this is a debug-only function. Do not use for anything but debugging, because it's slow
  238. // and because it will signal "finished" incorrectly when the whole ring is full
  239. // Expects RSX get relative to the base of the buffer (i.e. 0 when Get == the byte 0 of this PCring)
  240. bool NotifyRsxGet( uint nRsxControlRegisterGet )
  241. {
  242. if( nRsxControlRegisterGet == ( m_nPut & ~ODD_BIT ) )
  243. {
  244. m_nEnd = m_nPut ^ ODD_BIT; // assume this means we've processed all SYSRING buffer
  245. return true;
  246. }
  247. else
  248. {
  249. return false;
  250. }
  251. }
  252. bool IsRsxFinished( uint nRsxControlRegisterGet )
  253. {
  254. return nRsxControlRegisterGet == ( m_nPut & ~ODD_BIT );
  255. }
  256. bool IsDone()const
  257. {
  258. return ( m_nEnd == m_nPut ^ ODD_BIT );
  259. }
  260. }
  261. ALIGN16_POST;
  262. inline void PcRingRo_t::Init( uint nLabel )
  263. {
  264. m_head[0].SetWriteTextureLabel( nLabel, SysFifo::ODD_BIT | sizeof( SetLabelAlignedCommand_t ) ); // m_nPut == 0 -> signal == ODD_BIT
  265. m_head[1].SetWriteTextureLabel( nLabel, sizeof( SetLabelAlignedCommand_t ) ); // m_nPut == ODD_BIT -> signal == 0
  266. }
  267. #endif