Improved MTGS (added better suspend/resume support), and work on savestates a bit (still not working tho)

git-svn-id: http://pcsx2.googlecode.com/svn/trunk@1908 96395faa-99c1-11dd-bbfe-3dabce05a288
This commit is contained in:
Jake.Stine 2009-09-23 09:53:21 +00:00
parent 2046b8e7b4
commit aaa3b773c6
31 changed files with 1109 additions and 985 deletions

View File

@ -120,6 +120,8 @@ struct u128
u128( u32 src ) :
lo( src )
, hi( 0 ) {}
u128() {}
};
struct s128
@ -136,6 +138,8 @@ struct s128
s128( s32 src ) :
lo( src )
, hi( 0 ) {}
s128() {}
};
#else

View File

@ -20,10 +20,11 @@
// --------------------------------------------------------------------------------------
// <<< Important Notes to Plugin Authors >>>
// --------------------------------------------------------------------------------------
// * Exceptions thrown by plugins may not be handled correctly if allowed to escape the
// scope of the plugin, and could result in odd crashes. For C++ plugins this means
// ensuring that any code that uses 'new' or STL containers (string, list, vector, etc)
// are contained within a try{} block, since the STL can throw std::bad_alloc.
// * C++ only: Exceptions thrown by plugins may not be handled correctly if allowed to
// escape the scope of the plugin, and could result in unexpected behavior or odd crashes.
// For C++ plugins this means ensuring that any code that uses 'new' or STL containers
// (string, list, vector, etc) are contained within a try{} block, since the STL can
// throw std::bad_alloc.
//
// * Many callbacks are optional, and have been marked as such. Any optional callback can be
// left NULL. Any callback not marked optional and left NULL will cause the emulator to
@ -39,9 +40,9 @@
// allocated by a plugin must be freed by that plugin.
//
// * C++ exception handling cannot be used by either plugin callbacks or emulator callbacks.
// This includes the Console callbacks, for example, since the nature of C++ RTTI could
// cause a C++ plugin wth its own catch handlers to catch exceptions of mismatched types
// from the emulator.
// This includes the emulator's Console callbacks, for example, since the nature of C++
// ID-based RTTI could cause a C++ plugin with its own catch handlers to catch exceptions
// of mismatched types from the emulator.
//
@ -700,9 +701,6 @@ typedef struct _PS2E_ComponentAPI_Mcd
// Returns:
// 0 if the card is not available, or 1 if it is available.
//
// Exceptions:
// None. This function should not throw.
//
BOOL (PS2E_CALLBACK* McdIsPresent)( PS2E_THISPTR thisptr, uint port, uint slot );
// McdRead
@ -802,9 +800,6 @@ typedef struct _PS2E_ComponentAPI_Pad
// Returns:
// 0 if the card is not available, or 1 if it is available.
//
// Exceptions:
// None. This function should not throw.
//
BOOL (PS2E_CALLBACK* PadIsPresent)( PS2E_THISPTR thisptr, uint port, uint slot );
// PadStartPoll
@ -813,9 +808,6 @@ typedef struct _PS2E_ComponentAPI_Pad
// Returns:
// First byte in response to the poll (Typically 0xff).
//
// Exceptions:
// None. This function should not throw.
//
u8 (PS2E_CALLBACK* PadStartPoll)( PS2E_THISPTR thisptr, uint port, uint slot );
// PadPoll
@ -824,9 +816,6 @@ typedef struct _PS2E_ComponentAPI_Pad
// Returns:
// Next byte in response to the poll.
//
// Exceptions:
// None. This function should not throw.
//
u8 (PS2E_CALLBACK* PadPoll)( PS2E_THISPTR thisptr, u8 value );
// PadKeyEvent
@ -836,9 +825,6 @@ typedef struct _PS2E_ComponentAPI_Pad
// PS2E_KeyEvent: Key being pressed or released. Should stay valid until next call to
// PadKeyEvent or plugin is closed with EmuClose.
//
// Exceptions:
// None. This function should not throw.
//
typedef PS2E_KeyEvent* (CALLBACK* PadKeyEvent)();
void* reserved[8];

View File

@ -549,9 +549,9 @@ public:
template< class Key, class T >
class HashMap : public google::dense_hash_map<Key, T, CommonHashClass>
{
public:
typedef typename google::dense_hash_map<Key, T, CommonHashClass> _parent;
public:
using _parent::operator[];
using _parent::end;
typedef typename _parent::const_iterator const_iterator;

View File

@ -53,18 +53,20 @@ wxString GetTranslation( const char* msg )
// LogicErrors enabled as First-Chance exceptions regardless, so do it now. :)
//
// Returns:
// FALSE if the assertion succeeded (condition is valid), or true if the assertion
// TRUE if the assertion succeeded (condition is valid), or FALSE if the assertion
// failed. The true clause is only reachable in release builds, and can be used by code
// to provide a "stable" escape clause for unexpected behavior.
//
DEVASSERT_INLINE bool DevAssert( bool condition, const char* msg )
{
if( condition ) return false;
if( IsDevBuild )
throw Exception::LogicError( msg );
if( condition ) return true;
wxASSERT_MSG_A( false, msg );
return true;
if( IsDevBuild && !IsDebugBuild )
throw Exception::LogicError( msg );
return false;
}
// --------------------------------------------------------------------------------------

View File

@ -123,8 +123,6 @@ namespace Threading
}
else
pthread_detach( m_thread );
m_running = false;
}
// Blocks execution of the calling thread until this thread completes its task. The
@ -137,25 +135,15 @@ namespace Threading
//
sptr PersistentThread::Block()
{
if( _InterlockedExchange( &m_detached, true ) )
{
// already detached: if we're still running then its an invalid operation
if( m_running )
throw Exception::InvalidOperation( "Blocking on detached threads requires manual semaphore implementation." );
return m_returncode;
}
else
{
DevAssert( !IsSelf(), "Thread deadlock detected; Block() should never be called by the owner thread." );
DevAssert( !IsSelf(), "Thread deadlock detected; Block() should never be called by the owner thread." );
if( m_running )
#if wxUSE_GUI
m_sem_finished.WaitGui();
#else
m_sem_finished.Wait();
#endif
return m_returncode;
}
return m_returncode;
}
bool PersistentThread::IsSelf() const

View File

@ -377,7 +377,7 @@ void cdvdReadKey(u8 arg0, u16 arg1, u32 arg2, u8* key) {
{
ElfCRC = loadElfCRC( str );
ElfApplyPatches();
GSsetGameCRC( ElfCRC, 0 );
mtgsThread.SendGameCRC( ElfCRC );
}
}
@ -510,7 +510,7 @@ void cdvdDetectDisk()
{
ElfCRC = loadElfCRC( str.ToAscii().data() );
ElfApplyPatches();
GSsetGameCRC( ElfCRC, 0 );
mtgsThread.SendGameCRC( ElfCRC );
}
}

View File

@ -443,6 +443,7 @@ __forceinline void rcntUpdate_vSync()
if (vsyncCounter.Mode == MODE_VSYNC)
{
eeRecIsReset = false;
mtgsThread.PollStatus();
SysCoreThread::Get().StateCheck();
if( eeRecIsReset )
{

View File

@ -175,11 +175,11 @@ void __fastcall WriteFIFO_page_6(u32 mem, const mem128_t *value)
psHu64(0x6008) = value[1];
FreezeRegs(1);
mtgsThread->PrepDataPacket(GIF_PATH_3, nloop0_packet, 1);
u64* data = (u64*)mtgsThread->GetDataPacketPtr();
mtgsThread.PrepDataPacket(GIF_PATH_3, nloop0_packet, 1);
u64* data = (u64*)mtgsThread.GetDataPacketPtr();
data[0] = value[0];
data[1] = value[1];
mtgsThread->SendDataPacket();
mtgsThread.SendDataPacket();
FreezeRegs(0);
}

View File

@ -67,7 +67,7 @@ void _gs_ChangeTimings( u32 framerate, u32 iTicks )
void gsOnModeChanged( u32 framerate, u32 newTickrate )
{
mtgsThread->SendSimplePacket( GS_RINGTYPE_MODECHANGE, framerate, newTickrate, 0 );
mtgsThread.SendSimplePacket( GS_RINGTYPE_MODECHANGE, framerate, newTickrate, 0 );
}
static bool gsIsInterlaced = false;
@ -92,9 +92,7 @@ void gsInit()
void gsReset()
{
// Sanity check in case the plugin hasn't been initialized...
if( mtgsThread == NULL ) return;
mtgsThread->Reset();
mtgsThread.ResetGS();
gsOnModeChanged(
(gsRegionMode == Region_NTSC) ? FRAMERATE_NTSC : FRAMERATE_PAL,
@ -110,36 +108,8 @@ void gsReset()
psHu32(GIF_MODE) = 0;
}
bool gsGIFSoftReset( int mask )
{
if( GSgifSoftReset == NULL )
{
static bool warned = false;
if( !warned )
{
Console::Notice( "GIF Warning > Soft reset requested, but the GS plugin doesn't support it!" );
//warned = true;
}
return false;
}
mtgsThread->GIFSoftReset( mask );
return true;
}
void gsGIFReset()
{
// fixme - should this be here? (air)
//memzero_obj(g_RealGSMem);
// none of this should be here, its a GIF reset, not GS, only the dma side of it is reset. (Refraction)
// perform a soft reset (but do not do a full reset if the soft reset API is unavailable)
//gsGIFSoftReset( 7 );
//GSCSRr = 0x551B4000; // Set the FINISH bit to 1 for now
//GSIMR = 0x7f00;
psHu32(GIF_STAT) = 0;
psHu32(GIF_CTRL) = 0;
psHu32(GIF_MODE) = 0;
@ -147,20 +117,21 @@ void gsGIFReset()
void gsCSRwrite(u32 value)
{
// Our emulated GS has no FIFO...
/*if( value & 0x100 ) { // FLUSH
//Console::WriteLn("GS_CSR FLUSH GS fifo: %x (CSRr=%x)", value, GSCSRr);
}*/
if (value & 0x200) { // resetGS
// perform a soft reset -- and fall back to doing a full reset if the plugin doesn't
// support soft resets.
// perform a soft reset -- which is a clearing of all GIFpaths -- and fall back to doing
// a full reset if the plugin doesn't support soft resets.
if( !gsGIFSoftReset( 7 ) )
mtgsThread->SendSimplePacket( GS_RINGTYPE_RESET, 0, 0, 0 );
if( GSgifSoftReset != NULL )
{
GIFPath_Clear( GIF_PATH_1 );
GIFPath_Clear( GIF_PATH_2 );
GIFPath_Clear( GIF_PATH_3 );
}
else
{
mtgsThread.SendSimplePacket( GS_RINGTYPE_RESET, 0, 0, 0 );
}
CSRw |= 0x1f;
GSCSRr = 0x551B4000; // Set the FINISH bit to 1 - GS is always at a finish state as we don't have a FIFO(saqib)
@ -168,12 +139,13 @@ void gsCSRwrite(u32 value)
}
else if( value & 0x100 ) // FLUSH
{
// Our emulated GS has no FIFO, but if it did, it would flush it here...
//Console::WriteLn("GS_CSR FLUSH GS fifo: %x (CSRr=%x)", value, GSCSRr);
}
else
{
CSRw |= value & 0x1f;
mtgsThread->SendSimplePacket( GS_RINGTYPE_WRITECSR, CSRw, 0, 0 );
mtgsThread.SendSimplePacket( GS_RINGTYPE_WRITECSR, CSRw, 0, 0 );
GSCSRr = ((GSCSRr&~value)&0x1f)|(GSCSRr&~0x1f);
}
@ -204,7 +176,7 @@ __forceinline void gsWrite8(u32 mem, u8 value)
gsCSRwrite((CSRw & ~0xff000000) | (value << 24)); break;
default:
*PS2GS_BASE(mem) = value;
mtgsThread->SendSimplePacket(GS_RINGTYPE_MEMWRITE8, mem&0x13ff, value, 0);
mtgsThread.SendSimplePacket(GS_RINGTYPE_MEMWRITE8, mem&0x13ff, value, 0);
}
GIF_LOG("GS write 8 at %8.8lx with data %8.8lx", mem, value);
}
@ -248,7 +220,7 @@ __forceinline void gsWrite16(u32 mem, u16 value)
}
*(u16*)PS2GS_BASE(mem) = value;
mtgsThread->SendSimplePacket(GS_RINGTYPE_MEMWRITE16, mem&0x13ff, value, 0);
mtgsThread.SendSimplePacket(GS_RINGTYPE_MEMWRITE16, mem&0x13ff, value, 0);
}
//////////////////////////////////////////////////////////////////////////
@ -273,7 +245,7 @@ __forceinline void gsWrite32(u32 mem, u32 value)
}
*(u32*)PS2GS_BASE(mem) = value;
mtgsThread->SendSimplePacket(GS_RINGTYPE_MEMWRITE32, mem&0x13ff, value, 0);
mtgsThread.SendSimplePacket(GS_RINGTYPE_MEMWRITE32, mem&0x13ff, value, 0);
}
//////////////////////////////////////////////////////////////////////////
@ -309,7 +281,7 @@ void __fastcall gsWrite64_generic( u32 mem, const mem64_t* value )
GIF_LOG("GS Write64 at %8.8lx with data %8.8x_%8.8x", mem, srcval32[1], srcval32[0]);
*(u64*)PS2GS_BASE(mem) = *value;
mtgsThread->SendSimplePacket(GS_RINGTYPE_MEMWRITE64, mem&0x13ff, srcval32[0], srcval32[1]);
mtgsThread.SendSimplePacket(GS_RINGTYPE_MEMWRITE64, mem&0x13ff, srcval32[0], srcval32[1]);
}
//////////////////////////////////////////////////////////////////////////
@ -350,8 +322,8 @@ void __fastcall gsWrite128_generic( u32 mem, const mem128_t* value )
writeTo[0] = value[0];
writeTo[1] = value[1];
mtgsThread->SendSimplePacket(GS_RINGTYPE_MEMWRITE64, masked_mem, srcval32[0], srcval32[1]);
mtgsThread->SendSimplePacket(GS_RINGTYPE_MEMWRITE64, masked_mem+8, srcval32[2], srcval32[3]);
mtgsThread.SendSimplePacket(GS_RINGTYPE_MEMWRITE64, masked_mem, srcval32[0], srcval32[1]);
mtgsThread.SendSimplePacket(GS_RINGTYPE_MEMWRITE64, masked_mem+8, srcval32[2], srcval32[3]);
}
__forceinline u8 gsRead8(u32 mem)
@ -393,7 +365,7 @@ void gsSyncLimiterLostTime( s32 deltaTime )
//Console::WriteLn("LostTime on the EE!");
mtgsThread->SendSimplePacket(
mtgsThread.SendSimplePacket(
GS_RINGTYPE_STARTTIME,
deltaTime,
0,
@ -537,7 +509,7 @@ __forceinline void gsFrameSkip( bool forceskip )
void gsPostVsyncEnd( bool updategs )
{
*(u32*)(PS2MEM_GS+0x1000) ^= 0x2000; // swap the vsync field
mtgsThread->PostVsyncEnd( updategs );
mtgsThread.PostVsyncEnd( updategs );
}
void _gs_ResetFrameskip()
@ -548,7 +520,7 @@ void _gs_ResetFrameskip()
// Disables the GS Frameskip at runtime without any racy mess...
void gsResetFrameSkip()
{
mtgsThread->SendSimplePacket(GS_RINGTYPE_FRAMESKIP, 0, 0, 0);
mtgsThread.SendSimplePacket(GS_RINGTYPE_FRAMESKIP, 0, 0, 0);
}
void gsDynamicSkipEnable()
@ -564,5 +536,5 @@ void SaveStateBase::gsFreeze()
{
FreezeMem(PS2MEM_GS, 0x2000);
Freeze(CSRw);
mtgsFreeze();
gifPathFreeze();
}

View File

@ -16,12 +16,16 @@
#pragma once
#include "Common.h"
#include "Utilities/Threading.h"
#include "SysThreads.h"
PCSX2_ALIGNED16( extern u8 g_RealGSMem[0x2000] );
#define GSCSRr *((u64*)(g_RealGSMem+0x1000))
#define GSIMR *((u32*)(g_RealGSMem+0x1010))
#define GSSIGLBLID ((GSRegSIGBLID*)(g_RealGSMem+0x1080))
PCSX2_ALIGNED16( extern u8 g_RealGSMem[Ps2MemSize::GSregs] );
#define PS2MEM_GS g_RealGSMem
#define PS2GS_BASE(mem) (g_RealGSMem+(mem&0x13ff))
#define GSCSRr ((u64&)*(g_RealGSMem+0x1000))
#define GSIMR ((u32&)*(g_RealGSMem+0x1010))
#define GSSIGLBLID ((GSRegSIGBLID&)*(g_RealGSMem+0x1080))
enum GS_RegionMode
{
@ -29,105 +33,6 @@ enum GS_RegionMode
Region_PAL
};
extern GS_RegionMode gsRegionMode;
/////////////////////////////////////////////////////////////////////////////////////////
// MTGS GIFtag Parser - Declaration
//
// The MTGS needs a dummy "GS plugin" for processing SIGNAL, FINISH, and LABEL
// commands. These commands trigger gsIRQs, which need to be handled accurately
// in synch with the EE (which can be running several frames ahead of the MTGS)
//
// Yeah, it's a lot of work, but the performance gains are huge, even on HT cpus.
struct GSRegSIGBLID
{
u32 SIGID;
u32 LBLID;
};
enum GIF_FLG
{
GIF_FLG_PACKED = 0,
GIF_FLG_REGLIST = 1,
GIF_FLG_IMAGE = 2,
GIF_FLG_IMAGE2 = 3
};
enum GIF_REG
{
GIF_REG_PRIM = 0x00,
GIF_REG_RGBA = 0x01,
GIF_REG_STQ = 0x02,
GIF_REG_UV = 0x03,
GIF_REG_XYZF2 = 0x04,
GIF_REG_XYZ2 = 0x05,
GIF_REG_TEX0_1 = 0x06,
GIF_REG_TEX0_2 = 0x07,
GIF_REG_CLAMP_1 = 0x08,
GIF_REG_CLAMP_2 = 0x09,
GIF_REG_FOG = 0x0a,
GIF_REG_XYZF3 = 0x0c,
GIF_REG_XYZ3 = 0x0d,
GIF_REG_A_D = 0x0e,
GIF_REG_NOP = 0x0f,
};
// GIFTAG
// Members of this structure are in CAPS to help visually denote that they are representative
// of actual hw register states of the GIF, unlike the internal tracking vars in GIFPath, which
// are modified during the GIFtag unpacking process.
struct GIFTAG
{
u32 NLOOP : 15;
u32 EOP : 1;
u32 dummy0 : 16;
u32 dummy1 : 14;
u32 PRE : 1;
u32 PRIM : 11;
u32 FLG : 2;
u32 NREG : 4;
u32 REGS[2];
GIFTAG() {}
};
struct GIFPath
{
const GIFTAG tag; // The "original tag -- modification allowed only by SetTag(), so let's make it const.
u8 regs[16]; // positioned after tag ensures 16-bit aligned (in case we SSE optimize later)
u32 nloop; // local copy nloop counts toward zero, and leaves the tag copy unmodified.
u32 curreg; // reg we left of on (for traversing through loops)
u32 numregs; // number of regs (when NREG is 0, numregs is 16)
u8 hasADreg; // has an A+D reg, if it doesn't have one, then it no need to check for gs interrupts
GIFPath();
__forceinline void PrepPackedRegs();
__forceinline void SetTag(const void* mem);
__forceinline bool StepReg() {
if ((++curreg & 0xf) == tag.NREG) {
curreg = 0;
if (--nloop == 0) {
return false;
}
}
return true;
}
__forceinline u8 GetReg() {
return regs[curreg&0xf];
}
};
/////////////////////////////////////////////////////////////////////////////
// MTGS Threaded Class Declaration
// Uncomment this to enable the MTGS debug stack, which tracks to ensure reads
// and writes stay synchronized. Warning: the debug stack is VERY slow.
//#define RINGBUF_DEBUG_STACK
enum GIF_PATH
{
GIF_PATH_1 = 0,
@ -135,6 +40,18 @@ enum GIF_PATH
GIF_PATH_3,
};
extern int GIFPath_ParseTag(GIF_PATH pathidx, const u8* pMem, u32 size);
extern void GIFPath_Reset();
extern void GIFPath_Clear( GIF_PATH pathidx );
extern GS_RegionMode gsRegionMode;
/////////////////////////////////////////////////////////////////////////////
// MTGS Threaded Class Declaration
// Uncomment this to enable the MTGS debug stack, which tracks to ensure reads
// and writes stay synchronized. Warning: the debug stack is VERY slow.
//#define RINGBUF_DEBUG_STACK
enum GS_RINGTYPE
{
@ -154,8 +71,8 @@ enum GS_RINGTYPE
, GS_RINGTYPE_SOFTRESET // issues a soft reset for the GIF
, GS_RINGTYPE_WRITECSR
, GS_RINGTYPE_MODECHANGE // for issued mode changes.
, GS_RINGTYPE_CRC
, GS_RINGTYPE_STARTTIME // special case for min==max fps frameskip settings
, GS_RINGTYPE_QUIT
};
@ -165,42 +82,27 @@ struct MTGS_FreezeData
s32 retval; // value returned from the call, valid only after an mtgsWaitGS()
};
class mtgsThreadObject : public Threading::PersistentThread
class mtgsThreadObject : public SysSuspendableThread
{
friend class SaveStateBase;
protected:
// Size of the ringbuffer as a power of 2 -- size is a multiple of simd128s.
// (actual size is 1<<m_RingBufferSizeFactor simd vectors [128-bit values])
// A value of 19 is a 8meg ring buffer. 18 would be 4 megs, and 20 would be 16 megs.
// Default was 2mb, but some games with lots of MTGS activity want 8mb to run fast (rama)
static const uint m_RingBufferSizeFactor = 19;
// size of the ringbuffer in simd128's.
static const uint m_RingBufferSize = 1<<m_RingBufferSizeFactor;
// Mask to apply to ring buffer indices to wrap the pointer from end to
// start (the wrapping is what makes it a ringbuffer, yo!)
static const uint m_RingBufferMask = m_RingBufferSize - 1;
typedef SysSuspendableThread _parent;
protected:
// note: when g_pGSRingPos == g_pGSWritePos, the fifo is empty
uint m_RingPos; // cur pos gs is reading from
uint m_WritePos; // cur pos ee thread is writing to
// used to regulate thread startup and gsInit
Threading::Semaphore m_sem_InitDone;
Threading::MutexLock m_lock_RingRestart;
Semaphore m_sem_OpenDone;
MutexLock m_lock_RingRestart;
// used to keep multiple threads from sending packets to the ringbuffer concurrently.
Threading::MutexLock m_PacketLocker;
MutexLock m_PacketLocker;
// Used to delay the sending of events. Performance is better if the ringbuffer
// has more than one command in it when the thread is kicked.
int m_CopyCommandTally;
int m_CopyDataTally;
volatile u32 m_RingBufferIsBusy;
volatile bool m_RingBufferIsBusy;
volatile bool m_LoadState;
// Counts the number of vsync frames queued in the MTGS ringbuffer. This is used to
// throttle the number of frames allowed to be rendered ahead of time for games that
@ -209,7 +111,7 @@ protected:
// Protection lock for the frame queue counter -- needed because we can't safely
// AtomicExchange from two threads.
Threading::MutexLock m_lock_FrameQueueCounter;
MutexLock m_lock_FrameQueueCounter;
// These vars maintain instance data for sending Data Packets.
// Only one data packet can be constructed and uploaded at a time.
@ -221,40 +123,39 @@ protected:
Threading::MutexLock m_lock_Stack;
#endif
// contains aligned memory allocations for gs and Ringbuffer.
SafeAlignedArray<u128,16> m_RingBuffer;
// mtgs needs its own memory space separate from the PS2. The PS2 memory is in
// sync with the EE while this stays in sync with the GS (ie, it lags behind)
u8* const m_gsMem;
public:
mtgsThreadObject();
virtual ~mtgsThreadObject() throw();
void Start();
void Cancel();
void Reset();
void GIFSoftReset( int mask );
void PollStatus();
// Waits for the GS to empty out the entire ring buffer contents.
// Used primarily for plugin startup/shutdown.
void WaitGS();
void ResetGS();
int PrepDataPacket( GIF_PATH pathidx, const u8* srcdata, u32 size );
int PrepDataPacket( GIF_PATH pathidx, const u32* srcdata, u32 size );
void SendDataPacket();
void SendGameCRC( u32 crc );
void WaitForOpen();
void Freeze( int mode, MTGS_FreezeData& data );
void SendSimplePacket( GS_RINGTYPE type, int data0, int data1, int data2 );
void SendPointerPacket( GS_RINGTYPE type, u32 data0, void* data1 );
u8* GetDataPacketPtr() const;
void Freeze( SaveStateBase& state );
void SetEvent();
void PostVsyncEnd( bool updategs );
protected:
void OpenPlugin();
void OnSuspendInThread();
void OnResumeInThread();
void OnResumeReady();
// Saves MMX/XMM REGS, posts an event to the mtgsThread flag and releases a timeslice.
// For use in surrounding loops that wait on the mtgs.
void PrepEventWait();
@ -262,23 +163,15 @@ protected:
// Restores MMX/XMM REGS. For use in surrounding loops that wait on the mtgs.
void PostEventWait() const;
// Processes a GIFtag & packet, and throws out some gsIRQs as needed.
// Used to keep interrupts in sync with the EE, while the GS itself
// runs potentially several frames behind.
int gifTransferDummy(GIF_PATH pathidx, const u8 *pMem, u32 size);
int _gifTransferDummy(GIF_PATH pathidx, const u8 *pMem, u32 size);
// Used internally by SendSimplePacket type functions
uint _PrepForSimplePacket();
void _FinishSimplePacket( uint future_writepos );
void _RingbufferLoop();
sptr ExecuteTask();
};
extern mtgsThreadObject* mtgsThread;
PCSX2_ALIGNED16_EXTERN( mtgsThreadObject mtgsThread );
void mtgsWaitGS();
void mtgsOpen();
/////////////////////////////////////////////////////////////////////////////
// Generalized GS Functions and Stuff
@ -301,7 +194,6 @@ extern void _gs_ChangeTimings( u32 framerate, u32 iTicks );
// used for resetting GIF fifo
bool gsGIFSoftReset( int mask );
void gsGIFReset();
void gsCSRwrite(u32 value);

View File

@ -98,12 +98,12 @@ static u32 WRITERING_DMA(u32 *pMem, u32 qwc)
{
psHu32(GIF_STAT) |= GIF_STAT_APATH3 | GIF_STAT_OPH;
int size = mtgsThread->PrepDataPacket(GIF_PATH_3, pMem, qwc);
u8* pgsmem = mtgsThread->GetDataPacketPtr();
int size = mtgsThread.PrepDataPacket(GIF_PATH_3, pMem, qwc);
u8* pgsmem = mtgsThread.GetDataPacketPtr();
memcpy_aligned(pgsmem, pMem, size<<4);
mtgsThread->SendDataPacket();
mtgsThread.SendDataPacket();
return size;
}
@ -116,7 +116,7 @@ int _GIFchain()
if (pMem == NULL)
{
// reset path3, fixes dark cloud 2
gsGIFSoftReset(4);
GIFPath_Clear( GIF_PATH_3 );
//must increment madr and clear qwc, else it loops
gif->madr += gif->qwc * 16;

View File

@ -13,21 +13,19 @@
* If not, see <http://www.gnu.org/licenses/>.
*/
#include "PrecompiledHeader.h"
#include <list>
#include <wx/datetime.h>
#include "Common.h"
#include "VU.h"
#include "GS.h"
#include "VU.h"
#include "iR5900.h"
#include "VifDma.h"
#include "SamplProf.h"
#include <list>
#include <wx/datetime.h>
// Uncomment this to enable profiling of the GS RingBufferCopy function.
//#define PCSX2_GSRING_SAMPLING_STATS
@ -46,152 +44,60 @@ using namespace std;
#define volatize(x) (*reinterpret_cast<volatile uint*>(&(x)))
/////////////////////////////////////////////////////////////////////////////
// BEGIN -- MTGS GIFtag Parse Implementation
//
// The MTGS needs a dummy "GS plugin" for processing SIGNAL, FINISH, and LABEL
// commands. These commands trigger gsIRQs, which need to be handled accurately
// in synch with the EE (which can be running several frames ahead of the MTGS)
//
// Yeah, it's a lot of work, but the performance gains are huge, even on HT cpus.
// the MTGS "dummy" GIFtag info!
// fixme: The real PS2 has a single internal PATH and 3 logical sources, not 3 entirely
// separate paths. But for that to work properly we need also interlocked path sources.
// That is, when the GIF selects a source, it sticks to that source until an EOP. Currently
// this is not emulated!
PCSX2_ALIGNED16( static GIFPath s_path[3] );
GIFPath::GIFPath() :
tag()
{
memzero_obj( *this );
}
// unpack the registers - registers are stored as a sequence of 4 bit values in the
// upper 64 bits of the GIFTAG. That sucks for us when handling partialized GIF packets
// coming in from paths 2 and 3, so we unpack them into an 8 bit array here.
//
__forceinline void GIFPath::PrepPackedRegs()
{
// Only unpack registers if we're starting a new pack. Otherwise the unpacked
// array should have already been initialized by a previous partial transfer.
if (curreg != 0) return;
u32 tempreg = tag.REGS[0];
numregs = ((tag.NREG-1)&0xf) + 1;
for (u32 i = 0; i < numregs; i++) {
if (i == 8) tempreg = tag.REGS[1];
regs[i] = tempreg & 0xf;
tempreg >>= 4;
}
}
__forceinline void GIFPath::SetTag(const void* mem)
{
const_cast<GIFTAG&>(tag) = *((GIFTAG*)mem);
nloop = tag.NLOOP;
curreg = 0;
}
static void _mtgsFreezeGIF( SaveStateBase& state, GIFPath (&paths)[3] )
{
for(int i=0; i<3; i++ )
{
state.Freeze( paths[i].tag );
state.Freeze( paths[i].nloop );
state.Freeze( paths[i].curreg );
state.Freeze( paths[i].numregs );
}
for(int i=0; i<3; i++ )
{
state.Freeze( paths[i].regs );
}
}
void SaveStateBase::mtgsFreeze()
{
FreezeTag( "mtgs" );
mtgsThread->Freeze( *this );
}
static void RegHandlerSIGNAL(const u32* data)
{
MTGS_LOG("MTGS SIGNAL data %x_%x CSRw %x IMR %x CSRr\n",data[0], data[1], CSRw, GSIMR, GSCSRr);
GSSIGLBLID->SIGID = (GSSIGLBLID->SIGID&~data[1])|(data[0]&data[1]);
if ((CSRw & 0x1))
{
if (!(GSIMR&0x100) )
{
gsIrq();
}
GSCSRr |= 1; // signal
}
}
static void RegHandlerFINISH(const u32* data)
{
MTGS_LOG("MTGS FINISH data %x_%x CSRw %x\n", data[0], data[1], CSRw);
if ((CSRw & 0x2))
{
if (!(GSIMR&0x200))
gsIrq();
GSCSRr |= 2; // finish
}
}
static void RegHandlerLABEL(const u32* data)
{
GSSIGLBLID->LBLID = (GSSIGLBLID->LBLID&~data[1])|(data[0]&data[1]);
}
// END -- MTGS GIFtag Parse Implementation
/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
// =====================================================================================================
// MTGS Threaded Class Implementation
// =====================================================================================================
// Size of the ringbuffer as a power of 2 -- size is a multiple of simd128s.
// (actual size is 1<<m_RingBufferSizeFactor simd vectors [128-bit values])
// A value of 19 is a 8meg ring buffer. 18 would be 4 megs, and 20 would be 16 megs.
// Default was 2mb, but some games with lots of MTGS activity want 8mb to run fast (rama)
static const uint RingBufferSizeFactor = 19;
// size of the ringbuffer in simd128's.
static const uint RingBufferSize = 1<<RingBufferSizeFactor;
// Mask to apply to ring buffer indices to wrap the pointer from end to
// start (the wrapping is what makes it a ringbuffer, yo!)
static const uint RingBufferMask = RingBufferSize - 1;
PCSX2_ALIGNED16( mtgsThreadObject mtgsThread );
struct MTGS_BufferedData
{
u128 m_Ring[RingBufferSize];
u8 Regs[Ps2MemSize::GSregs];
MTGS_BufferedData() {}
u128& operator[]( uint idx )
{
jASSUME( idx < RingBufferSize );
return m_Ring[idx];
}
};
PCSX2_ALIGNED( 32, static MTGS_BufferedData RingBuffer );
mtgsThreadObject* mtgsThread = NULL;
#ifdef RINGBUF_DEBUG_STACK
#include <list>
std::list<uint> ringposStack;
#endif
#ifdef PCSX2_DEBUG
// debug variable used to check for bad code bits where copies are started
// but never closed, or closed without having been started. (GSRingBufCopy calls
// should always be followed by a call to GSRINGBUF_DONECOPY)
// And it's not even used in the debug code.
//static int copyLock = 0;
#endif
typedef void (*GIFRegHandler)(const u32* data);
static GIFRegHandler s_GSHandlers[3] = { RegHandlerSIGNAL, RegHandlerFINISH, RegHandlerLABEL };
mtgsThreadObject::mtgsThreadObject() :
PersistentThread()
SysSuspendableThread()
, m_RingPos( 0 )
, m_WritePos( 0 )
, m_sem_InitDone()
, m_lock_RingRestart()
, m_PacketLocker( true ) // true - makes it a recursive lock
, m_CopyCommandTally( 0 )
, m_CopyDataTally( 0 )
, m_RingBufferIsBusy( 0 )
, m_RingBufferIsBusy( false )
, m_LoadState( false )
, m_QueuedFrames( 0 )
, m_lock_FrameQueueCounter()
, m_packet_size( 0 )
@ -200,41 +106,47 @@ mtgsThreadObject::mtgsThreadObject() :
#ifdef RINGBUF_DEBUG_STACK
, m_lock_Stack()
#endif
, m_RingBuffer( m_RingBufferSize + (Ps2MemSize::GSregs/sizeof(u128)) )
, m_gsMem( (u8*)m_RingBuffer.GetPtr( m_RingBufferSize ) )
{
}
void mtgsThreadObject::Start()
{
m_sem_InitDone.Reset();
PersistentThread::Start();
m_returncode = 0;
m_RingPos = 0;
m_WritePos = 0;
// Wait for the thread to finish initialization (it runs GSopen, which can take
// some time since it's creating a new window and all), and then check for errors.
m_RingBufferIsBusy = false;
m_LoadState = false;
m_sem_InitDone.WaitGui();
m_QueuedFrames = 0;
m_packet_size = 0;
m_packet_ringpos = 0;
_parent::Start();
m_ExecMode = ExecMode_Suspending;
SetEvent();
}
void mtgsThreadObject::PollStatus()
{
if( m_ExecMode == ExecMode_NoThreadYet )
{
if( m_returncode != 0 ) // means the thread failed to init the GS plugin
throw Exception::PluginOpenError( PluginId_GS );
}
if( m_returncode != 0 ) // means the thread failed to init the GS plugin
throw Exception::PluginOpenError( PluginId_GS );
}
mtgsThreadObject::~mtgsThreadObject() throw()
{
mtgsThreadObject::Cancel();
}
// Closes the GS "forcefully" without waiting for it to finish rendering it's pending
// queue of GS data.
void mtgsThreadObject::Cancel()
void mtgsThreadObject::OnResumeReady()
{
//SendSimplePacket( GS_RINGTYPE_QUIT, 0, 0, 0 );
//SetEvent();
//m_sem_finished.WaitGui();
PersistentThread::Cancel();
m_sem_OpenDone.Reset();
}
void mtgsThreadObject::Reset()
void mtgsThreadObject::ResetGS()
{
// MTGS Reset process:
// * clear the ringbuffer.
@ -246,133 +158,9 @@ void mtgsThreadObject::Reset()
MTGS_LOG( "MTGS: Sending Reset..." );
SendSimplePacket( GS_RINGTYPE_RESET, 0, 0, 0 );
SendSimplePacket( GS_RINGTYPE_FRAMESKIP, 0, 0, 0 );
SetEvent();
memzero_obj( s_path );
}
#define incTag(x, y) do { \
pMem += (x); \
size -= (y); \
if ((pathidx==GIF_PATH_1)&&(pMem>=vuMemEnd)) pMem -= 0x4000; \
} while(false)
#define aMin(x, y) ((x < y) ? (x) : (y))
#define subVal(x, y) ((x > y) ? (x-y) : 0 )
#define optPrint(x, y) { if (x > y) DevCon::Status("Loops Optimized = %d", x); }
__forceinline void gsHandler(const u8* pMem) {
const int handler = pMem[8];
if (handler >= 0x60 && handler < 0x63) {
//DevCon::Status("GIF Tag Interrupt");
s_GSHandlers[handler&0x3]((const u32*)pMem);
}
}
// Parameters:
// size (path1) - difference between the end of VU memory and pMem.
// size (path2/3) - max size of incoming data stream, in qwc (simd128)
__forceinline int mtgsThreadObject::_gifTransferDummy(GIF_PATH pathidx, const u8* pMem, u32 size)
{
GIFPath& path = s_path[pathidx]; // Current Path
const u8* vuMemEnd = pMem + (size<<4); // End of VU1 Mem
if (pathidx==GIF_PATH_1) size = 0x400; // VU1 mem size
u32 startSize = size; // Start Size
while (size > 0) {
if (!path.nloop) {
path.SetTag(pMem);
incTag(16, 1);
if (pathidx == GIF_PATH_3) {
if (path.tag.FLG&2) Path3progress = IMAGE_MODE;
else Path3progress = TRANSFER_MODE;
}
}
else {
switch(path.tag.FLG) {
case GIF_FLG_PACKED:
path.PrepPackedRegs();
do {
if (path.GetReg() == 0xe) {
gsHandler(pMem);
}
incTag(16, 1);
} while(path.StepReg() && size > 0);
break;
case GIF_FLG_REGLIST:
{
size *= 2;
do { incTag(8, 1); }
while(path.StepReg() && size > 0);
if (size & 1) { incTag(8, 1); }
size /= 2;
}
break;
case GIF_FLG_IMAGE:
case GIF_FLG_IMAGE2:
{
int len = aMin(size, path.nloop);
incTag((len * 16), len);
path.nloop -= len;
}
break;
}
}
if (path.tag.EOP && !path.nloop) {
if (pathidx != GIF_PATH_2) {
break;
}
}
}
size = (startSize - size);
if (pathidx == GIF_PATH_3) {
if (path.tag.EOP && !path.nloop) {
Path3progress = STOPPED_MODE;
}
gif->madr += size * 16;
gif->qwc -= size;
}
return size;
}
// Processes a GIFtag & packet, and throws out some gsIRQs as needed.
// Used to keep interrupts in sync with the EE, while the GS itself
// runs potentially several frames behind.
// Parameters:
// size - max size of incoming data stream, in qwc (simd128)
__forceinline int mtgsThreadObject::gifTransferDummy(GIF_PATH pathidx, const u8* pMem, u32 size)
{
#ifdef PCSX2_GSRING_SAMPLING_STATS
static uptr profStartPtr = 0;
static uptr profEndPtr = 0;
if (profStartPtr == 0) {
__asm
{
__beginfunc:
mov profStartPtr, offset __beginfunc;
mov profEndPtr, offset __endfunc;
}
ProfilerRegisterSource( "GSRingBufCopy", (void*)profStartPtr, profEndPtr - profStartPtr );
}
#endif
int retSize = _gifTransferDummy(pathidx, pMem, size);
#ifdef PCSX2_GSRING_SAMPLING_STATS
__asm
{
__endfunc:
nop;
}
#endif
return retSize;
GIFPath_Reset();
}
void mtgsThreadObject::PostVsyncEnd( bool updategs )
@ -410,29 +198,74 @@ struct PacketTagType
};
extern bool renderswitch;
static volatile long gsIsOpened = 0;
static void _clean_close_gs( void* obj )
{
if( g_plugins != NULL )
int result = InterlockedExchange( &gsIsOpened, 0 );
if( result && (g_plugins != NULL) )
g_plugins->m_info[PluginId_GS].CommonBindings.Close();
}
void mtgsThreadObject::_RingbufferLoop()
static void dummyIrqCallback()
{
// dummy, because MTGS doesn't need this mess!
// (and zerogs does >_<)
}
void mtgsThreadObject::OpenPlugin()
{
if( gsIsOpened ) return;
memcpy_aligned( RingBuffer.Regs, PS2MEM_GS, sizeof(PS2MEM_GS) );
GSsetBaseMem( RingBuffer.Regs );
GSirqCallback( dummyIrqCallback );
if( renderswitch )
Console::WriteLn( "\t\tForced software switch enabled." );
if( GSopen2 != NULL )
m_returncode = GSopen2( (void*)&pDsp, 1 | (renderswitch ? 4 : 0) );
else
m_returncode = GSopen( (void*)&pDsp, "PCSX2", renderswitch ? 2 : 1 );
gsIsOpened = 1;
m_sem_OpenDone.Post();
if( m_returncode != 0 )
{
DevCon::WriteLn( "MTGS: GSopen Finished, return code: 0x%x", m_returncode );
pthread_exit( (void*)m_returncode );
}
GSCSRr = 0x551B4000; // 0x55190000
GSsetGameCRC( ElfCRC, 0 );
}
sptr mtgsThreadObject::ExecuteTask()
{
SetName( "MTGS" );
#ifdef RINGBUF_DEBUG_STACK
PacketTagType prevCmd;
#endif
gsIsOpened = false;
pthread_cleanup_push( _clean_close_gs, this );
while( true )
{
m_sem_event.Wait();
m_sem_event.Wait(); // ... because this does a cancel test itself..
StateCheck( false ); // false disables cancel test here!
AtomicExchange( m_RingBufferIsBusy, 1 );
m_RingBufferIsBusy = true;
// note: m_RingPos is intentionally not volatile, because it should only
// ever be modified by this thread.
while( m_RingPos != volatize(m_WritePos))
{
wxASSERT( m_RingPos < m_RingBufferSize );
wxASSERT( m_RingPos < RingBufferSize );
const PacketTagType& tag = (PacketTagType&)m_RingBuffer[m_RingPos];
const PacketTagType& tag = (PacketTagType&)RingBuffer[m_RingPos];
u32 ringposinc = 1;
#ifdef RINGBUF_DEBUG_STACK
@ -458,12 +291,14 @@ void mtgsThreadObject::_RingbufferLoop()
// stall for a bit to let the MainThread have time to update the g_pGSWritePos.
m_lock_RingRestart.Lock();
m_lock_RingRestart.Unlock();
StateCheck( false ); // disable cancel since the above locks are cancelable already
continue;
case GS_RINGTYPE_P1:
{
const int qsize = tag.data[0];
const u128* data = m_RingBuffer.GetPtr( m_RingPos+1 );
const u128* data = &RingBuffer[m_RingPos+1];
// make sure that tag>>16 is the MAX size readable
GSgifTransfer1((u32*)(data - 0x400 + qsize), 0x4000-qsize*16);
@ -475,7 +310,7 @@ void mtgsThreadObject::_RingbufferLoop()
case GS_RINGTYPE_P2:
{
const int qsize = tag.data[0];
const u128* data = m_RingBuffer.GetPtr( m_RingPos+1 );
const u128* data = &RingBuffer[m_RingPos+1];
GSgifTransfer2((u32*)data, qsize);
ringposinc += qsize;
}
@ -484,7 +319,7 @@ void mtgsThreadObject::_RingbufferLoop()
case GS_RINGTYPE_P3:
{
const int qsize = tag.data[0];
const u128* data = m_RingBuffer.GetPtr( m_RingPos+1 );
const u128* data = &RingBuffer[m_RingPos+1];
GSgifTransfer3((u32*)data, qsize);
ringposinc += qsize;
}
@ -514,16 +349,16 @@ void mtgsThreadObject::_RingbufferLoop()
break;
case GS_RINGTYPE_MEMWRITE8:
m_gsMem[tag.data[0]] = (u8)tag.data[1];
RingBuffer.Regs[tag.data[0]] = (u8)tag.data[1];
break;
case GS_RINGTYPE_MEMWRITE16:
*(u16*)(m_gsMem+tag.data[0]) = (u16)tag.data[1];
*(u16*)(RingBuffer.Regs+tag.data[0]) = (u16)tag.data[1];
break;
case GS_RINGTYPE_MEMWRITE32:
*(u32*)(m_gsMem+tag.data[0]) = tag.data[1];
*(u32*)(RingBuffer.Regs+tag.data[0]) = tag.data[1];
break;
case GS_RINGTYPE_MEMWRITE64:
*(u64*)(m_gsMem+tag.data[0]) = *(u64*)&tag.data[1];
*(u64*)(RingBuffer.Regs+tag.data[0]) = *(u64*)&tag.data[1];
break;
case GS_RINGTYPE_FREEZE:
@ -563,15 +398,14 @@ void mtgsThreadObject::_RingbufferLoop()
_gs_ChangeTimings( tag.data[0], tag.data[1] );
break;
case GS_RINGTYPE_CRC:
GSsetGameCRC( tag.data[0], 0 );
break;
case GS_RINGTYPE_STARTTIME:
m_iSlowStart += tag.data[0];
break;
case GS_RINGTYPE_QUIT:
// have to use some low level code, because all the standard Close api does is
// trigger this very ringbuffer message!
return;
#ifdef PCSX2_DEVBUILD
default:
Console::Error("GSThreadProc, bad packet (%x) at m_RingPos: %x, m_WritePos: %x", tag.command, m_RingPos, m_WritePos);
@ -585,62 +419,39 @@ void mtgsThreadObject::_RingbufferLoop()
}
uint newringpos = m_RingPos + ringposinc;
wxASSERT( newringpos <= m_RingBufferSize );
newringpos &= m_RingBufferMask;
wxASSERT( newringpos <= RingBufferSize );
newringpos &= RingBufferMask;
AtomicExchange( m_RingPos, newringpos );
}
AtomicExchange( m_RingBufferIsBusy, 0 );
m_RingBufferIsBusy = false;
}
pthread_cleanup_pop( true );
}
static void dummyIrqCallback()
{
// dummy, because MTGS doesn't need this mess!
// (and zerogs does >_<)
}
sptr mtgsThreadObject::ExecuteTask()
{
SetName( "MTGS" );
memcpy_aligned( m_gsMem, PS2MEM_GS, sizeof(PS2MEM_GS) );
GSsetBaseMem( m_gsMem );
GSirqCallback( dummyIrqCallback );
if( renderswitch )
Console::WriteLn( "\t\tForced software switch enabled." );
if( GSopen2 != NULL )
m_returncode = GSopen2( (void*)&pDsp, 1 | (renderswitch ? 4 : 0) );
else
m_returncode = GSopen( (void*)&pDsp, "PCSX2", renderswitch ? 2 : 1 );
DevCon::WriteLn( "MTGS: GSopen Finished, return code: 0x%x", m_returncode );
GSCSRr = 0x551B4000; // 0x55190000
m_sem_InitDone.Post();
if (m_returncode != 0) { return m_returncode; } // error msg will be issued to the user by Plugins.c
#ifdef RINGBUF_DEBUG_STACK
PacketTagType prevCmd;
#endif
_RingbufferLoop();
return 0;
}
void mtgsThreadObject::OnSuspendInThread()
{
_clean_close_gs( NULL );
}
void mtgsThreadObject::OnResumeInThread()
{
if( !m_LoadState )
OpenPlugin();
}
// Waits for the GS to empty out the entire ring buffer contents.
// Used primarily for plugin startup/shutdown.
void mtgsThreadObject::WaitGS()
{
// Freeze registers because some kernel code likes to destroy them
DevAssert( !IsSelf(), "This method is only allowed from threads *not* named MTGS." );
if( IsSuspended() ) return;
// FIXME : Use semaphores instead of spinwaits.
SetEvent();
while( volatize(m_RingPos) != volatize(m_WritePos) )
{
Timeslice();
//SpinWait();
}
while( volatize(m_RingPos) != volatize(m_WritePos) ) Timeslice();
}
// Sets the gsEvent flag and releases a timeslice.
@ -665,7 +476,7 @@ void mtgsThreadObject::PostEventWait() const
u8* mtgsThreadObject::GetDataPacketPtr() const
{
return (u8*)m_RingBuffer.GetPtr( m_packet_ringpos );
return (u8*)&RingBuffer[m_packet_ringpos];
}
// Closes the data packet send command, and initiates the gs thread (if needed).
@ -675,12 +486,12 @@ void mtgsThreadObject::SendDataPacket()
jASSUME( m_packet_size != 0 );
uint temp = m_packet_ringpos + m_packet_size;
jASSUME( temp <= m_RingBufferSize );
temp &= m_RingBufferMask;
jASSUME( temp <= RingBufferSize );
temp &= RingBufferMask;
if( IsDebugBuild )
{
if( m_packet_ringpos + m_packet_size < m_RingBufferSize )
if( m_packet_ringpos + m_packet_size < RingBufferSize )
{
uint readpos = volatize(m_RingPos);
if( readpos != m_WritePos )
@ -742,10 +553,6 @@ static u32 ringtx_inf[32][32];
static u32 ringtx_inf_s[32];
#endif
#ifdef PCSX2_GSRING_SAMPLING_STATS
static u32 GSRingBufCopySz = 0;
#endif
// returns the amount of giftag data not processed (in simd128 values).
// Return value is used by VU1 XGKICK to hack-fix data packets which are too
// large for VU1 memory.
@ -810,13 +617,13 @@ int mtgsThreadObject::PrepDataPacket( GIF_PATH pathidx, const u8* srcdata, u32 s
jASSUME( m_packet_size == 0 );
// Sanity checks! (within the confines of our ringbuffer please!)
jASSUME( size < m_RingBufferSize );
jASSUME( writepos < m_RingBufferSize );
jASSUME( size < RingBufferSize );
jASSUME( writepos < RingBufferSize );
m_packet_size = gifTransferDummy(pathidx, srcdata, size);
m_packet_size = GIFPath_ParseTag(pathidx, srcdata, size);
size = m_packet_size + 1; // takes into account our command qword.
if( writepos + size < m_RingBufferSize )
if( writepos + size < RingBufferSize )
{
// generic gs wait/stall.
// if the writepos is past the readpos then we're safe.
@ -840,7 +647,7 @@ int mtgsThreadObject::PrepDataPacket( GIF_PATH pathidx, const u8* srcdata, u32 s
PostEventWait();
}
}
else if( writepos + size > m_RingBufferSize )
else if( writepos + size > RingBufferSize )
{
// If the incoming packet doesn't fit, then start over from
// the start of the ring buffer (it's a lot easier than trying
@ -918,7 +725,7 @@ int mtgsThreadObject::PrepDataPacket( GIF_PATH pathidx, const u8* srcdata, u32 s
// Command qword: Low word is the command, and the high word is the packet
// length in SIMDs (128 bits).
PacketTagType& tag = (PacketTagType&)m_RingBuffer[m_WritePos];
PacketTagType& tag = (PacketTagType&)RingBuffer[m_WritePos];
tag.command = pathidx+1;
tag.data[0] = m_packet_size;
m_packet_ringpos = m_WritePos + 1;
@ -935,9 +742,9 @@ __forceinline uint mtgsThreadObject::_PrepForSimplePacket()
#endif
uint future_writepos = m_WritePos+1;
jASSUME( future_writepos <= m_RingBufferSize );
jASSUME( future_writepos <= RingBufferSize );
future_writepos &= m_RingBufferMask;
future_writepos &= RingBufferMask;
if( future_writepos == volatize(m_RingPos) )
{
@ -963,7 +770,7 @@ void mtgsThreadObject::SendSimplePacket( GS_RINGTYPE type, int data0, int data1,
//ScopedLock locker( m_PacketLocker );
const uint thefuture = _PrepForSimplePacket();
PacketTagType& tag = (PacketTagType&)m_RingBuffer[m_WritePos];
PacketTagType& tag = (PacketTagType&)RingBuffer[m_WritePos];
tag.command = type;
tag.data[0] = data0;
@ -978,7 +785,7 @@ void mtgsThreadObject::SendPointerPacket( GS_RINGTYPE type, u32 data0, void* dat
//ScopedLock locker( m_PacketLocker );
const uint thefuture = _PrepForSimplePacket();
PacketTagType& tag = (PacketTagType&)m_RingBuffer[m_WritePos];
PacketTagType& tag = (PacketTagType&)RingBuffer[m_WritePos];
tag.command = type;
tag.data[0] = data0;
@ -987,60 +794,39 @@ void mtgsThreadObject::SendPointerPacket( GS_RINGTYPE type, u32 data0, void* dat
_FinishSimplePacket( thefuture );
}
void mtgsThreadObject::SendGameCRC( u32 crc )
{
SendSimplePacket( GS_RINGTYPE_CRC, crc, 0, 0 );
}
void mtgsThreadObject::WaitForOpen()
{
if( !gsIsOpened )
m_sem_OpenDone.WaitGui();
m_sem_OpenDone.Reset();
}
void mtgsThreadObject::Freeze( int mode, MTGS_FreezeData& data )
{
if( mode == FREEZE_LOAD )
{
AtomicExchange( m_RingPos, m_WritePos );
SendPointerPacket( GS_RINGTYPE_FREEZE, mode, &data );
m_LoadState = true;
SetEvent();
Resume();
}
else
SendPointerPacket( GS_RINGTYPE_FREEZE, mode, &data );
mtgsWaitGS();
m_LoadState = false;
}
// Waits for the GS to empty out the entire ring buffer contents.
// Used primarily for plugin startup/shutdown.
void mtgsWaitGS()
{
if( mtgsThread == NULL ) return;
mtgsThread->WaitGS();
mtgsThread.WaitGS();
}
// Exceptions:
// ThreadCreationError - Thready could not be created (indicates OS resource limitations)
// PluginFailure - GS plugin's "GSopen" call failed.
//
void mtgsOpen()
{
// better not be a thread already running, yo!
if( mtgsThread != NULL ) return;
mtgsThread = new mtgsThreadObject();
try
{
mtgsThread->Start();
}
catch( ... )
{
// if the thread start fails for any reason then set the handle to null.
// The handle is used as a NULL test of thread running status, which is why
// we really need to do this. :)
safe_delete( mtgsThread );
throw;
}
}
void mtgsThreadObject::GIFSoftReset( int mask )
{
if(mask & 1) memzero_obj(s_path[0]);
if(mask & 2) memzero_obj(s_path[1]);
if(mask & 4) memzero_obj(s_path[2]);
if( GSgifSoftReset == NULL ) return;
MTGS_LOG( "MTGS: Sending GIF Soft Reset (mask: %d)", mask );
mtgsThread->SendSimplePacket( GS_RINGTYPE_SOFTRESET, mask, 0, 0 );
}
void mtgsThreadObject::Freeze( SaveStateBase& state )
{
_mtgsFreezeGIF( state, s_path );
}
// this function is needed because of recompiled calls from iGS.cpp
// (currently used in GCC only)
//void mtgsRingBufSimplePacket( s32 command, u32 data0, u32 data1, u32 data2 )
//{
// mtgsThread->SendSimplePacket( (GS_RINGTYPE)command, data0, data1, data2 );
//}

View File

@ -38,9 +38,6 @@ extern u8 *psS; //0.015 mb, scratch pad
#define PS2MEM_EROM psER
#define PS2MEM_SCRATCH psS
#define PS2MEM_GS g_RealGSMem
#define PS2GS_BASE(mem) (g_RealGSMem+(mem&0x13ff))
// Various useful locations
#define spr0 ((DMACh*)&PS2MEM_HW[0xD000])
#define spr1 ((DMACh*)&PS2MEM_HW[0xD400])
@ -57,11 +54,6 @@ extern u8 *psS; //0.015 mb, scratch pad
#define ipu0dma ((DMACh *)&PS2MEM_HW[0xb000])
#define ipu1dma ((DMACh *)&PS2MEM_HW[0xb400])
// From Gif.h
#define GSCSRr *((u64*)(g_RealGSMem+0x1000))
#define GSIMR *((u32*)(g_RealGSMem+0x1010))
#define GSSIGLBLID ((GSRegSIGBLID*)(g_RealGSMem+0x1080))
#define PSM(mem) (vtlb_GetPhyPtr((mem)&0x1fffffff)) //pcsx2 is a competition.The one with most hacks wins :D
#define psHs8(mem) (*(s8 *)&PS2MEM_HW[(mem) & 0xffff])

View File

@ -828,16 +828,14 @@ extern void spu2Irq();
static bool OpenPlugin_CDVD()
{
if( CDVDapi_Plugin.open( NULL ) ) return false;
if( CDVDapi_Plugin.open(NULL) ) return false;
CDVDapi_Plugin.newDiskCB( cdvdNewDiskCB );
return true;
}
static bool OpenPlugin_GS()
{
if( mtgsThread != NULL ) return true;
mtgsOpen(); // mtgsOpen raises its own exception on error
GSsetGameCRC( ElfCRC, 0 );
mtgsThread.Resume();
return true;
}
@ -848,7 +846,7 @@ static bool OpenPlugin_PAD()
static bool OpenPlugin_SPU2()
{
if( SPU2open( (void*)&pDsp ) ) return false;
if( SPU2open((void*)&pDsp) ) return false;
SPU2irqCallback( spu2Irq, spu2DMA4Irq, spu2DMA7Irq );
if( SPU2setDMABaseAddr != NULL ) SPU2setDMABaseAddr((uptr)psxM);
@ -870,7 +868,7 @@ static bool OpenPlugin_USB()
{
usbHandler = NULL;
if( USBopen( (void*)&pDsp ) ) return false;
if( USBopen((void*)&pDsp) ) return false;
USBirqCallback( usbIrq );
usbHandler = USBirqHandler();
if( USBsetRAM != NULL )
@ -880,7 +878,7 @@ static bool OpenPlugin_USB()
static bool OpenPlugin_FW()
{
if( FWopen( (void*)&pDsp ) ) return false;
if( FWopen((void*)&pDsp) ) return false;
FWirqCallback( fwIrq );
return true;
}
@ -916,9 +914,12 @@ void PluginManager::Open()
Console::Status( "Opening plugins..." );
const PluginInfo* pi = tbl_PluginInfo; do {
g_plugins->Open( pi->id );
Open( pi->id );
} while( ++pi, pi->shortname != NULL );
mtgsThread.WaitForOpen();
mtgsThread.PollStatus();
Console::Status( "Plugins opened successfully." );
}
@ -929,11 +930,9 @@ void PluginManager::Close( PluginsEnum_t pid )
if( pid == PluginId_GS )
{
if( mtgsThread == NULL ) return;
// force-close PAD before GS, because the PAD depends on the GS window.
Close( PluginId_PAD );
safe_delete( mtgsThread );
mtgsThread.Suspend();
}
else if( pid == PluginId_CDVD )
DoCDVDclose();
@ -1007,6 +1006,8 @@ void PluginManager::Init()
//
void PluginManager::Shutdown()
{
mtgsThread.Cancel(); // speedier shutdown!
Close();
DbgCon::Status( "Shutting down plugins..." );
@ -1039,10 +1040,10 @@ bool PluginManager::DoFreeze( PluginsEnum_t pid, int mode, freezeData* data )
{
if( (pid == PluginId_GS) && wxThread::IsMain() )
{
MTGS_FreezeData woot = { data, 0 };
// GS needs some thread safety love...
mtgsThread->SendPointerPacket( GS_RINGTYPE_FREEZE, mode, &woot );
mtgsWaitGS();
MTGS_FreezeData woot = { data, 0 };
mtgsThread.Freeze( mode, woot );
return woot.retval != -1;
}
else

View File

@ -18,6 +18,9 @@
#include "App.h"
#include "HostGui.h"
#include "zlib/zlib.h"
using namespace Threading;
static wxScopedPtr< SafeArray<u8> > g_RecoveryState;
@ -39,10 +42,67 @@ namespace StateRecovery {
SysClearExecutionCache();
}
SafeArray<u8> gzSavingBuffer;
class gzThreadClass : public PersistentThread
{
typedef PersistentThread _parent;
protected:
gzFile m_file;
public:
gzThreadClass( const wxString& file ) :
m_file( gzopen( file.ToUTF8().data(), "wb" ) )
{
if( m_file == NULL )
throw Exception::CreateStream( file, "Cannot create savestate file for writing." );
Start();
}
virtual void DoThreadCleanup()
{
gzSavingBuffer.Dispose();
if( m_file != NULL )
{
gzclose( m_file );
m_file = NULL;
}
_parent::DoThreadCleanup();
}
virtual ~gzThreadClass() throw()
{
// fixme: something a little more graceful than Block, perhaps?
Block();
}
protected:
int ExecuteTask()
{
if( (m_file == NULL) || (gzSavingBuffer.GetSizeInBytes() == 0) ) return 0 ;
SetName( "Savestate::gzipper" );
gzwrite( m_file, gzSavingBuffer.GetPtr(), gzSavingBuffer.GetSizeInBytes() );
return 0;
}
};
wxScopedPtr<gzThreadClass> gzThread;
void SaveToFile( const wxString& file )
{
SafeArray<u8> buf;
memSavingState( buf ).FreezeAll();
SysSuspend( false );
gzThread.reset( NULL ); // blocks on any existing gzipping business.
memSavingState( gzSavingBuffer ).FreezeAll();
// start that encoding thread:
gzThread.reset( new gzThreadClass( file ) );
SysResume();
}
// Saves recovery state info to the given saveslot, or saves the active emulation state

View File

@ -112,7 +112,7 @@ void SaveStateBase::FreezeBios()
if( !m_DidBios )
{
if( memcmp( descin, descout, 128 ) != 0 )
if( memcmp( descin, descout.ToAscii().data(), 128 ) != 0 )
{
Console::Error(
"\n\tWarning: BIOS Version Mismatch, savestate may be unstable!\n"
@ -144,8 +144,8 @@ void SaveStateBase::FreezeMainMemory()
void SaveStateBase::FreezeRegisters()
{
if( IsLoading() )
PreLoadPrep();
//if( IsLoading() )
// PreLoadPrep();
// Second Block - Various CPU Registers and States
// -----------------------------------------------
@ -206,7 +206,7 @@ bool SaveStateBase::FreezeSection()
FreezeTag( "BiosVersion" );
Freeze( sectlen );
if( sectlen != MainMemorySizeInBytes )
if( sectlen != 128 )
{
throw Exception::BadSavedState( wxEmptyString,
L"Invalid size encountered on BiosVersion section.",
@ -223,6 +223,7 @@ bool SaveStateBase::FreezeSection()
{
FreezeTag( "MainMemory" );
int seekpos = m_idx+4;
int sectlen = MainMemorySizeInBytes;
Freeze( sectlen );
if( sectlen != MainMemorySizeInBytes )
@ -234,6 +235,8 @@ bool SaveStateBase::FreezeSection()
}
FreezeMainMemory();
int realsectsize = m_idx - seekpos;
wxASSERT( sectlen == realsectsize );
m_sectid++;
}
break;
@ -241,7 +244,7 @@ bool SaveStateBase::FreezeSection()
case FreezeId_Registers:
{
FreezeTag( "HardwareRegisters" );
int seekpos = m_idx;
int seekpos = m_idx+4;
int sectsize;
Freeze( sectsize );
@ -263,13 +266,14 @@ bool SaveStateBase::FreezeSection()
);
}
}
m_sectid++;
}
break;
case FreezeId_Plugin:
{
FreezeTag( "Plugin" );
int seekpos = m_idx;
int seekpos = m_idx+4;
int sectsize;
Freeze( sectsize );
@ -296,34 +300,33 @@ bool SaveStateBase::FreezeSection()
// following increments only affect Saving mode, are ignored by Loading mode.
m_pid++;
if( m_pid > PluginId_Count )
m_sectid++;
if( m_pid >= PluginId_Count )
m_sectid = FreezeId_End;
}
break;
case FreezeId_Unknown:
default:
if( IsSaving() )
m_sectid = FreezeId_End;
else
{
// Skip unknown sections with a warning log.
// Maybe it'll work! (haha?)
wxASSERT( IsSaving() );
int size;
Freeze( m_tagspace );
Freeze( size );
m_tagspace[sizeof(m_tagspace)-1] = 0;
// Skip unknown sections with a warning log.
// Maybe it'll work! (haha?)
Console::Notice(
"Warning: Unknown tag encountered while loading savestate; going to ignore it!\n"
"\tTagname: %s, Size: %d", m_tagspace, size
);
m_idx += size;
}
int size;
Freeze( m_tagspace );
Freeze( size );
m_tagspace[sizeof(m_tagspace)-1] = 0;
Console::Notice(
"Warning: Unknown tag encountered while loading savestate; going to ignore it!\n"
"\tTagname: %s, Size: %d", m_tagspace, size
);
m_idx += size;
break;
}
wxSafeYield( NULL );
return true;
}
@ -332,7 +335,7 @@ void SaveStateBase::FreezeAll()
m_sectid = (int)FreezeId_End+1;
m_pid = PluginId_GS;
while( FreezeSection() ) ;
while( FreezeSection() );
}
//////////////////////////////////////////////////////////////////////////////////

View File

@ -36,13 +36,13 @@ enum FreezeSectionId
{
FreezeId_End,
FreezeId_Memory,
FreezeId_Registers,
// A BIOS tag should always be saved in conjunction with Memory or Registers tags,
// but can be skipped if the savestate has only plugins.
FreezeId_Bios,
FreezeId_Memory,
FreezeId_Registers,
FreezeId_Plugin,
// anything here and beyond we can skip, with a warning
@ -159,9 +159,7 @@ protected:
void psxRcntFreeze();
void sio2Freeze();
// called by gsFreeze automatically.
void mtgsFreeze();
void gifPathFreeze(); // called by gsFreeze
};
// --------------------------------------------------------------------------------------

View File

@ -263,6 +263,20 @@ void SysClearExecutionCache()
void SysLoadState( const wxString& srcfile )
{
SafeArray<u8> buf;
gzFile gzfp = gzopen( srcfile.ToUTF8().data(), "rb" );
if( gzfp == NULL )
throw Exception::BadSavedState( srcfile, "File not found, or permission denied!" );
int curidx = 0;
do
{
buf.MakeRoomFor( curidx+327680 );
gzread( gzfp, buf.GetPtr(curidx), 327680 );
curidx += 327680;
} while( !gzeof(gzfp) );
gzclose( gzfp );
memLoadingState joe( buf ); // this could throw n StateLoadError.
// we perform a full backup to memory first so that we can restore later if the
@ -272,7 +286,8 @@ void SysLoadState( const wxString& srcfile )
SysClearExecutionCache();
cpuReset();
joe.FreezeAll();
//joe.FreezeAll();
StateRecovery::Recover();
}
// Maps a block of memory for use as a recompiled code buffer, and ensures that the

View File

@ -16,10 +16,10 @@
#include "PrecompiledHeader.h"
#include "Common.h"
#include "System.h"
#include "SysThreads.h"
#include "SaveState.h"
#include "Elfheader.h"
#include "Plugins.h"
#include "CoreEmuThread.h"
#include "R5900.h"
#include "R3000A.h"
@ -27,6 +27,248 @@
static __threadlocal SysCoreThread* tls_coreThread = NULL;
// --------------------------------------------------------------------------------------
// SysSuspendableThread *External Thread* Implementations
// (Called form outside the context of this thread)
// --------------------------------------------------------------------------------------
SysSuspendableThread::SysSuspendableThread() :
m_ExecMode( ExecMode_NoThreadYet )
, m_lock_ExecMode()
, m_ResumeEvent()
, m_SuspendEvent()
, m_ResumeProtection( false )
{
}
SysSuspendableThread::~SysSuspendableThread() throw()
{
}
void SysSuspendableThread::Start()
{
if( !DevAssert( m_ExecMode == ExecMode_NoThreadYet, "SysSustainableThread:Start(): Invalid execution mode" ) ) return;
m_ResumeEvent.Reset();
m_SuspendEvent.Reset();
_parent::Start();
}
// Pauses the emulation state at the next PS2 vsync, and returns control to the calling
// thread; or does nothing if the core is already suspended. Calling this thread from the
// Core thread will result in deadlock.
//
// Parameters:
// isNonblocking - if set to true then the function will not block for emulation suspension.
// Defaults to false if parameter is not specified. Performing non-blocking suspension
// is mostly useful for starting certain non-Emu related gui activities (improves gui
// responsiveness).
//
void SysSuspendableThread::Suspend( bool isBlocking )
{
if( IsSelf() || !IsRunning() ) return;
{
ScopedLock locker( m_lock_ExecMode );
if( m_ExecMode == ExecMode_Suspended )
return;
if( m_ExecMode == ExecMode_Running )
m_ExecMode = ExecMode_Suspending;
DevAssert( m_ExecMode == ExecMode_Suspending, "ExecMode should be nothing other than Suspended..." );
}
m_sem_event.Post();
m_SuspendEvent.WaitGui();
}
// Resumes the core execution state, or does nothing is the core is already running. If
// settings were changed, resets will be performed as needed and emulation state resumed from
// memory savestates.
//
// Exceptions (can occur on first call only):
// PluginInitError - thrown if a plugin fails init (init is performed on the current thread
// on the first time the thread is resumed from it's initial idle state)
// ThreadCreationError - Insufficient system resources to create thread.
//
void SysSuspendableThread::Resume()
{
if( IsSelf() ) return;
{
ScopedLock locker( m_lock_ExecMode );
switch( m_ExecMode )
{
case ExecMode_Running: return;
case ExecMode_NoThreadYet:
Start();
m_ExecMode = ExecMode_Suspending;
// fall through...
case ExecMode_Suspending:
// we need to make sure and wait for the emuThread to enter a fully suspended
// state before continuing...
locker.Unlock(); // no deadlocks please, thanks. :)
m_SuspendEvent.WaitGui();
break;
}
}
DevAssert( m_ExecMode == ExecMode_Suspended,
"SysSuspendableThread is not in a suspended/idle state? wtf!" );
m_ExecMode = ExecMode_Running;
m_ResumeProtection = true;
OnResumeReady();
m_ResumeProtection = false;
m_ResumeEvent.Post();
}
// --------------------------------------------------------------------------------------
// SysSuspendableThread *Worker* Implementations
// (Called from the context of this thread only)
// --------------------------------------------------------------------------------------
void SysSuspendableThread::DoThreadCleanup()
{
ScopedLock locker( m_lock_ExecMode );
m_ExecMode = ExecMode_NoThreadYet;
_parent::DoThreadCleanup();
}
void SysSuspendableThread::StateCheck( bool isCancelable )
{
// Shortcut for the common case, to avoid unnecessary Mutex locks:
if( m_ExecMode == ExecMode_Running )
{
if( isCancelable ) pthread_testcancel();
return;
}
// Oh, seems we need a full lock, because something special is happening!
ScopedLock locker( m_lock_ExecMode );
switch( m_ExecMode )
{
#ifdef PCSX2_DEVBUILD // optimize out handlers for these cases in release builds.
case ExecMode_NoThreadYet:
// threads should never have this state set while the thread is in any way
// active or alive. (for obvious reasons!!)
DevAssert( false, "Invalid execution state detected." );
break;
#endif
case ExecMode_Running:
// Yup, need this a second time. Variable state could have changed while we
// were trying to acquire the lock above.
if( isCancelable )
pthread_testcancel();
break;
case ExecMode_Suspending:
{
OnSuspendInThread();
m_ExecMode = ExecMode_Suspended;
m_SuspendEvent.Post();
}
// fall through...
case ExecMode_Suspended:
m_lock_ExecMode.Unlock();
while( m_ExecMode == ExecMode_Suspended )
m_ResumeEvent.WaitGui();
OnResumeInThread();
break;
jNO_DEFAULT;
}
}
// --------------------------------------------------------------------------------------
// EECoreThread *External Thread* Implementations
// (Called form outside the context of this thread)
// --------------------------------------------------------------------------------------
SysCoreThread::SysCoreThread( PluginManager& plugins ) :
m_resetRecompilers( false )
, m_resetProfilers( false )
, m_shortSuspend( false )
, m_plugins( plugins )
{
}
SysCoreThread::~SysCoreThread() throw()
{
_parent::Cancel();
}
void SysCoreThread::Start()
{
m_plugins.Init();
_parent::Start();
}
// Suspends the system without closing plugins or updating GUI status.
// Should be used for savestates or other actions which happen very quickly.
void SysCoreThread::ShortSuspend()
{
m_shortSuspend = true;
Suspend();
m_shortSuspend = false;
}
// Resumes the core execution state, or does nothing is the core is already running. If
// settings were changed, resets will be performed as needed and emulation state resumed from
// memory savestates.
//
// Exceptions (can occur on first call only):
// PluginInitError - thrown if a plugin fails init (init is performed on the current thread
// on the first time the thread is resumed from it's initial idle state)
// ThreadCreationError - Insufficient system resources to create thread.
//
void SysCoreThread::OnResumeReady()
{
if( m_shortSuspend ) return;
if( m_resetRecompilers || m_resetProfilers )
{
SysClearExecutionCache();
m_resetRecompilers = false;
m_resetProfilers = false;
}
}
// Applies a full suite of new settings, which will automatically facilitate the necessary
// resets of the core and components (including plugins, if needed). The scope of resetting
// is determined by comparing the current settings against the new settings.
void SysCoreThread::ApplySettings( const Pcsx2Config& src )
{
if( src == EmuConfig ) return;
const bool resumeWhenDone = !m_ResumeProtection && !IsSuspended();
if( !m_ResumeProtection ) Suspend();
m_resetRecompilers = ( src.Cpu != EmuConfig.Cpu ) || ( src.Gamefixes != EmuConfig.Gamefixes ) || ( src.Speedhacks != EmuConfig.Speedhacks );
m_resetProfilers = (src.Profiler != EmuConfig.Profiler );
const_cast<Pcsx2Config&>(EmuConfig) = src;
if( resumeWhenDone ) Resume();
}
// --------------------------------------------------------------------------------------
// EECoreThread *Worker* Implementations
// (Called from the context of this thread only)
// --------------------------------------------------------------------------------------
SysCoreThread& SysCoreThread::Get()
{
wxASSERT_MSG( tls_coreThread != NULL, L"This function must be called from the context of a running SysCoreThread." );
@ -35,10 +277,8 @@ SysCoreThread& SysCoreThread::Get()
void SysCoreThread::CpuInitializeMess()
{
m_plugins.Open();
cpuReset();
SysClearExecutionCache();
m_plugins.Open();
if( StateRecovery::HasState() )
{
@ -86,8 +326,6 @@ void SysCoreThread::CpuInitializeMess()
loadElfFile( elf_file );
}
}
GSsetGameCRC( ElfCRC, 0 );
}
// special macro which disables inlining on functions that require their own function stackframe.
@ -119,11 +357,7 @@ sptr SysCoreThread::ExecuteTask()
SetName( "EE Core" );
tls_coreThread = this;
while( m_ExecMode != ExecMode_Running )
{
m_ResumeEvent.WaitGui();
}
StateCheck();
CpuInitializeMess();
StateCheck();
CpuExecute();
@ -131,170 +365,22 @@ sptr SysCoreThread::ExecuteTask()
return 0;
}
void SysCoreThread::StateCheck()
void SysCoreThread::OnSuspendInThread()
{
ScopedLock locker( m_lock_ExecMode );
switch( m_ExecMode )
{
case ExecMode_NoThreadYet:
case ExecMode_Idle:
// threads should never have an idle execution state set while the
// thread is in any way active or alive.
DevAssert( false, "Invalid execution state detected." );
break;
case ExecMode_Running:
pthread_testcancel();
break;
case ExecMode_Suspending:
{
m_plugins.Close();
m_ExecMode = ExecMode_Suspended;
m_SuspendEvent.Post();
}
// fall through...
case ExecMode_Suspended:
m_lock_ExecMode.Unlock();
while( m_ExecMode == ExecMode_Suspended )
m_ResumeEvent.WaitGui();
m_plugins.Open();
break;
}
if( !m_shortSuspend )
m_plugins.Close();
}
SysCoreThread::SysCoreThread( PluginManager& plugins ) :
m_ExecMode( ExecMode_NoThreadYet )
, m_lock_ExecMode()
, m_resetRecompilers( false )
, m_resetProfilers( false )
, m_plugins( plugins )
, m_ResumeEvent()
, m_SuspendEvent()
void SysCoreThread::OnResumeInThread()
{
m_plugins.Open();
}
// Invoked by the pthread_exit or pthread_cancel
void SysCoreThread::DoThreadCleanup()
{
m_plugins.Shutdown();
PersistentThread::DoThreadCleanup();
}
SysCoreThread::~SysCoreThread() throw()
{
PersistentThread::Cancel();
}
// Resumes the core execution state, or does nothing is the core is already running. If
// settings were changed, resets will be performed as needed and emulation state resumed from
// memory savestates.
//
// Exceptions (can occur on first call only):
// PluginInitError - thrown if a plugin fails init (init is performed on the current thread
// on the first time the thread is resumed from it's initial idle state)
// ThreadCreationError - Insufficient system resources to create thread.
//
void SysCoreThread::Resume()
{
if( IsSelf() ) return;
{
ScopedLock locker( m_lock_ExecMode );
if( m_ExecMode == ExecMode_NoThreadYet )
{
m_plugins.Init();
Start();
m_ExecMode = ExecMode_Idle;
}
if( m_ExecMode == ExecMode_Running )
return;
if( m_ExecMode == ExecMode_Suspending )
{
// if there are resets to be done, then we need to make sure and wait for the
// emuThread to enter a fully suspended state before continuing...
if( m_resetRecompilers || m_resetProfilers )
{
locker.Unlock(); // no deadlocks please, thanks. :)
m_SuspendEvent.WaitGui();
}
else
{
m_ExecMode = ExecMode_Running;
return;
}
}
}
DevAssert( (m_ExecMode == ExecMode_Suspended) || (m_ExecMode == ExecMode_Idle),
"EmuCoreThread is not in a suspended or idle state? wtf!" );
OnResumeReady();
if( m_resetRecompilers || m_resetProfilers )
{
SysClearExecutionCache();
m_resetRecompilers = false;
m_resetProfilers = false;
}
m_ExecMode = ExecMode_Running;
m_ResumeEvent.Post();
}
// Pauses the emulation state at the next PS2 vsync, and returns control to the calling
// thread; or does nothing if the core is already suspended. Calling this thread from the
// Core thread will result in deadlock.
//
// Parameters:
// isNonblocking - if set to true then the function will not block for emulation suspension.
// Defaults to false if parameter is not specified. Performing non-blocking suspension
// is mostly useful for starting certain non-Emu related gui activities (improves gui
// responsiveness).
//
void SysCoreThread::Suspend( bool isBlocking )
{
if( IsSelf() || !IsRunning() ) return;
{
ScopedLock locker( m_lock_ExecMode );
if( (m_ExecMode == ExecMode_Suspended) || (m_ExecMode == ExecMode_Idle) )
return;
if( m_ExecMode == ExecMode_Running )
m_ExecMode = ExecMode_Suspending;
DevAssert( m_ExecMode == ExecMode_Suspending, "ExecMode should be nothing other than Suspended..." );
}
m_SuspendEvent.WaitGui();
}
// Applies a full suite of new settings, which will automatically facilitate the necessary
// resets of the core and components (including plugins, if needed). The scope of resetting
// is determined by comparing the current settings against the new settings.
void SysCoreThread::ApplySettings( const Pcsx2Config& src )
{
if( src == EmuConfig ) return;
const bool isSuspended = IsSuspended();
Suspend();
m_resetRecompilers = ( src.Cpu != EmuConfig.Cpu ) || ( src.Gamefixes != EmuConfig.Gamefixes ) || ( src.Speedhacks != EmuConfig.Speedhacks );
m_resetProfilers = (src.Profiler != EmuConfig.Profiler );
const_cast<Pcsx2Config&>(EmuConfig) = src;
if( !isSuspended ) Resume();
_parent::DoThreadCleanup();
}

View File

@ -2059,8 +2059,8 @@ void _vuXGKICK(VURegs * VU)
u8* data = ((u8*)VU->Mem + ((VU->VI[_Is_].US[0]*16) & 0x3fff));
u32 size;
size = mtgsThread->PrepDataPacket( GIF_PATH_1, data, (0x4000-((VU->VI[_Is_].US[0]*16) & 0x3fff)) >> 4);
u8* pmem = mtgsThread->GetDataPacketPtr();
size = mtgsThread.PrepDataPacket( GIF_PATH_1, data, (0x4000-((VU->VI[_Is_].US[0]*16) & 0x3fff)) >> 4);
u8* pmem = mtgsThread.GetDataPacketPtr();
if((size << 4) > (u32)(0x4000-((VU->VI[_Is_].US[0]*16) & 0x3fff)))
{
@ -2074,7 +2074,7 @@ void _vuXGKICK(VURegs * VU)
else {
memcpy_aligned(pmem, (u8*)VU->Mem+((VU->VI[_Is_].US[0]*16) & 0x3fff), size<<4);
}
mtgsThread->SendDataPacket();
mtgsThread.SendDataPacket();
}
void _vuXTOP(VURegs * VU) {

View File

@ -1887,12 +1887,12 @@ static int __fastcall Vif1TransDirectHL(u32 *data)
FreezeRegs(1);
// copy 16 bytes the fast way:
const u64* src = (u64*)splittransfer[0];
mtgsThread->PrepDataPacket(GIF_PATH_2, nloop0_packet, 1);
u64* dst = (u64*)mtgsThread->GetDataPacketPtr();
mtgsThread.PrepDataPacket(GIF_PATH_2, nloop0_packet, 1);
u64* dst = (u64*)mtgsThread.GetDataPacketPtr();
dst[0] = src[0];
dst[1] = src[1];
mtgsThread->SendDataPacket();
mtgsThread.SendDataPacket();
FreezeRegs(0);
if (vif1.tag.size == 0) vif1.cmd = 0;
@ -1928,9 +1928,9 @@ static int __fastcall Vif1TransDirectHL(u32 *data)
FreezeRegs(1);
// Round ret up, just in case it's not 128bit aligned.
const uint count = mtgsThread->PrepDataPacket(GIF_PATH_2, data, (ret + 3) >> 2);
memcpy_fast(mtgsThread->GetDataPacketPtr(), data, count << 4);
mtgsThread->SendDataPacket();
const uint count = mtgsThread.PrepDataPacket(GIF_PATH_2, data, (ret + 3) >> 2);
memcpy_fast(mtgsThread.GetDataPacketPtr(), data, count << 4);
mtgsThread.SendDataPacket();
FreezeRegs(0);

View File

@ -219,10 +219,11 @@ typedef HashTools::Dictionary<const GlobalCommandDescriptor*> CommandDictionary;
class AcceleratorDictionary : public HashTools::HashMap<int, const GlobalCommandDescriptor*>
{
typedef HashTools::HashMap<int, const GlobalCommandDescriptor*> _parent;
protected:
public:
typedef HashTools::HashMap<int, const GlobalCommandDescriptor*> _parent;
using _parent::operator[];
AcceleratorDictionary();
@ -438,6 +439,8 @@ protected:
class AppEmuThread : public SysCoreThread
{
typedef SysCoreThread _parent;
protected:
wxKeyEvent m_kevt;
@ -446,7 +449,7 @@ public:
virtual ~AppEmuThread() throw();
virtual void Suspend( bool isBlocking=true );
virtual void StateCheck();
virtual void StateCheck( bool isCancelable=true );
virtual void ApplySettings( const Pcsx2Config& src );
virtual void OnResumeReady();
@ -484,7 +487,7 @@ extern void AppSaveSettings();
extern void AppApplySettings( const AppConfig* oldconf=NULL );
extern void SysStatus( const wxString& text );
extern void SysSuspend();
extern void SysSuspend( bool closePlugins = true );
extern void SysResume();
extern void SysReset();
extern void SysExecute();

View File

@ -77,7 +77,7 @@ AppEmuThread::~AppEmuThread() throw()
void AppEmuThread::Suspend( bool isBlocking )
{
SysCoreThread::Suspend( isBlocking );
_parent::Suspend( isBlocking );
AppInvoke( MainFrame, ApplySettings() );
// Clear the sticky key statuses, because hell knows what'll change while the PAD
@ -90,7 +90,9 @@ void AppEmuThread::Suspend( bool isBlocking )
void AppEmuThread::OnResumeReady()
{
DevAssert( wxThread::IsMain(), "SysCoreThread can only be resumed from the main/gui thread." );
if( !DevAssert( wxThread::IsMain(), "SysCoreThread can only be resumed from the main/gui thread." ) ) return;
if( m_shortSuspend ) return;
ApplySettings( g_Conf->EmuOptions );
@ -109,18 +111,15 @@ static const int pxID_PadHandler_Keydown = 8030;
extern int TranslateGDKtoWXK( u32 keysym );
#endif
void AppEmuThread::StateCheck()
void AppEmuThread::StateCheck( bool isCancelable )
{
SysCoreThread::StateCheck();
_parent::StateCheck( isCancelable );
const keyEvent* ev = PADkeyEvent();
if( ev == NULL || (ev->key == 0) ) return;
GetPluginManager().KeyEvent( *ev );
m_kevt.SetEventType( ( ev->evt == KEYPRESS ) ? wxEVT_KEY_DOWN : wxEVT_KEY_UP );
const bool isDown = (ev->evt == KEYPRESS);
#ifdef __WXMSW__
@ -881,9 +880,12 @@ void SysResume()
AppInvoke( CoreThread, Resume() );
}
void SysSuspend()
void SysSuspend( bool closePlugins )
{
AppInvoke( CoreThread, Suspend() );
if( closePlugins )
AppInvoke( CoreThread, Suspend(closePlugins) );
else
AppInvoke( CoreThread, ShortSuspend() );
}
void SysReset()

View File

@ -141,7 +141,7 @@ namespace Implementations
{
g_Pcsx2Recording ^= 1;
mtgsThread->SendSimplePacket(GS_RINGTYPE_RECORD, g_Pcsx2Recording, 0, 0);
mtgsThread.SendSimplePacket(GS_RINGTYPE_RECORD, g_Pcsx2Recording, 0, 0);
if( SPU2setupRecording != NULL ) SPU2setupRecording(g_Pcsx2Recording, NULL);
}
@ -157,6 +157,10 @@ namespace Implementations
// --------------------------------------------------------------------------------------
// CommandDeclarations table
// --------------------------------------------------------------------------------------
// This is our manualized introspection/reflection table. In a cool language like C# we'd
// have just grabbed this info from enumerating the members of a class and assigning
// properties to each method in the class. But since this is C++, we have to do the the
// goold old fashioned way! :)
static const GlobalCommandDescriptor CommandDeclarations[] =
{

View File

@ -37,6 +37,8 @@ bool States_isSlotUsed(int num)
// returns true if the new state was loaded, or false if nothing happened.
void States_Load( const wxString& file )
{
SysSuspend();
try
{
SysLoadState( file );

View File

@ -15,58 +15,4 @@
#pragma once
#include "Utilities/Threading.h"
using namespace Threading;
// --------------------------------------------------------------------------------------
// SysCoreThread class
// --------------------------------------------------------------------------------------
class SysCoreThread : public PersistentThread
{
protected:
enum ExecutionMode
{
ExecMode_NoThreadYet,
ExecMode_Idle,
ExecMode_Running,
ExecMode_Suspending,
ExecMode_Suspended
};
protected:
volatile ExecutionMode m_ExecMode;
MutexLock m_lock_ExecMode;
bool m_resetRecompilers;
bool m_resetProfilers;
PluginManager& m_plugins;
Semaphore m_ResumeEvent;
Semaphore m_SuspendEvent;
public:
static SysCoreThread& Get();
public:
explicit SysCoreThread( PluginManager& plugins );
virtual ~SysCoreThread() throw();
bool IsSuspended() const { return (m_ExecMode == ExecMode_Suspended); }
virtual void Suspend( bool isBlocking = true );
virtual void Resume();
virtual void ApplySettings( const Pcsx2Config& src );
virtual void StateCheck();
virtual void DoThreadCleanup();
// This function is called by Resume immediately prior to releasing the suspension of
// the core emulation thread. You should overload this rather than Resume(), since
// Resume() has a lot of checks and balances to prevent re-entrance and race conditions.
virtual void OnResumeReady() {};
protected:
void CpuInitializeMess();
void CpuExecute();
virtual sptr ExecuteTask();
};
#include "SysThreads.h"

377
pcsx2/ps2/GIFpath.cpp Normal file
View File

@ -0,0 +1,377 @@
/* PCSX2 - PS2 Emulator for PCs
* Copyright (C) 2002-2009 PCSX2 Dev Team
*
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with PCSX2.
* If not, see <http://www.gnu.org/licenses/>.
*/
#include "PrecompiledHeader.h"
#include "Common.h"
#include "GS.h"
#include "VifDma.h"
// --------------------------------------------------------------------------------------
// GIFpath -- the GIFtag Parser
// --------------------------------------------------------------------------------------
struct GSRegSIGBLID
{
u32 SIGID;
u32 LBLID;
};
enum GIF_FLG
{
GIF_FLG_PACKED = 0,
GIF_FLG_REGLIST = 1,
GIF_FLG_IMAGE = 2,
GIF_FLG_IMAGE2 = 3
};
enum GIF_REG
{
GIF_REG_PRIM = 0x00,
GIF_REG_RGBA = 0x01,
GIF_REG_STQ = 0x02,
GIF_REG_UV = 0x03,
GIF_REG_XYZF2 = 0x04,
GIF_REG_XYZ2 = 0x05,
GIF_REG_TEX0_1 = 0x06,
GIF_REG_TEX0_2 = 0x07,
GIF_REG_CLAMP_1 = 0x08,
GIF_REG_CLAMP_2 = 0x09,
GIF_REG_FOG = 0x0a,
GIF_REG_XYZF3 = 0x0c,
GIF_REG_XYZ3 = 0x0d,
GIF_REG_A_D = 0x0e,
GIF_REG_NOP = 0x0f,
};
// GIFTAG
// Members of this structure are in CAPS to help visually denote that they are representative
// of actual hw register states of the GIF, unlike the internal tracking vars in GIFPath, which
// are modified during the GIFtag unpacking process.
struct GIFTAG
{
u32 NLOOP : 15;
u32 EOP : 1;
u32 dummy0 : 16;
u32 dummy1 : 14;
u32 PRE : 1;
u32 PRIM : 11;
u32 FLG : 2;
u32 NREG : 4;
u32 REGS[2];
GIFTAG() {}
};
// --------------------------------------------------------------------------------------
// GIFPath -- PS2 GIFtag info (one for each path).
// --------------------------------------------------------------------------------------
// fixme: The real PS2 has a single internal PATH and 3 logical sources, not 3 entirely
// separate paths. But for that to work properly we need also interlocked path sources.
// That is, when the GIF selects a source, it sticks to that source until an EOP. Currently
// this is not emulated!
struct GIFPath
{
const GIFTAG tag; // The "original tag -- modification allowed only by SetTag(), so let's make it const.
u8 regs[16]; // positioned after tag ensures 16-bit aligned (in case we SSE optimize later)
u32 nloop; // local copy nloop counts toward zero, and leaves the tag copy unmodified.
u32 curreg; // reg we left of on (for traversing through loops)
u32 numregs; // number of regs (when NREG is 0, numregs is 16)
GIFPath();
void PrepPackedRegs();
void SetTag(const void* mem);
bool StepReg();
u8 GetReg();
int ParseTag(GIF_PATH pathidx, const u8* pMem, u32 size);
};
typedef void (*GIFRegHandler)(const u32* data);
struct GifPathStruct
{
const GIFRegHandler Handlers[3];
GIFPath path[3];
__forceinline GIFPath& operator[]( int idx ) { return path[idx]; }
};
// --------------------------------------------------------------------------------------
// SIGNAL / FINISH / LABEL (WIP!!)
// --------------------------------------------------------------------------------------
// The current implementation for these is very incomplete, especially SIGNAL, which needs
// an extra VM-state status var to be handled correctly.
//
// SIGNAL : This register is a double-throw. If the SIGNAL bit in CSR is clear, set the CSR
// and raise a gsIrq. If CSR is already *set*, then ignore all subsequent drawing operations
// and writes to general purpose registers to the GS. (note: I'm pretty sure this includes
// direct GS and GSreg accesses, as well as those coming through the GIFpath -- but that
// behavior isn't confirmed yet). Privlidged writes are still active.
//
// Ignorance continues until the SIGNAL bit in CSR is manually cleared by the EE. And here's
// the tricky part: the interrupt from the second SIGNAL is still pending, and should be
// raised once the EE has reset the *IMR* mask for SIGNAL -- meaning setting the bit to 1
// (disabled/masked) and then back to 0 (enabled/unmasked).
//
static void RegHandlerSIGNAL(const u32* data)
{
GIF_LOG("MTGS SIGNAL data %x_%x CSRw %x IMR %x CSRr\n",data[0], data[1], CSRw, GSIMR, GSCSRr);
GSSIGLBLID.SIGID = (GSSIGLBLID.SIGID&~data[1])|(data[0]&data[1]);
if ((CSRw & 0x1))
{
if (!(GSIMR&0x100) )
{
gsIrq();
}
GSCSRr |= 1; // signal
}
}
// FINISH : Enables end-of-draw signaling. When FINISH is written it tells the GIF to
// raise a gsIrq and set the FINISH bit of CSR when the current operation is finished.
// As far as I can figure, this feature is meant for EE/GS synchronization when the EE
// wants to utilize GS post-processing effects. We don't need to emulate that part of
// it since we flush/interlock the GS for those specific read operations.
//
// However! We should properly emulate handling partial-DMA transfers on PATH2 and
// PATH3 of the GIF, which means only signaling FINISH if nloop==0.
//
static void RegHandlerFINISH(const u32* data)
{
GIF_LOG("GIFpath FINISH data %x_%x CSRw %x\n", data[0], data[1], CSRw);
if ((CSRw & 0x2))
{
if (!(GSIMR&0x200))
gsIrq();
GSCSRr |= 2; // finish
}
}
static void RegHandlerLABEL(const u32* data)
{
GIF_LOG( "GIFpath LABEL" );
GSSIGLBLID.LBLID = (GSSIGLBLID.LBLID&~data[1])|(data[0]&data[1]);
}
PCSX2_ALIGNED16( static GifPathStruct s_gifPath ) =
{
RegHandlerSIGNAL, RegHandlerFINISH, RegHandlerLABEL
};
// --------------------------------------------------------------------------------------
// GIFPath Method Implementations
// --------------------------------------------------------------------------------------
GIFPath::GIFPath() : tag()
{
memzero_obj( *this );
}
__forceinline bool GIFPath::StepReg()
{
if ((++curreg & 0xf) == tag.NREG) {
curreg = 0;
if (--nloop == 0) {
return false;
}
}
return true;
}
__forceinline u8 GIFPath::GetReg() { return regs[curreg]; }
// unpack the registers - registers are stored as a sequence of 4 bit values in the
// upper 64 bits of the GIFTAG. That sucks for us when handling partialized GIF packets
// coming in from paths 2 and 3, so we unpack them into an 8 bit array here.
//
__forceinline void GIFPath::PrepPackedRegs()
{
// Only unpack registers if we're starting a new pack. Otherwise the unpacked
// array should have already been initialized by a previous partial transfer.
if (curreg != 0) return;
u32 tempreg = tag.REGS[0];
numregs = ((tag.NREG-1)&0xf) + 1;
for (u32 i = 0; i < numregs; i++) {
if (i == 8) tempreg = tag.REGS[1];
regs[i] = tempreg & 0xf;
tempreg >>= 4;
}
}
__forceinline void GIFPath::SetTag(const void* mem)
{
const_cast<GIFTAG&>(tag) = *((GIFTAG*)mem);
nloop = tag.NLOOP;
curreg = 0;
}
void SaveStateBase::gifPathFreeze()
{
FreezeTag( "GIFpath" );
Freeze( s_gifPath.path );
}
static __forceinline void gsHandler(const u8* pMem) {
const int handler = pMem[8];
if (handler >= 0x60 && handler < 0x63) {
//DevCon::Status("GIF Tag Interrupt");
s_gifPath.Handlers[handler&0x3]((const u32*)pMem);
}
}
#define incTag(x, y) do { \
pMem += (x); \
size -= (y); \
if ((pathidx==GIF_PATH_1)&&(pMem>=vuMemEnd)) pMem -= 0x4000; \
} while(false)
#define aMin(x, y) ((x < y) ? (x) : (y))
#define subVal(x, y) ((x > y) ? (x-y) : 0 )
// Parameters:
// size (path1) - difference between the end of VU memory and pMem.
// size (path2/3) - max size of incoming data stream, in qwc (simd128)
__forceinline int GIFPath::ParseTag(GIF_PATH pathidx, const u8* pMem, u32 size)
{
const u8* vuMemEnd = pMem + (size<<4); // End of VU1 Mem
if (pathidx==GIF_PATH_1) size = 0x400; // VU1 mem size
const u32 startSize = size; // Start Size
while (size > 0) {
if (!nloop) {
SetTag(pMem);
incTag(16, 1);
if (pathidx == GIF_PATH_3) {
if (tag.FLG&2) Path3progress = IMAGE_MODE;
else Path3progress = TRANSFER_MODE;
}
}
else {
switch(tag.FLG) {
case GIF_FLG_PACKED:
PrepPackedRegs();
do {
if (GetReg() == 0xe) {
gsHandler(pMem);
}
incTag(16, 1);
} while(StepReg() && size > 0);
break;
case GIF_FLG_REGLIST:
{
size *= 2;
do { incTag(8, 1); }
while(StepReg() && size > 0);
if (size & 1) { incTag(8, 1); }
size /= 2;
}
break;
case GIF_FLG_IMAGE:
case GIF_FLG_IMAGE2:
{
int len = aMin(size, nloop);
incTag((len * 16), len);
nloop -= len;
}
break;
}
}
if (tag.EOP && !nloop) {
if (pathidx != GIF_PATH_2) {
break;
}
}
}
size = (startSize - size);
if (pathidx == GIF_PATH_3) {
if (tag.EOP && !nloop) {
Path3progress = STOPPED_MODE;
}
gif->madr += size * 16;
gif->qwc -= size;
}
return size;
}
// Processes a GIFtag & packet, and throws out some gsIRQs as needed.
// Used to keep interrupts in sync with the EE, while the GS itself
// runs potentially several frames behind.
// Parameters:
// size - max size of incoming data stream, in qwc (simd128)
__forceinline int GIFPath_ParseTag(GIF_PATH pathidx, const u8* pMem, u32 size)
{
#ifdef PCSX2_GSRING_SAMPLING_STATS
static uptr profStartPtr = 0;
static uptr profEndPtr = 0;
if (profStartPtr == 0) {
__asm
{
__beginfunc:
mov profStartPtr, offset __beginfunc;
mov profEndPtr, offset __endfunc;
}
ProfilerRegisterSource( "GSRingBufCopy", (void*)profStartPtr, profEndPtr - profStartPtr );
}
#endif
int retSize = s_gifPath[pathidx].ParseTag(pathidx, pMem, size);
#ifdef PCSX2_GSRING_SAMPLING_STATS
__asm
{
__endfunc:
nop;
}
#endif
return retSize;
}
// Clears all GIFpath data to zero.
void GIFPath_Reset()
{
memzero_obj( s_gifPath.path );
}
// This is a hackfix tool provided for "canceling" the contents of the GIFpath when
// invalid GIFdma states are encountered (tpyically needed for PATH3 only).
__forceinline void GIFPath_Clear( GIF_PATH pathidx )
{
memzero_obj(s_gifPath.path[pathidx]);
if( GSgifSoftReset == NULL ) return;
mtgsThread.SendSimplePacket( GS_RINGTYPE_SOFTRESET, (1<<pathidx), 0, 0 );
}

View File

@ -492,7 +492,7 @@
</File>
</Filter>
<Filter
Name="EmuCore"
Name="System"
>
<File
RelativePath="..\..\Dump.cpp"
@ -534,6 +534,14 @@
RelativePath="..\..\System.cpp"
>
</File>
<File
RelativePath="..\..\System\SysThreads.cpp"
>
</File>
<File
RelativePath="..\..\System\SysThreads.h"
>
</File>
<Filter
Name="ISO"
>
@ -685,10 +693,6 @@
RelativePath="..\..\ps2\BiosTools.cpp"
>
</File>
<File
RelativePath="..\..\ps2\CoreEmuThread.cpp"
>
</File>
<Filter
Name="EmotionEngine"
>
@ -1448,6 +1452,10 @@
<Filter
Name="GS"
>
<File
RelativePath="..\..\ps2\GIFpath.cpp"
>
</File>
<File
RelativePath="..\..\GS.cpp"
>
@ -1884,10 +1892,6 @@
RelativePath="..\..\ps2\BiosTools.h"
>
</File>
<File
RelativePath="..\..\ps2\CoreEmuThread.h"
>
</File>
</Filter>
</Filter>
<Filter
@ -2370,7 +2374,7 @@
<Tool
Name="VCCustomBuildTool"
Description=""
CommandLine="&quot;$(InputDir)\bin2cpp.cmd&quot; $(InputFileName)"
CommandLine="&quot;$(InputDir)\bin2cpp.cmd&quot; $(InputFileName)&#x0D;&#x0A;"
Outputs="&quot;$(InputDir)\$(InputName).h"
/>
</FileConfiguration>
@ -2380,7 +2384,7 @@
<Tool
Name="VCCustomBuildTool"
Description=""
CommandLine="&quot;$(InputDir)\bin2cpp.cmd&quot; $(InputFileName)"
CommandLine="&quot;$(InputDir)\bin2cpp.cmd&quot; $(InputFileName)&#x0D;&#x0A;"
Outputs="&quot;$(InputDir)\$(InputName).h"
/>
</FileConfiguration>
@ -2390,7 +2394,7 @@
<Tool
Name="VCCustomBuildTool"
Description=""
CommandLine="&quot;$(InputDir)\bin2cpp.cmd&quot; $(InputFileName)"
CommandLine="&quot;$(InputDir)\bin2cpp.cmd&quot; $(InputFileName)&#x0D;&#x0A;"
Outputs="&quot;$(InputDir)\$(InputName).h"
/>
</FileConfiguration>

View File

@ -6,7 +6,7 @@
>
<Tool
Name="VCCLCompilerTool"
AdditionalIncludeDirectories="./;../../;../../x86;&quot;../../x86/ix86-32&quot;;../libs;../../IPU"
AdditionalIncludeDirectories="./;../../;../../x86;&quot;../../x86/ix86-32&quot;;../../IPU;../../System"
PreprocessorDefinitions="__i386__;TIXML_USE_STL;_SCL_SECURE_NO_WARNINGS"
RuntimeTypeInfo="false"
PrecompiledHeaderFile="$(IntDir)/pcsx2.pch"

View File

@ -1106,8 +1106,8 @@ void __fastcall mVU_XGKICK_(u32 addr) {
addr &= 0x3ff;
u8* data = microVU1.regs->Mem + (addr*16);
u32 diff = 0x400 - addr;
u32 size = mtgsThread->PrepDataPacket(GIF_PATH_1, data, diff);
u8* pDest = mtgsThread->GetDataPacketPtr();
u32 size = mtgsThread.PrepDataPacket(GIF_PATH_1, data, diff);
u8* pDest = mtgsThread.GetDataPacketPtr();
if (size > diff) {
// fixme: one of these days the following *16's will get cleaned up when we introduce
@ -1121,7 +1121,7 @@ void __fastcall mVU_XGKICK_(u32 addr) {
else {
memcpy_aligned(pDest, microVU1.regs->Mem + (addr*16), size*16);
}
mtgsThread->SendDataPacket();
mtgsThread.SendDataPacket();
}
microVUt(void) mVU_XGKICK_DELAY(mV, bool memVI) {

View File

@ -1974,10 +1974,10 @@ void VU1XGKICK_MTGSTransfer(u32 *pMem, u32 addr)
u32 size;
u8* data = ((u8*)pMem + (addr&0x3fff));
size = mtgsThread->PrepDataPacket(GIF_PATH_1, data, (0x4000-(addr&0x3fff)) / 16);
size = mtgsThread.PrepDataPacket(GIF_PATH_1, data, (0x4000-(addr&0x3fff)) / 16);
jASSUME( size > 0 );
u8* pmem = mtgsThread->GetDataPacketPtr();
u8* pmem = mtgsThread.GetDataPacketPtr();
if((size << 4) > (0x4000-(addr&0x3fff)))
{
@ -1992,6 +1992,6 @@ void VU1XGKICK_MTGSTransfer(u32 *pMem, u32 addr)
memcpy_aligned(pmem, (u8*)pMem+addr, size<<4);
}
mtgsThread->SendDataPacket();
mtgsThread.SendDataPacket();
}
//------------------------------------------------------------------