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
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
|