|
|
//========== Copyright � Valve Corporation, All rights reserved. ========
#include "tier0/memalloc.h"
#include "ps3/ps3_gcm_config.h"
#include "spudrawqueue.h"
#include "ps3gcmstate.h"
void SpuDrawQueue::Init( uint nBufferSize, uint32 * pSignal, FnFlushCallback_t fnFlushCallback, FnStallCallback_t fnStallCallback ) { if( nBufferSize < 2 * DRAWQUEUE_LSRING_SIZE ) { Warning("SpuDrawQueue requested size (%d bytes) is too small (must be at least %d), auto-adjusting\n", nBufferSize, 2 * DRAWQUEUE_LSRING_SIZE ); nBufferSize = 2 * DRAWQUEUE_LSRING_SIZE; } m_pBuffer = ( uint32* ) g_ps3gcmGlobalState.IoSlackAlloc( 128, nBufferSize ); m_pBufferEnd = AddBytes( m_pBuffer, nBufferSize & -16 ); m_pPut = m_pGet = m_pBuffer; *pSignal = GetSignal(); m_pSignal = pSignal;
m_fnFlushCallback = fnFlushCallback; m_fnStallCallback = fnStallCallback; m_fnFlushCallbackStack = NULL; #ifdef _DEBUG
m_nAllocBreakAddress = NULL; m_nAllocCount = m_nCollectCount = 0; m_nAllocBreak = m_nCollectBreak = 0; #endif
m_nAllocWords = 0; m_pFlushWatermark = AddBytes( m_pBuffer, DRAWQUEUE_LSRING_SIZE ); if( m_pFlushWatermark + 8 >= m_pBufferEnd ) { 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 ); } }
void SpuDrawQueue::PushFlushCallback( FnFlushCallback_t fnNewCallback ) { Assert( !m_fnFlushCallbackStack ); m_fnFlushCallbackStack = m_fnFlushCallback; m_fnFlushCallback = fnNewCallback; }
void SpuDrawQueue::PopFlushCallback() { Assert( m_fnFlushCallbackStack ); m_fnFlushCallback = m_fnFlushCallbackStack; m_fnFlushCallbackStack = NULL; }
void SpuDrawQueue::Shutdown() { g_ps3gcmGlobalState.IoSlackFree( m_pBuffer ); }
void SpuDrawQueue::UnallocToAlign() { m_pPut = ( uint32* )( uintp( m_pPut ) & -16 ); }
//////////////////////////////////////////////////////////////////////////
// REENTRANT: m_fnFlushCallback can in turn call AllocWords with a small number of words
//
uint32 *SpuDrawQueue::AllocWords( uint nWords /*, uint nAlignMask, uint nAlignValue*/ ) { #ifdef _DEBUG
uint32 * pSavePut = m_pPut, *pSaveGet = m_pGet;(void)(pSavePut, pSaveGet); m_nAllocCount++; if( m_nAllocCount == m_nAllocBreak ) DebuggerBreak(); #endif
Assert( nWords * sizeof( uint32 ) <= SPUDRAWQUEUE_NOPCOUNT_MASK ); uint32 * pOldPut = m_pPut, * pAllocation = pOldPut;//( uint32* )( uintp( pOldPut ) + ( ( nAlignValue - uintp( pOldPut ) ) & nAlignMask ) );
uint32 * pNewPut = pAllocation + nWords; bool bWrap = false;
if( pNewPut > m_pBufferEnd ) // do we need to wrap?
{ //we have to wrap...
if( m_pPut < m_pBufferEnd ) *m_pPut = SPUDRAWQUEUE_NOPCOUNT_METHOD | ( m_pBufferEnd - m_pPut - 1 ); pNewPut = m_pBuffer + nWords; bWrap = true; pAllocation = m_pBuffer; }
// since this put may be the last, we need to make sure that even after alignment, put != get
// so we wait for the space to free up for aligned put
uint32 * pNewAlignedPut = ( uint32* )AlignValue( uintp( pNewPut ), DMA_ALIGNMENT );
if( bWrap ? pOldPut <= m_pFlushWatermark || m_pFlushWatermark < pNewAlignedPut: pOldPut <= m_pFlushWatermark && m_pFlushWatermark < pNewAlignedPut ) { // collects , aligns and submits commands to SPU
m_fnFlushCallback( this ); // m_pPut may have changed slightly for alignment or EndZPass(), so we need to reconsider wrapping and recompute all pointers
pOldPut = m_pPut; pAllocation = pOldPut; pNewPut = pOldPut + nWords; bWrap = false;
if( pNewPut > m_pBufferEnd ) // do we need to wrap?
{ //we have to wrap...
if( m_pPut < m_pBufferEnd ) *m_pPut = SPUDRAWQUEUE_NOPCOUNT_METHOD | ( m_pBufferEnd - m_pPut - 1 ); pNewPut = m_pBuffer + nWords; bWrap = true; pAllocation = m_pBuffer; }
// since this put may be the last, we need to make sure that even after alignment, put != get
// so we wait for the space to free up for aligned put
pNewAlignedPut = ( uint32* )AlignValue( uintp( pNewPut ), DMA_ALIGNMENT ); }
// we must not allow new put == get, because it will cause the whole ring to suddenly be marked as empty
uint nSpins = 0; while( bWrap ? pOldPut < m_pGet || m_pGet <= pNewAlignedPut : pOldPut < m_pGet && m_pGet <= pNewAlignedPut ) { if( nSpins++ > 2 ) { m_fnStallCallback( this, m_pGet, nWords ); }
SetSignal( *m_pSignal ); } Assert( pNewPut >= m_pBuffer && pNewPut <= m_pBufferEnd ); Assert( pAllocation >= m_pBuffer && pAllocation <= m_pBufferEnd ); Assert( pAllocation + nWords >= m_pBuffer && pAllocation + nWords <= m_pBufferEnd );
m_pPut = pNewPut; // we don't need to use up the whole aligned buffer
#ifdef _DEBUG
if( pAllocation == m_nAllocBreakAddress ) DebuggerBreak(); #endif
m_nAllocWords += nWords; return pAllocation; }
// This is called within the Flush callback. May change m_pPut
// returns the number of bytes written from UNaligned start to UNaligned end
uint SpuDrawQueue::Collect( uint32 * pStartBatch, uint32 * pEndBatch, CDmaListConstructor & dmac ) { #ifdef _DEBUG
CDmaListConstructor saveDmac = dmac;(void)saveDmac; m_nCollectCount++; Assert( m_nCollectCount != m_nCollectBreak ); #endif
Assert( pStartBatch >= m_pBuffer && pStartBatch <= m_pBufferEnd && pEndBatch >= m_pBuffer && pEndBatch <= m_pBufferEnd ); uint nSize = 0; if( pEndBatch != pStartBatch ) // or else it's an empty transaction, nothing to upload
{ // align the put pointer for DMA - always safe because SPUs can't be processing the remainder of 16-byte block
// while we're writing into its beginning.
// while( uintp( pEndBatch ) & ( DMA_ALIGNMENT - 1 ) )
// {
// *( pEndBatch++ ) = 0;
// }
if( pEndBatch > pStartBatch ) { // it wraps
dmac.AddInputDmaLargeUnalignedRegion( pStartBatch, pEndBatch ); nSize += uintp( pEndBatch ) - uintp( pStartBatch ); } else { if( pStartBatch != m_pBufferEnd ) { dmac.AddInputDmaLargeUnalignedRegion( pStartBatch, m_pBufferEnd ); nSize += uintp( m_pBufferEnd ) - uintp( pStartBatch ); } dmac.AddInputDmaLargeUnalignedRegion( m_pBuffer, pEndBatch ); nSize += uintp( pEndBatch ) - uintp( m_pBuffer ); } } SetFlushWatermarkFrom( pEndBatch ); return nSize; }
void SpuDrawQueue::SetFlushWatermarkFrom( uint32 *pPut ) { m_pFlushWatermark = ( uint32* )( ( uintp( pPut ) + DRAWQUEUE_LSRING_SIZE ) & -16 ); while( m_pFlushWatermark >= m_pBufferEnd ) { m_pFlushWatermark -= m_pBufferEnd - m_pBuffer; } }
uint SpuDrawQueue::Length( uint32 * pBegin, uint32 * pEnd )const { Assert( IsValidCursor( pBegin ) && IsValidCursor( pEnd ) ); if( pBegin < pEnd ) return uintp( pEnd ) - uintp( pBegin ); else return ( uintp( m_pBufferEnd ) - uintp( pBegin ) ) + ( uintp( pEnd ) - uintp( m_pBuffer ) ); }
|