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.

650 lines
24 KiB

  1. // Copyright � 2010, Valve Corporation, All rights reserved. ========
  2. #include "tier0/platform.h"
  3. #include "tier0/dbg.h"
  4. #include "tier1/convar.h"
  5. #include "ps3/ps3gcmlabels.h"
  6. #include "ps3gcmstate.h"
  7. #include "spugcm.h"
  8. #include "rsxflip.h"
  9. CFlipHandler g_flipHandler;
  10. ConVar r_drop_user_commands( "r_drop_user_commands", "0" );
  11. ConVar r_ps3_mlaa( "r_ps3_mlaa", "1" ); //
  12. ConVar r_ps3_vblank_miss_threshold( "r_ps3_vblank_miss_threshold", "0.08", FCVAR_DEVELOPMENTONLY, "How much % of vsync time is allowed after vblank for frames that missed vsync to tear and flip immediately" );
  13. #if GCM_ALLOW_TIMESTAMPS
  14. int32 g_ps3_timestampBeginIdx = GCM_REPORT_TIMESTAMP_FRAME_FIRST;
  15. #endif
  16. #if 0 // defined(_DEBUG)
  17. char ALIGN16 g_flipLog[256][32] ALIGN16_POST;
  18. uint g_flipLogIdx = 0;
  19. #define FLIP_LOG(MSG,...) \
  20. { \
  21. uint nLogIdx = cellAtomicIncr32( &g_flipLogIdx ) & ( ARRAYSIZE( g_flipLog ) - 1 ); \
  22. int nCount = V_snprintf( g_flipLog[nLogIdx], sizeof( g_flipLog[nLogIdx] ), MSG, ##__VA_ARGS__ ); \
  23. int zeroSize = sizeof( g_flipLog[0] ) - 4 - nCount; \
  24. V_memset( g_flipLog[nLogIdx] + nCount, 0, zeroSize ); \
  25. *(uint32*)( g_flipLog[nLogIdx] + sizeof( g_flipLog[0] ) - 4 ) = __mftb(); \
  26. }
  27. #define ENABLE_FLIP_LOG 1
  28. #define FlipAssert( X ) do{if(!(X))DebuggerBreak();}while(false)
  29. uint g_flipUserCommands[1024][2];
  30. #else
  31. #define FLIP_LOG(MSG,...)
  32. #define FlipAssert( X )
  33. #define ENABLE_FLIP_LOG 0
  34. #endif
  35. void CEdgePostWorkload::Kick( void * dst, uint nSetLabel )
  36. {
  37. if( !m_isInitialized )
  38. return;
  39. extern ConVar r_ps3_mlaa;
  40. FLIP_LOG("mlaa %d,mode=%Xh,label=%d", nSetLabel, g_flipHandler.m_nMlaaFlagsThisFrame, *m_mlaaContext.rsxLabelAddress );
  41. edgePostMlaaWait( &m_mlaaContext );
  42. FlipAssert( vec_all_eq( *g_spuGcm.m_pMlaaBufferCookie, g_vuSpuGcmCookie ) );
  43. //FLIP_LOG("mlaa init %d", nSetLabel );
  44. edgePostInitializeWorkload( &m_workload, m_stages, STAGE_COUNT );
  45. bool isMlaaRelativeEdgeDetection = true;
  46. uint8
  47. nMlaaThresholdBase (0x0a), // from Edge sample: these are pretty good threshold values, but you might find better ones...
  48. nMlaaThresholdFactor(0x59),
  49. nMlaaAbsoluteThreshold(0x20);
  50. uint nWidth = g_ps3gcmGlobalState.m_nRenderSize[0], nHeight = g_ps3gcmGlobalState.m_nRenderSize[1];
  51. FlipAssert( nWidth <= 1280 && nWidth >= 640 && nHeight <= 720 && nHeight >= 480 );
  52. //FLIP_LOG("mlaa prep %d", nSetLabel );
  53. edgePostMlaaPrepareWithRelativeThreshold( &m_mlaaContext, g_spuGcm.m_pMlaaBuffer, IsResultInMainMemory()? g_spuGcm.m_pMlaaBufferOut : dst,
  54. nWidth, nHeight,
  55. g_ps3gcmGlobalState.m_nSurfaceRenderPitch,
  56. isMlaaRelativeEdgeDetection?nMlaaThresholdBase:nMlaaAbsoluteThreshold,
  57. isMlaaRelativeEdgeDetection?nMlaaThresholdFactor:0,
  58. g_flipHandler.m_nMlaaFlagsThisFrame,
  59. nSetLabel );
  60. //FLIP_LOG("mlaa kick %d", nSetLabel );
  61. edgePostMlaaKickTasks( &m_mlaaContext );
  62. FLIP_LOG("mlaa kicked %d,label=%d", nSetLabel, *m_mlaaContext.rsxLabelAddress );
  63. FlipAssert( vec_all_eq( *g_spuGcm.m_pMlaaBufferCookie, g_vuSpuGcmCookie ) );
  64. }
  65. void RsxInterruptFifo::Init()
  66. {
  67. m_nGet = m_nPut = 0;
  68. }
  69. uint RsxInterruptFifo::Queue( uint8 nCause, uint8 nSurfaceFlipIdx )
  70. {
  71. Event_t event;
  72. event.m_nCause = nCause;
  73. event.m_nSurfaceFlipIdx = nSurfaceFlipIdx;
  74. return Queue( event );
  75. }
  76. uint RsxInterruptFifo::Queue( const Event_t &event )
  77. {
  78. while( ( m_nPut - m_nGet ) >= MAX_EVENT_COUNT - 1 )
  79. {
  80. sys_timer_usleep( 100 ); // this should NEVER happen
  81. }
  82. #if ENABLE_FLIP_LOG
  83. switch( event.m_nCause )
  84. {
  85. case GCM_USERCMD_POSTPROCESS:
  86. FLIP_LOG( "queue:post %d", event.m_nSurfaceFlipIdx );
  87. break;
  88. case GCM_USERCMD_FLIPREADY:
  89. FLIP_LOG( "queue:flip %d sys%d", event.m_nSurfaceFlipIdx, g_flipHandler.m_nSystemFlipId[ event.m_nSurfaceFlipIdx ] );
  90. break;
  91. default:
  92. FLIP_LOG("Unknown event %d", event.m_nCause );
  93. break;
  94. }
  95. #endif
  96. m_queue[ m_nPut & ( MAX_EVENT_COUNT - 1 ) ] = event;
  97. return ++m_nPut; // Should be atomic if there are multiple event producer threads
  98. }
  99. uint RsxInterruptFifo::GetPutMarker()const
  100. {
  101. return m_nPut;
  102. }
  103. int RsxInterruptFifo::HasEvents( uint nMarker )
  104. {
  105. uint nGet = m_nGet;
  106. Assert( int( nMarker - nGet ) >= 0 );
  107. return int( nMarker - nGet );
  108. }
  109. RsxInterruptFifo::Event_t & RsxInterruptFifo::PeekEvent()
  110. {
  111. uint nGet = m_nGet;
  112. Assert( nGet != m_nPut );
  113. return m_queue[ nGet & ( MAX_EVENT_COUNT - 1 ) ];
  114. }
  115. const RsxInterruptFifo::Event_t RsxInterruptFifo::DequeueEvent( )
  116. {
  117. Event_t event = PeekEvent();
  118. m_nGet++; // should be atomic if there's more than one consumer
  119. return event;
  120. }
  121. void RsxInterruptFifo::QueueRsxInterrupt()
  122. {
  123. uint32 *pReplace = NULL;
  124. #if ENABLE_FLIP_LOG
  125. //FLIP_LOG( "q%X", m_nPut );
  126. g_flipUserCommands[ m_nPut & ( ARRAYSIZE( g_flipUserCommands ) - 1 ) ][ 0 ] = m_nPut;
  127. pReplace = &g_flipUserCommands[ m_nPut & ( ARRAYSIZE( g_flipUserCommands ) - 1 ) ][ 1 ];
  128. *pReplace = uint32( gCellGcmCurrentContext->current );
  129. #endif
  130. /*
  131. if( IsCert() // don't deliberately drop anything in CERT
  132. || 0 == r_drop_user_commands.GetInt() // don't drop anything if drop==0
  133. || ( ( rand() % 100 ) >= r_drop_user_commands.GetInt() ) // drop 1% means in 99% of cases we still want to SetUserCommand
  134. )
  135. GCM_FUNC( cellGcmSetUserCommand, m_nPut );
  136. GCM_FUNC( cellGcmSetWriteTextureLabel, GCM_LABEL_LAST_INTERRUPT_GET, m_nPut );
  137. */
  138. // directly putting it to SPUGCM queue instead of routing it through GCM_FUNC
  139. g_spuGcm.GetDrawQueue()->Push3( SPUDRAWQUEUE_QUEUE_RSX_INTERRUPT_METHOD | GCM_LABEL_LAST_INTERRUPT_GET, m_nPut, ( uintp )pReplace );
  140. }
  141. void CFlipHandler::Init()
  142. {
  143. m_interruptFifo.Init();
  144. /*
  145. V_memset( m_nDebugStates, 0, sizeof( m_nDebugStates ) );
  146. m_nDebugStates[RENDERING_SURFACE] = -1;
  147. */
  148. m_nFlipSurfaceIdx = 0;
  149. m_nFlipSurfaceCount = 0;
  150. m_nVblankCounter = 100; // how many vblanks since the last flip?
  151. m_bEdgePostResultAlreadyInLocalMemory = false;
  152. m_nMlaaFlagsThisFrame = 0; // disable MLAA before the first BeginScene() is called
  153. m_nMlaaFlagMaskNextFrame = ~0u;
  154. for( int i = 0; i < ARRAYSIZE( m_surfaceEdgePost ) ; ++i ) // initially, the post processing of surfaces is disabled
  155. m_surfaceEdgePost[i] = 0;
  156. // simulated initial state: we just flipped to surface 1, then 2, thus leaving surface 1 (then 0) available to render into
  157. // event[1] may not be set for MLAA mode because in order to start rendering into surface 0 (which we're rendering into), we "waited" for event 1
  158. for ( int j = 2; j < ARRAYSIZE( m_evFlipReady ); ++ j )
  159. m_evFlipReady[j].Set();
  160. //m_nLastFlippedSurfaceIdx = CPs3gcmDisplay::SURFACE_COUNT - 1 ;
  161. m_pLastInterruptGet = cellGcmGetLabelAddress( GCM_LABEL_LAST_INTERRUPT_GET );
  162. *m_pLastInterruptGet = 0;
  163. cellGcmSetVBlankHandler( INTERRUPT_VBlankHandler );
  164. cellGcmSetUserHandler( INTERRUPT_UserHandler );
  165. }
  166. void CFlipHandler::Shutdown()
  167. {
  168. cellGcmSetVBlankHandler( NULL );
  169. cellGcmSetUserHandler( NULL );
  170. }
  171. //////////////////////////////////////////////////////////////////////////
  172. // 1. draw PS/3 system menus into the surface
  173. // 2. queue a reliable "flip ready" event for GCM interrupt thread to process and flip surface to this
  174. //
  175. void CFlipHandler::QmsPrepareFlipSubmit( GcmUserCommandEnum_t nEvent, uint surfaceFlipIdx )
  176. {
  177. uint32 nSystemFlipId = GCM_FUNC_NOINLINE( cellGcmSetPrepareFlip, surfaceFlipIdx );
  178. m_nSystemFlipId[surfaceFlipIdx] = nSystemFlipId;
  179. Assert( !m_evFlipReady[ surfaceFlipIdx ].Check() );
  180. m_interruptFifo.Queue( nEvent, surfaceFlipIdx );
  181. }
  182. ConVar r_ps3_mlaa_pulse( "r_ps3_mlaa_pulse", "0" );
  183. enum EdgePostFlags_t {
  184. EDGE_POST_MLAA_FLAG_MASK = ( EDGE_POST_MLAA_MODE_ENABLED | EDGE_POST_MLAA_MODE_SHOW_EDGES | EDGE_POST_MLAA_MODE_SINGLE_SPU_TRANSPOSE | EDGE_POST_MLAA_MODE_TRANSPOSE_64 )
  185. };
  186. void CFlipHandler::BeginScene()
  187. {
  188. #if GCM_ALLOW_TIMESTAMPS
  189. if ( g_ps3_timestampBeginIdx >= 0 )
  190. {
  191. GCM_FUNC( cellGcmSetTimeStamp, g_ps3_timestampBeginIdx );
  192. g_ps3_timestampBeginIdx = -1;
  193. }
  194. #endif
  195. m_nMlaaFlagsThisFrame = r_ps3_mlaa.GetInt() & EDGE_POST_MLAA_FLAG_MASK;
  196. if( int nPulse = r_ps3_mlaa_pulse.GetInt() )
  197. {
  198. if( 1 & ( g_spuGcm.m_nFrame / nPulse ) )
  199. {
  200. m_nMlaaFlagsThisFrame = 0; // disable for 16 frames = 1/2 second
  201. }
  202. }
  203. m_nMlaaFlagsThisFrame &= m_nMlaaFlagMaskNextFrame;
  204. //m_nMlaaFlagMaskNextFrame = (uint)-1;
  205. }
  206. void CFlipHandler::TransferMlaaResultIfNecessary( uint nSurfacePrevFlipIdx )
  207. {
  208. if( m_bEdgePostResultAlreadyInLocalMemory )
  209. return;
  210. if( g_edgePostWorkload.ShouldUseLabelForSynchronization() )
  211. {
  212. GCM_FUNC( cellGcmSetWaitLabel, GCM_LABEL_EDGEPOSTMLAA, nSurfacePrevFlipIdx );
  213. }
  214. else
  215. {
  216. // wait for SPU to finish post-processing previous surface
  217. uint32 *pPrevJts = &g_spuGcm.m_pEdgePostRsxLock[ nSurfacePrevFlipIdx ];
  218. if( *pPrevJts != CELL_GCM_RETURN() )
  219. {
  220. GCM_FUNC( cellGcmSetCallCommand, uintp( pPrevJts ) + g_spuGcmShared.m_nIoOffsetDelta );
  221. }
  222. }
  223. //
  224. // NOTE: we can start post-processing before SetPrepareFlip, it only makes sense since we don't always use interrupt to do so
  225. // if we ever do proper synchronization with SPU workload, we should kick Edge Post here, before SetPrepareFlip
  226. //
  227. if( g_edgePostWorkload.IsResultInMainMemory() )
  228. {
  229. CPs3gcmLocalMemoryBlockSystemGlobal & prevSurfaceColor = g_ps3gcmGlobalState.m_display.surfaceColor[nSurfacePrevFlipIdx];
  230. GCM_FUNC( cellGcmSetTransferImage, CELL_GCM_TRANSFER_MAIN_TO_LOCAL,
  231. prevSurfaceColor.Offset(), g_ps3gcmGlobalState.m_nSurfaceRenderPitch, 0, 0,
  232. uintp( g_spuGcm.m_pMlaaBufferOut ) + g_ps3gcmGlobalState.m_nIoOffsetDelta, g_ps3gcmGlobalState.m_nSurfaceRenderPitch, 0, 0,
  233. g_ps3gcmGlobalState.m_nRenderSize[0], g_ps3gcmGlobalState.m_nRenderSize[1],
  234. 4 );
  235. }
  236. m_bEdgePostResultAlreadyInLocalMemory = true;
  237. }
  238. bool CFlipHandler::QmsAdviceBeforeDrawPrevFramebuffer()
  239. {
  240. uint nSurfacePrevFlipIdx = g_ps3gcmGlobalState.m_display.PrevSurfaceIndex( 1 );
  241. uint8 prevPostProcessed = m_surfaceEdgePost[nSurfacePrevFlipIdx];
  242. if( prevPostProcessed ) // did previous surface need post-processing?
  243. {
  244. // we'd actually be free to start MLAA here instead of in Flip, for the cost of one more RSX->PPU interrupt
  245. // but we don't do that because we only may do so when the LAST player draws, and we don't know if this post processing
  246. // that will now start is related to the LAST player
  247. // we don't need to do that until flip if we're using deferred queue
  248. // although if we're using deferred queue and we run out of space there, we stop using it, replay it and start defer-render into previous frame
  249. TransferMlaaResultIfNecessary( nSurfacePrevFlipIdx );
  250. // do the post-processing on this frame, in the mean time render into previous frame
  251. return true;
  252. }
  253. return false; // there's no need to switch surfaces now
  254. }
  255. void CFlipHandler::Flip()
  256. {
  257. #if GCM_ALLOW_TIMESTAMPS
  258. OnFrameTimestampAvailableMST( 1.0f );
  259. #endif
  260. extern ConVar mat_vsync;
  261. m_bVSync = mat_vsync.GetBool();
  262. g_ps3gcmGlobalState.CmdBufferFlush( CPs3gcmGlobalState::kFlushForcefully );
  263. g_spuGcm.GetDrawQueue()->Push1( SPUDRAWQUEUE_FRAMEEVENT_METHOD | SDQFE_END_FRAME );
  264. uint surfaceFlipIdx = g_ps3gcmGlobalState.m_display.surfaceFlipIdx, nSurfaceNextFlipIdx = g_ps3gcmGlobalState.m_display.NextSurfaceIndex( 1 ), nSurfaceAfterNextFlipIdx = g_ps3gcmGlobalState.m_display.NextSurfaceIndex( 2 ), nSurfacePrevFlipIdx = g_ps3gcmGlobalState.m_display.PrevSurfaceIndex( 1 );
  265. /*
  266. uint nScreenWidth = g_ps3gcmGlobalState.m_nRenderSize[0];
  267. uint nScreenY = 40;
  268. g_ps3gcmGlobalState.DrawDebugStripe( nScreenWidth * surfaceFlipIdx / 3, nScreenY, 0, nScreenWidth / 3, 4 );
  269. g_ps3gcmGlobalState.DrawDebugStripe( ( g_spuGcm.m_nFrame & 0xF ) * ( nScreenWidth / 16 ), 34, 0, ( nScreenWidth / 16 ) * ( 1 + m_nFlipSurfaceCount ), 1 );
  270. */
  271. // let interrupt know we're ready to post-process the new frame, and we wanna flip the previous frame
  272. //g_ps3gcmGlobalState.CmdBufferFinish();
  273. uint32 * pThisJts = g_spuGcm.m_pEdgePostRsxLock + surfaceFlipIdx; // may be NULL + idx
  274. Assert( !g_spuGcm.m_pEdgePostRsxLock || *pThisJts == CELL_GCM_RETURN() );
  275. uint8 prevPostProcessed = m_surfaceEdgePost[nSurfacePrevFlipIdx];
  276. uint8 thisPostProcess = g_spuGcm.m_pMlaaBuffer ? ( uint8 ) ( m_nMlaaFlagsThisFrame & EDGE_POST_MLAA_FLAG_MASK ): 0 ;
  277. if( prevPostProcessed ) // did previous surface need post-processing?
  278. {
  279. TransferMlaaResultIfNecessary( nSurfacePrevFlipIdx );
  280. //if( g_spuGcm.m_bUseDeferredDrawQueue )
  281. {
  282. // now is the time to execute all the deferred commands, if there are any
  283. // NOTE: this will often do nothing , because current frame would've flushed previous frame deferred commands already
  284. // right before starting writing its own
  285. g_spuGcm.ExecuteDeferredDrawQueue( 1 );
  286. }
  287. //g_ps3gcmGlobalState.DrawDebugStripe( nScreenWidth * surfaceFlipIdx / 3, 44, surfaceFlipIdx, nScreenWidth / 3, 2, -1 );
  288. // prepare flip of previous frame - Edge Post processed buffer
  289. // the previous frame was post-processed; we'll prepare flip on it.
  290. QmsPrepareFlipSubmit( GCM_USERCMD_FLIPREADY, nSurfacePrevFlipIdx );
  291. }
  292. else
  293. {
  294. // if previous frame wasn't post-processed, don't flip it because we don't want to flip the same framebuffer twice (although we probably could)
  295. // so we don't have anything to flip here, but have a frame to post-process
  296. g_spuGcm.ExecuteDeferredDrawQueue( 1 );
  297. }
  298. m_surfaceEdgePost[surfaceFlipIdx] = thisPostProcess; // is post-process required for this surface ?
  299. if( thisPostProcess )
  300. {
  301. if( !( m_nMlaaFlagsThisFrame & EDGE_POST_MLAA_MODE_ENABLED ) )
  302. {
  303. m_bEdgePostResultAlreadyInLocalMemory = true; // don't attempt to transfer the results; we don't _really_ do edge post processing, so we consider the results are in memory already
  304. }
  305. else
  306. {
  307. // EDGE POST TODO: JTS - the previous EdgePost must release it. To avoid overwriting edge post buffer before it finished tranferring back to local memory
  308. // to release JTS from the future, we can use a separate ring buffer "JTS-RET" sequences and just call into it here.
  309. // or we can wait for a label and set it from SPU
  310. // as a simplification, we can just wait for edge post to finish synchronously on ppu
  311. // we can also use a mutex of sorts and insert JTS here only when edge post is not finished yet
  312. // we only can start transferring the image after the SPU is done streaming previous frame (if previous frame was post-processed)
  313. // so wait for SPU to release previous frame, if it was post-processed.
  314. // Also, if SPU didn't finish post-processing, then we need to synchronize (wait on RSX for SPU to be done)
  315. // but in many cases SPU will be done by now, so we don't need to spend 900+ns in RSX front-end on CALL+RET
  316. if( !g_edgePostWorkload.ShouldUseLabelForSynchronization() )
  317. {
  318. *pThisJts = CELL_GCM_JUMP( uintp( pThisJts ) + g_spuGcmShared.m_nIoOffsetDelta ); // this will be JTS for SPU to overwrite when post-processing of this frame is done
  319. }
  320. CPs3gcmLocalMemoryBlockSystemGlobal & surfaceColor = g_ps3gcmGlobalState.m_display.surfaceColor[surfaceFlipIdx];
  321. GCM_FUNC( cellGcmSetTransferImage, CELL_GCM_TRANSFER_LOCAL_TO_MAIN,
  322. uintp( g_spuGcm.m_pMlaaBuffer ) + g_ps3gcmGlobalState.m_nIoOffsetDelta, g_ps3gcmGlobalState.m_nSurfaceRenderPitch, 0, 0,
  323. surfaceColor.Offset(), g_ps3gcmGlobalState.m_nSurfaceRenderPitch, 0, 0,
  324. g_ps3gcmGlobalState.m_nRenderSize[0], g_ps3gcmGlobalState.m_nRenderSize[1],
  325. 4 );
  326. // This frame was rendered and transferred to main memory; we'll let interrupt thread know it's ready for Edge Post processing
  327. m_interruptFifo.Queue( GCM_USERCMD_POSTPROCESS, surfaceFlipIdx );
  328. m_bEdgePostResultAlreadyInLocalMemory = false;
  329. }
  330. }
  331. else
  332. {
  333. // we aren't post-processing this frame, so we need to just prepare flip and flip this framebuffer
  334. g_spuGcm.ExecuteDeferredDrawQueue( 0 );
  335. QmsPrepareFlipSubmit( GCM_USERCMD_FLIPREADY, surfaceFlipIdx );
  336. m_bEdgePostResultAlreadyInLocalMemory = true; // don't attempt to transfer the results; we don't do edge post - processing, so we consider the results are in memory already
  337. }
  338. g_spuGcm.FlipDeferredDrawQueue( );
  339. if( thisPostProcess && !prevPostProcessed )
  340. {
  341. // we absolutely MUST reset RSX state before the next frame.
  342. // QmsPrepareFlipSubmit() does that by definition, but if we don't call it in this Flip (i.e. when !prevPostProcessed && thisPostProcess)
  343. // we must FORCE RSX state reset
  344. g_spuGcm.GetDrawQueue()->Push1( SPUDRAWQUEUE_RESETRSXSTATE_METHOD );
  345. }
  346. #if GCM_ALLOW_TIMESTAMPS
  347. {
  348. // The current frame has just finished, insert a timestamp instruction right before flip
  349. GCM_FUNC( cellGcmSetTimeStamp, surfaceFlipIdx * 2 + GCM_REPORT_TIMESTAMP_FRAME_FIRST + 1 );
  350. g_ps3_timestampBeginIdx = nSurfaceNextFlipIdx * 2 + GCM_REPORT_TIMESTAMP_FRAME_FIRST;
  351. }
  352. #endif
  353. m_interruptFifo.QueueRsxInterrupt();
  354. g_ps3gcmGlobalState.CmdBufferFlush( CPs3gcmGlobalState::kFlushEndFrame );
  355. //g_ps3gcmGlobalState.CmdBufferFinish();
  356. //
  357. // Make sure that the next framebuffer is free to render into. For that to be so,
  358. // the flip should happen from the next to the buffer after next. When that happens,
  359. // the TV shows the buffer after next, and the next buffer is not visible to the user,
  360. // so it's allowed to render into the next buffer.
  361. //
  362. FLIP_LOG( "ev Wait %d", nSurfaceAfterNextFlipIdx );
  363. m_evFlipReady[ nSurfaceAfterNextFlipIdx ].Wait();
  364. m_evFlipReady[ nSurfaceAfterNextFlipIdx ].Reset();
  365. FLIP_LOG( "Draw %d, ev Reset %d", nSurfaceNextFlipIdx, nSurfaceAfterNextFlipIdx );
  366. #if GCM_ALLOW_TIMESTAMPS
  367. {
  368. // Since the previous flip completely finished, we can grab its timestamps now
  369. uint32 uiLastFrameTimestampIdx = ( nSurfaceAfterNextFlipIdx ) * 2 + GCM_REPORT_TIMESTAMP_FRAME_FIRST;
  370. uint64 uiStartTimestamp = cellGcmGetTimeStamp( uiLastFrameTimestampIdx );
  371. uint64 uiEndTimestamp = cellGcmGetTimeStamp( uiLastFrameTimestampIdx + 1 );
  372. uint64 uiRsxTimeInNanoSeconds = uiEndTimestamp - uiStartTimestamp;
  373. OnFrameTimestampAvailableRsx( uiRsxTimeInNanoSeconds / 1000000.0f );
  374. }
  375. #endif
  376. }
  377. bool IsRsxReadyForNoninteractiveRefresh( )
  378. {
  379. uint nSurfaceAfterNextFlipIdx = g_ps3gcmGlobalState.m_display.NextSurfaceIndex( 2 );
  380. return g_flipHandler.m_evFlipReady[ nSurfaceAfterNextFlipIdx ].Check();
  381. // if we are 3 vblanks past last flip already, another refresh would be welcome ; if we have no surfaces to flip in this case, we are most likely ready to flip right away
  382. // another thing to check is the interrupt FIFO: if it's not idle, let's just postpone being ready
  383. // return g_flipHandler.m_nVblankCounter > 3 && g_flipHandler.m_nFlipSurfaceCount == 0 && g_flipHandler.m_interruptFifo.IsIdle();
  384. }
  385. void CFlipHandler::TryFlipVblank()
  386. {
  387. // artificially simulate an interrupt for cause, because there's suspicion it was dropped
  388. //
  389. // only attempt to generate artificial interrupts if our ready flip queue is empty, otherwise there's no need
  390. // to tap the narrow 15.6Mb/s bus
  391. uint nMarker = *g_flipHandler.m_pLastInterruptGet;
  392. m_nVblankCounter ++;
  393. #if ENABLE_FLIP_LOG
  394. static int m_nLastFlipLogIdx = 0;
  395. if( m_nLastFlipLogIdx != g_flipLogIdx )
  396. {
  397. V_snprintf( g_flipLog[m_nLastFlipLogIdx], sizeof( g_flipLog[m_nLastFlipLogIdx] ), "%X.Vblanks ..%d", nMarker, m_nVblankCounter );
  398. }
  399. else
  400. {
  401. m_nLastFlipLogIdx = cellAtomicIncr32( &g_flipLogIdx ) & ( ARRAYSIZE( g_flipLog ) - 1 );
  402. V_snprintf( g_flipLog[m_nLastFlipLogIdx], sizeof( g_flipLog[m_nLastFlipLogIdx] ), "%X.Vblank %d", nMarker, m_nVblankCounter );
  403. }
  404. #endif
  405. TryPumpEvents( nMarker, 1 );
  406. }
  407. bool CFlipHandler::TryFlipSurface( uint isVblank )
  408. {
  409. if( !m_nFlipSurfaceCount )
  410. {
  411. return false;
  412. }
  413. if( m_bVSync )
  414. {
  415. if( m_nVblankCounter < m_nPresentFrequency )
  416. {
  417. //FLIP_LOG( "no flip: %d vblanks", m_nVblankCounter, m_nPresentFrequency );
  418. return false;
  419. }
  420. if( !isVblank )
  421. {
  422. double flVSyncInterval = m_flVBlankTimestamp - m_flVBlankTimestamp0, flMissThreshold = r_ps3_vblank_miss_threshold.GetFloat() * flVSyncInterval;
  423. double flMiss = Plat_FloatTime() - m_flVBlankTimestamp;
  424. if ( flMiss > flMissThreshold )
  425. {
  426. FLIP_LOG("no flip: %.2fms miss", flMiss * 1000 );
  427. return false; // wait for another vsync, missed by too much
  428. }
  429. }
  430. }
  431. // flip the surface immediately
  432. uint nSystemFlipId = m_nSystemFlipId[ m_nFlipSurfaceIdx ];
  433. cellGcmSetFlipImmediate( nSystemFlipId );
  434. #ifdef GCM_ALLOW_TIMESTAMPS
  435. // Collect time since previous flip
  436. double flFlipImmediateTimestamp = Plat_FloatTime();
  437. OnFrameTimestampAvailableFlip( ( flFlipImmediateTimestamp - m_flFlipImmediateTimestamp ) * 1000.0f );
  438. m_flFlipImmediateTimestamp = flFlipImmediateTimestamp;
  439. #endif
  440. FLIP_LOG( isVblank ? "vFlip%u, ev Set %u" : "_Flip%u, ev Set %u", nSystemFlipId, m_nFlipSurfaceIdx );
  441. // Release PPU QMS thread waiting for this flip
  442. m_evFlipReady[m_nFlipSurfaceIdx].Set();
  443. m_nFlipSurfaceIdx = ( m_nFlipSurfaceIdx + 1 ) % CPs3gcmDisplay::SURFACE_COUNT;
  444. m_nFlipSurfaceCount--;
  445. m_nVblankCounter = 0;
  446. return true;
  447. }
  448. void CFlipHandler::TryPumpEvents( uint nMarker, uint isVblank )
  449. {
  450. if ( m_mutexOfInterruptThread.TryLock() )
  451. {
  452. PumpEventsUnsafe( nMarker );
  453. TryFlipSurface( isVblank ); // this will often be duplicate call
  454. g_flipHandler.m_mutexOfInterruptThread.Unlock();
  455. }
  456. }
  457. void CFlipHandler::PumpEventsUnsafe( uint nMarker )
  458. {
  459. while( m_interruptFifo.HasEvents( nMarker ) )
  460. {
  461. if( !OnRsxInterrupt( m_interruptFifo.DequeueEvent() ) )
  462. break;
  463. }
  464. }
  465. bool RsxInterruptFifo::IsValidMarker( uint nMarker )
  466. {
  467. return ( nMarker - m_nGet ) <= MAX_EVENT_COUNT;
  468. }
  469. bool CFlipHandler::OnRsxInterrupt( const RsxInterruptFifo::Event_t event )
  470. {
  471. switch( event.m_nCause )
  472. {
  473. case GCM_USERCMD_POSTPROCESS:
  474. {
  475. // start edge post processing phase here; we can't do the flip yet because we didn't post-process the buffer yet
  476. // Simulating MLAA job running and adding the cause to the end of the array some time in the nearest (4-5ms) future
  477. void * pColorSurface = g_ps3gcmGlobalState.m_display.surfaceColor[event.m_nSurfaceFlipIdx].DataInLocalMemory();
  478. if( true )
  479. {
  480. // g_spuGcm.SyncMlaa();
  481. g_edgePostWorkload.Kick( pColorSurface, event.m_nSurfaceFlipIdx );
  482. }
  483. else
  484. {
  485. FLIP_LOG( "mlaa sync %d", event.m_nSurfaceFlipIdx );
  486. g_spuGcm.SyncMlaa( pColorSurface );
  487. g_spuGcm.m_pEdgePostRsxLock[event.m_nSurfaceFlipIdx] = CELL_GCM_RETURN(); // this will be poked by the SPU job
  488. }
  489. }
  490. break;
  491. case GCM_USERCMD_FLIPREADY:
  492. FlipAssert( ( m_nFlipSurfaceIdx + m_nFlipSurfaceCount ) % CPs3gcmDisplay::SURFACE_COUNT == event.m_nSurfaceFlipIdx );
  493. FLIP_LOG( "flip ready %d:sys%d", event.m_nSurfaceFlipIdx, m_nSystemFlipId[event.m_nSurfaceFlipIdx] );
  494. m_nFlipSurfaceCount++;
  495. break;
  496. }
  497. return true;
  498. }
  499. void CFlipHandler::INTERRUPT_VBlankHandler( const uint32 head )
  500. {
  501. double flVBlankTimestampSave = g_flipHandler.m_flVBlankTimestamp;
  502. g_flipHandler.m_flVBlankTimestamp = Plat_FloatTime();
  503. g_flipHandler.m_flVBlankTimestamp0 = flVBlankTimestampSave;
  504. g_flipHandler.TryFlipVblank( );
  505. }
  506. void CFlipHandler::INTERRUPT_UserHandler( const uint32 nMarker )
  507. {
  508. if( g_flipHandler.m_interruptFifo.IsValidMarker( nMarker ) )
  509. {
  510. //FLIP_LOG( "%X.UserInterrupt", nMarker );
  511. g_flipHandler.TryPumpEvents( nMarker, 0 );
  512. }
  513. else
  514. {
  515. // invalid marker: this marker has already happened; skip it
  516. //FLIP_LOG( "%X.ERROR.UserInterrupt", nMarker );
  517. DebuggerBreak();
  518. }
  519. }
  520. void Ps3gcmFlip_SetFlipPresentFrequency( int nNumVBlanks )
  521. {
  522. if ( g_flipHandler.m_nPresentFrequency != nNumVBlanks )
  523. {
  524. nNumVBlanks = MAX( 1, nNumVBlanks );
  525. nNumVBlanks = MIN( 12, nNumVBlanks );
  526. if ( g_flipHandler.m_nPresentFrequency != nNumVBlanks )
  527. {
  528. g_flipHandler.m_nPresentFrequency = nNumVBlanks;
  529. }
  530. }
  531. }
  532. /*
  533. void CFlipHandler::OnState( int nState, int nValue )
  534. {
  535. m_nDebugStates[nState] = nValue;
  536. if( m_nDebugStates[RENDERING_SURFACE] == m_nDebugStates[DISPLAYING_SURFACE] )
  537. DebuggerBreak();
  538. }*/