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
210 lines
6.8 KiB
//========== 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 ) );
|
|
}
|