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

//========== 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 ) );
}