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

//========== Copyright © Valve Corporation, All rights reserved. ========
//
// Producer-consumer FIFO ring buffers
// THese are shared between SPU and PPU, and define some common misc functions
//
//
#ifndef VJOBS_PCRING_HDR
#define VJOBS_PCRING_HDR
#include "ps3/ps3_gcm_config.h"
#include "ps3/spu_job_shared.h"
struct ALIGN16 SetLabelAlignedCommand_t
{
union CmdUnion_t
{
uint32 m_nCmd[4];
vector unsigned int m_vuCmd;
};
CmdUnion_t m_cmd;
// uint32 m_nMethodSetSemaphoreOffset;
// uint32 m_nSemaphoreOffset;
// uint32 m_nMethodSemaphoreRelease;
// uint32 m_nSemaphoreValue;
void SetWriteTextureLabel( uint nIndex, uint nValue )
{
uint offset = 0x10 * nIndex;
#ifdef SPU
m_cmd.m_vuCmd = ( vector unsigned int )
{
CELL_GCM_METHOD(CELL_GCM_NV4097_SET_SEMAPHORE_OFFSET, 1),
(offset),
CELL_GCM_METHOD(CELL_GCM_NV4097_TEXTURE_READ_SEMAPHORE_RELEASE, 1),
(nValue)
};
#else
uint32 * p = m_cmd.m_nCmd;
CELL_GCM_METHOD_SET_SEMAPHORE_OFFSET(p, offset);
CELL_GCM_METHOD_TEXTURE_READ_SEMAPHORE_RELEASE(p, nValue);
#endif
}
void UpdateWriteTextureLabel( uint nValue )
{
m_cmd.m_nCmd[3] = nValue;
}
uint32 GetWriteTextureLabel( )
{
return m_cmd.m_nCmd[3];
}
}
ALIGN16_POST;
// read-only part of pcring
struct ALIGN16 PcRingRo_t
{
SetLabelAlignedCommand_t m_head[2]; // the variants of the head sync
void Init( uint nLabel );
};
//
// producer-consumer FIFO ring buffer for command buffer.
// Kept in main memory, controlled/produced by SPU, consumed by RSX
//
class ALIGN16 SysFifo
{
public:
uint32 m_eaBuffer; // the buffer begin, EA
uint32 m_nSize; // the whole buffer size
// the important thing here is that put and end pointers are independent
// and can be updated from different threads lock-free, wait-free
// 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
uint32 m_nPut, m_nEnd; // high bit means odd-even ring
enum {ODD_BIT = 0x80000000};
void Init( uintp eaBuffer, uint nSize, uint nPut = 0 )
{
// put may be anywhere (it must be the GCM control register PUT, realtively to eaBuffer), but it must be aligned and within the buffer
Assert( nPut < nSize && !( 0xF & nPut ) );
m_eaBuffer = ( uint32 )eaBuffer;
m_nSize = nSize;
m_nPut = nPut;
m_nEnd = ODD_BIT;
}
void HardReset()
{
m_nPut = 0;
m_nEnd = ODD_BIT;
}
// must wrap before put?
bool MustWrap( uint nPutBytes ) const
{
return ( ( m_nPut + nPutBytes ) & ~ODD_BIT ) > m_nSize;
}
bool IsOrdered( uint nSignal0, uint nSignal1 )
{
if( ( nSignal0 ^ nSignal1 ) & ODD_BIT )
{
return ( nSignal1 & ~ODD_BIT ) <= ( nSignal0 & ~ODD_BIT );
}
else
{
return nSignal0 <= nSignal1;
}
}
bool CanPutNoWrap( uint nPutBytes ) const
{
Assert( !MustWrap( nPutBytes ) );
if ( ( m_nPut ^ m_nEnd ) & ODD_BIT ) // bits are different => we have enough space till the end of the buffer
{
Assert( ( m_nPut | ODD_BIT ) >= ( m_nEnd | ODD_BIT ) ); // the End must be trailing behind Put , only in the NEXT ring
return true;
}
else
{
// bits are the same => we have continuous unsigned range between put, put+add, end
// 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
return ( m_nPut + nPutBytes < m_nEnd );
}
}
bool CanWrapAndPut( uint nPutBytes )const
{
if ( ( m_nPut ^ m_nEnd ) & ODD_BIT ) // to wrap, "end" must be in the next ring
{
// Important: Assume that we'll reset Put to 0 when we put ... and "add" must be before "end"
// 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
return ( nPutBytes < ( m_nEnd & ~ODD_BIT ) );
}
else
{
Assert( m_nPut <= m_nEnd ); // the End must be in front of Put, since it's in the same ring
return false;
}
}
void Wrap( )
{
Assert( ( m_nPut ^ m_nEnd ) & ODD_BIT );
m_nPut = ( ~m_nPut ) & ODD_BIT; // begin from the start, only in the next ring (invert odd/even)
}
// prepare to Put(nBytes); wrap if necessary; don't do anything unless subsequent Put(nBytes) is valid
enum PreparePutEnum_t
{
PUT_PREPARED_WRAPPED,
PUT_PREPARED_NOWRAP,
PUT_PREPARE_FAILED
};
PreparePutEnum_t PreparePut( uint nBytes )
{
if( MustWrap( nBytes ) )
{
if( CanWrapAndPut( nBytes ) )
{
Wrap();
Assert( CanPutNoWrap( nBytes ) );
return PUT_PREPARED_WRAPPED;
}
}
else
{
if( CanPutNoWrap( nBytes ) )
{
return PUT_PREPARED_NOWRAP;
}
}
return PUT_PREPARE_FAILED;
}
// NOTE: the guarantee of this function is that multiple Puts are additive: Put(100) is equivalent to Put(25),Put(75) and such
void Put( uint nPutBytes )
{
Assert( CanPutNoWrap( nPutBytes ) );
m_nPut += nPutBytes;
}
uint EaPut( )const
{
return m_eaBuffer + ( m_nPut & ~ODD_BIT );
}
uint PutToEa( uint nPut )const
{
return m_eaBuffer + ( nPut & ~ODD_BIT );
}
uint EaWrapAndPut()const // EA of PUT after Wrap() is executed
{
return m_eaBuffer + sizeof( SetLabelAlignedCommand_t );
}
// how much memory is left in this ring, without Wrapping?
int GetNoWrapCapacity()const
{
return m_nSize - ( m_nPut & ~ODD_BIT );
}
// returns a value that will signal that the buffer has been processed to m_nPut pointer
uint GetSignal()const
{
return m_nPut ^ ODD_BIT;
}
// FFFFFFFF would imply a 2-Gb buffer, which we clearly can't have on PS3
static uint GetInvalidSignal() { return 0xFFFFFFFF; }
const SetLabelAlignedCommand_t * GetHead( PcRingRo_t &ro )const
{
return &ro.m_head[m_nPut >> 31];
}
const SetLabelAlignedCommand_t * GetNextHead( PcRingRo_t &ro )const
{
return &ro.m_head[( ~m_nPut ) >> 31];
}
// notify about a signal coming in asynchronously , must be a result of GetSignal() after Put()
void NotifySignal( uint nSignal )
{
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 );
if( SPUGCM_ENABLE_NOTIFY_RSX_GET )
{
// we can artificially set the signal ahead sometimes, because we have 2 streams of signals from RSX :
// THe control register GET and the cmd buffer label (GCM_LABEL_SYSRING_SIGNAL)
// so we'll filter extra signals here: we may NOT step back
if( !( ( nSignal ^ m_nEnd ) & ODD_BIT ) && nSignal < m_nEnd )
{
return;// skip this: signal and end are in the same ring and signal is earlier than end
}
}
AssertSpuMsg( ( ( nSignal ^ m_nPut ) & ODD_BIT ) ?
( nSignal & ~ODD_BIT ) <= ( m_nPut & ~ODD_BIT )// signal and put are in different rings
:
nSignal >= m_nPut, // signal and put are in the same ring
"{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
);
m_nEnd = nSignal;
}
// NotifySignal() version that can tolerate outdated signals due to different latencies between SPU and RSX
void NotifySignalSafe( uint nSignal )
{
if( ( ( nSignal ^ m_nPut ) & ODD_BIT ) ?
( nSignal & ~ODD_BIT ) <= ( m_nPut & ~ODD_BIT )// signal and put are in different rings
:
nSignal >= m_nPut ) // signal and put are in the same ring
{
m_nEnd = nSignal;
}
}
bool IsSignalDifferent( uint nSignal )
{
return m_nEnd != nSignal;
}
// WARNING this is a debug-only function. Do not use for anything but debugging, because it's slow
// and because it will signal "finished" incorrectly when the whole ring is full
// Expects RSX get relative to the base of the buffer (i.e. 0 when Get == the byte 0 of this PCring)
bool NotifyRsxGet( uint nRsxControlRegisterGet )
{
if( nRsxControlRegisterGet == ( m_nPut & ~ODD_BIT ) )
{
m_nEnd = m_nPut ^ ODD_BIT; // assume this means we've processed all SYSRING buffer
return true;
}
else
{
return false;
}
}
bool IsRsxFinished( uint nRsxControlRegisterGet )
{
return nRsxControlRegisterGet == ( m_nPut & ~ODD_BIT );
}
bool IsDone()const
{
return ( m_nEnd == m_nPut ^ ODD_BIT );
}
}
ALIGN16_POST;
inline void PcRingRo_t::Init( uint nLabel )
{
m_head[0].SetWriteTextureLabel( nLabel, SysFifo::ODD_BIT | sizeof( SetLabelAlignedCommand_t ) ); // m_nPut == 0 -> signal == ODD_BIT
m_head[1].SetWriteTextureLabel( nLabel, sizeof( SetLabelAlignedCommand_t ) ); // m_nPut == ODD_BIT -> signal == 0
}
#endif