This commit is contained in:
nephacks
2025-06-04 03:22:50 +02:00
parent f234f23848
commit f12416cffd
14243 changed files with 6446499 additions and 26 deletions

18
common/ps3/job_notify.h Normal file
View File

@@ -0,0 +1,18 @@
//========== Copyright © Valve Corporation, All rights reserved. ========
#ifndef JOB_NOTIFY_HDR
#define JOB_NOTIFY_HDR
#include "ps3/spu_job_shared.h"
namespace job_notify
{
struct ALIGN16 NotifyArea_t
{
uint32 m_nCopyTo;
uint32 m_nCopyFrom;
uint32 m_nSpuId;
}ALIGN16_POST;
}
#endif

10
common/ps3/prx.cpp Normal file
View File

@@ -0,0 +1,10 @@
#include "dbg.h"
#include "tier1/interface.h"
#include "ps3/ps3_helpers.h"
#ifndef DLLNAME
#error DLLNAME is not defined
#define DLLNAME unknownprxmodule
#endif
PS3_PRX_APPSYSTEM_MODULE( DLLNAME )

11
common/ps3/ps3_assert.h Normal file
View File

@@ -0,0 +1,11 @@
// PS3 Assert : As of SDK 084, the PS3 assert found in assert.h will simply break execution
// by calling __abort: this will exit the application instead of breaking
// execution in the debugger.
#if defined(_PS3) && defined(_DEBUG)
#ifdef assert
#undef assert
#endif
#define assert(v)
//if(!(v)) { *(int*)0x1 = 0; }
#endif

View File

@@ -0,0 +1,17 @@
// IMAGEBUILDER SHOULD MAKE A LINE LIKE THIS WITH THE CHANGELIST NUMBER #DEFINED IN:
// IT'S OKAY IF IT BLOWS AWAY THE REST OF THIS FILE
// ONLY THIS ONE #define IS NECESSARY:
// #define APPCHANGELISTVERSION 936857
// IMAGEBUILDER SHOULD NOT EMIT THE FOLLOWING LINES
// THEY INDICATE THAT THIS IS *NOT* AN AUTOBUILT IMAGE
// AND SHOULD BE ABSENT FROM THE AUTOBUILDER'S LOCAL
// VERSION OF THIS .H
#define PS3CLVERMAJOR 0
#define PS3CLVERMINOR 1

116
common/ps3/ps3_console.h Normal file
View File

@@ -0,0 +1,116 @@
#ifndef PS3_CONSOLE_H
#define PS3_CONSOLE_H
#include <cell/cell_fs.h>
#include "ps3_platform.h"
#include <sysutil/sysutil_sysconf.h>
#include "ps3/ps3_vxconsole.h"
#include "ps3/ps3_core.h"
#include "tier0/dbg.h"
#if 0
//-- The following, subject to being moved somewhere more sensible - DL
#define PS3_SYSUTIL_CALLBACK_SLOT 0
#define SOUND_SYSUTIL_CALLBACK_SLOT 1
bool PS3_RegisterSysUtilCallback( void );
bool PS3_RegisterDiscChangeCallback( void );
void PS3_UnRegisterSysUtilCallback( void );
void PS3_UnRegisterDiscChangeCallback( void );
void PS3_CheckSysUtilCallback( void );
int PS3_OpenSystemConfigurationDialog(CellSysconfType iConfType,CellSysconfCallback pCallBack, void * pUserData );
void PS3_CloseSystemConfigurationDialog();
void PS3_CleanSystemConfigurationDialog();
//--
#endif
/*
//void PS3_SendRemoteCommand( const char* dbgCommand, bool bAsync );
bool PS3_DebugString( unsigned int color, const char* format, ... );
bool PS3_IsConsoleConnected();
void PS3_InitConsoleMonitor( bool bWaitForConnect = false );
int XBX_rAddCommands(int numCommands, const char* commands[], const char* help[]);
//updates the status of the console connection and handles new messages
void PS3_UpdateConsoleMonitor();
//language settings
const char* PS3_GetLanguageString( void );
bool PS3_IsLocalized( void );
*/
// for integration with vxconsole profile bars:
#define XBX_MAX_PROFILE_COUNTERS 64
#define XMAKECOLOR( r, g, b ) ((unsigned int)(((unsigned char)(r)|((unsigned int)((unsigned char)(g))<<8))|(((unsigned int)(unsigned char)(b))<<16)))
class IPS3Console
{
public:
// // // X360 console interface
virtual void SendRemoteCommand( const char *dbgCommand, bool bAsync ) = 0;
virtual void SendPrefixedDECIMessage( const char *prefix, const char *message, bool async ) = 0;
virtual void DebugString( unsigned int color, const char *format, ... ) = 0;
virtual bool IsConsoleConnected() = 0;
virtual void InitConsoleMonitor( bool bWaitForConnect = false ) = 0;
virtual void DisconnectConsoleMonitor() = 0;
virtual void FlushDebugOutput() = 0;
virtual bool GetXboxName( char *, unsigned * ) = 0;
virtual void CrashDump( bool ) = 0;
virtual void CrashDumpFullHeap( bool ) = 0;
virtual void DumpDllInfo( const char *pBasePath ) = 0;
// virtual void OutputDebugString( const char * ) = 0;
virtual bool IsDebuggerPresent() = 0;
virtual int SetProfileAttributes( const char *pProfileName, int numCounters, const char *names[], COLORREF colors[] ) = 0;
virtual void SetProfileData( const char *pProfileName, int numCounters, unsigned int *counters ) = 0;
virtual int MemDump( const char *pDumpFileName ) = 0;
virtual int TimeStampLog( float time, const char *pString ) = 0;
virtual int MaterialList( int nMaterials, const xMaterialList_t *pXMaterialList ) = 0;
virtual int TextureList( int nTextures, const xTextureList_t *pXTextureList ) = 0;
virtual int SoundList( int nSounds, const xSoundList_t *pXSoundList ) = 0;
virtual int MapInfo( const xMapInfo_t *pXMapInfo ) = 0;
virtual int AddCommands( int numCommands, const char *commands[], const char* help[] ) = 0;
virtual int ModelList( int nModels, const xModelList_t *pList ) = 0;
virtual int DataCacheList( int nItems, const xDataCacheItem_t* pItems ) = 0;
virtual int VProfNodeList( int nItems, const xVProfNodeItem_t *pItems ) = 0;
virtual int TraceComplete( void ) = 0;
virtual int BugReporter( void ) = 0;
virtual bool SendBinaryData( const void *pData, int iDataSize, bool bAsync = true, DWORD dwSyncTimout = 15000 ) = 0; //returns false if sync call timed out or not connected. Otherwise true
virtual int SyncDvdDevCache() = 0;
virtual int SyncShaderCache() = 0;
virtual int Version( int nVersion ) = 0;
// // // PS3 specific
// manually send a frame buffer across the DECI wire because the VRAM capture comes with all
// sorts of untenable encumberances
virtual void TransmitScreenshot( char *pFrameBuffer, uint32 uWidth, uint32 uHeight, uint32 uPitch, uint32 uColorFmt ) = 0;
// send an arbitrary blob of data of a given length. You have to supply the prefix yourself (avoids an unnecessary additional copy).
virtual int SendBinaryDECI( const uint8 *dbgCommand, uint length, bool bHaltOnError = true ) = 0;
// a function that receives fake xbox messages
typedef void (*fRemoteCommandSink_t)(const char *pRemoteCommandBuffer);
// synchronous message pumping, could make an async thread in the future if necessary
virtual void PumpMessage( fRemoteCommandSink_t ) = 0; // pump up to ONE remote command message synchronously
// Add delegates that are called when the console is connected/disconnected to VXConsole
typedef void (*fOnConnectDelegate_t)( void );
typedef void (*fOnDisconnectDelegate_t)( void );
virtual void AddOnConnectDelegate( fOnConnectDelegate_t pOnConnectDelegate ) = 0;
virtual void AddOnDisconnectDelegate( fOnDisconnectDelegate_t pOnDisconnectDelegate ) = 0;
};
PLATFORM_INTERFACE IPS3Console *g_pValvePS3Console;
PLATFORM_INTERFACE void ValvePS3ConsoleInit();
PLATFORM_INTERFACE void ValvePS3ConsoleShutdown();
#endif //PS3_CONSOLE_H

522
common/ps3/ps3_core.h Normal file
View File

@@ -0,0 +1,522 @@
//========= Copyright © 1996-2004, Valve LLC, All rights reserved. ============
//
// Purpose: XBox HAL - Game to hardware abstraction
//
//=============================================================================
#pragma once
#define XBX_BREAK(x) if (x) {DebugBreak();}
//#define XBX_MAX_DPORTS 4
// PS3 supports up to 7 controllers!
#define XUSER_MAX_COUNT 7
#define XBX_MAX_EVENTS 32
#define XBX_MAX_BUTTONSAMPLE 32768
#define XBX_MAX_ANALOGSAMPLE 255
#define XBX_MAX_MESSAGE 1024
#define XBX_MAX_PATH MAX_PATH
#define XBX_MAX_RCMDLENGTH 256
#define XBX_MAX_RCMDNAMELEN 32
#define XBX_HDD_CLUSTERSIZE 16384
#define XBX_INVALID_STORAGE_ID ((DWORD)(-1))
#define XBX_STORAGE_DECLINED ((DWORD)(-2))
#define XBX_INVALID_USER_ID ((DWORD)(-1))
#define XBX_USER_SETTINGS_CONTAINER_DRIVE "CFG"
#define XBX_USER_SAVES_CONTAINER_DRIVE "SAV"
typedef uint64_t XUID;
#define IsEqualXUID( a, b ) ( ( a ) == ( b ) )
typedef struct {
BYTE ab[8]; // xbox to xbox key identifier
} XNKID;
typedef struct {
BYTE ab[16]; // xbox to xbox key exchange key
} XNKEY;
#define XCONTENT_MAX_DISPLAYNAME_LENGTH 128
#define XCONTENT_MAX_FILENAME_LENGTH 42
#define XCONTENTDEVICE_MAX_NAME_LENGTH 27
typedef DWORD XCONTENTDEVICEID, *PXCONTENTDEVICEID;
typedef struct _XCONTENT_DATA
{
XCONTENTDEVICEID DeviceID;
DWORD dwContentType;
wchar_t szDisplayName[XCONTENT_MAX_DISPLAYNAME_LENGTH];
char szFileName[XCONTENT_MAX_FILENAME_LENGTH];
} XCONTENT_DATA, *PXCONTENT_DATA;
// could be dvd or hdd, actual device depends on source of xbe launch
#define XBX_DVD_DRIVE "D:\\"
#define XBX_BOOT_DRIVE "D:\\"
#define XBX_SWAP_DIRECTORY "T:\\HL2\\HL2X\\SWAP\\"
#define XBX_PERSISTENT_DIR "T:\\HL2\\"
#define XBX_HDD_SAVE_DIRECTORY "U:\\"
#define XBX_IOTHREAD_STACKSIZE 32768
#define XBX_IOTHREAD_PRIORITY THREAD_PRIORITY_HIGHEST
#define XBX_SCREEN_WIDTH 640
#define XBX_SCREEN_HEIGHT 480
#define XBOX_MINBORDERSAFE 0.04f
#define XBOX_MAXBORDERSAFE 0.04f
#ifndef GPU_RESOLVE_ALIGNMENT
#define GPU_RESOLVE_ALIGNMENT 8
#endif
#define XBX_CALCSIG_TYPE XCALCSIG_FLAG_NON_ROAMABLE
#define XBX_VIRTUAL_BASEDIR "r:\\hl2"
#define XBX_GAMEDIR "hl2x"
#if defined( _DEBUG )
#define XBX_XBE_BASE_FILENAME "hl2d_xbox.xbe"
#elif defined( _RELEASE )
#define XBX_XBE_BASE_FILENAME "hl2r_xbox.xbe"
#else
#define XBX_XBE_BASE_FILENAME "hl2_xbox.xbe"
#endif
// Path to our running executable
#define XBX_XBE_PATH XBX_BOOT_DRIVE XBX_XBE_BASE_FILENAME
#define CLR_DEFAULT 0xFF000000
#define CLR_WARNING 0x0000FFFF
#define CLR_ERROR 0x000000FF
#define XBX_ALIGN(x,y) (((x)+((y)-1))&~((y)-1))
// disk space requirements
#define HL2_SAVEIMAGE_BYTES ( 1024 * 70 )
#define HL2_SAVEGAME_BYTES ( 1024 * 1024 * 10 )
#define HL2_CONFIGFILE_BYTES XBX_HDD_CLUSTERSIZE
#define HL2_PERSISTENT_BYTES_NEEDED ( HL2_CONFIGFILE_BYTES + HL2_SAVEGAME_BYTES * 2 )
#define HL2_USERSAVE_BYTES_NEEDED ( HL2_SAVEGAME_BYTES + HL2_SAVEIMAGE_BYTES + XBX_GetSaveGameOverhead() )
#define FILE_BEGIN 0
#define FILE_CURRENT 1
#define FILE_END 2
typedef enum
{
XC_NULL,
XC_NORMAL,
XC_IBEAM,
XC_WAIT,
XC_CROSS,
XC_UP,
XC_SIZENWSE,
XC_SIZENESW,
XC_SIZEWE,
XC_SIZENS,
XC_SIZEALL,
XC_NO,
XC_HAND,
XC_MAXCURSORS,
} xCursor_e;
typedef enum
{
XK_NULL,
XK_BUTTON_UP,
XK_BUTTON_DOWN,
XK_BUTTON_LEFT,
XK_BUTTON_RIGHT,
XK_BUTTON_START,
XK_BUTTON_BACK,
XK_BUTTON_STICK1,
XK_BUTTON_STICK2,
XK_BUTTON_A,
XK_BUTTON_B,
XK_BUTTON_X,
XK_BUTTON_Y,
XK_BUTTON_LEFT_SHOULDER,
XK_BUTTON_RIGHT_SHOULDER,
XK_BUTTON_LTRIGGER,
XK_BUTTON_RTRIGGER,
XK_STICK1_UP,
XK_STICK1_DOWN,
XK_STICK1_LEFT,
XK_STICK1_RIGHT,
XK_STICK2_UP,
XK_STICK2_DOWN,
XK_STICK2_LEFT,
XK_STICK2_RIGHT,
XK_BUTTON_INACTIVE_START, // Special key that is passed through on disabled controllers
XK_BUTTON_FIREMODE_SELECTOR_1,
XK_BUTTON_FIREMODE_SELECTOR_2,
XK_BUTTON_FIREMODE_SELECTOR_3,
XK_BUTTON_RELOAD,
XK_BUTTON_TRIGGER,
XK_BUTTON_PUMP_ACTION,
XK_XBUTTON_ROLL_RIGHT,
XK_XBUTTON_ROLL_LEFT,
XK_MAX_KEYS,
} xKey_t;
typedef enum
{
XVRB_NONE, // off
XVRB_ERROR, // fatal error
XVRB_ALWAYS, // no matter what
XVRB_WARNING, // non-fatal warnings
XVRB_STATUS, // status reports
XVRB_ALL,
} xverbose_e;
typedef enum
{
XOF_READ = 0x01, // read access
XOF_WRITE = 0x02, // write access
XOF_CREATE = 0x04, // create if not exist
} xopenfile_e;
typedef enum
{
XSF_SET = FILE_BEGIN,
XSF_CUR = FILE_CURRENT,
XSF_END = FILE_END,
} xseekfile_e;
typedef enum
{
XFA_LOCALONLY,
XFA_REMOTEONLY,
XFA_LOCALFIRST,
} xFileAccess_e;
typedef enum
{
XEV_NULL,
XEV_KEY,
XEV_REMOTECMD,
XEV_LISTENER_NOTIFICATION,
XEV_QUIT,
XEV_GAMEPAD_UNPLUGGED,
XEV_GAMEPAD_INSERTED,
} xevent_e;
typedef struct xevent_s
{
xevent_e event;
int arg1;
int arg2;
int arg3;
uint64 sysutil_status;
uint64 sysutil_param;
} xevent_t;
typedef struct xevent_SYS_SIGNINCHANGED_s
{
XUID xuid[ XUSER_MAX_COUNT ];
int state[ XUSER_MAX_COUNT ];
DWORD dwParam;
} xevent_SYS_SIGNINCHANGED_t;
typedef struct ps3syscbckeventhdlr_s
{
int (*pfnHandler)( xevent_t const &ev );
} ps3syscbckeventhdlr_t;
typedef enum
{
MDIR_NULL = 0x00,
MDIR_UP = 0x01,
MDIR_DOWN = 0x02,
MDIR_LEFT = 0x04,
MDIR_RIGHT = 0x08,
} xMouseDir_e;
typedef struct
{
const char *pName;
const char *pGroupName;
const char *pFormatName;
int size;
int width;
int height;
int depth;
int numLevels;
int binds;
int refCount;
int sRGB;
int edram;
int procedural;
int cacheableState;
int cacheableSize;
int final;
int failed;
int pwl;
int reduced;
} xTextureList_t;
typedef struct
{
const char *pName;
const char *pShaderName;
int refCount;
} xMaterialList_t;
typedef struct
{
char name[MAX_PATH];
char formatName[32];
int rate;
int bits;
int channels;
int looped;
int dataSize;
int numSamples;
int streamed;
int quality;
} xSoundList_t;
typedef struct
{
float position[3];
float angle[3];
char mapPath[256];
char savePath[256];
int build;
int skill;
char details[1024];
} xMapInfo_t;
/******************************************************************************
XBX_CONSOLE.CPP
******************************************************************************/
extern int XBX_rAddCommands(int numCommands, const char* commands[], const char* help[]);
extern int XBX_rTextureList(int nTextures, const xTextureList_t* pXTextureList);
extern int XBX_rTimeStampLog(float time, const char *pString);
#define XBX_rMaterialList if ( !g_pValvePS3Console ); else g_pValvePS3Console->MaterialList //(int nMaterials, const xMaterialList_t* pXMaterialList);
// inline int XBX_rSoundList(int nSounds, const xSoundList_t* pXSoundList) { return g_pValvePS3Console ? g_pValvePS3Console->SoundList( nSounds, pXSoundList ) : -1 ; }
#define XBX_rSoundList if ( !g_pValvePS3Console ); else g_pValvePS3Console->SoundList
extern int XBX_rMemDump( int nStatsID );
#define XBX_rMapInfo if ( !g_pValvePS3Console ) ; else g_pValvePS3Console->MapInfo //( xMapInfo_t *pMapInfo ) { return g_pValvePS3Console ? g_pValvePS3Console->MapInfo( pMapInfo ) : -1 ; }
/******************************************************************************
XBX_DEBUG.CPP
******************************************************************************/
// extern void XBX_SendRemoteCommand(const char* dbgCommand, bool async);
#define XBX_SendRemoteCommand if ( !g_pValvePS3Console ) ; else g_pValvePS3Console->SendRemoteCommand
#define XBX_SendPrefixedMsg if ( !g_pValvePS3Console ) ; else g_pValvePS3Console->SendPrefixedDECIMessage
// extern void XBX_DebugString(xverbose_e verbose, COLORREF color, const char* format, ...);
#define XBX_DebugString if ( !g_pValvePS3Console ) ; else g_pValvePS3Console->DebugString
extern void XBX_SetVerbose(xverbose_e verbose);
extern void XBX_InitDebug(void);
extern void XBX_Log( const char *pString );
extern bool g_xbx_bNoVXConsole;
/******************************************************************************
XBX_DEVICES.CPP
******************************************************************************/
extern void XBX_InitDevices(void);
extern void XBX_SampleDevices(void);
extern void XBX_SetRumble( float fLeftMotor, float fRightMotor );
extern void XBX_StopRumble( void );
extern void XBX_SetActiveController( int port );
extern int XBX_GetActiveController();
extern bool XBX_IsControllerValid( int port );
/******************************************************************************
XBX_DISPLAY.CPP
******************************************************************************/
extern u32_t XBX_GetDisplayTime(void);
extern void XBX_CreateDisplay(void);
extern void XBX_BeginFrame(void);
extern void XBX_EndFrame(void);
//EAPS3extern void XBX_HookD3DDevice(IDirect3DDevice8* pD3DDevice);
//EAPS3extern IDirect3DDevice8 *g_xbx_pD3DDevice;
extern u32_t g_xbx_numVBlanks;
extern u32_t g_xbx_frameTime;
extern int g_xbx_numFrames;
/******************************************************************************
XBX_FILEIO.CPP
******************************************************************************/
enum xFileDevice_e
{
XFD_NULL,
XFD_LOCALHDD,
XFD_REMOTEHDD,
XFD_DVDROM,
XFD_TITLE_PERSISTENT_HDD,
};
enum XFileMode_t
{
XFM_BINARY,
XFM_TEXT
};
//EAPS3 : XBox structure
struct WIN32_FIND_DATA
{
};
//EAPS3 : XBox structure
struct XGAME_FIND_DATA
{
};
extern void XBX_FixupFilename(const char* input, char* output, xFileDevice_e& outFileDevice, bool bWarnInvalid = true);
extern void XBX_SetRemotePath(const char* remotePath);
extern void XBX_SetLocalPath(const char* localPath);
extern const char *XBX_GetLocalPath();
extern FILE* XBX_fopen(const char* filename, const char* options);
extern int XBX_setvbuf( FILE *fp, char *,int mode, size_t size );
extern int XBX_fclose(FILE* fp);
extern int XBX_fseek(FILE *fp, long pos, int seekType);
extern long XBX_ftell(FILE *fp);
extern int XBX_feof(FILE *fp);
extern size_t XBX_fread(void *dest, size_t size, size_t count, FILE *fp);
extern size_t XBX_fwrite(const void *src, size_t size, size_t count, FILE *fp);
extern bool XBX_setmode( FILE *fp, int mode );
extern size_t XBX_vfprintf(FILE *fp, const char *fmt, va_list list);
extern int XBX_ferror(FILE *fp);
extern int XBX_fflush(FILE *fp);
extern char* XBX_fgets(char *dest, int destSize, FILE *fp);
extern int XBX_stat(const char *path, struct _stat *buf);
//extern int XBX_unlink(const char* filename);
extern int XBX_rename(const char* pszFrom, const char* pszTo);
extern HANDLE XBX_FindFirstFile(const char *findname, WIN32_FIND_DATA *dat);
extern BOOL XBX_FindNextFile(HANDLE handle, WIN32_FIND_DATA *dat);
extern BOOL XBX_FindClose(HANDLE handle);
extern void XBX_SetFileAccess(xFileAccess_e mode);
extern void XBX_EnableFileSync(bool bSync);
//extern int XBX_mkdir( const char * );
extern void XBX_rFileSync(const char *pFileName);
extern DWORD XBX_GetSigSize( DWORD sigType );
extern void XBX_CalculateSignature( BYTE *buff, void *pSig, DWORD buffSize, DWORD sigType );
extern bool XBX_ValidateSignature( BYTE *pBuffer, DWORD size, DWORD sigType );
extern bool g_xbx_bFileSync;
extern DWORD XBX_GetSigSize( DWORD sigType );
extern void XBX_CalculateSignature( BYTE *buff, void *pSig, DWORD buffSize, DWORD sigType );
extern bool XBX_ValidateSignature( BYTE *pBuffer, DWORD size, DWORD sigType );
extern bool XBX_SaveFileExists( const wchar_t *pName, XGAME_FIND_DATA *fileData );
extern bool XBX_SaveNumberExists( const int number, XGAME_FIND_DATA *fileData );
/******************************************************************************
XBX_MEMORY.CPP
******************************************************************************/
extern void XBX_InitMemory(void);
extern void XBX_EnableMemoryTrace(bool enable);
extern unsigned int g_xbx_memoryD3DCost;
/******************************************************************************
XBX_PROFILE.CPP
******************************************************************************/
class xVProfNodeItem_t;
extern int XBX_rSetProfileAttributes(const char *pProfileName, int numCounters, const char *names[], COLORREF colors[]);
extern void XBX_rSetProfileData( const char *pProfileName, int numCounters, unsigned int *counters );
extern void XBX_rVProfNodeList( int nItems, const xVProfNodeItem_t *pItems );
/******************************************************************************
XBX_SYSTEM.CPP
******************************************************************************/
extern void XBX_StringCopyToWide( WCHAR *pDst, const char *pSrc );
extern u32_t XBX_GetSystemTime(void);
ULARGE_INTEGER XBX_GetFreeBytes( const char *drive );
extern unsigned int XBX_GetBlocksNeeded( const char *drive, DWORD bytesRequested );
extern DWORD XBX_GetSaveGameOverhead( void );
extern void XBX_Error(const char* format, ...);
extern void XBX_Init();
extern bool XBX_IsRetailMode();
extern void XBX_RelaunchHL2( unsigned int contextCode = 0, const char *pszArgs = "", void *pRelaunchData = NULL, unsigned nBytesRelaunchData = 0 );
extern bool XBX_GetRelaunchContext( unsigned int *pContextCode, __int64 *pStartTime );
extern bool XBX_GetRelaunchData( void *pRelaunchData, unsigned maxBytes );
extern void XBX_LaunchInstaller( unsigned int contextCode );
extern void XBX_LaunchDashboard( char chDrive, int spaceNeeded );
extern LPSTR g_xbx_pCmdLine;
extern const char* g_xbx_version;
extern bool XBX_NoXBDM();
typedef HRESULT (STDCALL *DmSendNotificationStringFunc_t)(LPCSTR sz);
typedef HRESULT (STDCALL *DmRegisterCommandProcessorFunc_t)(LPCSTR szProcessor, PVOID /*HACK:/needsport/ WAS:PDM_CMDPROC /Vitaliy/ */ pfn);
typedef HRESULT (STDCALL *DmCaptureStackBackTraceFunc_t)(ULONG FramesToCapture, PVOID *BackTrace);
typedef BOOL (STDCALL *DmIsDebuggerPresentFunc_t)(void);
extern DmSendNotificationStringFunc_t CallDmSendNotificationString;
extern DmRegisterCommandProcessorFunc_t CallDmRegisterCommandProcessor;
extern DmCaptureStackBackTraceFunc_t CallDmCaptureStackBackTrace;
extern DmIsDebuggerPresentFunc_t CallDmIsDebuggerPresent;
/******************************************************************************
XBX_EVENTS.CPP
******************************************************************************/
// Event handling
PLATFORM_INTERFACE bool XBX_NotifyCreateListener( uint64_t categories );
PLATFORM_INTERFACE void XBX_QueueEvent( xevent_e event, int arg1, int arg2, int arg3 );
PLATFORM_INTERFACE void XBX_ProcessEvents( void );
PLATFORM_INTERFACE void XBX_DispatchEventsQueue( void );
// Accessors
PLATFORM_INTERFACE const char* XBX_GetLanguageString( void );
PLATFORM_INTERFACE bool XBX_IsLocalized( void );
PLATFORM_INTERFACE bool XBX_IsAudioLocalized( void );
PLATFORM_INTERFACE const char *XBX_GetNextSupportedLanguage( const char *pLanguage, bool *pbHasAudio );
PLATFORM_INTERFACE bool XBX_IsRestrictiveLanguage( void );
PLATFORM_INTERFACE u32_t XBX_GetImageChangelist( void );
//
// Storage devices management
//
PLATFORM_INTERFACE void XBX_ResetStorageDeviceInfo();
PLATFORM_INTERFACE DWORD XBX_DescribeStorageDevice( DWORD nStorageID );
PLATFORM_INTERFACE char const *XBX_MakeStorageContainerRoot( int iController, char const *szRootName, char *pBuffer, int numBufferBytes );
PLATFORM_INTERFACE DWORD XBX_GetStorageDeviceId( int iController );
PLATFORM_INTERFACE void XBX_SetStorageDeviceId( int iController, DWORD id );
//
// Information about game primary user
//
PLATFORM_INTERFACE DWORD XBX_GetPrimaryUserId( void );
PLATFORM_INTERFACE void XBX_SetPrimaryUserId( DWORD id );
PLATFORM_INTERFACE DWORD XBX_GetPrimaryUserIsGuest( void );
PLATFORM_INTERFACE void XBX_SetPrimaryUserIsGuest( DWORD bPrimaryUserIsGuest );
//
// Disabling and enabling input from controllers
//
PLATFORM_INTERFACE void XBX_ResetUserIdSlots();
PLATFORM_INTERFACE void XBX_ClearUserIdSlots();
//
// Mapping between game slots and controllers
//
PLATFORM_INTERFACE int XBX_GetUserId( int nSlot );
PLATFORM_INTERFACE int XBX_GetSlotByUserId( int idx );
PLATFORM_INTERFACE void XBX_SetUserId( int nSlot, int idx );
PLATFORM_INTERFACE void XBX_ClearSlot( int nSlot );
PLATFORM_INTERFACE void XBX_ClearUserId( int idx );
PLATFORM_INTERFACE DWORD XBX_GetUserIsGuest( int nSlot );
PLATFORM_INTERFACE void XBX_SetUserIsGuest( int nSlot, DWORD dwUserIsGuest );
//
// Number of game users
//
PLATFORM_INTERFACE DWORD XBX_GetNumGameUsers( void );
PLATFORM_INTERFACE void XBX_SetNumGameUsers( DWORD numGameUsers );
//
// Invite related functions
//
PLATFORM_INTERFACE XNKID XBX_GetInviteSessionId( void );
PLATFORM_INTERFACE void XBX_SetInviteSessionId( XNKID nSessionId );
PLATFORM_INTERFACE XUID XBX_GetInvitedUserXuid( void );
PLATFORM_INTERFACE void XBX_SetInvitedUserXuid( XUID xuid );
PLATFORM_INTERFACE DWORD XBX_GetInvitedUserId( void );
PLATFORM_INTERFACE void XBX_SetInvitedUserId( DWORD nUserId );

45
common/ps3/ps3_d3dstubs.h Normal file
View File

@@ -0,0 +1,45 @@
//========= Copyright © 1996-2004, Valve LLC, All rights reserved. ============
//
// Purpose: XBox win32 stubs
//
// $NoKeywords: $
//=============================================================================
#pragma once
typedef void* IDirect3D9;
typedef void* IDirect3DDevice9;
typedef void* IDirect3DVertexShader9;
typedef void* IDirect3DPixelShader9;
typedef void* IDirect3DVertexDeclaration9;
typedef void* IDirect3DTexture9;
typedef void* IDirect3DBaseTexture9;
typedef void* IDirect3DCubeTexture9;
typedef void* IDirect3DSurface9;
typedef void* IDirect3DIndexBuffer9;
typedef void* IDirect3DVertexBuffer9;
typedef void* IDirect3DVolumeTexture9;
typedef void ID3DXFont;
typedef void* D3DADAPTER_IDENTIFIER9;
typedef void* D3DCAPS9;
typedef void* D3DMATERIAL9;
typedef void* D3DVIEWPORT9;
typedef void* D3DLIGHT9;
typedef void* D3DVERTEXELEMENT9;
// not sure what behavior to emulate yet
#define D3DLOCK_NOSYSLOCK 0
#define D3DLOCK_DISCARD 0
#define D3DENUM_WHQL_LEVEL 0
// collapse sampler state back into texture state
typedef enum
{
D3DSAMP_ADDRESSU = D3DTSS_ADDRESSU,
D3DSAMP_ADDRESSV = D3DTSS_ADDRESSV,
D3DSAMP_ADDRESSW = D3DTSS_ADDRESSW,
D3DSAMP_MINFILTER = D3DTSS_MINFILTER,
D3DSAMP_MAGFILTER = D3DTSS_MAGFILTER,
D3DSAMP_MIPFILTER = D3DTSS_MIPFILTER,
D3DSAMP_MAXANISOTROPY = D3DTSS_MAXANISOTROPY,
} D3DSAMPLERSTATETYPE;

447
common/ps3/ps3_helpers.h Normal file
View File

@@ -0,0 +1,447 @@
#ifndef __PS3_HELPERS__
#define __PS3_HELPERS__
#ifndef SN_TARGET_PS3
#error
#endif
#include <cellstatus.h>
#include <sys/paths.h>
#include <sys/prx.h>
#include <sys/synchronization.h>
#include <sys/ppu_thread.h>
#include <sys/timer.h>
#include "ppu_intrinsics.h"
#include <np.h>
#include <np/drm.h>
// Forward declarations
#ifndef _CERT
#define PS3PRXLOADDIAGNOSTIC printf
#else
#define PS3PRXLOADDIAGNOSTIC( ... ) ((void)0)
#endif
struct TLSGlobals;
// PS3 PRX load parameters structures
struct PS3_PrxLoadParametersBase_t
{
int32_t cbSize;
sys_prx_id_t sysPrxId;
uint64_t uiFlags;
uint64_t reserved[7];
};
struct PS3_PrxModuleEntry_t
{
PS3_PrxModuleEntry_t *pNextModule;
char chName[256];
uint32_t uiRefCount;
PS3_PrxLoadParametersBase_t prxParams[0];
};
extern "C" PS3_PrxModuleEntry_t ** PS3_PrxGetModulesList();
struct PS3_GcmSharedData;
// exported from tier0
extern "C" PS3_GcmSharedData *g_pGcmSharedData;
struct PS3_GcmSharedData
{
void *m_pIoMemory;
uint32_t m_nIoMemorySize;
PS3_GcmSharedData()
{
memset(this, 0, sizeof(PS3_GcmSharedData));
}
// Thread for the QMS and Server (when host_thread_mode)
// Thread wakes up on a semaphore and when it does so it checks for
// sv_runflag and then qms runFlag and does the right thing
// This is hand coded because the PS3 scheduler sometimes (regardless of priorities)
// pushes out the main thread to run the server if the server is it's own job.
// So we create a single thread, one which we have the ability to explicitly
// run the server and the qms
// The semaphore is there so that we can sleep instead of exiting the job
// Otherwise the run flags actually control if we run locklessly
// CheckForServerRequest() is called eregularly on the QMS and so allows the
// server to interrupt the QMS and run pretty much where we need to to.
//
sys_ppu_thread_t m_thread;
sys_semaphore_t m_semaphore;
// Server
volatile int m_svRunFlag;
volatile int m_svDoneFlag;
volatile int m_numTicks;
void (*m_pfnAsyncServer)(int numTicks);
// QMS
volatile int m_qmsRunFlag;
volatile int m_qmsDoneFlag;
volatile void* m_cmat;
volatile void* m_ptr;
void (*m_func)(void*, void*);
// Audio
volatile int m_audioRunFlag;
volatile int m_audioDoneFlag;
void (*m_AudioFunc)(void);
// Endframe Defrag
volatile int m_bDeFrag;
// Create semaphore & thread
void Init()
{
// Create Semaphore
sys_semaphore_attribute_t attr;
sys_semaphore_attribute_initialize(attr);
int ret = sys_semaphore_create(&m_semaphore, &attr, 0, 2); // No point in allowing > 2 posts
if(ret != CELL_OK)
{
printf("Unable to create QMS sem\n");
}
}
void RunServer(void (*pfn)(int), int numticks)
{
// Set global data
m_pfnAsyncServer = pfn;
m_numTicks = numticks;
__lwsync();
m_svRunFlag = 1;
// Post semaphore incase trhead sleeps
sys_semaphore_post(m_semaphore, 1);
}
void RunQMS(void (*func)(void* cmat, void* ptr), void* cmat, void* ptr )
{
m_func = func;
m_cmat = cmat;
m_ptr = ptr;
__lwsync();
m_qmsRunFlag = 1;
// Post semaphore incase trhead sleeps
sys_semaphore_post(m_semaphore, 1);
}
void WaitForServer()
{
while (!m_svDoneFlag) sys_timer_usleep(60);
m_svDoneFlag = 0;
}
void WaitForQMS()
{
while (!m_qmsDoneFlag) sys_timer_usleep(60);
m_qmsDoneFlag = 0;
}
void CheckForServerRequest()
{
if (m_svRunFlag)
{
m_svRunFlag = 0;
m_pfnAsyncServer(m_numTicks);
m_svDoneFlag = 1;
}
}
// Audio
void RunAudio(void (*pfn)(void))
{
// Set global data
m_AudioFunc = pfn;
__lwsync();
m_audioRunFlag = 1;
// Post semaphore incase trhead sleeps
sys_semaphore_post(m_semaphore, 1);
}
void WaitForAudio()
{
while (!m_audioDoneFlag) sys_timer_usleep(60);
m_audioDoneFlag = 0;
}
void CheckForAudioRequest()
{
if (g_pGcmSharedData->m_audioRunFlag)
{
g_pGcmSharedData->m_audioRunFlag = 0;
g_pGcmSharedData->m_AudioFunc();
g_pGcmSharedData->m_audioDoneFlag = 1;
}
}
};
class CPs3ContentPathInfo;
struct PS3_LoadTier0_Parameters_t : public PS3_PrxLoadParametersBase_t
{
typedef TLSGlobals * ( *PFNGETTLSGLOBALS )();
PFNGETTLSGLOBALS pfnGetTlsGlobals; // [IN] [ from launcher_main to tier0 ]
PS3_PrxModuleEntry_t **ppPrxModulesList; // [IN] [ from launcher_main: head of the list of loaded PRX modules ]
CPs3ContentPathInfo *pPS3PathInfo; // [IN] [ from launcher_main to tier0 ]
uint64_t fiosLaunchTime; // [IN] [ from launcher_main: the time when the launcher was launched, the baseline time ]
uint32_t nCLNumber; // [IN] [ from launcher_main to tier0: the changelist number for this image (0 if unknown) ]
void(*pfnPushMarker)( const char * pName );
void(*pfnPopMarker)();
void(*pfnSwapBufferMarker)();
// Raw SPU libSN functions
void (*snRawSPULockHandler) (void);
void (*snRawSPUUnlockHandler) (void);
void (*snRawSPUNotifyCreation) (unsigned int uID);
void (*snRawSPUNotifyDestruction) (unsigned int uID);
void (*snRawSPUNotifyElfLoad) (unsigned int uID, unsigned int uEntry, const char *pFileName);
void (*snRawSPUNotifyElfLoadNoWait) (unsigned int uID, unsigned int uEntry, const char *pFileName);
void (*snRawSPUNotifyElfLoadAbs) (unsigned int uID, unsigned int uEntry, const char *pFileName);
void (*snRawSPUNotifyElfLoadAbsNoWait) (unsigned int uID, unsigned int uEntry, const char *pFileName);
void (*snRawSPUNotifySPUStopped) (unsigned int uID);
void (*snRawSPUNotifySPUStarted) (unsigned int uID);
struct PS3_GcmSharedData *m_pGcmSharedData;
typedef void ( *PFNSHUTDOWN )();
PFNSHUTDOWN pfnTier0Shutdown; // [OUT] [ tier0 shutdown procedure to be invoked ]
};
struct PS3_LoadLauncher_Parameters_t : public PS3_PrxLoadParametersBase_t
{
typedef int ( *PFNLAUNCHERMAIN )( int argc, char **argv );
PFNLAUNCHERMAIN pfnLauncherMain; // [OUT] [ launcher entry point ]
typedef void ( *PFNSHUTDOWN )();
PFNSHUTDOWN pfnLauncherShutdown; // [OUT] [ launcher shutdown procedure to be invoked ]
};
struct PS3_LoadAppSystemInterface_Parameters_t : public PS3_PrxLoadParametersBase_t
{
typedef void* ( *PFNCREATEINTERFACE )( const char *pName, int *pReturnCode );
PFNCREATEINTERFACE pfnCreateInterface; // [OUT] [ app system module create interface entry point ]
uint64_t reserved[8];
};
inline int PS3_PrxLoad( const char *path, PS3_PrxLoadParametersBase_t *params )
{
#define NP_POOL_SIZE (128*1024)
static uint8_t np_pool[NP_POOL_SIZE];
int res;
int modres;
sys_prx_id_t id;
if ( !params )
return -1;
PS3_PrxModuleEntry_t ** ppPrxModulesList = PS3_PrxGetModulesList();
//
// Walk the loaded list
//
for ( PS3_PrxModuleEntry_t *pEntry = *ppPrxModulesList; pEntry; pEntry = pEntry->pNextModule )
{
if ( strcmp( pEntry->chName, path ) )
continue;
++ pEntry->uiRefCount;
memcpy( params, pEntry->prxParams, ( pEntry->prxParams->cbSize <= params->cbSize ) ? pEntry->prxParams->cbSize : params->cbSize );
PS3PRXLOADDIAGNOSTIC("PRX MODULE ADDREF: %s [0x%08X] (refs=%u)\n", path, params->sysPrxId, pEntry->uiRefCount);
return 0;
}
//
// Load a new instance of PRX
//
params->sysPrxId = -1;
// If sceNp wasn't already initalised then we need to un-init it after we're done here. If Steam is loaded
// after this and it finds NP is already intialised it won't like it
int npInit = sceNpInit( NP_POOL_SIZE, np_pool );
SceNpDrmKey key = { 0x2B, 0x8E, 0xD3, 0xE4, 0xDF, 0xF1, 0x43, 0xA2, 0xA5, 0xD7, 0x4D, 0x8D, 0x89, 0x29, 0xC5, 0xF4 };
sceNpDrmIsAvailable( &key, path );
id = sys_prx_load_module(path, 0, NULL);
if (id < CELL_OK)
{
PS3PRXLOADDIAGNOSTIC("sys_prx_load_module failed: %s [0x%08x]\n", path, id);
return id;
}
PS3PRXLOADDIAGNOSTIC("PRX MODULE LOADED: %s [0x%08X]\n", path, id);
if ( npInit != SCE_NP_ERROR_ALREADY_INITIALIZED )
{
sceNpTerm();
}
params->sysPrxId = id;
res = sys_prx_start_module(id, params->cbSize, params, &modres, 0, NULL);
if (res < CELL_OK)
{
PS3PRXLOADDIAGNOSTIC("sys_prx_start_module failed: %s [0x%08x]\n", path, res);
return res;
}
PS3PRXLOADDIAGNOSTIC("PRX MODULE STARTED: %s [0x%08X 0x%08X]\n", path, id, res);
//
// Add to the loaded list
//
if ( void *pvEntry = malloc( sizeof( PS3_PrxModuleEntry_t ) + params->cbSize ) )
{
PS3_PrxModuleEntry_t *pPrxEntry = ( PS3_PrxModuleEntry_t * ) pvEntry;
pPrxEntry->pNextModule = *ppPrxModulesList;
*ppPrxModulesList = pPrxEntry;
strncpy( pPrxEntry->chName, path, sizeof( pPrxEntry->chName ) );
pPrxEntry->uiRefCount = 1;
memcpy( pPrxEntry->prxParams, params, params->cbSize );
}
return modres;
}
inline int PS3_PrxUnload( sys_prx_id_t id )
{
PS3_PrxModuleEntry_t ** ppPrxModulesList = PS3_PrxGetModulesList();
//
// Walk the loaded list
//
for ( PS3_PrxModuleEntry_t *pEntry = *ppPrxModulesList, **ppFromPrevEntry = ppPrxModulesList;
pEntry; ppFromPrevEntry = &pEntry->pNextModule, pEntry = pEntry->pNextModule )
{
if ( pEntry->prxParams->sysPrxId != id )
continue;
if ( -- pEntry->uiRefCount )
{
PS3PRXLOADDIAGNOSTIC("PRX MODULE RELREF: %s [0x%08X] (refs=%u)\n", pEntry->chName, pEntry->prxParams->sysPrxId, pEntry->uiRefCount);
return 0;
}
else
{
PS3PRXLOADDIAGNOSTIC("PRX MODULE UNLOAD: %s [0x%08X]\n", pEntry->chName, pEntry->prxParams->sysPrxId, pEntry->uiRefCount);
*ppFromPrevEntry = pEntry->pNextModule;
free( pEntry );
break;
}
}
//
// Perform the system unload process
//
int modres;
int res;
res = sys_prx_stop_module(id, 0, NULL, &modres, 0, NULL);
if (res < CELL_OK)
{
PS3PRXLOADDIAGNOSTIC("sys_prx_stop_module failed: id=0x%08x, 0x%08x\n", id, res);
return res;
}
PS3PRXLOADDIAGNOSTIC("PRX MODULE STOPPED: id=0x%08x, 0x%08x\n", id, res);
res = sys_prx_unload_module(id, 0, NULL);
if (res < CELL_OK)
{
PS3PRXLOADDIAGNOSTIC("sys_prx_unload_module failed: id=0x%08X, 0x%08x\n", id, res);
return res;
}
PS3PRXLOADDIAGNOSTIC("PRX MODULE UNLOADED: id=0x%08x, 0x%08x\n", id, res);
return modres;
}
//////////////////////////////////////////////////////////////////////////
//
// DEFINITION OF BASIC APPSYSTEM PRX IMPLEMENTATION
//
//
#include "ps3_changelistver.h"
#ifndef PS3CLVERMAJOR
#define PS3CLVERMAJOR ((APPCHANGELISTVERSION / 256) % 256)
#define PS3CLVERMINOR (APPCHANGELISTVERSION % 256)
#endif
#ifdef _DEBUG
#define PS3_PRX_SYS_MODULE_INFO_FULLMACROREPLACEMENTHELPER(ps3modulename) SYS_MODULE_INFO( ps3modulename##_dbg, 0, PS3CLVERMAJOR, PS3CLVERMINOR)
#else
#define PS3_PRX_SYS_MODULE_INFO_FULLMACROREPLACEMENTHELPER(ps3modulename) SYS_MODULE_INFO( ps3modulename##_rel, 0, PS3CLVERMAJOR, PS3CLVERMINOR)
#endif
#define PS3_PRX_SYS_MODULE_START_NAME_FULLMACROREPLACEMENTHELPER(ps3modulename) _##ps3modulename##_ps3_prx_entry
#define PS3_PRX_APPSYSTEM_MODULE( ps3modulename ) \
\
PS3_PRX_SYS_MODULE_INFO_FULLMACROREPLACEMENTHELPER( ps3modulename ); \
SYS_MODULE_START( PS3_PRX_SYS_MODULE_START_NAME_FULLMACROREPLACEMENTHELPER( ps3modulename ) ); \
\
extern "C" int PS3_PRX_SYS_MODULE_START_NAME_FULLMACROREPLACEMENTHELPER( ps3modulename )( unsigned int args, void *pArg ) \
{ \
Assert( args >= sizeof( PS3_LoadAppSystemInterface_Parameters_t ) ); \
PS3_LoadAppSystemInterface_Parameters_t *pParams = reinterpret_cast< PS3_LoadAppSystemInterface_Parameters_t * >( pArg ); \
Assert( pParams->cbSize >= sizeof( PS3_LoadAppSystemInterface_Parameters_t ) ); \
pParams->pfnCreateInterface = CreateInterface; \
return SYS_PRX_RESIDENT; \
} \
//
//
// END DEFINITION OF BASIC APPSYSTEM PRX IMPLEMENTATION
//
//
//////////////////////////////////////////////////////////////////////////
#endif // __PS3_HELPERS__

43
common/ps3/ps3_launch.h Normal file
View File

@@ -0,0 +1,43 @@
//========= Copyright © 1996-2005, Valve LLC, All rights reserved. ============
//
// Purpose: Common XBox Launch data passed between apps
//
//=============================================================================
#include "ps3_platform.h"
#define RELAUNCH_MAGIC_NUMBER 0xbd122969
// used to hold persistent states across restart
struct RelaunchHeader_t
{
unsigned int magicNumber;
unsigned int contextCode; // the context code that was used
unsigned int nBytesRelaunchData;
unsigned int activeDevice; // which controller was active
__int64 startTime; // used to track duration of relaunch
bool bRetail; // running as retail mode
bool bInDebugger; // in debug session
};
#pragma pack()
#define GetRelaunchHeader( x ) (((RelaunchHeader_t *)(((unsigned int)(x)) + MAX_LAUNCH_DATA_SIZE / 2)) - 1)
// a context code is passed to installer or dashboard
// the dashboard passes the context code to the installer
// installer exits and launches HL2 with RelaunchHeader
#define CONTEXTCODE_HL2MAGIC 0x9E000000
#define CONTEXTCODE_MAGICMASK 0xFF000000
// xbe image type
#define CONTEXTCODE_DEBUG_XBE 0x00000001 // running the debug xbe
#define CONTEXTCODE_RELEASE_XBE 0x00000002 // running the release xbe
#define CONTEXTCODE_RETAIL_XBE 0x00000004 // running the retail xbe
// mode options
#define CONTEXTCODE_RETAIL_MODE 0x00000010 // running the desired xbe in retail mode
#define CONTEXTCODE_INDEBUGGER 0x00000020 // running during a debugger session
#define CONTEXTCODE_NO_XBDM 0x00000040 // No XBDM calls
// operation commands
#define CONTEXTCODE_DASHBOARD 0x00010000 // pass through immediately to hl2
#define CONTEXTCODE_ATTRACT 0x00020000 // run the attract mode
#define CONTEXTCODE_LOADMAP 0x00040000 // restart directly to load a map
#define CONTEXTCODE_QUIT 0x00080000 // quit game, go directly to main menu

202
common/ps3/ps3_platform.h Normal file
View File

@@ -0,0 +1,202 @@
//========= Copyright © 1996-2004, Valve LLC, All rights reserved. ============
//
// Purpose: PS/3 Platform include
//
// $NoKeywords: $
//=============================================================================
#ifndef _PS3_PLATFORM_H
#define _PS3_PLATFORM_H
//EAPS3 #undef Verify // can't use verify, because there's a Verify member of struct D3DPushBuffer
#include <stdio.h>
#include <math.h>
#include <wchar.h>
#include <string.h> // needed for memset
//EAPS3: Defines of win32 types used in Visual Studio.
typedef void VOID;
typedef const void * LPCVOID;
typedef unsigned long DWORD;
typedef DWORD * LPDWORD;
typedef int BOOL;
typedef int * LPBOOL;
typedef unsigned short WORD;
typedef float FLOAT;
typedef short SHORT;
typedef long LONG;
typedef char CHAR;
typedef unsigned int UINT;
typedef unsigned long ULONG;
typedef unsigned char BYTE;
typedef BYTE* LPBYTE;
typedef long long DWORDLONG;
typedef void* PVOID;
typedef int DWORD_PTR;
typedef int ULONG_PTR;
typedef long* LONG_PTR;
typedef unsigned int* UINT_PTR;
typedef WORD* LPWORD;
typedef ULONG_PTR * PDWORD_PTR;
typedef ULONG_PTR SIZE_T;
typedef ULONG_PTR * PSIZE_T;
typedef wchar_t WCHAR;
typedef CHAR TCHAR;
typedef const char* LPCSTR;
typedef char* LPSTR;
typedef LPSTR LPTSTR;
typedef const char* LPCTSTR;
typedef const wchar_t* LPCWSTR;
typedef WCHAR * LPWSTR;
typedef WCHAR * PWSTR;
typedef void* HICON;
typedef void* HCURSOR;
typedef void* HBRUSH;
typedef void* HMENU;
typedef void* HFONT;
typedef void* HBITMAP;
typedef DWORD COLORREF;
typedef void* HINSTANCE;
typedef UINT MMRESULT;
typedef long HRESULT;
typedef long LRESULT;
typedef void* HANDLE;
//typedef void* HWND;
typedef void* LPVOID;
typedef unsigned int WPARAM;
typedef int LPARAM;
typedef void* HDC;
typedef void* HHOOK;
typedef void* HMODULE;
typedef void* HKL;
typedef void* HKEY;
typedef HKEY* PHKEY;
typedef void* HGDIOBJ;
typedef WORD ATOM;
typedef HANDLE HGLOBAL;
//typedef WORD WAVEFORMATEX;
typedef long long __int64;
typedef float vec3_t[3];
typedef signed long long s64_t;
typedef unsigned long long u64_t;
typedef signed int s32_t;
typedef unsigned int u32_t;
typedef signed short s16_t;
typedef unsigned short u16_t;
typedef signed char s8_t;
typedef unsigned char u8_t;
typedef unsigned char byte_t;
typedef unsigned int rgba_t;
typedef long long ULARGE_INTEGER;
typedef u64_t ULONGLONG;
typedef struct _POINTL /* ptl */
{
LONG x;
LONG y;
} POINT, POINTL, *PPOINTL, *LPPOINT;
typedef struct _GUID
{
unsigned long Data1;
unsigned short Data2;
unsigned short Data3;
unsigned char Data4[8];
} GUID;
typedef struct tagRECT
{
LONG left;
LONG top;
LONG right;
LONG bottom;
} RECT, *PRECT, *NPRECT, *LPRECT;
typedef struct _FILETIME {
DWORD dwLowDateTime;
DWORD dwHighDateTime;
} FILETIME, *PFILETIME, *LPFILETIME;
typedef struct _SECURITY_ATTRIBUTES
{
DWORD nLength;
/* [size_is] */ LPVOID lpSecurityDescriptor;
BOOL bInheritHandle;
} SECURITY_ATTRIBUTES;
typedef struct _SECURITY_ATTRIBUTES *LPSECURITY_ATTRIBUTES;
// Function call convention
#define _cdecl
#define __cdecl
#define __declspec(x)
#define WINAPI
#define FAR
#define NEAR
#define CONST const
#define CALLBACK
#define IN
#define OPTIONAL
struct PDM_CMDCONT
{
};
#ifndef __stdcall
#define __stdcall
#endif
//typedef HRESULT (__stdcall *PDM_CMDPROC)(LPCSTR szCommand, LPSTR szResponse, DWORD cchResponse, PDM_CMDCONT pdmcc);
#undef __stdcall
typedef int (*PROC)();
typedef int (*FARPROC)();
typedef int (*NEARPROC)();
#define FAILED(x) (x < 0)
//EAPS3: Copied from malloc.h in Visual Studio.
#define _HEAPEMPTY (-1)
#define _HEAPOK (-2)
#define _HEAPBADBEGIN (-3)
#define _HEAPBADNODE (-4)
#define _HEAPEND (-5)
#define _HEAPBADPTR (-6)
#define _FREEENTRY 0
#define _USEDENTRY 1
// use for api's 'ignored' params for clarity
#define XBOX_DONTCARE 0
// trap debugging output
#define OutputDebugString(v) printf(v);
// not defined in ps3_system.cpp.
// DWORD GetTickCount();
// this comment means nothing
#define LOWORD(l) ((WORD)((DWORD_PTR)(l) & 0xffff))
#define HIWORD(l) ((WORD)((DWORD_PTR)(l) >> 16))
#define S_OK ((HRESULT)0x00000000L)
#ifndef _strnicmp
#define _strnicmp Q_strncasecmp
#endif
#ifndef wcsnicmp
// ???
#endif
#endif

View File

@@ -0,0 +1,7 @@
#ifndef _PS3_PSGL_VALVE_H_
#define _PS3_PSGL_VALVE_H_
#define GL_VALVE_WRITE_ONLY_NO_OVERWRITE 0x88BB
#endif // _PS3_PSGL_VALVE_H_

39
common/ps3/ps3_rtti.h Normal file
View File

@@ -0,0 +1,39 @@
#ifndef __PS3_RTTI__
#define __PS3_RTTI__
#include <stdio.h>
namespace ps3_rtti
{
template<class Klass>
class type_trait
{
private:
static const size_t m_nameSize=16;
public:
static const int m_typeTag;
static char m_name[m_nameSize];
static int TypeInfo() {return int(&m_typeTag);}
static const char* TypeName()
{
snprintf(m_name,m_nameSize,"%x",TypeInfo());
return m_name;
}
};
}
#define TYPE_INFO int
#define TYPEID(t) ps3_rtti::type_trait<t>::TypeInfo()
#define TYPENAME(t) ps3_rtti::type_trait<t>::TypeName()
#define TYPE_INFO_EQUAL(i1, i2) i1 == i2
#define INSTANCTIATE_TYPE(t) \
namespace ps3_rtti \
{ \
template <> const int type_trait<t>::m_typeTag=0; \
template <> char type_trait<t>::m_name[16] = ""; \
}
#define INVALID_TYPE_INFO int(0)
#endif

24
common/ps3/ps3_sn.h Normal file
View File

@@ -0,0 +1,24 @@
//========= Copyright © , Valve LLC, All rights reserved. ============
//
// These are the workarounds for snTuner inability to patch prx'es,
// as well as access prx symbols after attaching to a running game
// snTuner functions are exposed through pointers to functions in ELF
//
//////////////////////////////////////////////////////////////////////
#ifndef VALVE_PS3_SN_HDR
#define VALVE_PS3_SN_HDR
// Currently these are only defined on PS3, but there's no harm in declaring them on other platforms
#ifdef _PS3
extern "C" void(*g_pfnPushMarker)( const char * pName );
extern "C" void(*g_pfnPopMarker)();
extern "C" void(*g_pfnSwapBufferMarker)();
#else
inline void g_pfnPushMarker( const char * pName ){}
inline void g_pfnPopMarker(){}
inline void g_pfnSwapBufferMarker(){}
#endif
#endif

203
common/ps3/ps3_vxconsole.h Normal file
View File

@@ -0,0 +1,203 @@
//========= Copyright © 1996-2007, Valve LLC, All rights reserved. ============
//
// Purpose: PS3 VXConsole Common. Used for public remote access items and types.
//
//=============================================================================
#ifndef PS3_VXCONSOLE_H
#define PS3_VXCONSOLE_H
#pragma once
//PS3
// sent during connection, used to explicitly guarantee a binary compatibility
#define VXCONSOLE_PROTOCOL_VERSION 0x1001 // also used for the LPAR protocol number
#define VXCONSOLE_PROTOCOL_PORT 0
#define VXCONSOLE_PS3 1
#define VXCONSOLE_TTY_STREAM 13
typedef struct
{
char labelString[64];
COLORREF color;
} xrProfile_t;
typedef struct
{
char messageString[256];
float time;
float deltaTime;
size_t memory;
int deltaMemory;
} xrTimeStamp_t;
typedef struct
{
char nameString[256];
char shaderString[256];
int refCount;
} xrMaterial_t;
enum cacheableState_e
{
CS_STATIC = 0, // texture is not lru managed
CS_EVICTED, // texture mip0 is not in memory
CS_LOADING, // texture mip0 is loading
CS_VALID, // texture mip0 is valid for rendering
CS_MAX
};
typedef struct
{
char nameString[256];
char groupString[64];
char formatString[64];
int size;
int width;
int height;
int depth;
int numLevels;
int binds;
int refCount;
int sRGB;
int edram;
int procedural;
int cacheableState;
int cacheableSize : 31; // Packing avoids game-to-vxconsole protocol version conflict
uint final : 1;
int failed;
int pwl;
int reduced;
} xrTexture_t;
typedef struct
{
char nameString[256];
char formatString[64];
int rate;
int bits;
int channels;
int looped;
int dataSize;
int numSamples;
int streamed;
int quality;
} xrSound_t;
typedef struct
{
char nameString[128];
char helpString[256];
} xrCommand_t;
typedef struct
{
float position[3];
float angle[3];
char mapPath[256];
char savePath[256];
int build;
int skill;
char details[1024];
} xrMapInfo_t;
struct xrModel_t
{
char nameString[256];
int dataSize;
int numVertices;
int triCount;
int dataSizeLod0;
int numVerticesLod0;
int triCountLod0;
int numBones;
int numParts;
int numLODs;
int numMeshes;
};
struct xModelList_t
{
char name[MAX_PATH];
int dataSize;
int numVertices;
int triCount;
int dataSizeLod0;
int numVerticesLod0;
int triCountLod0;
int numBones;
int numParts;
int numLODs;
int numMeshes;
};
struct xrDataCacheItem_t
{
char nameString[256];
char sectionString[64];
int size;
int lockCount;
unsigned int clientId;
unsigned int itemData;
unsigned int handle;
};
struct xrVProfNodeItem_t
{
char nameString[128];
char budgetGroupString[128];
unsigned int budgetGroupColor;
unsigned int totalCalls;
double inclusiveTime;
double exclusiveTime;
};
struct xDataCacheItem_t
{
char name[MAX_PATH];
char section[64];
int size;
int lockCount;
unsigned int clientId;
unsigned int itemData;
unsigned int handle;
};
struct xVProfNodeItem_t
{
const char *pName;
const char *pBudgetGroupName;
unsigned int budgetGroupColor;
unsigned int totalCalls;
double inclusiveTime;
double exclusiveTime;
};
// Types of action taken in response to an rc_Assert() message
enum AssertAction_t
{
ASSERT_ACTION_BREAK = 0, // Break on this Assert
ASSERT_ACTION_IGNORE_THIS, // Ignore this Assert once
ASSERT_ACTION_IGNORE_ALWAYS, // Ignore this Assert from now on
ASSERT_ACTION_IGNORE_FILE, // Ignore all Asserts from this file from now on
ASSERT_ACTION_IGNORE_ALL, // Ignore all Asserts from now on
ASSERT_ACTION_OTHER // A more complex response requiring additional data (e.g. "ignore this Assert 5 times")
};
//id's for dispatching binary notifications to proper handlers
enum XBX_DBGBinaryNotification_HandlerID_t
{
XBX_DBG_BNH_STACKTRANSLATOR,
XBX_DBG_BNH_END,
};
#endif // PS3_VXCONSOLE_H

1600
common/ps3/ps3_win32stubs.h Normal file

File diff suppressed because it is too large Load Diff

255
common/ps3/ps3stubs.h Normal file
View File

@@ -0,0 +1,255 @@
//========= Copyright © 1996-2004, Valve LLC, All rights reserved. ============
//
// Purpose: Win32 replacements for Xbox code
//
//=============================================================================
#ifndef PS3STUBS_H
#define PS3STUBS_H
#include "ps3_platform.h"
#include "tier0/platform.h"
// Content creation/open flags
#define XCONTENTFLAG_NONE 0x00
#define XCONTENTFLAG_CREATENEW 0x00
#define XCONTENTFLAG_CREATEALWAYS 0x00
#define XCONTENTFLAG_OPENEXISTING 0x00
#define XCONTENTFLAG_OPENALWAYS 0x00
#define XCONTENTFLAG_TRUNCATEEXISTING 0x00
// Content attributes
#define XCONTENTFLAG_NOPROFILE_TRANSFER 0x00
#define XCONTENTFLAG_NODEVICE_TRANSFER 0x00
#define XCONTENTFLAG_STRONG_SIGNED 0x00
#define XCONTENTFLAG_ALLOWPROFILE_TRANSFER 0x00
#define XCONTENTFLAG_MOVEONLY_TRANSFER 0x00
#define XDEVICE_TYPE_GAMEPAD 0
#define XDEVICE_TYPE_MEMORY_UNIT 1
#define XDEVICE_TYPE_VOICE_MICROPHONE 2
#define XDEVICE_TYPE_VOICE_HEADPHONE 3
#define XDEVICE_TYPE_HIGHFIDELITY_MICROPHONE 4
// Constants for gamepad buttons
#define XINPUT_GAMEPAD_DPAD_UP 0x0001
#define XINPUT_GAMEPAD_DPAD_DOWN 0x0002
#define XINPUT_GAMEPAD_DPAD_LEFT 0x0004
#define XINPUT_GAMEPAD_DPAD_RIGHT 0x0008
#define XINPUT_GAMEPAD_START 0x0010
#define XINPUT_GAMEPAD_BACK 0x0020
#define XINPUT_GAMEPAD_LEFT_THUMB 0x0040
#define XINPUT_GAMEPAD_RIGHT_THUMB 0x0080
#define XINPUT_GAMEPAD_LEFT_SHOULDER 0x0100 // AG - Shoudler buttons should correspond to BLACK and WHITE in xboxStubs.h, but they don't seem to have proper bitfield values
#define XINPUT_GAMEPAD_RIGHT_SHOULDER 0x0200
#define XINPUT_GAMEPAD_LEFT_TRIGGER 0x0400
#define XINPUT_GAMEPAD_RIGHT_TRIGGER 0x0800
#define XINPUT_GAMEPAD_A 0x1000
#define XINPUT_GAMEPAD_B 0x2000
#define XINPUT_GAMEPAD_X 0x4000
#define XINPUT_GAMEPAD_Y 0x8000
#define XINPUT_LIGHTGUN_ONSCREEN 0x2000
#define XINPUT_LIGHTGUN_FRAME_DOUBLER 0x4000
#define XINPUT_LIGHTGUN_LINE_DOUBLER 0x8000
//#define XINPUT_GAMEPAD_LEFT_TRIGGER 6
//#define XINPUT_GAMEPAD_RIGHT_TRIGGER 7
#define XDEVICE_PORT0 0
#define XDEVICE_PORT1 1
#define XDEVICE_PORT2 2
#define XDEVICE_PORT3 3
#ifdef CONTROLLER2_CHEATS_ENABLED
#define XBX_MAX_DPORTS 2
#else
#define XBX_MAX_DPORTS 1
#endif // CONTROLLER2_CHEATS_ENABLED
#define XDEVICE_NO_SLOT 0
#define XDEVICE_TOP_SLOT 0
#define XDEVICE_BOTTOM_SLOT 1
#define CLR_DEFAULT 0xFF000000
#define CLR_WARNING 0x0000FFFF
#define CLR_ERROR 0x000000FF
// Device types available in XINPUT_CAPABILITIES
#define XINPUT_DEVTYPE_GAMEPAD 0x01
#define XINPUT_DEVTYPE_USB_KEYBOARD 0x02
// Device subtypes available in XINPUT_CAPABILITIES
#define XINPUT_DEVSUBTYPE_UNKNOWN 0x00
#define XINPUT_DEVSUBTYPE_GAMEPAD 0x01
#define XINPUT_DEVSUBTYPE_WHEEL 0x02
#define XINPUT_DEVSUBTYPE_ARCADE_STICK 0x03
#define XINPUT_DEVSUBTYPE_FLIGHT_STICK 0x04
#define XINPUT_DEVSUBTYPE_DANCEPAD 0x05
// Flags for XINPUT_CAPABILITIES
#define XINPUT_CAPS_FFB_SUPPORTED 0x0001
#define XINPUT_CAPS_WIRELESS 0x0002
#define XINPUT_CAPS_VOICE_SUPPORTED 0x0004
#define XINPUT_CAPS_PMD_SUPPORTED 0x0008
// Flags for XInputGetCapabilities
#define XINPUT_FLAG_GAMEPAD 0x00000001
#define XINPUT_FLAG_KEYBOARD 0x00000002
#define XINPUT_FLAG_REMOTE 0x00000004
#define XINPUT_FLAG_ANYDEVICE 0x000000FF
// Gamepad thresholds
#define XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE 7849
#define XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE 8689
#define XINPUT_GAMEPAD_TRIGGER_THRESHOLD 30
// Wind
// Structures used by XInput APIs
typedef struct _XINPUT_GAMEPAD
{
WORD wButtons;
BYTE bLeftTrigger;
BYTE bRightTrigger;
short sThumbLX;
short sThumbLY;
short sThumbRX;
short sThumbRY;
} XINPUT_GAMEPAD, *PXINPUT_GAMEPAD;
typedef struct _XINPUT_STATE
{
DWORD dwPacketNumber;
XINPUT_GAMEPAD Gamepad;
} XINPUT_STATE, *PXINPUT_STATE;
typedef struct _XINPUT_VIBRATION
{
WORD wLeftMotorSpeed;
WORD wRightMotorSpeed;
} XINPUT_VIBRATION, *PXINPUT_VIBRATION;
typedef struct _XINPUT_CAPABILITIES
{
BYTE Type;
BYTE SubType;
WORD Flags;
XINPUT_GAMEPAD Gamepad;
XINPUT_VIBRATION Vibration;
} XINPUT_CAPABILITIES, *PXINPUT_CAPABILITIES;
//typedef enum
//{
// XK_NULL,
// XK_BUTTON_UP,
// XK_BUTTON_DOWN,
// XK_BUTTON_LEFT,
// XK_BUTTON_RIGHT,
// XK_BUTTON_START,
// XK_BUTTON_BACK,
// XK_BUTTON_STICK1,
// XK_BUTTON_STICK2,
// XK_BUTTON_A,
// XK_BUTTON_B,
// XK_BUTTON_X,
// XK_BUTTON_Y,
// XK_BUTTON_BLACK,
// XK_BUTTON_WHITE,
// XK_BUTTON_LTRIGGER,
// XK_BUTTON_RTRIGGER,
// XK_STICK1_UP,
// XK_STICK1_DOWN,
// XK_STICK1_LEFT,
// XK_STICK1_RIGHT,
// XK_STICK2_UP,
// XK_STICK2_DOWN,
// XK_STICK2_LEFT,
// XK_STICK2_RIGHT,
// XK_MAX_KEYS,
//} xKey_t;
//
//typedef enum
//{
// XVRB_NONE, // off
// XVRB_ERROR, // fatal error
// XVRB_ALWAYS, // no matter what
// XVRB_WARNING, // non-fatal warnings
// XVRB_STATUS, // status reports
// XVRB_ALL,
//} xverbose_e;
typedef struct _XINPUT_RUMBLE
{
WORD wLeftMotorSpeed;
WORD wRightMotorSpeed;
} XINPUT_RUMBLE, *PXINPUT_RUMBLE;
#define XINPUT_FEEDBACK_HEADER_INTERNAL_SIZE 58
typedef struct _XINPUT_FEEDBACK_HEADER
{
DWORD dwStatus;
void* hEvent;
BYTE Reserved[XINPUT_FEEDBACK_HEADER_INTERNAL_SIZE];
} XINPUT_FEEDBACK_HEADER, *PXINPUT_FEEDBACK_HEADER;
typedef struct _XINPUT_FEEDBACK
{
XINPUT_FEEDBACK_HEADER Header;
union
{
XINPUT_RUMBLE Rumble;
};
} XINPUT_FEEDBACK, *PXINPUT_FEEDBACK;
//typedef struct _XINPUT_GAMEPAD
//{
// WORD wButtons;
// BYTE bAnalogButtons[8];
// short sThumbLX;
// short sThumbLY;
// short sThumbRX;
// short sThumbRY;
//} XINPUT_GAMEPAD, *PXINPUT_GAMEPAD;
typedef struct _XPP_DEVICE_TYPE
{
unsigned long Reserved[3];
} XPP_DEVICE_TYPE, *PXPP_DEVICE_TYPE;
typedef struct _XDEVICE_PREALLOC_TYPE
{
PXPP_DEVICE_TYPE DeviceType;
DWORD dwPreallocCount;
} XDEVICE_PREALLOC_TYPE, *PXDEVICE_PREALLOC_TYPE;
//typedef struct _XINPUT_STATE
//{
// DWORD dwPacketNumber;
// XINPUT_GAMEPAD Gamepad;
//} XINPUT_STATE, *PXINPUT_STATE;
typedef struct _XINPUT_POLLING_PARAMETERS
{
BYTE fAutoPoll:1;
BYTE fInterruptOut:1;
BYTE ReservedMBZ1:6;
BYTE bInputInterval;
BYTE bOutputInterval;
BYTE ReservedMBZ2;
} XINPUT_POLLING_PARAMETERS, *PXINPUT_POLLING_PARAMETERS;
/*EAPS3
void XBX_DebugString(xverbose_e verbose, COLORREF color, const char* format, ...);
void XBX_ProcessEvents(void);
void XInitDevices(DWORD dwPreallocTypeCount, PXDEVICE_PREALLOC_TYPE PreallocTypes);
DWORD XGetDevices(PXPP_DEVICE_TYPE DeviceType);
bool XGetDeviceChanges(PXPP_DEVICE_TYPE DeviceType, DWORD *pdwInsertions, DWORD *pdwRemovals);
HANDLE XInputOpen(PXPP_DEVICE_TYPE DeviceType, DWORD dwPort, DWORD dwSlot, PXINPUT_POLLING_PARAMETERS pPollingParameters);
void XInputClose(HANDLE hDevice);
DWORD XInputSetState(HANDLE hDevice, PXINPUT_FEEDBACK pFeedback);
DWORD XInputGetState(HANDLE hDevice, PXINPUT_STATE pState);
DWORD XInputPoll(HANDLE hDevice);
unsigned int XBX_GetSystemTime(void);
*/
#endif // XBOXSTUBS_H

View File

@@ -0,0 +1,273 @@
//================ Copyright (c) 1996-2010 Valve Corporation. All Rights Reserved. =================
//
// Double ring buffer used for bidirectional communication between RSX and SPU
// One ring buffer is (externally managed) jobchain(s) that call into entries in IO address space
// that RSX patches (changing from JTS to RET). The other ring buffer is supposedly in local memory
// and is supposedly split into segments. RSX consumes the local memory buffer, and releases it
// segment-by-segment. SPU runs ahead of it and produces the segments and notifies the RSX
// using JTS external to the classes here
//
//
#include <cell/spurs.h>
#include "ps3/dxabstract_gcm_shared.h"
#include "ps3/rsx_spu_double_ring.h"
#include "ps3/vjobchain4.h"
#include "vjobs/pcring.h"
#include "ps3/ps3gcmlabels.h"
// Can be LWSYNC or NOP if followed by RET
// Must be RET otherwise
// JTS->LWSYNC mutation allows for only 4-byte inline transfer from RSX, guaranteeing atomicity
#define MUTABLE_GUARD_COMMAND CELL_SPURS_JOB_COMMAND_LWSYNC
#ifndef SPU
void RsxSpuDoubleRing::SetIoBuffer( void * pIoBuffer, uint nIoBufferByteSize )
{
m_pIoBuffer = ( IoBufferEntry_t * )pIoBuffer;
m_nIoBufferNextIndex = 0;
m_nIoBufferCount = nIoBufferByteSize / sizeof( *m_pIoBuffer );
Assert( !( m_nIoBufferCount & ( m_nIoBufferCount - 1 ) ) );
// Don't initialize if IO buffer is being measured
if ( !m_pIoBuffer )
return;
// init all to RET, which means it's released and most of them ready to be reused
for( int i = 0; i < m_nIoBufferCount; ++i )
{
m_pIoBuffer[i].m_nMutableGuard = MUTABLE_GUARD_COMMAND;
m_pIoBuffer[i].m_nConstRet = CELL_SPURS_JOB_COMMAND_RET;
}
}
void RsxSpuDoubleRing::OnGcmInit( uint nIoBufferOffsetDelta )
{
m_nIoBufferOffsetDelta = nIoBufferOffsetDelta;
}
void RsxSpuDoubleRing::SetRsxBuffer( void * eaRsxBuffer, uint nRsxBufferSize, uint nIdealSegmentSize, uint nMaxJobsPerSegment )
{
if( nIdealSegmentSize & ( nIdealSegmentSize - 1 ) )
{
Error( "RsxSpuDoubleRing: invalid ideal segment size %d, must be a power of 2\n", nIdealSegmentSize );
}
if( nIdealSegmentSize > nRsxBufferSize / 2 )
{
Error( "RsxSpuDoubleRing: invalid ideal segment size %d (full buffer size %d), must be at most half the buffer size", nIdealSegmentSize, nRsxBufferSize );
}
m_nMaxSegmentsPerRing = nRsxBufferSize / MIN( nIdealSegmentSize, nMaxJobsPerSegment * 128 );
if( m_nIoBufferCount < /*ARRAYSIZE( m_pIoBaseGuards )*/4 * m_nMaxSegmentsPerRing ) // + 1 for the initial slot
{
Error( "RsxSpuDoubleRing: IO buffer is too small: there may be up to %d segments per ring, and there are only %d IO guard (JTS-RET) elements. Make IO buffer at least %u bytes large.\n", m_nMaxSegmentsPerRing, m_nIoBufferCount, 4 * m_nMaxSegmentsPerRing * sizeof( *m_pIoBuffer ) );
}
m_nIdealSegmentSize = nIdealSegmentSize;
m_nMaxJobsPerSegment = nMaxJobsPerSegment;
m_eaRsxBuffer = ( uintp )eaRsxBuffer;
m_eaRsxBufferEnd = m_eaRsxBuffer + nRsxBufferSize;
m_nIoBufferNextIndex = 0;
m_nRingRsxNextSegment = 0; // consider the rsx ring already done
// nothing is allocated by SPU
m_eaRingSpuBase = m_eaRsxBufferEnd;
// we consider that the last segment was signaled beyond the end of this segment
m_eaRingSpuLastSegment = m_eaRsxBufferEnd;
m_nRingSpuJobCount = 0;
// this segment is for reference to the bottom of rsx buffer only
// the whole RSX buffer is free for SPU to use
m_eaRingRsxBase = m_eaRsxBuffer;
m_ringSpu.EnsureCapacity( m_nMaxSegmentsPerRing );
m_ringRsx.EnsureCapacity( m_nMaxSegmentsPerRing );
}
#endif
void RsxSpuDoubleRing::InternalGuardAndLock( VjobChain4 * pSyncChain, uintp eaRsxMem )
{
if( m_nRingRsxNextSegment >= m_ringRsx.Count() )
{
// if we exhausted all RSX ring segments, it only may mean that m_eaRingRsxBase == m_eaRsxBuffer, so this can not happen
VjobSpuLog(
"RsxSpuDoubleRing::InternalGuardAndLock: Unexpected error in RSX-SPU double ring, something's very wrong\n"
"Please tell Sergiy the following numbers: %d,%d,%d,%d. @%X,@%X\n",
m_nRingRsxNextSegment, m_ringRsx.Count(), m_ringSpu.Count(), m_ringSpu.GetCapacity(),
m_eaRingRsxBase, m_eaRingSpuBase
);
}
// the next most common case when we have to wait for RSX: we don't have to switch the ring because there's plenty of space still available
// find the next segment to wait for ( may skip several segments )
Assert( m_nRingRsxNextSegment < m_ringRsx.Count() );
Segment_t segment;
if( m_nRingRsxNextSegment >= m_ringRsx.Count() || m_ringSpu.Count() >= m_ringSpu.GetCapacity() )
{
VjobSpuLog(
"RsxSpuDoubleRing::InternalGuardAndLock() hit an error condition, but will try to continue\n"
"Please tell Sergiy the following numbers: %d>=%d|%d>=%d. @%X,@%X\n",
m_nRingRsxNextSegment, m_ringRsx.Count(), m_ringSpu.Count(), m_ringSpu.GetCapacity(),
m_eaRingRsxBase, m_eaRingSpuBase
);
}
for( ; ; )
{
segment = m_ringRsx[m_nRingRsxNextSegment++];
Assert( segment.m_eaBase < m_eaRingRsxBase );
if( eaRsxMem >= segment.m_eaBase )
{
break; // we found the segment to wait on
}
if( m_nRingRsxNextSegment >= m_ringRsx.Count() )
{
// we exhausted all segments in the ring, so wait for the last segment and assume that'll be the end of this ring
segment.m_eaBase = m_eaRsxBuffer;
break;
}
}
// we either found the segment to wait on here, or exhausted all segments from the RSX ring.
// even if we exhausted all segments, it still means we found the LAST segment and we'll use that segment as the guard
uint64 * eaCall = pSyncChain->Push( ); // wait for the RSX to finish rendering from this memory before writing into it
VjobDmaPutfUint64( CELL_SPURS_JOB_COMMAND_CALL( segment.m_pSpuJts ), (uint32)eaCall, VJOB_IOBUFFER_DMATAG );
m_eaRingSpuBase = eaRsxMem;
m_eaRingRsxBase = segment.m_eaBase;
}
// Important side effects: may add to m_ringSpu
void RsxSpuDoubleRing::InternalSwitchRing( VjobChain4 * pSyncChain )
{
// if we haven't already, we need to wait for the segment 0 to avoid racing over it with SPU (to ensure serialization)
if( m_nRingRsxNextSegment < m_ringRsx.Count() )
{
// this should be a very rare occurence, because we don't normally jump across multiple segments; usually we have many allocations in a single segment
uint64 * eaCall = pSyncChain->Push( );
VjobDmaPutfUint64( CELL_SPURS_JOB_COMMAND_CALL( m_ringRsx.Tail().m_pSpuJts ), (uint32)eaCall, VJOB_IOBUFFER_DMATAG );
}
if( m_eaRingSpuBase < m_eaRingSpuLastSegment )
{
// since the last segment was created, there were allocations. Create a new segment to sync up to those allocations
Assert( m_eaRsxBuffer <= m_eaRingSpuBase );
m_eaRingSpuBase = m_eaRsxBuffer;
CommitSpuSegment( );
}
else
{
// since the last segment was created, there were NO allocations. Extend the last segment to include the slack we're dropping now
m_ringSpu.Tail().m_eaBase = m_eaRsxBuffer;
}
// now we switch the ring : SPU ring becomes RSX ring, RSX ring retires
//m_ringRsx.RemoveAll();
//m_ringRsx.Swap( m_ringSpu );
m_ringRsx.Assign( m_ringSpu );
m_ringSpu.RemoveAll();
AssertSpuMsg( m_ringRsx.Count() >= 2, "RSX ring has only %d segments! Something is very wrong with RSX-SPU double-ring\n", m_ringRsx.Count() );
Assert( m_ringRsx.Count() < m_nMaxSegmentsPerRing );
/*
for( uint i = ARRAYSIZE( m_pIoBaseGuards ); i--> 1; ) // range: ARRAYSIZE( m_pIoBaseGuards ) - 1 ... 1
{
m_pIoBaseGuards[i] = m_pIoBaseGuards[i - 1];
}
m_pIoBaseGuards[0] = m_ringRsx.Tail().m_pSpuJts;
*/
m_eaRingSpuBase = m_eaRsxBufferEnd;
m_eaRingSpuLastSegment = m_eaRsxBufferEnd;
m_eaRingRsxBase = m_eaRsxBufferEnd;
m_nRingSpuJobCount = 0;
m_nRingRsxNextSegment = 0;
// IMPORTANT RSX L2 CACHE INVALIDATION POINT
// we've run out of a ring; start a new one, invalidate the texture cache because we're using it for fragment programs and
// the new ring will reuse the same memory which can be in RSX L2 cache, which doesn't invalidate when we DMA the new content into the new ring
GCM_FUNC( cellGcmSetInvalidateTextureCache, CELL_GCM_INVALIDATE_TEXTURE );
}
inline void WaitGuard( volatile uint64 *pGuard, uint64 nValueToWaitFor )
{
int nAttempts = 0;
while( VjobDmaGetUint64( (uint)pGuard, DMATAG_SYNC, 0, 0 ) != nValueToWaitFor )
{
if( 100 == nAttempts++ )
{
VjobSpuLog( "Stall in WaitGuard : probably not enough IO buffer memory for the SPU side ring\n" );
}
}
/*
if( *pGuard != nValueToWaitFor )
{
g_nWaitGuardSpins++;
extern bool g_bEnableStallWarnings;
if( g_bEnableStallWarnings )
{
Warning( "Stall in WaitGuard : probably not enough IO buffer memory for the SPU side ring\n" );
}
while( *pGuard != nValueToWaitFor )
{
g_nWaitGuardSpins++;
sys_timer_usleep( 60 );
}
}
*/
}
// creates a new segment in SPU ring, allocates a JTS-RET guard for it, and pushes GCM command to release it
// Assumption: the memory in eaBase and up has already been used up by RSX commands up to this point
// Important side effects: adds to m_ringSpu
void RsxSpuDoubleRing::CommitSpuSegment( )
{
// check that RSX ran away at least 2 segments ahead; this guarantees that there are no SPU jobs waiting to be unblocked by any IoBuffer guards
volatile uint64 * pIoBaseGuard = &( m_pIoBuffer[( m_nIoBufferNextIndex + m_nMaxSegmentsPerRing * 3 ) & ( m_nIoBufferCount - 1 )].m_nMutableGuard ); //m_pIoBaseGuards[ ARRAYSIZE( m_pIoBaseGuards ) - 1 ];
WaitGuard( pIoBaseGuard, MUTABLE_GUARD_COMMAND );
uint64 * eaJtsRetGuard = &( m_pIoBuffer[ ( m_nIoBufferNextIndex++ ) & ( m_nIoBufferCount - 1 ) ].m_nMutableGuard );
Assert( VjobDmaGetUint64( ( uint )eaJtsRetGuard, DMATAG_SYNC, 0, 0 ) == MUTABLE_GUARD_COMMAND );
VjobDmaPutfUint64( CELL_SPURS_JOB_COMMAND_JTS, (uint)eaJtsRetGuard, VJOB_IOBUFFER_DMATAG );
m_ringSpu.AddToTail( Segment_t( m_eaRingSpuBase, eaJtsRetGuard ) );
// Signal from RSX to SPU that RSX is done with this segment of local buffer and will go ahead and render using the next shader
void * lsCmdBufferData = NULL;
GCM_CTX_RESERVE( 2 + 4 + 10 + 2 + 4 ); // don't let callback insert anything between the following commands
//GCM_FUNC( cellGcmSetWriteBackEndLabel, GCM_LABEL_DEBUG0, uintp( eaJtsRetGuard ) );
GCM_FUNC( cellGcmSetTransferLocation, CELL_GCM_LOCATION_MAIN );
GCM_FUNC( cellGcmSetInlineTransferPointer, uintp( eaJtsRetGuard ) + m_nIoBufferOffsetDelta, 2, &lsCmdBufferData );
// CELL_SPURS_JOB_OPCODE_RET (7|(14 << 3))
( ( uint32* )lsCmdBufferData )[0] = ( uint32( uint64( MUTABLE_GUARD_COMMAND ) >> 32 ) ); // uint64 to avoid any compiler issues
( ( uint32* )lsCmdBufferData )[1] = ( uint32( MUTABLE_GUARD_COMMAND ) );
GCM_FUNC( cellGcmSetWriteTextureLabel, GCM_LABEL_DEBUG_FPCP_RING, uintp( eaJtsRetGuard ) );
m_eaRingSpuLastSegment = m_eaRingSpuBase;
m_nRingSpuJobCount = 0;
}

View File

@@ -0,0 +1,243 @@
//===== Copyright © 1996-2010, Valve Corporation, All rights reserved. ======//
//
// Purpose: A convenient, clean interface for communicating between UI and
// the PS3 save system.
//
// $NoKeywords: $
//===========================================================================//
#ifndef PS3_SAVEUIAPI_H
#define PS3_SAVEUIAPI_H
#include "threadtools.h"
#include "ps3_pathinfo.h"
class CUtlBuffer;
enum eSaveOperationTag
{
kSAVE_TAG_UNKNOWN = 0,
kSAVE_TAG_INITIALIZE,
kSAVE_TAG_WRITE_STEAMINFO,
kSAVE_TAG_WRITE_AUTOSAVE, // writing an autosave to the container
kSAVE_TAG_READ_SAVE, // reading a save from the container
kSAVE_TAG_WRITE_SAVE, // writing a manual save to the container
kSAVE_TAG_READ_SCREENSHOT, // reading a screenshot from the container
kSAVE_TAG_DELETE_SAVE, // deleting a save from the container
};
// this class will hold the result of an async operation.
// poll JobDone() until it returns true, then you can
// look at the other fields for what actually transpired.
class CPS3SaveRestoreAsyncStatus
{
public:
inline bool JobDone() { return !!m_bDone; }
inline int GetSonyReturnValue(); // will return either an error from the enum below or one of these (defined by Sony):
/*
CELL_SAVEDATA_RET_OK
success
CELL_SAVEDATA_ERROR_CBRESULT
Callback function returned an error
CELL_SAVEDATA_ERROR_ACCESS_ERROR
HDD access error
CELL_SAVEDATA_ERROR_INTERNAL
Fatal internal error
CELL_SAVEDATA_ERROR_PARAM
Error in parameter to be set to utility (application bug)
CELL_SAVEDATA_ERROR_NOSPACE
Insufficient free space (application bug: lack of free space must be judged and handled within the callback function)
CELL_SAVEDATA_ERROR_BROKEN
Save data corrupted (modification detected, etc.)
CELL_SAVEDATA_ERROR_FAILURE
Save/load of save data failed (file could not be found, etc.)
CELL_SAVEDATA_ERROR_BUSY
Save data utility function was called simultaneously
CELL_SAVEDATA_ERROR_NOUSER
Specified user does not exist
CELL_SAVEDATA_ERROR_SIZEOVER
Exceeds the maximum size of the saved data
CELL_SAVEDATA_ERROR_NODATA
Specified save data does not exist on the HDD
CELL_SAVEDATA_ERROR_NOTSUPPORTED
Called in an unsupported state
*/
enum eSaveErrors // our own error types; not from Sony but formatted the same so as to look uniform.
{
CELL_SAVEDATA_ERROR_THREAD_WAS_BUSY = -11,
CELL_SAVEDATA_ERROR_FILE_NOT_FOUND = -12, // you tried to load a file that wasn't in the container
CELL_SAVEDATA_ERROR_WRONG_USER = -13, // tried to open or operate on a container belonging to a different user
CELL_SAVEDATA_ERROR_NO_TOC = -14, // failed to read TOC
CELL_SAVEDATA_ERROR_WRAPPER = -15, // a general failure somewhere in the wrapper classes.
CELL_SAVEDATA_ERROR_NO_WORK_TO_DO = -16,
CELL_SAVEDATA_WARNING_ASYNC_FAILSAFE = -17, // an operation completed but somehow forgot to trip the async object (so a failsafe triggered)
};
uint32 m_nCurrentOperationTag; // an arbitrary enum that you can set to whatever you like, except 0. Zero means "not doing anything."
int m_nSonyRetValue;
bool m_bUseSystemDialogs; ///< when true, says that we should have the OS pop up dialogs for error conditions, rather than letting the game handle them based on the return value here.
CInterlockedInt m_bDone;
uint32 m_uiAdditionalDetails;
CPS3SaveRestoreAsyncStatus() : m_bDone(true), m_bUseSystemDialogs(false), m_nSonyRetValue(0), m_nCurrentOperationTag(kSAVE_TAG_UNKNOWN), m_uiAdditionalDetails( 0 )
{};
};
inline int CPS3SaveRestoreAsyncStatus::GetSonyReturnValue()
{
return m_nSonyRetValue;
}
class IPS3SaveSteamInfoProvider
{
public:
virtual CUtlBuffer * GetInitialLoadBuffer() = 0;
virtual CUtlBuffer * GetSaveBufferForCommit() = 0;
virtual CUtlBuffer * PrepareSaveBufferForCommit() = 0;
};
// the interface class
class IPS3SaveRestoreToUI : public IAppSystem
{
public:
// the maximum possible size of a COMMENT field (including the terminal zero)
enum { PS3_SAVE_COMMENT_LENGTH = 128 };
enum FileSecurity_t
{
kSECURE,
kHACKABLE,
kSYSTEM,
};
// information about one file in the container
struct CPS3ContainerFileInfo
{
char fileName[64];
char comment[PS3_SAVE_COMMENT_LENGTH];
FileSecurity_t fileType;
uint32 size; // in bytes
time_t modtime; // modification time as a POSIX time_t
CPS3ContainerFileInfo() { memset(fileName, 0, sizeof(fileName)); memset(comment, 0, sizeof(comment)); }
};
// the struct that gets written into by GetContainerInfo()
struct CPS3ContainerFacts
{
int hddFreeSizeKB; // free size on disk IN KILOBYTES
int sizeKB; // current size of container in kilobytes
bool bOwnedByAnotherUser; // can't load if this is the case
CUtlVectorConservative< CPS3ContainerFileInfo > files;
};
struct PS3SaveGameInfo_t
{
PS3SaveGameInfo_t() : m_nFileTime(0) {}
CUtlString m_InternalName; // eg 0000005.SAV
CUtlString m_Filename; // eg autosave.ps3.sav
CUtlString m_ScreenshotFilename; // eg autosave.ps3.tga if one exists
CUtlString m_Comment;
time_t m_nFileTime;
};
virtual ~IPS3SaveRestoreToUI(){};
public:
/// You have to call this before doing any other save operation. In particular,
/// there may be an error opening the container. Poll on Async, and when it's done,
/// look in the return value to see if it succeeded, or if not, why not.
/// When bCreateIfMissing is set, it will create a new container where none exists.
virtual void Initialize( CPS3SaveRestoreAsyncStatus *pAsync, IPS3SaveSteamInfoProvider *pSteamInfoProvider, bool bCreateIfMissing, int nKBRequired ) = 0;
// Save the given file into the container. (ie /dev_hdd1/tempsave/foo.ps3.sav will
// be written into the container as foo.ps3.sav ). You can optionally specify a second
// file to be written at the same time, eg a screenshot, because batching two writes
// to happen at once is far far faster than having two batches one after another.
// ALL game progress files must be
// written as secure; for now, even the screenshots should be, as that puts the work
// of CRC onto the operating system.
virtual void Write( CPS3SaveRestoreAsyncStatus *pAsync, const char *pSourcepath, const char *pScreenshotPath, const char *pComment, FileSecurity_t nSecurity = kSECURE ) = 0;
// A more complicated and intelligent form of save writing, specifically for the case of autosaves.
// The source filename given (as an absolute path) will be written to "autosave.ps3.sav".
// Meanwhile, the existing "autosave.ps3.sav" will be renamed "autosave01.ps3.sav",
// any "autosave01.ps3.sav" will be renamed "autosave02.ps3.sav", and so on up to a maximum
// number of autosaves N, plus the base autosave.ps3.sav. The highest "autosave%02d.ps3.sav"
// will therefore have a number N. Excess autosaves with numbers >N will be deleted.
// If you specify a ScreenshotExtension (such as "tga"), the same operation is performed
// for every file above, where ."sav" is replaced with the given extension.
virtual void WriteAutosave( CPS3SaveRestoreAsyncStatus *pAsync,
const char *pSourcePath, // eg "/dev_hdd1/tempsave/autosave.ps3.sav"
const char *pComment, // the comment field for the new autosave.
const unsigned int nMaxNumAutosaves ) = 0; // should be at least 1; the highest numbered autosave will be N-1.
// A way of writing clouded files into container, clouded files over
// a certain age are purged from container
virtual void WriteCloudFile( CPS3SaveRestoreAsyncStatus *pAsync,
const char *pSourcePath,
const unsigned int nMaxNumCloudFiles ) = 0; // should be at least 1; the highest numbered cloud file will be N-1.
// Load a file from the container into the given directory.
// give it a pointer to a CPS3SaveRestoreAsyncStatus struct that you have created and
// intend to poll.
virtual void Load( CPS3SaveRestoreAsyncStatus *pAsync, const char *pFilename, const char *pDestFullPath ) = 0;
// kill one or two files (eg, save and screenshot).
// async will respond when done.
virtual void Delete( CPS3SaveRestoreAsyncStatus *pAsync, const char *pFilename, const char *pOtherFilename = NULL ) = 0;
// synchronously retrieve information on the files in the container. Lacks some of the container-wide'
// info of the above function, and may have slightly out of date information, but is a synchronous call
// and returns precisely the structure needed by CBaseModPanel::GetSaveGameInfos().
virtual void GetFileInfoSync( CUtlVector< PS3SaveGameInfo_t > &saveGameInfos, bool bFindAll ) = 0;
// try to find Steam's schema file for the user and stuff it into the container.
// returns false if it couldn't find the file locally (in which case you should
// not wait for the async object to be "done", as the job wasn't initiated);
// true if it found it locally and queued up an async job to write it.
virtual void WriteSteamInfo( CPS3SaveRestoreAsyncStatus *pAsync ) = 0;
// returns whether save thread is busy
virtual bool IsSaveUtilBusy() = 0;
// returns the m_nCurrentOperationTag field of the most recent async op to
// have run, or kSAVE_TAG_UNKNOWN if none has been enqueued yet. This tag
// changes the moment a job is made active and remains until the next job
// starts.
virtual uint32 GetCurrentOpTag() = 0;
// returns the version of container, used to fire off events when container
// contents changes.
virtual uint32 GetContainerModificationVersion() = 0;
// sets the cloud crypto key.
virtual void SetCloudFileCryptoKey( uint64 uiCloudCryptoKey ) = 0;
};
#define IPS3SAVEUIAPI_VERSION_STRING "IPS3SAVEUIAPI_001"
#endif

View File

@@ -0,0 +1,90 @@
//========= Copyright © 1996-2004, Valve LLC, All rights reserved. ============
//
// This common file serves as redirection to make minimal SPU-only preparation
// of a job and call into its Main function. The Main function is hidden behind
// the unique namespace, just like all global symbols, so that multiple similar
// jobs can easily compile into and link with the vjobs.prx module that can call
// them on PPU either for debugging, for fallback case, or for main processing
// on Xbox360
//
//#include <cell/spurs/job_chain.h>
#include <cell/spurs/job_queue.h>
#include <cell/spurs/job_context.h>
#include <spu_printf.h>
#ifdef USE_LSGUARD
#include <cell/lsguard.h>
#endif
#ifndef VJOB
#error "Please define VJOB to the project name in SPU job project. This will isolate it from other jobs when they compile into the common elf, prx or dll"
#endif
namespace VJOB
{
extern void Main( CellSpursJobContext2* stInfo, CellSpursJob256 *job );
}
CellSpursJobContext2* g_stInfo = 0;
uint32_t g_InterlockedBuffer[32] __attribute__((aligned(128)));
#ifdef VJOB_JOBCHAIN_JOB
// JobChain job: the symbol is "job"
extern "C"
void cellSpursJobMain2(CellSpursJobContext2* stInfo, CellSpursJob256 *job)
{
extern CellSpursJobContext2* g_stInfo;
g_stInfo = stInfo;
VJOB::Main( stInfo, job );
}
#else
// JobQueue job: the symbol is "jqjob"
void cellSpursJobQueueMain(
CellSpursJobContext2 *pContext,
CellSpursJob256 *pJob
)
{
extern CellSpursJobContext2* g_stInfo;
g_stInfo = pContext;
VJOB::Main( pContext, pJob );
}
#endif
void CheckBufferOverflow_Impl()
{
uint16_t nCause;
int nResult;
nResult = cellSpursJobMemoryCheckTest( &nCause );
if ( nResult != CELL_OK )
{
spu_printf( "cellSpursJobMemoryCheckTest() failed = %08X\n", nResult );
__asm volatile ("stopd $0,$0,$0");
}
#ifdef USE_LSGUARD
nResult = cellLsGuardCheckCorruption();
if ( nResult != CELL_OK )
{
spu_printf( "cellLsGuardCheckCorruption() failed = %08X\n", nResult );
__asm volatile ("stopd $0,$0,$0");
cellLsGuardRehash(); // We rehash to detect the next corruption
}
#endif
}
void CheckDmaGet_Impl( const void * pBuffer, size_t nSize )
{
#ifdef USE_LSGUARD
int nResult;
nResult = cellLsGuardCheckWriteAccess( pBuffer, nSize );
if ( nResult != CELL_OK )
{
spu_printf( "cellLsGuardCheckWriteAccess() failed = %08X\n", nResult );
spu_printf( "Address: %08X - Size: %d\n", (int)pBuffer, (int)nSize );
__asm volatile ("stopd $0,$0,$0");
}
#endif
}

View File

@@ -0,0 +1,435 @@
//========== Copyright © Valve Corporation, All rights reserved. ========
#include "ps3/spu_job_shared.h"
uint g_nBreakMask = 0;
void* AlignBuffer( void * pBuffer, uint nBytes )
{
if( !( uintp( pBuffer ) & 15 ) )
{
return pBuffer;
}
Assert( nBytes < 232*1024 ); // sanity check
vector int *pBegin = ( vector int * )( pBuffer ), *pEnd = ( vector int* )( uintp( pBuffer ) + nBytes );
vector int vLast = *pBegin;
vector int *pLast = pBegin;
vector unsigned char vShuf = vec_lvsl( 0, (uint8*)pBuffer );
while( pLast < pEnd )
{
vector int * pNext = pLast + 1;
vector int vNext = *pNext;
*pLast = vec_perm( vLast, vNext, vShuf );
pLast = pNext;
vLast = vNext;
}
return ( void* )( uintp( pBuffer ) & -16 );
}
//
// Adds constant nAdd to the given unaligned buffer of uint16's
//
void UnalignedBufferAddU16( uint16 * pBuffer, uint nCount, uint16 nAdd )
{
#ifdef SPU
if( nCount )
{
uint16 *pBufferEnd = pBuffer + nCount;
vector unsigned short vuAdd = vec_splat_u16( nAdd );
vector unsigned short vuLeft = spu_rlmaskqwbyte( vuAdd, -( 0xF & int( pBuffer ) ) );
vector unsigned short vuRight = spu_slqwbyte( vuAdd, 0xF & -int( pBufferEnd ) );
vector unsigned short * pLeft = ( vector unsigned short * )( uintp( pBuffer ) & -16 ), * pRight = ( vector unsigned short* )( uintp( pBufferEnd - 1 ) & -16 );
if( pLeft == pRight )
{
*pLeft = vec_add( *pLeft, vec_and( vuLeft, vuRight ) );
}
else
{
*pLeft = vec_add( *pLeft, vuLeft );
*pRight = vec_add( *pRight, vuRight );
for( vector unsigned short * p = pLeft + 1; p < pRight; ++p )
{
*p = vec_add( *p, vuAdd );
}
}
}
#else
for( uint i = 0; i < nCount; ++i )
{
pBuffer[i] += nAdd;
}
#endif
}
void TestUnalignedBufferAddU16( )
{
uint16 ALIGN16 test[8 * 6] ALIGN16_POST;
for( uint l = 0; l <= 8; ++l )
{
for( uint e = l; e < ARRAYSIZE( test ); ++e )
{
V_memset( test, 0, sizeof( test ) );
UnalignedBufferAddU16( test + l, e - l, e+1 );
for( uint t = 0; t < l; ++ t )
Assert( test[t] == 0 );
for( uint t = l; t < e; ++t )
Assert( test[t] == e+1 );
for( uint t = e; t < ARRAYSIZE( test ); ++t )
Assert( test[t] == 0 );
}
}
}
#ifndef SPU
void TestAlignBuffer()
{
for( uint i = 0; i < 16; ++i )
{
uint8 ALIGN16 test[16 * 10] ALIGN16_POST;
for( uint j = i; j < sizeof( test ); ++j )
test[j] = uint8( j - i );
uint8 * pBeginTest = (uint8*)AlignBuffer( test + i, sizeof( test ) - 16 );
Assert( pBeginTest == test );
for( uint j = 0; j < sizeof( test ) - 16; ++j )
Assert( test[j] == uint8( j ) );
}
}
CellSpursJobContext2* g_stInfo = NULL;
static void SyncDmaListTransfer( void * pDmaList, uint nDmaListSize, void * pTarget, uint nTargetMaxSize )
{
Assert( !( nDmaListSize & 7 ) && !( uintp( pDmaList ) & 0xF ) );
//uintp dmaTarget = ( uintp ) pTarget, dmaTargetEnd = dmaTarget + nTargetMaxSize;
CellSpursJobInputList * pInputDmaList = ( CellSpursJobInputList* )pDmaList, *pInputDmaListEnd = ( CellSpursJobInputList * )( uintp( pDmaList ) + nDmaListSize );
uintp lsDmaTarget = ( uintp ) pTarget, lsDmaTargetEnd = lsDmaTarget + nTargetMaxSize;
for ( CellSpursJobInputList * pDmaElement = pInputDmaList; pDmaElement < pInputDmaListEnd; pDmaElement++ )
{
Assert( pDmaElement->asInputList.size <= 16 * 1024 ); // max size of a DMA element
uintp lsDmaEnd = lsDmaTarget + pDmaElement->asInputList.size;
Assert( lsDmaEnd <= lsDmaTargetEnd );
V_memcpy( ( void* )lsDmaTarget, ( const void* ) pDmaElement->asInputList.eal, pDmaElement->asInputList.size );
lsDmaTarget = AlignValue( lsDmaEnd, 16 ); // for small transfers, we must stalign every transfer by 16
}
}
void VjobPushJob( void ( *pfnMain )( CellSpursJobContext2 * stInfo, CellSpursJob256 * job ), CellSpursJob128 * job )
{
CellSpursJobContext2 info;
V_memset( &info, 0, sizeof( info ) );
void * ioBuffer = MemAlloc_AllocAligned( job->header.sizeInOrInOut, 16 );
info.ioBuffer = ioBuffer;
info.eaJobDescriptor = ( uintp ) job;
CellSpursJob256 jobCopy;
V_memcpy( &jobCopy, job, sizeof( *job ) );
SyncDmaListTransfer( job->workArea.dmaList, job->header.sizeDmaList, ioBuffer, job->header.sizeInOrInOut );
g_stInfo = &info;
pfnMain( &info, ( CellSpursJob256* ) job );
g_stInfo = NULL;
MemAlloc_FreeAligned( ioBuffer );
}
void VjobSpuLog( const char * p, ... )
{
va_list args;
va_start( args, p );
char szBuffer[2048];
V_vsnprintf( szBuffer, sizeof( szBuffer ), p, args );
Msg( "SPU-on-PPU: %s\n", szBuffer );
va_end( args );
}
#define Check(b) if(!(b))DebuggerBreak();
void VjobDmaPut(
const void * ls,
uint64_t ea,
uint32_t size,
uint32_t tag,
uint32_t tid,
uint32_t rid
)
{
Check( !( size & 0xF ) && size <= 16 * 1024 );
Check( !( ea & 0xF ) && !( uintp( ls ) & 0xF ) );
V_memcpy( ( void* )( uintp )ea, ls, size );
}
void VjobDmaLargePut(
const void * ls,
uint64_t ea,
uint32_t size,
uint32_t tag,
uint32_t tid,
uint32_t rid
)
{
Check( !( size & 0xF ) && size <= 240 * 1024 );
Check( !( ea & 0xF ) && !( uintp( ls ) & 0xF ) );
V_memcpy( ( void* )( uintp )ea, ls, size );
}
void VjobDmaLargePutf(
const void * ls,
uint64_t ea,
uint32_t size,
uint32_t tag,
uint32_t tid,
uint32_t rid
)
{
VjobDmaLargePut( ls, ea, size, tag, tid, rid );
}
void VjobDmaUnalignedPutf(
const void *ls,
uint64_t ea,
uint32_t size,
uint32_t tag,
uint32_t tid,
uint32_t rid
)
{
Assert( 0 == ( 0xF & ( uintp( ls ) ^ ea ) ) );
V_memcpy( (void*)(uintp)ea, ls, size );
}
void VjobDmaUnalignedPut(
const void *ls,
uint64_t ea,
uint32_t size,
uint32_t tag,
uint32_t tid,
uint32_t rid
)
{
Assert( 0 == ( 0xF & ( uintp( ls ) ^ ea ) ) );
V_memcpy( (void*)(uintp)ea, ls, size );
}
void VjobDmaLargePutb(
const void * ls,
uint64_t ea,
uint32_t size,
uint32_t tag,
uint32_t tid,
uint32_t rid
)
{
VjobDmaLargePut( ls, ea, size, tag, tid, rid );
}
void VjobDmaPutf(
const void * ls,
uint64_t ea,
uint32_t size,
uint32_t tag,
uint32_t tid,
uint32_t rid
)
{
Check( !( size & 0xF ) && size <= 16 * 1024 );
Check( !( ea & 0xF ) && !( uintp( ls ) & 0xF ) );
V_memcpy( ( void* )( uintp )ea, ls, size );
}
void VjobDmaSmallPut(
const void * ls,
uint64_t ea,
uint32_t size,
uint32_t tag,
uint32_t tid,
uint32_t rid
)
{
Check( !( size & ( size - 1 ) ) );
Check( !( 0xF & ( ea ^ uintp( ls ) ) ) );
if ( size == 4 )
{
// special case to handle atomically, because we may use this to write RSX registers
*( uint32* )( uintp )ea = *( uint32* )ls;
}
else
{
V_memcpy( ( void* )( uintp )ea, ls, size );
}
}
void VjobDmaGet(
void * ls,
uint64_t ea,
uint32_t size,
uint32_t tag,
uint32_t tid,
uint32_t rid
)
{
Check( !( size & ( size - 1 ) ) );
Check( !( 0xF & ( ea | uintp( ls ) ) ) );
if ( size == 4 )
{
// special case to handle atomically, because we may use this to read RSX registers
*( uint32* )ls = *( uint32* )( uintp )ea;
}
else
{
V_memcpy( ls, ( const void* )( uintp )ea, size );
}
}
void VjobDmaGetf(
void * ls,
uint64_t ea,
uint32_t size,
uint32_t tag,
uint32_t tid,
uint32_t rid
)
{
VjobDmaGet( ls, ea, size, tag, tid, rid );
}
// NOTE: implementation must wait for tag
uint32_t VjobDmaGetUint32(
uint64_t ea,
uint32_t tag,
uint32_t tid,
uint32_t rid
)
{
return * ( volatile uint32 * )( uintp )ea;
}
void VjobDmaPutUint32(
uint32_t value,
uint64_t ea,
uint32_t tag,
uint32_t tid,
uint32_t rid
)
{
( * ( volatile uint32 * )( uintp )ea ) = value;
}
uint64_t VjobDmaGetUint64(
uint64_t ea,
uint32_t tag,
uint32_t tid,
uint32_t rid
)
{
return *( volatile uint64 * )( uintp )ea;
}
void VjobDmaPutUint64(
uint64_t value,
uint64_t ea,
uint32_t tag,
uint32_t tid,
uint32_t rid
)
{
( * ( volatile uint64 * )( uintp )ea ) = value;
}
void VjobDmaListGet(
void * ls,
uint64_t ea,
const CellDmaListElement * list,
uint32_t listSize,
uint32_t tag,
uint32_t tid,
uint32_t rid
)
{
Check( !( listSize % 8 ) );
uint8 * pLsTarget = ( uint8* )ls;
for ( uint i = 0; i < listSize / 8; ++i )
{
uint64 nSize = list[i].size;
VjobDmaGet( pLsTarget, ea + list[i].eal, ( uint32 )nSize, tag, tid, rid );
}
}
void VjobDmaSmallGet(
void * ls,
uint64_t ea,
uint32_t size,
uint32_t tag,
uint32_t tid,
uint32_t rid
)
{
Check( !( size & ( size - 1 ) ) );
Check( !( 0xF & ( ea ^ uintp( ls ) ) ) );
V_memcpy( ls, ( const void* )( uintp )ea, size );
}
void VjobDmaSmallPutf(
const void * ls,
uint64_t ea,
uint32_t size,
uint32_t tag,
uint32_t tid,
uint32_t rid
)
{
Check( !( size & ( size - 1 ) ) );
Check( !( 0xF & ( ea ^ uintp( ls ) ) ) );
V_memcpy( ( void* )( uintp )ea, ls, size );
}
void VjobDmaSmallPutb(
const void * ls,
uint64_t ea,
uint32_t size,
uint32_t tag,
uint32_t tid,
uint32_t rid
)
{
Check( !( size & ( size - 1 ) ) );
Check( !( 0xF & ( ea ^ uintp( ls ) ) ) );
V_memcpy( ( void* )( uintp )ea, ls, size );
}
void VjobPpuRereadEA( uintp ea )
{
__lwsync();
int eaContent = *( volatile int * ) ea;
__lwsync();
}
#endif

753
common/ps3/spu_job_shared.h Normal file
View File

@@ -0,0 +1,753 @@
//========= Copyright © 1996-2004, Valve LLC, All rights reserved. ============
//
// This is the common include file to be included in SPU jobs.
// It takes care to remap/emulate some SPU-specific functinality on PPU
//
#ifndef PS3_SPU_JOB_SHARED_HDR
#define PS3_SPU_JOB_SHARED_HDR
#ifdef _PS3
#include <ps3/ps3_platform.h>
#include <cell/spurs/job_chain.h>
#include <cell/spurs/job_queue.h>
#include <cell/spurs/job_queue_port2.h>
#include <cell/dma/types.h>
//
// NOTE: Enable the following block for debugging GCM on SPU; works as of SDK 350
//
#if 0 && defined( __SPU__ )
#include <cell/gcm/gcm_macros.h>
#undef CELL_GCM_ASSERT
#undef CELL_GCM_ASSERTS
#define CELL_GCM_ASSERT(condition) Assert( condition )
#define CELL_GCM_ASSERTS(condition, description) AssertSpuMsg( condition, description )
#define CELL_GCM_ASSERT_ENABLE
#endif
enum DmaTagEnum_t
{
DMATAG_SYNC = 2, // used for synchronous transfers, where we need the transfer to finish very soon/immediately after issuing
DMATAG_TEXTURES = 3,
DMATAG_SHADERS = 4,
DMATAG_SCRATCH = 5, // used for DMA PUTs from Scratch memory, so we need to wait for this to finish before job finishes
// each jobchain needs 2 dma tags, up to tag 30
// DMATAG_EDGE_JOBCHAIN = 8,
// DMATAG_FPCP_JOBCHAIN = 10,
// DMATAG_GCM_JOBCHAIN = 12,
DMATAG_ANIM = 8, // non immediate dma's
DMATAG_BUILDINDICES = 8,
DMATAG_BUILDRENDERABLES = 8,
}; // shouldn't overlap with the tags used by the workload
// Enable this define to disable assert. This may be necessary to detect timing issues in DEBUG and RELEASE,
// or incorrectly generated code from compiler. When LSGUARD is enabled, we disable asserts to force potential issues.
#ifdef USE_LSGUARD
# define DISABLE_ASSERT
#endif
template <typename T>
inline T* AddBytes( T* p, int nBytes )
{
return ( T* )( int( p ) + nBytes );
}
template <typename T>
inline T Min( T a, T b )
{
return a < b ? a : b;
}
template <typename T>
inline T Max( T a, T b )
{
return a > b ? a : b;
}
template <typename T>
inline void Swap( T& a , T & b )
{
T c = a; a = b; b = c;
}
// <sergiy> should I port platform.h to SPU?
#ifdef SPU
#include <cell/spurs/job_context.h>
#include "cell/spurs/common.h"
#include <cell/atomic.h>
#include <spu_intrinsics.h>
#include <vmx2spu.h>
#define PPU_ONLY(X)
#define SPU_ONLY(X) X
#define vector __vector
void CheckBufferOverflow_Impl();
void CheckDmaGet_Impl( const void * pBuffer, size_t nSize );
#if defined(_CERT) || defined(DISABLE_ASSERT)
# define VjobSpuLog(...)
# define DebuggerBreak()
# define Warning(...)
# define CheckBufferOverflow()
# define CheckDmaGet(p, size)
#else
# include <spu_printf.h>
# define VjobSpuLog( MSG, ... ) spu_printf( "[%d]" MSG, cellSpursGetCurrentSpuId(), ##__VA_ARGS__ )
# define Msg( MSG, ... ) spu_printf( "[%d]" MSG, cellSpursGetCurrentSpuId(), ##__VA_ARGS__ )
#ifndef BASETYPES_H
#define DebuggerBreak() __asm volatile ("stopd $0,$0,$0")
#endif
# define Warning( MSG, ... ) spu_printf( "[%d] Warning: " MSG, cellSpursGetCurrentSpuId(), ##__VA_ARGS__ )
# define CELL_DMA_ASSERT_VERBOSE
# define CheckBufferOverflow() CheckBufferOverflow_Impl()
# define CheckDmaGet(p, size) CheckDmaGet_Impl( p, size )
#endif
#define LWSYNC_PPU_ONLY()
#define VJOB_IOBUFFER_DMATAG g_stInfo->dmaTag // fake DMA tag
#include <cell/spurs/common.h>
#define VjobDmaPut cellDmaPut
#define VjobDmaGet cellDmaGet
#define VjobDmaGetf cellDmaGetf
#define VjobDmaListGet cellDmaListGet
#define VjobDmaLargePut cellDmaLargePut
#define VjobDmaLargePutf cellDmaLargePutf
//#define VjobDmaLargePutb cellDmaLargePutb
#define VjobDmaPutf cellDmaPutf
#define VjobDmaSmallPut cellDmaSmallPut
#define VjobDmaSmallPutf cellDmaSmallPutf
//#define VjobDmaSmallPutb cellDmaSmallPutb
#define VjobDmaSmallGet cellDmaSmallGet
#define VjobWaitTagStatusAll cellDmaWaitTagStatusAll
#define VjobWaitTagStatusImmediate cellDmaWaitTagStatusImmediate
#define VjobDmaGetUint32 cellDmaGetUint32
#define VjobDmaPutUint32 cellDmaPutUint32
#define VjobDmaGetUint64 cellDmaGetUint64
#define VjobDmaPutUint64 cellDmaPutUint64
#define VjobDmaUnalignedPutf cellDmaUnalignedPutf
#define VjobDmaUnalignedPut cellDmaUnalignedPut
#define VjobDmaPutfUintTemplate(SIZE, value, ea, tag, tid, rid) \
do { \
uint64_t __cellDma_ea = ea; \
uint32_t __cellDma_tag = tag; \
qword _buf = (qword)spu_splats(value); \
cellDmaDataAssert(__cellDma_ea,sizeof(uint##SIZE##_t),__cellDma_tag); \
cellDmaAndWait(cellDmaEa2Ls(__cellDma_ea,&_buf),__cellDma_ea,sizeof(uint##SIZE##_t),__cellDma_tag,MFC_CMD_WORD(tid,rid,MFC_PUTF_CMD)); \
} while(0)
#define VjobDmaPutfUint8(value, ea, tag) cellDmaPutUintTemplate(8, ((uint8_t)value), ea, tag, 0, 0)
#define VjobDmaPutfUint16(value, ea, tag) cellDmaPutUintTemplate(16, ((uint16_t)value), ea, tag, 0, 0)
#define VjobDmaPutfUint32(value, ea, tag) cellDmaPutUintTemplate(32, ((uint32_t)value), ea, tag, 0, 0)
#define VjobDmaPutfUint64(value, ea, tag) cellDmaPutUintTemplate(64, ((uint64_t)value), ea, tag, 0, 0)
#define VjobSpuId() int( cellSpursGetCurrentSpuId() )
#define V_memset __builtin_memset
#define V_memcpy __builtin_memcpy
#if !defined ARRAYSIZE
#define ARRAYSIZE( ARRAY ) ( sizeof( ARRAY ) / sizeof( ( ARRAY )[0] ) )
#endif
typedef signed int int32;
typedef unsigned int uint;
typedef signed char int8;
typedef unsigned char uint8;
typedef signed short int16;
typedef unsigned short uint16;
typedef signed int int32;
typedef unsigned int uint32;
typedef signed long long int64;
typedef unsigned long long uint64;
typedef unsigned int uintp;
typedef vector float fltx4 ;
#define INT_MAX 0x7fffffff
#define DECL_ALIGN(x) __attribute__( ( aligned( x ) ) )
#ifndef BASETYPES_H
#define ALIGN16 DECL_ALIGN(16)
#define ALIGN16_POST
#define ALIGN128 DECL_ALIGN(128)
#define ALIGN128_POST
template <typename T>
inline T AlignValue( T val, uintp alignment )
{
return ( T )( ( ( uintp )val + alignment - 1 ) & ~( alignment - 1 ) );
}
#define ALIGN_VALUE( val, alignment ) ( ( val + alignment - 1 ) & ~( alignment - 1 ) )
inline bool IsPowerOfTwo( uint x )
{
return ( x & ( x - 1 ) ) == 0;
}
#endif
#define FORCEINLINE inline /* __attribute__ ((always_inline)) */
#define IsPlatformPS3() 1
#define IsPlatformPS3_PPU() 0
#define IsPlatformPS3_SPU() 1
#define IsPlatformX360() 0
#define IsPlatformOSX() 0
#if !defined RESTRICT
#define RESTRICT
#endif
#define V_memset __builtin_memset
#define V_memcpy __builtin_memcpy
inline void VjobPpuRereadEA( uintp ea ){}
#if defined(_CERT) || defined(DISABLE_ASSERT)
#define Assert(x) ((void)(0))
#define AssertSpuMsg(x,MSG,...)((void)0)
#ifndef DBG_H
#define COMPILE_TIME_ASSERT( pred ) // to avoid any unpredictable affects in the optimizer
#endif
#else
#define DBGFLAG_ASSERT
#ifndef DBG_H
#define Assert(x) do{if( !( x ) ) { spu_printf( "Assert on SPU[%d](" #x ")\n", cellSpursGetCurrentSpuId() ); DebuggerBreak(); } }while(0)
#endif
#define AssertSpuMsg(x,MSG,...) do{if( !( x ) ) { spu_printf( "Assert on SPU[%d](" #x "), " MSG, cellSpursGetCurrentSpuId(), ## __VA_ARGS__ ); DebuggerBreak(); } }while(0)
#ifndef DBG_H
#define COMPILE_TIME_ASSERT( pred ) switch(0){case 0:case pred:;}
#endif
#endif
// mimic the PPU class on SPU
// template< int bytesAlignment, class T >
// class CAlignedNewDelete : public T
// {public:
// }
// WARNING: SLOWNESS. DO NOT USE IN PRODUCTION.
inline void DebugMemcpyEa( uint eaDest, uint eaSrc, uint nSize, void *lsScratch )
{
Assert( ! ( 0xF & ( eaSrc | eaDest | nSize ) ) );
uint nBytesLeft = nSize, nOffset = 0;
while( nBytesLeft )
{
uint nChunk = Min<uint>( 16 * 1024, nBytesLeft );
VjobDmaGet( lsScratch, eaSrc + nOffset, nChunk, DMATAG_SYNC, 0, 0 );
VjobWaitTagStatusAll( 1 << DMATAG_SYNC );
VjobDmaPut( lsScratch, eaDest + nOffset, nChunk, DMATAG_SYNC, 0, 0 );
VjobWaitTagStatusAll( 1 << DMATAG_SYNC );
nBytesLeft -= nChunk;
nOffset += nChunk;
}
}
#define vec_to_uint32(X) si_to_uint( ( qword )( X ) )
#define VjobQueuePort2PushJob( eaPort, eaJob, sizeDesc, tag, dmaTag, flag ) cellSpursJobQueuePort2PushJob( (uintp)( eaPort ), (uintp)( eaJob ), ( sizeDesc ), ( tag ), ( dmaTag ), ( flag ) )
#define VjobQueuePort2PushSync( eaPort2, tagMask, dmaTag, flag ) cellSpursJobQueuePort2PushSync( ( uintp ) ( eaPort2), ( tagMask ), ( dmaTag ), ( flag ) )
inline void VjobQueuePort2PushJobBlocking( CellSpursJobQueuePort2 *eaPort2, CellSpursJobHeader *eaJob, size_t sizeDesc, uint nQueueTag, uint nDmaTag )
{
int nError;
for(;;)
{
nError = cellSpursJobQueuePort2PushJob( uintp( eaPort2 ), uintp( eaJob ) , sizeDesc, nQueueTag, nDmaTag, CELL_SPURS_JOBQUEUE_FLAG_NON_BLOCKING );
if( nError != CELL_SPURS_JOB_ERROR_AGAIN )
{
break;
}
}
if ( nError != CELL_OK )
{
VjobSpuLog( "Cannot push job, error %d. RSX is going to hang, then SPUs, then PPU.\n", nError );
DebuggerBreak();
}
}
inline void VjobQueuePort2PushSyncBlocking( CellSpursJobQueuePort2 *eaPort2, unsigned tagMask, uint nDmaTag )
{
int nError;
for(;;)
{
nError = cellSpursJobQueuePort2PushSync( uintp( eaPort2 ), tagMask, nDmaTag, CELL_SPURS_JOBQUEUE_FLAG_NON_BLOCKING );
if( nError != CELL_SPURS_JOB_ERROR_AGAIN )
{
break;
}
}
if ( nError != CELL_OK )
{
VjobSpuLog( "Cannot push job, error %d. RSX is going to hang, then SPUs, then PPU.\n", nError );
DebuggerBreak();
}
}
#else
#include "tier0/platform.h"
#include "tier1/strtools.h"
#include "mathlib/ssemath.h"
#include <altivec.h>
#include <cell/spurs/job_context_types.h>
inline uint32_t GetCurrentSpuId()
{
return 0xFFFFFFFF;
}
using namespace ::cell::Spurs;
extern void VjobSpuLog( const char * p, ... );
#define VJOB_IOBUFFER_DMATAG 0 // fake DMA tag
#define PPU_ONLY(X) X
#define SPU_ONLY(X)
#ifdef _DEBUG
#define AssertSpuMsg(x,MSG,...) do { if( !( x ) ) { Warning( "Assert(" #x "), " MSG, ## __VA_ARGS__ ); DebuggerBreak(); } }while( 0 )
#else
#define AssertSpuMsg(x,MSG,...)
#endif
#define VjobQueuePort2PushJob( eaPort, eaJob, sizeDesc, tag, dmaTag, flag ) cellSpursJobQueuePort2PushJob( (CellSpursJobQueuePort2 *)( eaPort ), (CellSpursJobHeader *)( eaJob ), ( sizeDesc ), ( tag ), ( flag ) )
#define VjobQueuePort2PushSync( eaPort2, tagMask, dmaTag, flag ) cellSpursJobQueuePort2PushSync( (CellSpursJobQueuePort2 *) ( eaPort2), ( tagMask ), ( flag ) )
inline void VjobQueuePort2PushJobBlocking( CellSpursJobQueuePort2 *eaPort2, CellSpursJobHeader *eaJob, size_t sizeDesc, uint nQueueTag, uint nDmaTag )
{
int nError = cellSpursJobQueuePort2PushJob( eaPort2, eaJob, sizeDesc, nQueueTag, 0 );// synchronous call
(void) nError;
Assert( nError == CELL_OK );
}
inline void VjobQueuePort2PushSyncBlocking( CellSpursJobQueuePort2 *eaPort2, unsigned tagMask, uint nDmaTag )
{
int nError = cellSpursJobQueuePort2PushSync( eaPort2, tagMask, 0 ); // synchronous call
(void) nError;
Assert( nError == CELL_OK );
}
#define VjobSpuId() -1
#define LWSYNC_PPU_ONLY() __lwsync()
extern void VjobDmaPut(
const void * ls,
uint64_t ea,
uint32_t size,
uint32_t tag,
uint32_t tid,
uint32_t rid
);
extern void VjobDmaGet(
void * ls,
uint64_t ea,
uint32_t size,
uint32_t tag,
uint32_t tid,
uint32_t rid
);
extern void VjobDmaGetf(
void * ls,
uint64_t ea,
uint32_t size,
uint32_t tag,
uint32_t tid,
uint32_t rid
);
extern void VjobDmaListGet(
void *ls,
uint64_t ea,
const CellDmaListElement *list,
uint32_t listSize,
uint32_t tag,
uint32_t tid,
uint32_t rid
);
extern void VjobDmaLargePut(
const void * ls,
uint64_t ea,
uint32_t size,
uint32_t tag,
uint32_t tid,
uint32_t rid
);
extern void VjobDmaLargePutf(
const void * ls,
uint64_t ea,
uint32_t size,
uint32_t tag,
uint32_t tid,
uint32_t rid
);
extern void VjobDmaLargePutb(
const void * ls,
uint64_t ea,
uint32_t size,
uint32_t tag,
uint32_t tid,
uint32_t rid
);
extern void VjobDmaPutf(
const void * ls,
uint64_t ea,
uint32_t size,
uint32_t tag,
uint32_t tid,
uint32_t rid
);
extern void VjobDmaSmallPut(
const void * ls,
uint64_t ea,
uint32_t size,
uint32_t tag,
uint32_t tid,
uint32_t rid
);
extern void VjobDmaSmallGet(
void * ls,
uint64_t ea,
uint32_t size,
uint32_t tag,
uint32_t tid,
uint32_t rid
);
extern void VjobDmaSmallPutb(
const void * ls,
uint64_t ea,
uint32_t size,
uint32_t tag,
uint32_t tid,
uint32_t rid
);
extern void VjobDmaSmallPutf(
const void * ls,
uint64_t ea,
uint32_t size,
uint32_t tag,
uint32_t tid,
uint32_t rid
);
// NOTE: implementation must wait for tag
uint32_t VjobDmaGetUint32(
uint64_t ea,
uint32_t tag,
uint32_t tid,
uint32_t rid
);
void VjobDmaPutUint32(
uint32_t value,
uint64_t ea,
uint32_t tag,
uint32_t tid,
uint32_t rid
);
uint64_t VjobDmaGetUint64(
uint64_t ea,
uint32_t tag,
uint32_t tid,
uint32_t rid
);
void VjobDmaPutUint64(
uint64_t value,
uint64_t ea,
uint32_t tag,
uint32_t tid,
uint32_t rid
);
void VjobDmaUnalignedPutf(
const void *ls,
uint64_t ea,
uint32_t size,
uint32_t tag,
uint32_t tid,
uint32_t rid
);
void VjobDmaUnalignedPut(
const void *ls,
uint64_t ea,
uint32_t size,
uint32_t tag,
uint32_t tid,
uint32_t rid
);
// These functions are empty because I'm too lazy to implement deferred DMA emulation ...
inline uint VjobWaitTagStatusAll( uint nTagMask ){ return nTagMask;}
inline uint VjobWaitTagStatusImmediate( uint nTagMask ) { return nTagMask ; }
#define VjobDmaPutfUint8(value, ea, tag) *(uint8*)ea = (uint8)value
#define VjobDmaPutfUint16(value, ea, tag) *(uint16*)ea = (uint16)value
#define VjobDmaPutfUint32(value, ea, tag) *(uint32*)ea = (uint32)value
#define VjobDmaPutfUint64(value, ea, tag) *(uint64*)ea = (uint64)value
void VjobPushJob( void ( *pfnMain )( CellSpursJobContext2 * stInfo, CellSpursJob256 * job ), CellSpursJob128 * job );
extern void VjobSpuLog( const char * p, ... );
extern void VjobPpuRereadEA( uintp ea );
inline void DebugMemcpyEa( uint eaDest, uint eaSrc, uint nSize, void *lsScratch )
{
Assert( ! ( 0xF & ( eaSrc | eaDest | nSize ) ) );
memcpy( (void*)eaDest, (void*)eaSrc, nSize );
}
extern void TestAlignBuffer();
#define vec_to_uint32(X) (*(uint32*)&(X))
#endif // SPU
#define VjobDmaEa2Ls16(ea, ls) ((uintptr_t)(ls)+((uint32_t)(ea)&15))
#define VjobDmaEa2Ls128(ea, ls) ((uintptr_t)(ls)+((uint32_t)(ea)&127))
inline uint32* PrepareSmallPut32( vector unsigned int * lsAligned, volatile uint32 * eaUnaligned, uint32 nInitialValue )
{
Assert( !( 3 & uint( lsAligned ) ) );
uint32 * ls = ( uint32* )VjobDmaEa2Ls16( eaUnaligned, lsAligned );
*ls = nInitialValue;
return ls;
}
inline uint64* PrepareSmallPut64( vector unsigned int * lsAligned, volatile uint64 * eaUnaligned, uint64 nInitialValue )
{
Assert( !( 7 & uint( lsAligned ) ) );
uint64 * ls = ( uint64* )VjobDmaEa2Ls16( eaUnaligned, lsAligned );
*ls = nInitialValue;
return ls;
}
extern CellSpursJobContext2* g_stInfo;
#ifndef IsDebug
# ifdef _DEBUG
# define IsDebug() true
# else
# define IsDebug() false
# endif
#endif
#ifndef IsCert
# ifdef _CERT
# define IsCert() true
# else
# define IsCert() false
# endif
#endif
extern uint g_nBreakMask ;
#ifdef _CERT
# define BreakOn( nId )
#else
# define BreakOn( nId ) do \
{ \
if( g_nBreakMask & ( 1 << nId ) ) \
DebuggerBreak(); \
}while( 0 )
#endif
inline void VjobDebugSpinCycles( uint nCycles )
{
if( !IsCert() )
{
#ifdef SPU
uint nStart = spu_read_decrementer();
while( nStart - spu_read_decrementer() < nCycles / 40 )
continue;
#else
sys_timer_usleep( nCycles / 3200 );
/*
uint nStart = __mftb();
while( __mftb() - nStart() < nCycles / 40 )
continue;
*/
#endif
}
}
// this is the DMA list element without notify or reserved fields, so that it's easy to fill it in
// and be sure there is no garbage left (in notify and reserved fields) and there are no bit field operations (to store size, which is effectively only 14-bit value)
struct BasicDmaListElement_t
{
uint32 size;
uint32 eal;
};
// shifts unaligned pBuffer of given size left by 0..15 bytes to make it aligned
// returns the aligned pointer, pBuffer & -16
extern void* AlignBuffer( void * pBuffer, uint nBytes);
//
// Adds constant nAdd to the given unaligned buffer of uint16's
//
extern void UnalignedBufferAddU16( uint16 * pBuffer, uint nCount, uint16 nAdd );
// SpursJob_t must be one of CellSpursJob64, CellSpursJob128, CellSpursJob256,...
// JobParam_t is the parameter structure passed to the job
template < typename JobParam_t , typename SpursJob_t >
inline JobParam_t * VjobGetJobParams( void * pJob )
{
Assert( sizeof( JobParam_t ) + sizeof( CellSpursJobHeader ) <= sizeof( SpursJob_t ) );
JobParam_t * pJobParams = ( JobParam_t* ) ( uintp( pJob ) + ( sizeof( SpursJob_t ) - sizeof( JobParam_t ) ) );
Assert( uintp( pJobParams + 1 ) == uintp( pJob ) + sizeof( SpursJob_t ) );
return pJobParams;
}
extern void UnalignedBufferAddU16( );
template <uint n> struct Log2{};
template<>struct Log2<8> {enum{VALUE=3};};
template<>struct Log2<16>{enum{VALUE=4};};
template<>struct Log2<32>{enum{VALUE=5};};
template<>struct Log2<256>{enum{VALUE=8};};
#define COMPILE_TIME_LOG2(VAL) ( Log2<VAL>::VALUE )
inline void ZeroMemAligned( void * p, uint nSize )
{
Assert( !( ( uintp( p ) | nSize ) & 15 ) );
for( uint i = 0; i < nSize; i += 16 )
{
*( vec_uint4* )( uintp( p ) + i ) = (vec_uint4){0,0,0,0};
}
}
inline void CopyMemAligned( void * pDst, const void * pSrc, uint nSize )
{
Assert( !( ( uintp( pDst ) | uintp( pSrc ) | nSize ) & 15 ) );
for( uint i = 0; i < nSize; i += 16 )
{
*( vec_uint4* )( uintp( pDst ) + i ) = *( vec_uint4* )( uintp( pSrc ) + i );
}
}
///////////////////////////////////////////////////////////////////////////
//
// Reference implementation
//
template <uint nBitCount>
class CBitArray
{
public:
void Clear()
{
for( uint i = 0; i < ( nBitCount >> 7 ); ++i )
{
m_qword[i] = ( vec_uint4 ){0,0,0,0};
}
//m_nSetCount = 0;
}
void SetRange( uint nStart, uint nEnd )
{
nEnd = Min( nEnd, nBitCount );
if( nStart > nEnd )
return;
//m_nSetCount = Max( nEnd, m_nSetCount );
uint nMask = uint( -1 ) >> ( nStart & 0x1F );
for( uint i = ( nStart >> 5 ); i < ( nEnd >> 5); ++i )
{
m_u32[i] |= nMask;
nMask = uint( -1 );
}
nMask &= ~( uint( -1 ) >> ( nEnd & 0x1F ) );
m_u32[ nEnd >> 5 ] |= nMask;
}
//uint GetSetCount()const{return m_nSetCount;}
uint GetFirst1( uint nFrom )const
{
for( uint i = nFrom; i < nBitCount; ++i )
if( GetBit( i ) )
return i;
return nBitCount;
}
uint GetFirst0( uint nFrom )const
{
for( uint i = nFrom; i < nBitCount; ++i )
if( !GetBit( i ) )
return i;
return nBitCount;
}
uint GetBit( uint n )const
{
return m_u32[ n >> 5 ] & ( 0x80000000 >> ( n & 0x1F ) );
}
protected:
union
{
vec_uint4 m_qword[ ( nBitCount + 127 ) / 128 ];
uint32 m_u32[ ( nBitCount + 31 ) / 32 ];
};
//uint m_nSetCount;
};
#endif // _PS3
#endif

View File

151
common/ps3/spugcm_lsring0.h Normal file
View File

@@ -0,0 +1,151 @@
//========== Copyright © Valve Corporation, All rights reserved. ========
// Easy ("tier0") implementations of simple GCM contexts
//
#ifndef PS3_SPUGCM_LSRING0_HDR
#define PS3_SPUGCM_LSRING0_HDR
class CSpuGcmMeasureBuffer: public CellGcmContextData
{
public:
CSpuGcmMeasureBuffer( )
{
this->begin = 0;
this->end = 0;
this->current = 0;
this->callback = CallbackDelegator;
}
uint GetSizeBytes()const
{
return uintp( this->end );
}
uint GetSizeWords()const
{
return this->end - this->begin;
}
protected:
void Callback( uint nCount )
{
this->end = ( uint32* )AlignValue( uintp( this->current + nCount ), 16 );
}
static int32_t CallbackDelegator( struct CellGcmContextData *pContext, uint32_t nCount )
{
static_cast<CSpuGcmMeasureBuffer*>( pContext )->Callback( nCount );
return CELL_OK;
}
};
class CSpuGcmAlignedBuffer: public CellGcmContextData
{
public:
void Init( void * lsBuffer, uint lsBufferSize, uint eaBegin, uint nIoOffsetDelta )
{
uint nShift = ( eaBegin - uint( lsBuffer ) ) & 0x7F;
this->begin = ( uint32* )( uintp( lsBuffer ) + nShift );
m_eaBuffer = eaBegin;
m_nIoOffsetDelta = nIoOffsetDelta;
this->end = ( uint32* )( uintp( this->begin ) + lsBufferSize );
this->current = this->begin;
this->callback = CallbackDelegator;
Assert( uint( this->begin ) >= uint( lsBuffer ) && !( 0x7F & ( uint( this->begin ) ^ eaBegin ) ) );
}
uint LsToLocalOffset( uint32 * lsCommand )
{
return EaToLocalOffset( LsToEa( lsCommand ) );
}
uint LsToEa( uint32 * lsCommand )
{
return uintp( lsCommand ) - uintp( this->begin ) + m_eaBuffer;
}
uint32 * EaToLs( uint32 eaCommand )
{
return ( uint32* )( ( eaCommand - m_eaBuffer ) + uintp( this->begin ) );
}
uint EaToLocalOffset( uint eaCommand )
{
return eaCommand + m_nIoOffsetDelta;
}
void AlignWithNops()
{
while ( 0xF & uintp( this->current ) )
{
*( this->current++ ) = CELL_GCM_METHOD_NOP;
}
}
void AppendJumpToNext()
{
*( this->current++ ) = CELL_GCM_JUMP( LsToLocalOffset( this->current + 1 ) );
}
void AppendJumpToNextIfNeededForDmaPutJtn()
{
// the JTN is not needed if the whole buffer fits into 128-byte cache line
Assert( this->current < this->end );
if( ( uintp( this->current ) ^ ( uintp( this->end ) - 1 ) ) & -128 )
{
// the first and the last bytes are in separate cache lines; we need to insert JTN at the beginning
AppendJumpToNext();
}
}
void Append( const SetLabelAlignedCommand_t & cmd )
{
AlignWithNops();
*( vector unsigned int * )( this->current ) = cmd.m_cmd.m_vuCmd;
this->current += 4;
}
// DMA put a segment of command buffer using JTN method. The start of the buffer shall be JTN, unless the buffer is small enough to be DMA'd
void DmaPutJtn()
{
Assert( this->current <= this->end && !( 0x7F & ( uintp( this->begin ) ^ m_eaBuffer ) ) );
while ( this->current < this->end )
{
*( this->current ++ ) = CELL_GCM_METHOD_NOP;
}
// skip the first 16 bytes where JTN resides; skip the whole cache line, while we're at it
Assert( this->current <= this->end );
uint32 * pRest = ( uint32* )( ( uintp( this->begin ) + 128 ) & -128 );
Assert( pRest > this->begin );
if ( pRest < this->end )
{
//VjobSpuLog( "lsring0 put %p..%p->%X tag:%d\n", pRest, this->end, LsToEa( pRest ), VJOB_IOBUFFER_DMATAG );
Assert( !( 0x7F & LsToEa( pRest ) ) );
VjobDmaPut( pRest, LsToEa( pRest ), uintp( this->end ) - uintp( pRest ), VJOB_IOBUFFER_DMATAG, 0, 0 );
//VjobSpuLog( "lsring0 putf (JTN %X) %p..%p->%X tag:%d\n", *this->begin, this->begin, pRest, m_eaBuffer, VJOB_IOBUFFER_DMATAG );
VjobDmaPutf( this->begin, m_eaBuffer, uintp( pRest ) - uintp( this->begin ), VJOB_IOBUFFER_DMATAG, 0, 0 );
}
else
{
// this case is pretty simple and doesn't require JTN at the beginning of the memory block , because it will be DMA'd atomically
// check that we really start the block with JTN
//Assert( CELL_GCM_JUMP( EaToLocalOffset( m_eaBuffer ) + 4 ) == *this->begin );
// overwrite JTN with NOP as we won't need JTN
//*this->begin = CELL_GCM_METHOD_NOP;
Assert( pRest >= this->end && this->end - this->begin <= 128 / 4 );
VjobDmaPutf( this->begin, m_eaBuffer, uintp( this->end ) - uintp( this->begin ), VJOB_IOBUFFER_DMATAG, 0, 0 );
}
}
protected:
uint m_eaBuffer;
uint m_nIoOffsetDelta;
protected:
void Callback( uint nCount )
{
DebuggerBreak();
}
static int32_t CallbackDelegator( struct CellGcmContextData *pContext, uint32_t nCount )
{
static_cast<CSpuGcmAlignedBuffer*>( pContext )->Callback( nCount );
return CELL_ERROR_ERROR_FLAG;
}
};
#endif

382
common/ps3/vjobchain.cpp Normal file
View File

@@ -0,0 +1,382 @@
//========== Copyright © Valve Corporation, All rights reserved. ========
#include "vjobchain.h"
#include "vjobutils.h"
#include "vjobs/root.h"
#include "tier1/strtools.h"
#include "tier0/miniprofiler.h"
CMiniProfiler g_mpRun, g_mpJoin, g_mpPush, g_mpPush2;
int VjobChain::Init( VJobsRoot * pRoot, uint nMaxContention, const char* pFormatName, ... )
{
Assert( !( uintp( &m_guard ) & 0x7F ) );
{
va_list args
va_start( args, pFormatName );
V_vsnprintf( m_name, sizeof( m_name ), pFormatName, args );
}
cell::Spurs::JobChainAttribute attr;
uint8_t nVjobChainPriority[8] = {0,12,12,12,12,12,0,0}; // priority lower than the main job queue, in order to yield
attr.initialize( &attr, m_headBlock, 128, 1, nVjobChainPriority, nMaxContention, true, 0,1, false, 256, 1 );
attr.setName( m_name );
m_pLastBlock = NULL; // NOT running by default
CELL_MUST_SUCCEED( JobChain::createWithAttribute( &pRoot->m_spurs, &m_spursJobChain, &attr ) );
V_memset( &m_notifyArea, 0, sizeof( m_notifyArea ) );
V_memset( &m_jobNotify, 0, sizeof( m_jobNotify ) );
m_jobNotify.header = *( pRoot->m_pJobNotify );
m_jobNotify.header.useInOutBuffer = 1;
Assert( !( uint( &m_jobNotify ) & 63 ) ); // should be 64-byte aligned
AddInputDma( &m_jobNotify, sizeof( m_notifyArea ), &m_notifyArea );
m_notifyArea.m_nCopyFrom = 1;
m_notifyArea.m_nCopyTo = 0; // SPU will mark copyTo = 1, PPU will mark it back to 0; at this time, we may actually mark the notify as completed
m_jobNotify.workArea.userData[1] = 0; // function: default
CELL_MUST_SUCCEED( m_guard.initialize( &m_spursJobChain, &m_guard, 1 /*notifyCount*/, 1 /*requestSpuCount(ignored)*/, 1 /*autoReset*/ ) );
m_headBlock[0] = CELL_SPURS_JOB_COMMAND_SYNC; // wait for all previous list commands to finish
m_headBlock[1] = CELL_SPURS_JOB_COMMAND_JOB( &m_jobNotify );
m_headBlock[2] = CELL_SPURS_JOB_COMMAND_GUARD( &m_guard );
m_headBlock[BLOCK_COMMANDS] = ( uint64 )-1ll;
CELL_MUST_SUCCEED( m_spursJobChain.run() );
m_nSpinWaitNotify = 0;
return CELL_OK;
}
int VjobChain::Run()
{
if( !IsRunning() )
{
CMiniProfilerGuard mpg( &g_mpRun );
Assert( m_notifyArea.m_nCopyTo ); // the jobchain must be joined if its not in Running state
m_notifyArea.m_nCopyTo = 0;
m_pLastBlock = m_headBlock;
m_nCurrentBlockCommands = 3; // right after the SYNC-JOB(notify)-GUARD prefix
m_headBlock[m_nCurrentBlockCommands] = CELL_SPURS_JOB_COMMAND_JTS;
// __lwsync(); // make sure we complete sync reset and write JTS before notify - probably not necessary because the guard should have a barrier for sure
m_guard.notify(); // let the jobchain go
return CELL_OK;
}
else
{
return CELL_OK; // it's valid to try to run a running chain in our interface...
}
}
int VjobChain::End( )
{
if( IsRunning() )
{
Assert( m_pLastBlock[m_nCurrentBlockCommands] == CELL_SPURS_JOB_COMMAND_JTS );
Assert( m_notifyArea.m_nCopyTo == 0 ); // make sure we reset sync correctly
m_pLastBlock[m_nCurrentBlockCommands] = CELL_SPURS_JOB_COMMAND_RESET_PC( m_headBlock );
return 0;
}
else
return -1; // you should not end non-running instance
}
void VjobChain::Shutdown()
{
if( IsRunning() )
{
m_pLastBlock[m_nCurrentBlockCommands] = CELL_SPURS_JOB_COMMAND_END;
}
m_spursJobChain.shutdown();
m_spursJobChain.join();
}
int VjobChain::Join()
{
if( IsRunning() )
{
#ifdef _DEBUG
CellSpursJobChainInfo info;
m_spursJobChain.getInfo( &info );
#endif
CMiniProfilerGuard mpg( &g_mpJoin );
// wait for reset sync notification to come through
volatile job_notify::NotifyArea_t *pNotify = &m_notifyArea;
Assert( pNotify->m_nCopyFrom );
while( !pNotify->m_nCopyTo )
{
++m_nSpinWaitNotify;
}
if( m_nSpinWaitNotify )
{
// <HACK> Sergiy : I'm taking this out for now because jobchain double-buffering is effectively temporarily hosed
// Warning( "VjobChain %s: stall in join, %d spins\n", m_name, m_nSpinWaitNotify );
m_nSpinWaitNotify = 0;
}
// free up the memory of the jobs that are now known to have dispatched
if( m_headBlock != m_pLastBlock )
{
uint64 *pBlock = m_headBlock;
do
{
Assert( pBlock[BLOCK_COMMANDS] == (uint64)-1ll );
uint64 eaNext = pBlock[BLOCK_COMMANDS - 1];
Assert( ( eaNext & 0xFFFFFFFF00000007ull ) == 3 );
pBlock = ( uint64 * )( uintp( eaNext ) & ~7 );
Assert( pBlock[BLOCK_COMMANDS] == (uint64)-1ll );
delete[]pBlock;
}
while( pBlock != m_pLastBlock );
}
m_pLastBlock = NULL; // idle state
return CELL_OK;
}
else
return 0; // valid to join twice;
}
void VjobChain::Push( uint64 nCommand )
{
Assert( IsRunning() );
uint64 * pNextCommand = &m_pLastBlock[m_nCurrentBlockCommands++]; // JTS to patch
if( m_nCurrentBlockCommands < BLOCK_COMMANDS )
{
CMiniProfilerGuard mpg( &g_mpPush );
pNextCommand[1] = CELL_SPURS_JOB_COMMAND_JTS;
__lwsync(); // ordering: create JobHeader, insert next JTS --> patch current JTS with JOB command
*pNextCommand = nCommand;
}
else
{
CMiniProfilerGuard mpg( &g_mpPush2 );
m_pLastBlock = new uint64[BLOCK_COMMANDS+1];
m_pLastBlock[BLOCK_COMMANDS] = ( uint64 )-1ll; // marker
m_pLastBlock[0] = nCommand;
m_pLastBlock[m_nCurrentBlockCommands = 1] = CELL_SPURS_JOB_COMMAND_JTS;
__lwsync(); // ordering: create JobHeader, allocate & reset new segment with JOB command in it --> patch JTS in old segment with NEXT command
*pNextCommand = CELL_SPURS_JOB_COMMAND_NEXT( m_pLastBlock );
}
}
void VjobChain::Push( const uint64 * nCommands, uint nCommandCount )
{
// todo: make it more optimal by removing extra lwsync's
for( uint i = 0; i < nCommandCount; ++i )
{
Push( nCommands[i] );
}
}
int VjobChain2::Init( VJobsRoot * pRoot, uint nMaxContention, const char* pName )
{
m_vjobChainRing = ( VjobChain * )MemAlloc_AllocAligned( VJOB_CHAINS * sizeof( VjobChain ), 128 );
for( uint i = 0; i < 2; ++i )
{
int nError = m_vjobChainRing[i].Init( pRoot, nMaxContention, "%s%d", pName, i );
if( nError )
return nError;
}
m_nCurrentChain = 0;
return 0;
}
void VjobChain2::Begin()
{
m_vjobChainRing[( m_nCurrentChain + 1 ) % VJOB_CHAINS].Join();
VjobChain & jobchain = Jobchain();
jobchain.Join(); // join the jobchain that we'll be using now
jobchain.Run();
}
void VjobChain2::End()
{
VjobChain & jobchain = Jobchain();
jobchain.End();
m_nCurrentChain = ( m_nCurrentChain + 1 ) % VJOB_CHAINS; // swap the job chain
}
void VjobChain2::Shutdown()
{
for( uint i = 0; i < 2; ++i )
{
m_vjobChainRing[i].Shutdown();
}
MemAlloc_FreeAligned( m_vjobChainRing );
}
int VjobChain3::Init( VJobsRoot * pRoot, uint nMaxContention, uint nMinCommandsPerBuffer, uint8_t nVjobChainPriority[8], const char* pName, uint nDmaTags )
{
m_pName = pName;
const uint nSizeOfJobDescriptor = 128, nMaxGrabbedJob = 4;
// we need at least 4 commands
uint nBufferSize = sizeof( VjobBufferHeader_t ) + sizeof( uint64 ) * MAX( nMinCommandsPerBuffer, VjobBuffer_t::VERBATIM_COMMAND_COUNT + 2 ); // +2 is for user's command and JTN
nBufferSize = AlignValue( nBufferSize, 128 );
m_nMaxCommandsPerBuffer = ( nBufferSize - sizeof( VjobBufferHeader_t ) ) / sizeof( uint64 );
uint nAllocationSize = sizeof( cell::Spurs::JobChain ) + nBufferSize * BUFFER_COUNT;
m_pSpursJobChain = ( cell::Spurs::JobChain* )MemAlloc_AllocAligned( nAllocationSize, 128 );
V_memset( m_pSpursJobChain, 0, nAllocationSize );
m_pBuffers[0] = ( VjobBuffer_t * )( m_pSpursJobChain + 1 );
m_nFrontBuffer = 0;
m_pFrontBuffer = m_pBuffers[0];
for( int i = 1; i < BUFFER_COUNT; ++i )
{
m_pBuffers[i] = ( VjobBuffer_t * )( uintp( m_pBuffers[ i - 1 ] ) + nBufferSize );
}
cell::Spurs::JobChainAttribute attr;
attr.initialize( &attr, m_pFrontBuffer->m_spursCommands, nSizeOfJobDescriptor, nMaxGrabbedJob, nVjobChainPriority, nMaxContention, true, nDmaTags, nDmaTags + 1, false, 256, 1 );
attr.setName( pName );
CELL_MUST_SUCCEED( JobChain::createWithAttribute( &pRoot->m_spurs, m_pSpursJobChain, &attr ) );
for( int i = 0; i < BUFFER_COUNT; ++i )
{
Assert( !( uintp( m_pBuffers[i] ) & 0x7F ) );
m_pBuffers[i]->Init( pRoot, m_pSpursJobChain );
}
StartCommandBuffer( 0, CELL_SPURS_JOB_COMMAND_NOP );
CELL_MUST_SUCCEED( m_pSpursJobChain->run() );
#ifdef _DEBUG
sys_timer_usleep( 100 );
#endif
m_nSpinWaitNotify = 0;
return CELL_OK;
}
void VjobBuffer_t::Init( VJobsRoot * pRoot, cell::Spurs::JobChain * pSpursJobChain )
{
m_jobNotify.header = *( pRoot->m_pJobNotify );
m_jobNotify.header.useInOutBuffer = 1;
AddInputDma( &m_jobNotify, sizeof( m_notifyArea ), &m_notifyArea );
m_notifyArea.m_nCopyFrom = 1;
// SPU will mark copyTo = 1, PPU will mark it back to 0; at this time, we may actually mark the notify as completed; 1 means "previous buffer is free"
// Then we'll start command buffer, which will reset the ready flag. But then we run the jobchain, which will run job_notify and set the flag back again, thus starting the ring
m_notifyArea.m_nCopyTo = 1;
m_jobNotify.workArea.userData[1] = 0; // function: default
uint nCommands = 0;
m_spursCommands[nCommands++] = CELL_SPURS_JOB_COMMAND_SYNC; // wait for all previous list commands to finish
m_spursCommands[nCommands++] = CELL_SPURS_JOB_COMMAND_JOB( &m_jobNotify );
#ifdef VJOBCHAIN3_GUARD
Assert( !( uintp( &m_guard ) & -128 ) );
CELL_MUST_SUCCEED( m_guard.initialize( pSpursJobChain, &m_guard, 1 /*notifyCount*/, 1 /*requestSpuCount(ignored)*/, 1 /*autoReset*/ ) );
m_spursCommands[nCommands++] = CELL_SPURS_JOB_COMMAND_GUARD( &m_guard );
#endif
#ifdef _DEBUG
m_jobNotify2.header = *( pRoot->m_pJobNotify );
m_jobNotify2.header.useInOutBuffer = 1;
AddInputDma( &m_jobNotify2, sizeof( m_notifyArea2 ), &m_notifyArea2 );
m_jobNotify2.workArea.userData[1] = 0; // function: default
m_notifyArea2.m_nCopyFrom = 1;
m_notifyArea2.m_nCopyTo = 0; // just for debugging, to see when this job gets executed
m_spursCommands[nCommands++] = CELL_SPURS_JOB_COMMAND_JOB( &m_jobNotify2 );
#endif
Assert( nCommands == VjobBuffer_t::VERBATIM_COMMAND_COUNT );
m_spursCommands[VjobBuffer_t::VERBATIM_COMMAND_COUNT] = CELL_SPURS_JOB_COMMAND_JTS;
}
void VjobChain3::WaitForEntryNotify( VjobBuffer_t * pBuffer )
{
volatile job_notify::NotifyArea_t *pNotify = &pBuffer->m_notifyArea;
Assert( pNotify->m_nCopyFrom );
while( !pNotify->m_nCopyTo )
{
++m_nSpinWaitNotify;
sys_timer_usleep( 30 );
}
if( m_nSpinWaitNotify )
{
Warning( "VjobChain %s: stall in WaitForEntryNotify, %d spins\n", m_pName, m_nSpinWaitNotify );
m_nSpinWaitNotify = 0;
}
}
uint64* VjobChain3::SwapCommandBuffer( uint64 nInsertCommand )
{
uint64 * pSpursIsSpinningHere = &m_pFrontBuffer->m_spursCommands[ m_nFrontBufferCommandCount ];
Assert( m_nFrontBufferCommandCount < m_nMaxCommandsPerBuffer );
uint nNext1Buffer = ( m_nFrontBuffer + 1 ) % BUFFER_COUNT, nNext2Buffer = ( m_nFrontBuffer + 2 ) % BUFFER_COUNT;
VjobBuffer_t * pNext1Buffer = m_pBuffers[ nNext1Buffer ], * pNext2Buffer = m_pBuffers[ nNext2Buffer ];
// before we can declare the next1 buffer "front", we need to make sure it's fully ready to accept commands, i.e. that it was fully read by SPURS
// for that, we check the next2 buffer notification area
WaitForEntryNotify( pNext1Buffer );
WaitForEntryNotify( pNext2Buffer );
// if next2 buffer has been notified, next1 must have been notified long ago
Assert( pNext1Buffer->m_notifyArea.m_nCopyTo );
uint64* pInsertionPoint = StartCommandBuffer( nNext1Buffer, nInsertCommand );
Assert( pNext1Buffer == m_pFrontBuffer );
// implicit lwsync is here
*pSpursIsSpinningHere = CELL_SPURS_JOB_COMMAND_NEXT( pNext1Buffer->m_spursCommands ); // jump to the next buffer
return pInsertionPoint;
}
uint64* VjobChain3::StartCommandBuffer( uint nNext1Buffer, uint64 nInsertCommand )
{
m_nFrontBuffer = nNext1Buffer;
m_pFrontBuffer = m_pBuffers[ nNext1Buffer ];
// the ready marker is presumed to be present; SPURS must have gone through this buffer in the previous ring, otherwise we can't use it
Assert( m_pFrontBuffer->m_notifyArea.m_nCopyTo == 1 );
// reset the ready marker; SPURS didn't get to this buffer yet (we're about to reuse them and we didn't jump to it yet)
m_pFrontBuffer->m_notifyArea.m_nCopyTo = 0;
#ifdef _DEBUG
m_pFrontBuffer->m_notifyArea2.m_nCopyFrom++;
m_pFrontBuffer->m_notifyArea2.m_nCopyTo = 0;
#endif
uint64 * pCommand = &m_pFrontBuffer->m_spursCommands[ VjobBuffer_t::VERBATIM_COMMAND_COUNT ];
*pCommand = nInsertCommand;
m_pFrontBuffer->m_spursCommands[ VjobBuffer_t::VERBATIM_COMMAND_COUNT + 1 ] = CELL_SPURS_JOB_COMMAND_JTS;
m_nFrontBufferCommandCount = VjobBuffer_t::VERBATIM_COMMAND_COUNT + 1;
#ifdef VJOBCHAIN3_GUARD
m_pFrontBuffer->m_guard.notify(); // let the jobchain go through
// implicit lwsync is here
#else
__lwsync();
#endif
return pCommand;
}
void VjobChain3::End()
{
Assert( m_nFrontBufferCommandCount < m_nMaxCommandsPerBuffer );
m_pFrontBuffer->m_spursCommands[ m_nFrontBufferCommandCount ] = CELL_SPURS_JOB_COMMAND_END;
m_pSpursJobChain->shutdown();
}
void VjobChain3::Join()
{
Assert( m_pFrontBuffer->m_spursCommands[ m_nFrontBufferCommandCount ] == CELL_SPURS_JOB_COMMAND_END );
m_pSpursJobChain->join();
MemAlloc_FreeAligned( m_pSpursJobChain );
m_pSpursJobChain = NULL;
}

189
common/ps3/vjobchain.h Normal file
View File

@@ -0,0 +1,189 @@
//========== Copyright © Valve Corporation, All rights reserved. ========
#if !defined( VJOBS_CHAINUTILS_HDR ) && defined( _PS3 )
#define VJOBS_CHAINUTILS_HDR
#include "tier0/platform.h"
#include <cell/spurs.h>
#include "ps3/job_notify.h"
struct VJobsRoot;
//
// The chain consists of blocks of commands; the head block has SYNC-JOB(notify)-GUARD sequence
// initially the chain waits on the GUARD , and is ready to "run". "Run" means releasing the guard.
// I'm using GUARD instead of JTS because patching JTS will render it unpatcheable until jobchain execution completes;
// and if it's unpatcheable, it effectively can't be used as a guard for the next cycle
// Each block refers to the next one by inserting NEXT in the very last slot
// the last block is pointed to by m_pLastBlock, which is NULL initially (before entering the "run" state)
//
//
struct ALIGN128 VjobChain
{
public:
cell::Spurs::JobChain m_spursJobChain;
cell::Spurs::JobGuard m_guard;
CellSpursJob64 m_jobNotify;
job_notify::NotifyArea_t m_notifyArea;
enum { BLOCK_COMMANDS = 256 };
uint64 m_headBlock[BLOCK_COMMANDS+1]; // in one variant, the first entry in this list is used for END command; overwrite it with NOP to release the list
uint64 * m_pLastBlock;
uint m_nCurrentBlockCommands;
uint m_nSpinWaitNotify;
char m_name[16];
public:
int Init( VJobsRoot * pRoot, uint nMaxContention, const char* pFormatName, ... );
bool IsRunning()const { return m_pLastBlock != NULL; }
int Run();
void Push( uint64 nCommand );
void Push( const uint64 * nCommands, uint nCommandCount );
int End( );
int Join();
void Shutdown();
JobChain & Jobchain() { return m_spursJobChain; }
}ALIGN128_POST;
// VjobChain2 hosts 2 jobchains and double-buffers between them
class VjobChain2
{
public:
int Init( VJobsRoot * pRoot, uint nMaxContention, const char* pName );
void Begin();
void End();
void Shutdown();
VjobChain& Jobchain(){ return m_vjobChainRing[m_nCurrentChain]; }
protected:
enum{VJOB_CHAINS = 2};
VjobChain *m_vjobChainRing; // may be more than double-buffered if necessary
uint m_nCurrentChain;
};
//#define VJOBCHAIN3_GUARD
struct ALIGN128 VjobBufferHeader_t
{
public:
#ifdef VJOBCHAIN3_GUARD
cell::Spurs::JobGuard m_guard;
#endif
CellSpursJob64 m_jobNotify;
job_notify::NotifyArea_t m_notifyArea;
#ifdef _DEBUG
CellSpursJob64 m_jobNotify2;
job_notify::NotifyArea_t m_notifyArea2;
#endif
}
ALIGN128_POST;
struct VjobBuffer_t: public VjobBufferHeader_t
{
public:
enum ConstEnum_t
{
VERBATIM_COMMAND_COUNT = 2 // we employ syncronization scheme: SYNC, JOB(notify), ...
#ifdef VJOBCHAIN3_GUARD // we add GUARD, ...
+ 1
#endif
#ifdef _DEBUG
+ 1 // we add JOB(notify2), ...
#else
#endif
};
uint64 m_spursCommands[16]; // there will be at least verbatim commands, a user command, and a NEXT
void Init( VJobsRoot * pRoot, cell::Spurs::JobChain * pSpursJobChain );
};
// VjobChain3 has only 1 jobchain but double-buffers it to facilitate continuous wait-free execution
class VjobChain3
{
protected:
enum ConstEnum_t {
BUFFER_COUNT = 4
};
cell::Spurs::JobChain *m_pSpursJobChain;
VjobBuffer_t * m_pBuffers[BUFFER_COUNT];
VjobBuffer_t * m_pFrontBuffer;
uint m_nFrontBuffer; // the buffer currently in use
uint m_nMaxCommandsPerBuffer; // max count of commands fitting into one buffer
uint m_nFrontBufferCommandCount; // count of commands in the current front buffer
uint m_nSpinWaitNotify; // did we spin waiting for job_notify ? if we did, we probably need to increase the command buffer size
uint64 m_nLastCommandPushed; // at the beginning of the scene, it's considered to be synced up
const char * m_pName;
public:
int Init( VJobsRoot * pRoot, uint nMaxContention, uint nMinCommandsPerBuffer, uint8_t nVjobChainPriority[8], const char* pName, uint nDmaTags );
uint64* Push( uint64 nCommand );
uint64* PushSyncJobSync( uint64 nCommand );
void PushSync();
void Shutdown(){End();Join();}
void End();
void Join();
protected:
void WaitForEntryNotify( VjobBuffer_t * pBuffer );
uint64* StartCommandBuffer( uint nNext1Buffer, uint64 nInsertCommand );
uint64* SwapCommandBuffer( uint64 nInsertCommand );
};
inline uint64* VjobChain3::Push( uint64 nCommand )
{
uint64 * pInsertionPoint;
if( m_nFrontBufferCommandCount == m_nMaxCommandsPerBuffer - 1 )
{
// time to switch the buffer
pInsertionPoint = SwapCommandBuffer( nCommand );
}
else
{
m_pFrontBuffer->m_spursCommands[ m_nFrontBufferCommandCount + 1 ] = CELL_SPURS_JOB_COMMAND_JTS;
__lwsync(); // Important: this sync ensures that both the command header AND JTS are written before SPU sees them
pInsertionPoint = &m_pFrontBuffer->m_spursCommands[ m_nFrontBufferCommandCount ];
*pInsertionPoint = nCommand;
m_nFrontBufferCommandCount ++;
}
m_nLastCommandPushed = nCommand;
return pInsertionPoint;
}
inline void VjobChain3::PushSync()
{
Push( CELL_SPURS_JOB_COMMAND_LWSYNC );
}
inline uint64* VjobChain3::PushSyncJobSync( uint64 nCommand )
{
if( m_nLastCommandPushed != CELL_SPURS_JOB_COMMAND_LWSYNC )
{
// we need to wait for previous jobs to finish in order to patch the state efficiently
// todo: double-buffer the states to avoid stalls, but only if we become SPU-bound here (un
Push( CELL_SPURS_JOB_COMMAND_LWSYNC );
}
uint64 * pInsertionPoint = Push( nCommand );
// this is instead of stalling successor because I'm not sure if it stalls all logical successors (some of which may be picked up by other SPUs)
// the SYNC here will ensure completion of the previous job before the new jobs will be pushed
Push( CELL_SPURS_JOB_COMMAND_LWSYNC );
return pInsertionPoint;
}
#endif

175
common/ps3/vjobchain4.cpp Normal file
View File

@@ -0,0 +1,175 @@
//========== Copyright © Valve Corporation, All rights reserved. ========
#include "ps3/vjobchain4.h"
vec_uint4 g_cellSpursJts16 = ( vec_uint4 ){ uint( CELL_SPURS_JOB_COMMAND_JTS >> 32 ), uint( CELL_SPURS_JOB_COMMAND_JTS ), uint( CELL_SPURS_JOB_COMMAND_JTS >> 32 ), uint( CELL_SPURS_JOB_COMMAND_JTS ) };
#ifndef SPU
#include "ps3/vjobutils.h"
#include "vjobs/root.h"
#include "tier1/strtools.h"
#include "tier0/miniprofiler.h"
int VjobChain4::Init( VJobsRoot * pRoot, uint nMaxContention, uint nMinCommandsPerBuffer, uint8_t nVjobChainPriority[8], uint nSizeOfJobDescriptor, uint nMaxGrabbedJob, const char* pName, uint nDmaTags )
{
m_pName = pName;
m_eaThis = this;
// we need at least 4 commands
uint nBufferSize = sizeof( VjobChain4BufferHeader_t ) + sizeof( uint64 ) * MAX( nMinCommandsPerBuffer, VjobChain4Buffer_t::VERBATIM_COMMAND_COUNT + 2 ); // +2 is for user's command and JTN
nBufferSize = AlignValue( nBufferSize, 128 );
m_nMaxCommandsPerBuffer = ( nBufferSize - sizeof( VjobChain4BufferHeader_t ) ) / sizeof( uint64 );
uint nAllocationSize = sizeof( cell::Spurs::JobChain ) + nBufferSize * BUFFER_COUNT;
m_pSpursJobChain = ( cell::Spurs::JobChain* )MemAlloc_AllocAligned( nAllocationSize, 128 );
V_memset( m_pSpursJobChain, 0, nAllocationSize );
m_pBuffers[0] = ( VjobChain4Buffer_t * )( m_pSpursJobChain + 1 );
m_nFrontBuffer = 0;
m_pFrontBuffer = m_pBuffers[0];
for( int i = 1; i < BUFFER_COUNT; ++i )
{
m_pBuffers[i] = ( VjobChain4Buffer_t * )( uintp( m_pBuffers[ i - 1 ] ) + nBufferSize );
}
cell::Spurs::JobChainAttribute attr;
attr.initialize( &attr, m_pFrontBuffer->m_spursCommands, nSizeOfJobDescriptor, nMaxGrabbedJob, nVjobChainPriority, nMaxContention, true, nDmaTags, nDmaTags + 1, false, Max<uint>( 256, nSizeOfJobDescriptor ), 1 );
attr.setName( pName );
CELL_MUST_SUCCEED( JobChain::createWithAttribute( &pRoot->m_spurs, m_pSpursJobChain, &attr ) );
for( int i = 0; i < BUFFER_COUNT; ++i )
{
Assert( !( uintp( m_pBuffers[i] ) & 0x7F ) );
m_pBuffers[i]->Init( pRoot, m_pSpursJobChain, m_nMaxCommandsPerBuffer );
}
*StartCommandBuffer( 0 ) = CELL_SPURS_JOB_COMMAND_NOP;
CELL_MUST_SUCCEED( m_pSpursJobChain->run() );
#ifdef _DEBUG
sys_timer_usleep( 100 );
#endif
m_nSpinWaitNotify = 0;
return CELL_OK;
}
void VjobChain4Buffer_t::Init( VJobsRoot * pRoot, cell::Spurs::JobChain * pSpursJobChain, uint nMaxCommandsPerBuffer )
{
Assert( 0 == ( 0x7F & uintp( &m_jobNotify ) ) );
m_jobNotify.header = *( pRoot->m_pJobNotify );
m_jobNotify.header.useInOutBuffer = 1;
AddInputDma( &m_jobNotify, sizeof( m_notifyArea ), &m_notifyArea );
m_notifyArea.m_nCopyFrom = 1;
// SPU will mark copyTo = 1, PPU will mark it back to 0; at this time, we may actually mark the notify as completed; 1 means "previous buffer is free"
// Then we'll start command buffer, which will reset the ready flag. But then we run the jobchain, which will run job_notify and set the flag back again, thus starting the ring
m_notifyArea.m_nCopyTo = 1;
m_jobNotify.workArea.userData[1] = 0; // function: default
uint nCommands = 0;
m_spursCommands[nCommands++] = CELL_SPURS_JOB_COMMAND_SYNC; // wait for all previous list commands to finish
m_spursCommands[nCommands++] = CELL_SPURS_JOB_COMMAND_JOB( &m_jobNotify );
Assert( nCommands == VjobChain4Buffer_t::VERBATIM_COMMAND_COUNT );
while( nCommands < nMaxCommandsPerBuffer )
{
m_spursCommands[nCommands++] = CELL_SPURS_JOB_COMMAND_JTS;
}
}
void VjobChain4::End()
{
Assert( m_nFrontBufferCommandCount < m_nMaxCommandsPerBuffer );
m_pFrontBuffer->m_spursCommands[ m_nFrontBufferCommandCount ] = CELL_SPURS_JOB_COMMAND_END;
m_pSpursJobChain->shutdown();
}
void VjobChain4::Join()
{
Assert( m_pFrontBuffer->m_spursCommands[ m_nFrontBufferCommandCount ] == CELL_SPURS_JOB_COMMAND_END );
m_pSpursJobChain->join();
MemAlloc_FreeAligned( m_pSpursJobChain );
m_pSpursJobChain = NULL;
}
#endif
void VjobChain4::WaitForEntryNotify( VjobChain4Buffer_t * eaBuffer )
{
volatile job_notify::NotifyArea_t *eaNotify = &eaBuffer->m_notifyArea;
// it doesn't matter what DMA tag we'll use for synchronous DMA get
Assert( VjobDmaGetUint32( (uint)&eaNotify->m_nCopyFrom, DMATAG_SYNC, 0, 0 ) );
while( !VjobDmaGetUint32( (uint)&eaNotify->m_nCopyTo, DMATAG_SYNC, 0, 0 ) )
{
++m_nSpinWaitNotify;
#ifndef SPU
sys_timer_usleep( 30 );
#endif
}
if( m_nSpinWaitNotify )
{
VjobSpuLog( "VjobChain: stall in WaitForEntryNotify, %d spins\n", m_nSpinWaitNotify );
m_nSpinWaitNotify = 0;
}
}
uint64* VjobChain4::SwapCommandBuffer( )
{
uint64 * eaSpursIsSpinningHere = &m_pFrontBuffer->m_spursCommands[ m_nFrontBufferCommandCount ];
Assert( m_nFrontBufferCommandCount < m_nMaxCommandsPerBuffer );
uint nNext1Buffer = ( m_nFrontBuffer + 1 ) % BUFFER_COUNT, nNext2Buffer = ( m_nFrontBuffer + 2 ) % BUFFER_COUNT;
VjobChain4Buffer_t * eaNext1Buffer = m_pBuffers[ nNext1Buffer ], * eaNext2Buffer = m_pBuffers[ nNext2Buffer ];
// before we can declare the next1 buffer "front", we need to make sure it's fully ready to accept commands, i.e. that it was fully read by SPURS
// for that, we check the next2 buffer notification area
WaitForEntryNotify( eaNext1Buffer );
WaitForEntryNotify( eaNext2Buffer );
// if next2 buffer has been notified, next1 must have been notified long ago
Assert( VjobDmaGetUint32( (uint)&eaNext1Buffer->m_notifyArea.m_nCopyTo, DMATAG_SYNC, 0, 0 ) );
uint64* pInsertionPoint = StartCommandBuffer( nNext1Buffer );
Assert( eaNext1Buffer == m_pFrontBuffer );
// implicit lwsync is here
VjobDmaPutfUint64( CELL_SPURS_JOB_COMMAND_NEXT( eaNext1Buffer->m_spursCommands ), (uint)eaSpursIsSpinningHere, DMATAG_SYNC ); // jump to the next buffer
return pInsertionPoint;
}
#ifndef SPU
void FillSpursJts( uint64 * eaCommands, uint nBufferCount )
{
for( uint i = 0; i < nBufferCount; ++i )
eaCommands[ i ] = CELL_SPURS_JOB_COMMAND_JTS;
}
#endif
//
// Initializes the buffer BEFORE the jobchain can jump to it. It's important to only jump to the next buffer
// after this function returns (either by inserting NEXT into previous buffer, or by call Run() on the jobchain)
// because this function lacks the necessary synchronization to operate safely on a buffer in-flight
//
uint64* VjobChain4::StartCommandBuffer( uint nNext1Buffer )
{
m_nFrontBuffer = nNext1Buffer;
m_pFrontBuffer = m_pBuffers[ nNext1Buffer ];
// the ready marker is presumed to be present; SPURS must have gone through this buffer in the previous ring, otherwise we can't use it
Assert( VjobDmaGetUint32( (uint)&m_pFrontBuffer->m_notifyArea.m_nCopyTo, DMATAG_SYNC, 0, 0) == 1 );
// reset the ready marker; SPURS didn't get to this buffer yet (we're about to reuse them and we didn't jump to it yet)
VjobDmaPutfUint32( 0, (uint)&m_pFrontBuffer->m_notifyArea.m_nCopyTo, DMATAG_SYNC );
uint64 * eaCommand = &m_pFrontBuffer->m_spursCommands[ VjobChain4Buffer_t::VERBATIM_COMMAND_COUNT ];
//VjobDmaPutfUint64( nInsertCommand, (uint)eaCommand, DMATAG_SYNC );
FillSpursJts( eaCommand, m_nMaxCommandsPerBuffer - VjobChain4Buffer_t::VERBATIM_COMMAND_COUNT );
m_nFrontBufferCommandCount = VjobChain4Buffer_t::VERBATIM_COMMAND_COUNT + 1;
LWSYNC_PPU_ONLY();
return eaCommand;
}

101
common/ps3/vjobchain4.h Normal file
View File

@@ -0,0 +1,101 @@
//========== Copyright © Valve Corporation, All rights reserved. ========
//
// This job chain wrapper is an infinite job chain with dynamic job submission
// It is single-threaded and not very fast in the current implementation,
// so it'll need to be optimized if we ever become SPU bound and use it often.
// To make it multithreaded, the easiest way is to lock every operation
// with a compare-exchange flag and a spin-wait, and use getllar to update "this" and the flag atomically
//
#if !defined( VJOBSCHAINU4_HDR ) && defined( _PS3 )
#define VJOBSCHAINU4_HDR
#include <cell/spurs.h>
#include "ps3/job_notify.h"
#include "ps3/spu_job_shared.h"
struct VJobsRoot;
struct ALIGN128 VjobChain4BufferHeader_t
{
public:
CellSpursJob64 m_jobNotify;
job_notify::NotifyArea_t m_notifyArea;
}
ALIGN128_POST;
struct VjobChain4Buffer_t: public VjobChain4BufferHeader_t
{
public:
enum ConstEnum_t
{
VERBATIM_COMMAND_COUNT = 2 // we employ syncronization scheme: SYNC, JOB(notify), ...
};
uint64 m_spursCommands[32]; // there will be at least verbatim commands, a user command, and a NEXT
void Init( VJobsRoot * pRoot, cell::Spurs::JobChain * pSpursJobChain, uint nMaxCommandsPerBuffer );
};
// VjobChain4 has only 1 jobchain but double-buffers it to facilitate continuous wait-free execution
class ALIGN16 VjobChain4
{
protected:
enum ConstEnum_t {
BUFFER_COUNT = 4
};
VjobChain4 * m_eaThis;
cell::Spurs::JobChain *m_pSpursJobChain;
VjobChain4Buffer_t * m_pBuffers[BUFFER_COUNT];
VjobChain4Buffer_t * m_pFrontBuffer;
uint m_nFrontBuffer; // the buffer currently in use
uint m_nMaxCommandsPerBuffer; // max count of commands fitting into one buffer
uint m_nFrontBufferCommandCount; // count of commands in the current front buffer
uint m_nSpinWaitNotify; // did we spin waiting for job_notify ? if we did, we probably need to increase the command buffer size
const char * m_pName;
public:
#ifndef SPU
int Init( VJobsRoot * pRoot, uint nMaxContention, uint nMinCommandsPerBuffer, uint8_t nVjobChainPriority[8], uint nSizeOfJobDescriptor, uint nMaxGrabbedJob, const char* pName, uint nDmaTags );
void Shutdown(){End();Join();}
void End();
void Join();
void Run(){ m_pSpursJobChain->run();}
#endif
uint64* Push( );
const char * GetName()const {return m_pName;}
protected:
void WaitForEntryNotify( VjobChain4Buffer_t * eaBuffer );
uint64* StartCommandBuffer( uint nNext1Buffer );
uint64* SwapCommandBuffer( );
}
ALIGN128_POST;
inline uint64* VjobChain4::Push( )
{
uint64 * pInsertionPoint;
Assert( m_nFrontBufferCommandCount < m_nMaxCommandsPerBuffer );
if( m_nFrontBufferCommandCount == m_nMaxCommandsPerBuffer - 1 )
{
// time to switch the buffer
pInsertionPoint = SwapCommandBuffer( );
}
else
{
Assert( VjobDmaGetUint64( (uint)&m_pFrontBuffer->m_spursCommands[ m_nFrontBufferCommandCount + 1 ], DMATAG_SYNC, 0, 0 ) == CELL_SPURS_JOB_COMMAND_JTS );
LWSYNC_PPU_ONLY(); // Important: this sync ensures that both the command header AND JTS are written before SPU sees them
pInsertionPoint = &m_pFrontBuffer->m_spursCommands[ m_nFrontBufferCommandCount ];
m_nFrontBufferCommandCount ++;
}
return pInsertionPoint;
}
extern vec_uint4 g_cellSpursJts16;
extern void FillSpursJts( uint64 * eaCommands, uint nBufferCount );
#endif

107
common/ps3/vjobpool.h Normal file
View File

@@ -0,0 +1,107 @@
//========== Copyright © Valve Corporation, All rights reserved. ========
#if !defined( VJOBPOOL_HDR ) && defined( _PS3 )
#define VJOBPOOL_HDR
#include "ps3/spu_job_shared.h"
#include "cell/atomic.h"
//#include "ps3/vjobutils.h"
template < typename Job, uint nAlignment = 128 >
class VjobPool
{
public:
#ifndef SPU
void Init( uint nJobs ) // must be integer-power-of-2
{
m_nJobIndexMask = nJobs - 1;
Assert( !( m_nJobIndexMask & nJobs ) );
uint nSizeToAllocate = sizeof( Job ) * nJobs;
m_eaPool = ( Job* )MemAlloc_AllocAligned( nSizeToAllocate, nAlignment );
V_memset( m_eaPool, 0, nSizeToAllocate );
m_nNextJob = 0;
m_nWaitSpins = 0;
}
void Shutdown()
{
// wait for all jobs to finish
for ( uint i = 0; i <= m_nJobIndexMask; ++i )
{
volatile const uint64 * pEa = ( const uint64* ) & m_eaPool[i];
while ( *pEa )
{
continue;
}
}
MemAlloc_FreeAligned( m_eaPool );
m_eaPool = NULL;
}
Job * Alloc( const CellSpursJobHeader & header ) // the job header is freed when its eaBinary is overwritten with zeroes from within the job
{
Job * pJob = Alloc();
V_memcpy( pJob, &header, sizeof( header ) );
return pJob;
}
#endif
Job * Alloc( ); // the job header is freed when its eaBinary is overwritten with zeroes from within the job
uint GetMarker()const { return m_nNextJob; }
uint GetCapacity()const { return m_nJobIndexMask + 1; }
int GetReserve( uint nMarker )const { return int( GetCapacity() - ( m_nNextJob - nMarker ) ); }
public:
uint m_nWaitSpins; // debug field
protected:
Job * m_eaPool;
uint m_nJobIndexMask;
// this is just a hint
uint m_nNextJob;
};
template < typename Job, uint nAlignment >
Job * VjobPool<Job, nAlignment>::Alloc( ) // the job header is freed when its eaBinary is overwritten with zeroes from within the job
{
uint nNextJob = m_nNextJob;
Job * eaJob = NULL;
for( ;; )
{
eaJob = &m_eaPool[ ( nNextJob & m_nJobIndexMask ) ];
#ifdef SPU
char ALIGN128 temp[128] ALIGN128_POST;
// it's important to fill in and compare the first 64 bits, because that's where eaBinary is, and the first 32 bits (MSB) may be zero
if( 0 == cellAtomicCompareAndSwap64( (uint64*)temp, ( uintp )eaJob, 0, ~0ull ) )
{
break;
}
++m_nWaitSpins;
#else
// it's important to fill in and compare the first 64 bits, because that's where eaBinary is, and the first 32 bits (MSB) may be zero
if( 0 == cellAtomicCompareAndSwap64( ( uint64* )eaJob, 0, ~0ull ) )
{
break;
}
if( ( ( ( ++nNextJob ) ^ m_nNextJob ) & m_nJobIndexMask ) == 0 )
{
// we've made the full circle; yield
m_nWaitSpins++;
sys_timer_usleep( 30 );
}
#endif
}
// this is just a hint
m_nNextJob = nNextJob + 1;
return eaJob;
}
#endif

175
common/ps3/vjobutils.cpp Normal file
View File

@@ -0,0 +1,175 @@
//========== Copyright © Valve Corporation, All rights reserved. ========
#include "vjobutils.h"
#ifdef VJOBS_ON_SPURS
const char * HumanReadableCellResult( int nCellResult )
{
switch ( nCellResult )
{
case CELL_SPURS_CORE_ERROR_AGAIN:
return "CELL_SPURS_CORE_ERROR_AGAIN";
case CELL_SPURS_CORE_ERROR_INVAL:
return "CELL_SPURS_CORE_ERROR_INVAL";
case CELL_SPURS_CORE_ERROR_NOSYS:
return "CELL_SPURS_CORE_ERROR_NOSYS";
case CELL_SPURS_CORE_ERROR_NOMEM:
return "CELL_SPURS_CORE_ERROR_NOMEM";
case CELL_SPURS_CORE_ERROR_SRCH:
return "CELL_SPURS_CORE_ERROR_SRCH";
case CELL_SPURS_CORE_ERROR_NOENT:
return "CELL_SPURS_CORE_ERROR_NOENT";
case CELL_SPURS_CORE_ERROR_NOEXEC:
return "CELL_SPURS_CORE_ERROR_NOEXEC";
case CELL_SPURS_CORE_ERROR_DEADLK:
return "CELL_SPURS_CORE_ERROR_DEADLK";
case CELL_SPURS_CORE_ERROR_PERM:
return "CELL_SPURS_CORE_ERROR_PERM";
case CELL_SPURS_CORE_ERROR_BUSY:
return "CELL_SPURS_CORE_ERROR_BUSY";
case CELL_SPURS_CORE_ERROR_ABORT:
return "CELL_SPURS_CORE_ERROR_ABORT";
case CELL_SPURS_CORE_ERROR_FAULT:
return "CELL_SPURS_CORE_ERROR_FAULT";
case CELL_SPURS_CORE_ERROR_CHILD:
return "CELL_SPURS_CORE_ERROR_CHILD";
case CELL_SPURS_CORE_ERROR_STAT:
return "CELL_SPURS_CORE_ERROR_STAT";
case CELL_SPURS_CORE_ERROR_ALIGN:
return "CELL_SPURS_CORE_ERROR_ALIGN";
case CELL_SPURS_CORE_ERROR_NULL_POINTER:
return "CELL_SPURS_CORE_ERROR_NULL_POINTER";
case CELL_SPURS_POLICY_MODULE_ERROR_AGAIN:
return "CELL_SPURS_POLICY_MODULE_ERROR_AGAIN";
case CELL_SPURS_POLICY_MODULE_ERROR_INVAL:
return "CELL_SPURS_POLICY_MODULE_ERROR_INVAL";
case CELL_SPURS_POLICY_MODULE_ERROR_NOSYS:
return "CELL_SPURS_POLICY_MODULE_ERROR_NOSYS";
case CELL_SPURS_POLICY_MODULE_ERROR_NOMEM:
return "CELL_SPURS_POLICY_MODULE_ERROR_NOMEM";
case CELL_SPURS_POLICY_MODULE_ERROR_SRCH:
return "CELL_SPURS_POLICY_MODULE_ERROR_SRCH";
case CELL_SPURS_POLICY_MODULE_ERROR_NOENT:
return "CELL_SPURS_POLICY_MODULE_ERROR_NOENT";
case CELL_SPURS_POLICY_MODULE_ERROR_NOEXEC:
return "CELL_SPURS_POLICY_MODULE_ERROR_NOEXEC";
case CELL_SPURS_POLICY_MODULE_ERROR_DEADLK:
return "CELL_SPURS_POLICY_MODULE_ERROR_DEADLK";
case CELL_SPURS_POLICY_MODULE_ERROR_PERM:
return "CELL_SPURS_POLICY_MODULE_ERROR_PERM";
case CELL_SPURS_POLICY_MODULE_ERROR_BUSY:
return "CELL_SPURS_POLICY_MODULE_ERROR_BUSY";
case CELL_SPURS_POLICY_MODULE_ERROR_ABORT:
return "CELL_SPURS_POLICY_MODULE_ERROR_ABORT";
case CELL_SPURS_POLICY_MODULE_ERROR_FAULT:
return "CELL_SPURS_POLICY_MODULE_ERROR_FAULT";
case CELL_SPURS_POLICY_MODULE_ERROR_CHILD:
return "CELL_SPURS_POLICY_MODULE_ERROR_CHILD";
case CELL_SPURS_POLICY_MODULE_ERROR_STAT:
return "CELL_SPURS_POLICY_MODULE_ERROR_STAT";
case CELL_SPURS_POLICY_MODULE_ERROR_ALIGN:
return "CELL_SPURS_POLICY_MODULE_ERROR_ALIGN";
case CELL_SPURS_POLICY_MODULE_ERROR_NULL_POINTER:
return "CELL_SPURS_POLICY_MODULE_ERROR_NULL_POINTER";
case CELL_SPURS_TASK_ERROR_AGAIN:
return "CELL_SPURS_TASK_ERROR_AGAIN";
case CELL_SPURS_TASK_ERROR_INVAL:
return "CELL_SPURS_TASK_ERROR_INVAL";
case CELL_SPURS_TASK_ERROR_NOSYS:
return "CELL_SPURS_TASK_ERROR_NOSYS";
case CELL_SPURS_TASK_ERROR_NOMEM:
return "CELL_SPURS_TASK_ERROR_NOMEM";
case CELL_SPURS_TASK_ERROR_SRCH:
return "CELL_SPURS_TASK_ERROR_SRCH";
case CELL_SPURS_TASK_ERROR_NOENT:
return "CELL_SPURS_TASK_ERROR_NOENT";
case CELL_SPURS_TASK_ERROR_NOEXEC:
return "CELL_SPURS_TASK_ERROR_NOEXEC";
case CELL_SPURS_TASK_ERROR_DEADLK:
return "CELL_SPURS_TASK_ERROR_DEADLK";
case CELL_SPURS_TASK_ERROR_PERM:
return "CELL_SPURS_TASK_ERROR_PERM";
case CELL_SPURS_TASK_ERROR_BUSY:
return "CELL_SPURS_TASK_ERROR_BUSY";
case CELL_SPURS_TASK_ERROR_ABORT:
return "CELL_SPURS_TASK_ERROR_ABORT";
case CELL_SPURS_TASK_ERROR_FAULT:
return "CELL_SPURS_TASK_ERROR_FAULT";
case CELL_SPURS_TASK_ERROR_CHILD:
return "CELL_SPURS_TASK_ERROR_CHILD";
case CELL_SPURS_TASK_ERROR_STAT:
return "CELL_SPURS_TASK_ERROR_STAT";
case CELL_SPURS_TASK_ERROR_ALIGN:
return "CELL_SPURS_TASK_ERROR_ALIGN";
case CELL_SPURS_TASK_ERROR_NULL_POINTER:
return "CELL_SPURS_TASK_ERROR_NULL_POINTER";
case CELL_SPURS_TASK_ERROR_FATAL:
return "CELL_SPURS_TASK_ERROR_FATAL";
case CELL_SPURS_TASK_ERROR_SHUTDOWN:
return "CELL_SPURS_TASK_ERROR_SHUTDOWN";
case CELL_SPURS_JOB_ERROR_AGAIN:
return "CELL_SPURS_JOB_ERROR_AGAIN";
case CELL_SPURS_JOB_ERROR_INVAL:
return "CELL_SPURS_JOB_ERROR_INVAL";
case CELL_SPURS_JOB_ERROR_NOSYS:
return "CELL_SPURS_JOB_ERROR_NOSYS";
case CELL_SPURS_JOB_ERROR_NOMEM:
return "CELL_SPURS_JOB_ERROR_NOMEM";
case CELL_SPURS_JOB_ERROR_SRCH:
return "CELL_SPURS_JOB_ERROR_SRCH";
case CELL_SPURS_JOB_ERROR_NOENT:
return "CELL_SPURS_JOB_ERROR_NOENT";
case CELL_SPURS_JOB_ERROR_NOEXEC:
return "CELL_SPURS_JOB_ERROR_NOEXEC";
case CELL_SPURS_JOB_ERROR_DEADLK:
return "CELL_SPURS_JOB_ERROR_DEADLK";
case CELL_SPURS_JOB_ERROR_PERM:
return "CELL_SPURS_JOB_ERROR_PERM";
case CELL_SPURS_JOB_ERROR_BUSY:
return "CELL_SPURS_JOB_ERROR_BUSY";
case CELL_SPURS_JOB_ERROR_JOB_DESCRIPTOR:
return "CELL_SPURS_JOB_ERROR_JOB_DESCRIPTOR";
case CELL_SPURS_JOB_ERROR_JOB_DESCRIPTOR_SIZE:
return "CELL_SPURS_JOB_ERROR_JOB_DESCRIPTOR_SIZE";
case CELL_SPURS_JOB_ERROR_FAULT:
return "CELL_SPURS_JOB_ERROR_FAULT";
case CELL_SPURS_JOB_ERROR_CHILD:
return "CELL_SPURS_JOB_ERROR_CHILD";
case CELL_SPURS_JOB_ERROR_STAT:
return "CELL_SPURS_JOB_ERROR_STAT";
case CELL_SPURS_JOB_ERROR_ALIGN:
return "CELL_SPURS_JOB_ERROR_ALIGN";
case CELL_SPURS_JOB_ERROR_NULL_POINTER:
return "CELL_SPURS_JOB_ERROR_NULL_POINTER";
case CELL_SPURS_JOB_ERROR_MEMORY_SIZE:
return "CELL_SPURS_JOB_ERROR_MEMORY_SIZE";
case CELL_SPURS_JOB_ERROR_UNKNOWN_COMMAND:
return "CELL_SPURS_JOB_ERROR_UNKNOWN_COMMAND";
case CELL_SPURS_JOB_ERROR_JOBLIST_ALIGNMENT:
return "CELL_SPURS_JOB_ERROR_JOBLIST_ALIGNMENT";
case CELL_SPURS_JOB_ERROR_JOB_ALIGNMENT:
return "CELL_SPURS_JOB_ERROR_JOB_ALIGNMENT";
case CELL_SPURS_JOB_ERROR_CALL_OVERFLOW:
return "CELL_SPURS_JOB_ERROR_CALL_OVERFLOW";
case CELL_SPURS_JOB_ERROR_ABORT:
return "CELL_SPURS_JOB_ERROR_ABORT";
case CELL_SPURS_JOB_ERROR_DMALIST_ELEMENT:
return "CELL_SPURS_JOB_ERROR_DMALIST_ELEMENT";
case CELL_SPURS_JOB_ERROR_NUM_CACHE:
return "CELL_SPURS_JOB_ERROR_NUM_CACHE";
case CELL_SPURS_JOB_ERROR_INVALID_BINARY:
return "CELL_SPURS_JOB_ERROR_INVALID_BINARY";
default:
return "<unknown>";
}
}
#endif

89
common/ps3/vjobutils.h Normal file
View File

@@ -0,0 +1,89 @@
//========== Copyright © Valve Corporation, All rights reserved. ========
#ifndef VJOB_SPURS_UTILS_HDR
#define VJOB_SPURS_UTILS_HDR
#ifndef _PS3
#error "This is PS3 specific header"
#endif
#include "vjobs_interface.h"
#include "ps3/vjobutils_shared.h"
#ifdef VJOBS_ON_SPURS
#include "tier0/logging.h"
#include "tier0/dbg.h"
#include "spu_printf.h"
#include "tier0/memalloc.h"
#include <cell/spurs.h>
#include <cell/spurs/system_workload.h>
using namespace ::cell::Spurs;
DECLARE_LOGGING_CHANNEL( LOG_VJOBS );
extern const char * HumanReadableCellResult( int nCellResult );
inline int CellVerify( int nResult, const char * pCmd, LoggingSeverity_t severity )
{
Assert( nResult == CELL_OK );
if( nResult != CELL_OK )
{
InternalMsg( LOG_VJOBS, severity, "Cell error %08X (%s) in\n\t%s\n", nResult, HumanReadableCellResult(nResult), pCmd );
}
return nResult;
}
inline int CellVerify2( int nResult, const char * pCmd, const char * pFile, const int nLine, LoggingSeverity_t severity )
{
Assert( nResult == CELL_OK );
if( nResult != CELL_OK )
{
InternalMsg( LOG_VJOBS, severity, "Cell error %08X (%s) in\n\t%s\n\t%s:%d", nResult, HumanReadableCellResult(nResult), pCmd, pFile, nLine );
}
return nResult;
}
#define CELL_VERIFY( CMD ) CellVerify2( CMD, #CMD, __FILE__, __LINE__, LS_ASSERT )
#define CELL_MUST_SUCCEED( CMD ) CellVerify2( CMD, #CMD, __FILE__, __LINE__, LS_ERROR )
#define CELL_MUST_SUCCEED2( CMD, DESC ) CellVerify2( CMD, DESC, __FILE__, __LINE__, LS_ERROR )
inline CellSpursJob64* NewJob64( const CellSpursJobHeader & jobHeader )
{
CellSpursJob64 * pJob = (CellSpursJob64 *)MemAlloc_AllocAligned( sizeof(CellSpursJob64), sizeof(CellSpursJob64) );
pJob->header = jobHeader;
pJob->workArea.dmaList[0] = 0;
pJob->workArea.dmaList[1] = 0;
return pJob;
};
inline CellSpursJob128* NewJob128( const CellSpursJobHeader & jobHeader )
{
CellSpursJob128 * pJob = (CellSpursJob128 *)MemAlloc_AllocAligned( sizeof(CellSpursJob128), sizeof(CellSpursJob128) );
__dcbz( pJob );
pJob->header = jobHeader;
return pJob;
};
inline CellSpursJob256* NewJob256( const CellSpursJobHeader & jobHeader )
{
CellSpursJob256 * pJob = (CellSpursJob256 *)MemAlloc_AllocAligned( sizeof(CellSpursJob256), sizeof(CellSpursJob256) );
__dcbz( pJob );
__dcbz( ((uint8*)pJob) + 128 );
pJob->header = jobHeader;
return pJob;
};
template <typename T>
inline void DeleteJob( T * pJob )
{
MemAlloc_FreeAligned( pJob );
}
#endif // VJOBS_ON_SPURS
DECLARE_LOGGING_CHANNEL(LOG_VJOBS);
#endif

View File

@@ -0,0 +1,224 @@
//========== Copyright © Valve Corporation, All rights reserved. ========
// This is shared between SPU and PPU
#ifndef COMMON_PS3_VJOBUTILS_SHARED_HDR
#define COMMON_PS3_VJOBUTILS_SHARED_HDR
#ifndef _PS3
#error "This is PS3 specific header"
#endif
#include "spu_job_shared.h"
#include <cell/spurs/job_descriptor.h>
template <typename T>
inline void AddInputDma( T * pJob, uint nSize, const void * pEa )
{
Assert( !( nSize & 0xF ) );
Assert( !( pJob->header.sizeDmaList & ( sizeof( uint64 ) - 1 ) ) );
//int nError = cellSpursJobGetInputList( &pJob->workArea.dmaList[pJob->header.sizeDmaList / sizeof( uint64_t )], nSize, (uint32) pEa );
//Assert( nError == CELL_OK );
pJob->workArea.dmaList[pJob->header.sizeDmaList / sizeof( uint64_t )] = ( uint64( nSize ) << 32 ) | ( uint32 )pEa;
// ( nSIze << 32 ) | pEa
pJob->header.sizeDmaList += sizeof( uint64_t );
pJob->header.sizeInOrInOut += nSize;
Assert( pJob->header.sizeDmaList <= sizeof( pJob->workArea ) );
}
template <typename T>
inline void AlignInputDma( T * pJob )
{
pJob->workArea.dmaList[pJob->header.sizeDmaList / sizeof( uint64_t )] = 0;
pJob->header.sizeDmaList = AlignValue( pJob->header.sizeDmaList, 16 );
Assert( pJob->header.sizeDmaList <= sizeof( pJob->workArea ) );
}
inline void AddCacheDma( struct CellSpursJob128 * pJob, uint nSize, const void * pEa )
{
int nError = cellSpursJobGetInputList( &pJob->workArea.dmaList[( pJob->header.sizeDmaList + pJob->header.sizeCacheDmaList ) / sizeof( uint64 ) ], nSize, (uint32)pEa );
( void )nError;
Assert( nError == CELL_OK );
pJob->header.sizeCacheDmaList += sizeof( uint64_t );
}
inline uint64 MakeDmaElement( void * pData, uint nSize )
{
return ((uint32)pData) | (uint64(nSize)<<32);
}
template <uint nSize>
inline void V_memcpy16( void *pDest, const void * pSrc )
{
Assert( !( nSize & 0xF ) && !( uintp( pDest ) & 0xF ) && !( uintp( pSrc ) & 0xF ) );
for( uint i = 0; i < nSize / 16; ++i )
{
( ( vector unsigned int * ) pDest )[i] = ( ( vector unsigned int * ) pSrc )[i];
}
}
class CDmaListConstructor
{
uint32 *m_pListBegin, *m_pList;
uint m_sizeInOrInOut;
uint m_sizeCacheDmaList;
public:
CDmaListConstructor( void * pList )
{
m_pList = m_pListBegin = ( uint32* )pList;
m_sizeInOrInOut = m_sizeCacheDmaList = 0;
}
void AddInputDma( uint nSize, const void *pEa )
{
Assert( !m_sizeCacheDmaList );
Assert( !( nSize & 0xF ) && nSize <= 16 * 1024 && ( !nSize || pEa ) );
Assert( !IsAddressInStack( pEa ) );
m_pList[0] = nSize;
m_pList[1] = ( uint32 )pEa;
m_pList += 2;
m_sizeInOrInOut += nSize;
}
void AddCacheDma( uint nSize, const void *pEa )
{
// WARNING : NEVER use size=0, as there's a bug in SPURS that can corrupt data if you do
Assert( !IsAddressInStack( pEa ) && pEa && nSize > 0 );
uint32 * pCache = AddBytes( m_pList, m_sizeCacheDmaList );
pCache[0] = nSize;
pCache[1] = ( uint32 )pEa;
m_sizeCacheDmaList += 8;
Assert( m_sizeCacheDmaList <= 32 );
}
void AddInputDmaLargeUnalignedRegion( void * pBegin, void * pEnd )
{
uint32 eaBeginAligned = uint32( pBegin ) & -16;
uint32 eaEndAligned = AlignValue( uint32( pEnd ), 16 );
AddInputDmaLarge( eaEndAligned - eaBeginAligned, ( void* )eaBeginAligned );
}
void* AddInputDmaUnalignedRegion( void * pBegin, void * pEnd, int nAlignment = 16 )
{
uint32 eaBeginAligned = uint32( pBegin ) & -nAlignment;
uint32 eaEndAligned = AlignValue( uint32( pEnd ), nAlignment );
AddInputDma( eaEndAligned - eaBeginAligned, ( void* )eaBeginAligned );
return ( void* )eaBeginAligned;
}
void AddInputDmaLarge( uint nMinReserve, uint nSize, const void * pEa )
{
AddInputDmaLarge( nSize, pEa );
Assert( !( nMinReserve & 15 ) );
if( nMinReserve > nSize )
{
m_sizeInOrInOut += nMinReserve - nSize;
}
}
uint AddInputDmaLargeRegion( const void * pBegin, const void * pEnd )
{
uint nSize = uintp( pEnd ) - uintp( pBegin );
AddInputDmaLarge( nSize, pBegin );
return nSize;
}
void AddInputDmaLarge( uint nSize, const void * pEa )
{
Assert( !( nSize & 0xF ) && nSize < 248 * 1024 );
Assert( !IsAddressInStack( pEa ) );
uint nSizeRemaining = nSize;
uintp eaRemaining = ( uintp )pEa;
const uint nMaxDmaElementSize = 16 * 1024;
while( nSizeRemaining > nMaxDmaElementSize )
{
m_pList[0] = nMaxDmaElementSize;
m_pList[1] = eaRemaining;
m_pList += 2;
nSizeRemaining -= nMaxDmaElementSize;
eaRemaining += nMaxDmaElementSize;
}
m_pList[0] = nSizeRemaining;
m_pList[1] = eaRemaining;
m_pList += 2;
m_sizeInOrInOut += nSize;
}
void AddSizeInOrInOut( uint nAddIoBufferSize )
{
Assert( !( nAddIoBufferSize & 0xF ) );
m_sizeInOrInOut += nAddIoBufferSize;
}
void EnsureCapacityInOrInOut( uint nCapacity )
{
Assert( !( nCapacity & 0xF ) );
m_sizeInOrInOut = Max( m_sizeInOrInOut, nCapacity );
}
void FinishIoBuffer( CellSpursJobHeader * pHeader )
{
FinishInOrIoBuffer( pHeader );
Assert( pHeader->useInOutBuffer == 1 );
}
void FinishInBuffer( CellSpursJobHeader * pHeader )
{
FinishInOrIoBuffer( pHeader );
Assert( pHeader->useInOutBuffer == 0 );
}
void FinishIoBuffer( CellSpursJobHeader * pHeader, void * pParams )
{
FinishIoBuffer( pHeader );
// we only use up to 256 byte jobs, which have up to 26 DMA slots. Check that the params belongs to this job structure
// and check that it doesn't overlap with IO DMA list or cache DMA list
Assert( uintp( pParams ) <= uintp( m_pListBegin + 26 * 2 ) && uintp( pParams ) >= uintp( m_pList ) + m_sizeCacheDmaList );
}
void FinishInBuffer( CellSpursJobHeader * pHeader, void * pParams )
{
FinishInBuffer( pHeader );
// we only use up to 256 byte jobs, which have up to 26 DMA slots. Check that the params belongs to this job structure
// and check that it doesn't overlap with IO DMA list or cache DMA list
Assert( uintp( pParams ) <= uintp( m_pListBegin + 26 * 2 ) && uintp( pParams ) >= uintp( m_pList ) + m_sizeCacheDmaList );
}
inline uint32 * operator [] ( int i )
{
uint32 * pResult = m_pListBegin + 2 * i;
Assert( pResult >= m_pList ); // are we not overwriting the dma list tail that we wrote previously?
return pResult;
}
private:
void FinishInOrIoBuffer( CellSpursJobHeader * pHeader )
{
pHeader->sizeDmaList = uintp( m_pList ) - uintp( m_pListBegin );
pHeader->sizeInOrInOut = m_sizeInOrInOut;
pHeader->sizeCacheDmaList = m_sizeCacheDmaList;
}
// We can't DMA from / to the PPU stack, let's verify that
bool IsAddressInStack( const void * pEa )
{
#if IsPlatformPS3_PPU()
uint64 fp = __reg(1);
void * minStack = ( void* )( ( uint32 ) fp - 16 * 1024 ); // The 16 * 1024 should is not really necessary (as it means somebody addresses some portion that could be erased by the stack.
// Never the less, we want to be more conservative.
void * maxStack = (void *)((uint32)fp + 64 * 1024); // Assume that the stack is 64 Kb deep, make sure there is no allocations around if the stack is smaller
return ( ( pEa >= minStack ) && ( pEa <= maxStack ) );
#else
return false;
#endif
}
};
#endif