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 ) : u128( u32 src ) :
lo( src ) lo( src )
, hi( 0 ) {} , hi( 0 ) {}
u128() {}
}; };
struct s128 struct s128
@ -136,6 +138,8 @@ struct s128
s128( s32 src ) : s128( s32 src ) :
lo( src ) lo( src )
, hi( 0 ) {} , hi( 0 ) {}
s128() {}
}; };
#else #else

View File

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

View File

@ -549,9 +549,9 @@ public:
template< class Key, class T > template< class Key, class T >
class HashMap : public google::dense_hash_map<Key, T, CommonHashClass> class HashMap : public google::dense_hash_map<Key, T, CommonHashClass>
{ {
public:
typedef typename google::dense_hash_map<Key, T, CommonHashClass> _parent; typedef typename google::dense_hash_map<Key, T, CommonHashClass> _parent;
public:
using _parent::operator[]; using _parent::operator[];
using _parent::end; using _parent::end;
typedef typename _parent::const_iterator const_iterator; 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. :) // LogicErrors enabled as First-Chance exceptions regardless, so do it now. :)
// //
// Returns: // 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 // 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. // to provide a "stable" escape clause for unexpected behavior.
// //
DEVASSERT_INLINE bool DevAssert( bool condition, const char* msg ) DEVASSERT_INLINE bool DevAssert( bool condition, const char* msg )
{ {
if( condition ) return false; if( condition ) return true;
if( IsDevBuild )
throw Exception::LogicError( msg );
wxASSERT_MSG_A( false, msg ); 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 else
pthread_detach( m_thread ); pthread_detach( m_thread );
m_running = false;
} }
// Blocks execution of the calling thread until this thread completes its task. The // Blocks execution of the calling thread until this thread completes its task. The
@ -137,25 +135,15 @@ namespace Threading
// //
sptr PersistentThread::Block() sptr PersistentThread::Block()
{ {
if( _InterlockedExchange( &m_detached, true ) ) DevAssert( !IsSelf(), "Thread deadlock detected; Block() should never be called by the owner thread." );
{
// 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." );
if( m_running )
#if wxUSE_GUI #if wxUSE_GUI
m_sem_finished.WaitGui(); m_sem_finished.WaitGui();
#else #else
m_sem_finished.Wait(); m_sem_finished.Wait();
#endif #endif
return m_returncode; return m_returncode;
}
} }
bool PersistentThread::IsSelf() const bool PersistentThread::IsSelf() const

View File

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

View File

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

View File

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

View File

@ -67,7 +67,7 @@ void _gs_ChangeTimings( u32 framerate, u32 iTicks )
void gsOnModeChanged( u32 framerate, u32 newTickrate ) 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; static bool gsIsInterlaced = false;
@ -92,9 +92,7 @@ void gsInit()
void gsReset() void gsReset()
{ {
// Sanity check in case the plugin hasn't been initialized... mtgsThread.ResetGS();
if( mtgsThread == NULL ) return;
mtgsThread->Reset();
gsOnModeChanged( gsOnModeChanged(
(gsRegionMode == Region_NTSC) ? FRAMERATE_NTSC : FRAMERATE_PAL, (gsRegionMode == Region_NTSC) ? FRAMERATE_NTSC : FRAMERATE_PAL,
@ -110,36 +108,8 @@ void gsReset()
psHu32(GIF_MODE) = 0; 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() 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_STAT) = 0;
psHu32(GIF_CTRL) = 0; psHu32(GIF_CTRL) = 0;
psHu32(GIF_MODE) = 0; psHu32(GIF_MODE) = 0;
@ -147,33 +117,35 @@ void gsGIFReset()
void gsCSRwrite(u32 value) 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 if (value & 0x200) { // resetGS
// perform a soft reset -- and fall back to doing a full reset if the plugin doesn't // perform a soft reset -- which is a clearing of all GIFpaths -- and fall back to doing
// support soft resets. // 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; 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) GSCSRr = 0x551B4000; // Set the FINISH bit to 1 - GS is always at a finish state as we don't have a FIFO(saqib)
GSIMR = 0x7F00; //This is bits 14-8 thats all that should be 1 GSIMR = 0x7F00; //This is bits 14-8 thats all that should be 1
} }
else if( value & 0x100 ) // FLUSH 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); //Console::WriteLn("GS_CSR FLUSH GS fifo: %x (CSRr=%x)", value, GSCSRr);
} }
else else
{ {
CSRw |= value & 0x1f; 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); GSCSRr = ((GSCSRr&~value)&0x1f)|(GSCSRr&~0x1f);
} }
@ -204,7 +176,7 @@ __forceinline void gsWrite8(u32 mem, u8 value)
gsCSRwrite((CSRw & ~0xff000000) | (value << 24)); break; gsCSRwrite((CSRw & ~0xff000000) | (value << 24)); break;
default: default:
*PS2GS_BASE(mem) = value; *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); 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; *(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; *(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]); GIF_LOG("GS Write64 at %8.8lx with data %8.8x_%8.8x", mem, srcval32[1], srcval32[0]);
*(u64*)PS2GS_BASE(mem) = *value; *(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[0] = value[0];
writeTo[1] = value[1]; writeTo[1] = value[1];
mtgsThread->SendSimplePacket(GS_RINGTYPE_MEMWRITE64, masked_mem, srcval32[0], srcval32[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+8, srcval32[2], srcval32[3]);
} }
__forceinline u8 gsRead8(u32 mem) __forceinline u8 gsRead8(u32 mem)
@ -393,7 +365,7 @@ void gsSyncLimiterLostTime( s32 deltaTime )
//Console::WriteLn("LostTime on the EE!"); //Console::WriteLn("LostTime on the EE!");
mtgsThread->SendSimplePacket( mtgsThread.SendSimplePacket(
GS_RINGTYPE_STARTTIME, GS_RINGTYPE_STARTTIME,
deltaTime, deltaTime,
0, 0,
@ -537,7 +509,7 @@ __forceinline void gsFrameSkip( bool forceskip )
void gsPostVsyncEnd( bool updategs ) void gsPostVsyncEnd( bool updategs )
{ {
*(u32*)(PS2MEM_GS+0x1000) ^= 0x2000; // swap the vsync field *(u32*)(PS2MEM_GS+0x1000) ^= 0x2000; // swap the vsync field
mtgsThread->PostVsyncEnd( updategs ); mtgsThread.PostVsyncEnd( updategs );
} }
void _gs_ResetFrameskip() void _gs_ResetFrameskip()
@ -548,7 +520,7 @@ void _gs_ResetFrameskip()
// Disables the GS Frameskip at runtime without any racy mess... // Disables the GS Frameskip at runtime without any racy mess...
void gsResetFrameSkip() void gsResetFrameSkip()
{ {
mtgsThread->SendSimplePacket(GS_RINGTYPE_FRAMESKIP, 0, 0, 0); mtgsThread.SendSimplePacket(GS_RINGTYPE_FRAMESKIP, 0, 0, 0);
} }
void gsDynamicSkipEnable() void gsDynamicSkipEnable()
@ -564,5 +536,5 @@ void SaveStateBase::gsFreeze()
{ {
FreezeMem(PS2MEM_GS, 0x2000); FreezeMem(PS2MEM_GS, 0x2000);
Freeze(CSRw); Freeze(CSRw);
mtgsFreeze(); gifPathFreeze();
} }

View File

@ -16,12 +16,16 @@
#pragma once #pragma once
#include "Common.h" #include "Common.h"
#include "Utilities/Threading.h" #include "SysThreads.h"
PCSX2_ALIGNED16( extern u8 g_RealGSMem[0x2000] ); PCSX2_ALIGNED16( extern u8 g_RealGSMem[Ps2MemSize::GSregs] );
#define GSCSRr *((u64*)(g_RealGSMem+0x1000))
#define GSIMR *((u32*)(g_RealGSMem+0x1010)) #define PS2MEM_GS g_RealGSMem
#define GSSIGLBLID ((GSRegSIGBLID*)(g_RealGSMem+0x1080)) #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 enum GS_RegionMode
{ {
@ -29,105 +33,6 @@ enum GS_RegionMode
Region_PAL 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 enum GIF_PATH
{ {
GIF_PATH_1 = 0, GIF_PATH_1 = 0,
@ -135,6 +40,18 @@ enum GIF_PATH
GIF_PATH_3, 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 enum GS_RINGTYPE
{ {
@ -154,8 +71,8 @@ enum GS_RINGTYPE
, GS_RINGTYPE_SOFTRESET // issues a soft reset for the GIF , GS_RINGTYPE_SOFTRESET // issues a soft reset for the GIF
, GS_RINGTYPE_WRITECSR , GS_RINGTYPE_WRITECSR
, GS_RINGTYPE_MODECHANGE // for issued mode changes. , GS_RINGTYPE_MODECHANGE // for issued mode changes.
, GS_RINGTYPE_CRC
, GS_RINGTYPE_STARTTIME // special case for min==max fps frameskip settings , 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() s32 retval; // value returned from the call, valid only after an mtgsWaitGS()
}; };
class mtgsThreadObject : public Threading::PersistentThread class mtgsThreadObject : public SysSuspendableThread
{ {
friend class SaveStateBase; typedef SysSuspendableThread _parent;
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;
protected: protected:
// note: when g_pGSRingPos == g_pGSWritePos, the fifo is empty // note: when g_pGSRingPos == g_pGSWritePos, the fifo is empty
uint m_RingPos; // cur pos gs is reading from uint m_RingPos; // cur pos gs is reading from
uint m_WritePos; // cur pos ee thread is writing to uint m_WritePos; // cur pos ee thread is writing to
// used to regulate thread startup and gsInit Semaphore m_sem_OpenDone;
Threading::Semaphore m_sem_InitDone; MutexLock m_lock_RingRestart;
Threading::MutexLock m_lock_RingRestart;
// used to keep multiple threads from sending packets to the ringbuffer concurrently. // 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 // 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. // has more than one command in it when the thread is kicked.
int m_CopyCommandTally; int m_CopyCommandTally;
int m_CopyDataTally; 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 // 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 // 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 // Protection lock for the frame queue counter -- needed because we can't safely
// AtomicExchange from two threads. // AtomicExchange from two threads.
Threading::MutexLock m_lock_FrameQueueCounter; MutexLock m_lock_FrameQueueCounter;
// These vars maintain instance data for sending Data Packets. // These vars maintain instance data for sending Data Packets.
// Only one data packet can be constructed and uploaded at a time. // Only one data packet can be constructed and uploaded at a time.
@ -221,40 +123,39 @@ protected:
Threading::MutexLock m_lock_Stack; Threading::MutexLock m_lock_Stack;
#endif #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: public:
mtgsThreadObject(); mtgsThreadObject();
virtual ~mtgsThreadObject() throw(); virtual ~mtgsThreadObject() throw();
void Start(); void Start();
void Cancel(); void PollStatus();
void Reset();
void GIFSoftReset( int mask );
// Waits for the GS to empty out the entire ring buffer contents. // Waits for the GS to empty out the entire ring buffer contents.
// Used primarily for plugin startup/shutdown. // Used primarily for plugin startup/shutdown.
void WaitGS(); void WaitGS();
void ResetGS();
int PrepDataPacket( GIF_PATH pathidx, const u8* srcdata, u32 size ); int PrepDataPacket( GIF_PATH pathidx, const u8* srcdata, u32 size );
int PrepDataPacket( GIF_PATH pathidx, const u32* srcdata, u32 size ); int PrepDataPacket( GIF_PATH pathidx, const u32* srcdata, u32 size );
void SendDataPacket(); 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 SendSimplePacket( GS_RINGTYPE type, int data0, int data1, int data2 );
void SendPointerPacket( GS_RINGTYPE type, u32 data0, void* data1 ); void SendPointerPacket( GS_RINGTYPE type, u32 data0, void* data1 );
u8* GetDataPacketPtr() const; u8* GetDataPacketPtr() const;
void Freeze( SaveStateBase& state );
void SetEvent(); void SetEvent();
void PostVsyncEnd( bool updategs ); void PostVsyncEnd( bool updategs );
protected: protected:
void OpenPlugin();
void OnSuspendInThread();
void OnResumeInThread();
void OnResumeReady();
// Saves MMX/XMM REGS, posts an event to the mtgsThread flag and releases a timeslice. // 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. // For use in surrounding loops that wait on the mtgs.
void PrepEventWait(); void PrepEventWait();
@ -262,23 +163,15 @@ protected:
// Restores MMX/XMM REGS. For use in surrounding loops that wait on the mtgs. // Restores MMX/XMM REGS. For use in surrounding loops that wait on the mtgs.
void PostEventWait() const; 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 // Used internally by SendSimplePacket type functions
uint _PrepForSimplePacket(); uint _PrepForSimplePacket();
void _FinishSimplePacket( uint future_writepos ); void _FinishSimplePacket( uint future_writepos );
void _RingbufferLoop();
sptr ExecuteTask(); sptr ExecuteTask();
}; };
extern mtgsThreadObject* mtgsThread; PCSX2_ALIGNED16_EXTERN( mtgsThreadObject mtgsThread );
void mtgsWaitGS(); void mtgsWaitGS();
void mtgsOpen();
///////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////
// Generalized GS Functions and Stuff // Generalized GS Functions and Stuff
@ -301,7 +194,6 @@ extern void _gs_ChangeTimings( u32 framerate, u32 iTicks );
// used for resetting GIF fifo // used for resetting GIF fifo
bool gsGIFSoftReset( int mask );
void gsGIFReset(); void gsGIFReset();
void gsCSRwrite(u32 value); 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; psHu32(GIF_STAT) |= GIF_STAT_APATH3 | GIF_STAT_OPH;
int size = mtgsThread->PrepDataPacket(GIF_PATH_3, pMem, qwc); int size = mtgsThread.PrepDataPacket(GIF_PATH_3, pMem, qwc);
u8* pgsmem = mtgsThread->GetDataPacketPtr(); u8* pgsmem = mtgsThread.GetDataPacketPtr();
memcpy_aligned(pgsmem, pMem, size<<4); memcpy_aligned(pgsmem, pMem, size<<4);
mtgsThread->SendDataPacket(); mtgsThread.SendDataPacket();
return size; return size;
} }
@ -116,7 +116,7 @@ int _GIFchain()
if (pMem == NULL) if (pMem == NULL)
{ {
// reset path3, fixes dark cloud 2 // reset path3, fixes dark cloud 2
gsGIFSoftReset(4); GIFPath_Clear( GIF_PATH_3 );
//must increment madr and clear qwc, else it loops //must increment madr and clear qwc, else it loops
gif->madr += gif->qwc * 16; gif->madr += gif->qwc * 16;

View File

@ -13,21 +13,19 @@
* If not, see <http://www.gnu.org/licenses/>. * If not, see <http://www.gnu.org/licenses/>.
*/ */
#include "PrecompiledHeader.h" #include "PrecompiledHeader.h"
#include <list>
#include <wx/datetime.h>
#include "Common.h" #include "Common.h"
#include "VU.h"
#include "GS.h" #include "GS.h"
#include "VU.h"
#include "iR5900.h" #include "iR5900.h"
#include "VifDma.h" #include "VifDma.h"
#include "SamplProf.h" #include "SamplProf.h"
#include <list>
#include <wx/datetime.h>
// Uncomment this to enable profiling of the GS RingBufferCopy function. // Uncomment this to enable profiling of the GS RingBufferCopy function.
//#define PCSX2_GSRING_SAMPLING_STATS //#define PCSX2_GSRING_SAMPLING_STATS
@ -46,152 +44,60 @@ using namespace std;
#define volatize(x) (*reinterpret_cast<volatile uint*>(&(x))) #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 // 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 #ifdef RINGBUF_DEBUG_STACK
#include <list> #include <list>
std::list<uint> ringposStack; std::list<uint> ringposStack;
#endif #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() : mtgsThreadObject::mtgsThreadObject() :
PersistentThread() SysSuspendableThread()
, m_RingPos( 0 ) , m_RingPos( 0 )
, m_WritePos( 0 ) , m_WritePos( 0 )
, m_sem_InitDone()
, m_lock_RingRestart() , m_lock_RingRestart()
, m_PacketLocker( true ) // true - makes it a recursive lock , m_PacketLocker( true ) // true - makes it a recursive lock
, m_CopyCommandTally( 0 ) , m_CopyCommandTally( 0 )
, m_CopyDataTally( 0 ) , m_CopyDataTally( 0 )
, m_RingBufferIsBusy( 0 ) , m_RingBufferIsBusy( false )
, m_LoadState( false )
, m_QueuedFrames( 0 ) , m_QueuedFrames( 0 )
, m_lock_FrameQueueCounter() , m_lock_FrameQueueCounter()
, m_packet_size( 0 ) , m_packet_size( 0 )
@ -200,41 +106,47 @@ mtgsThreadObject::mtgsThreadObject() :
#ifdef RINGBUF_DEBUG_STACK #ifdef RINGBUF_DEBUG_STACK
, m_lock_Stack() , m_lock_Stack()
#endif #endif
, m_RingBuffer( m_RingBufferSize + (Ps2MemSize::GSregs/sizeof(u128)) )
, m_gsMem( (u8*)m_RingBuffer.GetPtr( m_RingBufferSize ) )
{ {
} }
void mtgsThreadObject::Start() void mtgsThreadObject::Start()
{ {
m_sem_InitDone.Reset(); m_returncode = 0;
PersistentThread::Start(); m_RingPos = 0;
m_WritePos = 0;
// Wait for the thread to finish initialization (it runs GSopen, which can take m_RingBufferIsBusy = false;
// some time since it's creating a new window and all), and then check for errors. 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::~mtgsThreadObject() throw()
{ {
mtgsThreadObject::Cancel();
} }
// Closes the GS "forcefully" without waiting for it to finish rendering it's pending void mtgsThreadObject::OnResumeReady()
// queue of GS data.
void mtgsThreadObject::Cancel()
{ {
//SendSimplePacket( GS_RINGTYPE_QUIT, 0, 0, 0 ); m_sem_OpenDone.Reset();
//SetEvent();
//m_sem_finished.WaitGui();
PersistentThread::Cancel();
} }
void mtgsThreadObject::Reset() void mtgsThreadObject::ResetGS()
{ {
// MTGS Reset process: // MTGS Reset process:
// * clear the ringbuffer. // * clear the ringbuffer.
@ -246,133 +158,9 @@ void mtgsThreadObject::Reset()
MTGS_LOG( "MTGS: Sending Reset..." ); MTGS_LOG( "MTGS: Sending Reset..." );
SendSimplePacket( GS_RINGTYPE_RESET, 0, 0, 0 ); SendSimplePacket( GS_RINGTYPE_RESET, 0, 0, 0 );
SendSimplePacket( GS_RINGTYPE_FRAMESKIP, 0, 0, 0 ); SendSimplePacket( GS_RINGTYPE_FRAMESKIP, 0, 0, 0 );
SetEvent();
memzero_obj( s_path ); GIFPath_Reset();
}
#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;
} }
void mtgsThreadObject::PostVsyncEnd( bool updategs ) void mtgsThreadObject::PostVsyncEnd( bool updategs )
@ -410,29 +198,74 @@ struct PacketTagType
}; };
extern bool renderswitch; extern bool renderswitch;
static volatile long gsIsOpened = 0;
static void _clean_close_gs( void* obj ) 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(); 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 ); pthread_cleanup_push( _clean_close_gs, this );
while( true ) 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 // note: m_RingPos is intentionally not volatile, because it should only
// ever be modified by this thread. // ever be modified by this thread.
while( m_RingPos != volatize(m_WritePos)) 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; u32 ringposinc = 1;
#ifdef RINGBUF_DEBUG_STACK #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. // stall for a bit to let the MainThread have time to update the g_pGSWritePos.
m_lock_RingRestart.Lock(); m_lock_RingRestart.Lock();
m_lock_RingRestart.Unlock(); m_lock_RingRestart.Unlock();
StateCheck( false ); // disable cancel since the above locks are cancelable already
continue; continue;
case GS_RINGTYPE_P1: case GS_RINGTYPE_P1:
{ {
const int qsize = tag.data[0]; 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 // make sure that tag>>16 is the MAX size readable
GSgifTransfer1((u32*)(data - 0x400 + qsize), 0x4000-qsize*16); GSgifTransfer1((u32*)(data - 0x400 + qsize), 0x4000-qsize*16);
@ -475,7 +310,7 @@ void mtgsThreadObject::_RingbufferLoop()
case GS_RINGTYPE_P2: case GS_RINGTYPE_P2:
{ {
const int qsize = tag.data[0]; 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); GSgifTransfer2((u32*)data, qsize);
ringposinc += qsize; ringposinc += qsize;
} }
@ -484,7 +319,7 @@ void mtgsThreadObject::_RingbufferLoop()
case GS_RINGTYPE_P3: case GS_RINGTYPE_P3:
{ {
const int qsize = tag.data[0]; 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); GSgifTransfer3((u32*)data, qsize);
ringposinc += qsize; ringposinc += qsize;
} }
@ -514,16 +349,16 @@ void mtgsThreadObject::_RingbufferLoop()
break; break;
case GS_RINGTYPE_MEMWRITE8: case GS_RINGTYPE_MEMWRITE8:
m_gsMem[tag.data[0]] = (u8)tag.data[1]; RingBuffer.Regs[tag.data[0]] = (u8)tag.data[1];
break; break;
case GS_RINGTYPE_MEMWRITE16: 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; break;
case GS_RINGTYPE_MEMWRITE32: case GS_RINGTYPE_MEMWRITE32:
*(u32*)(m_gsMem+tag.data[0]) = tag.data[1]; *(u32*)(RingBuffer.Regs+tag.data[0]) = tag.data[1];
break; break;
case GS_RINGTYPE_MEMWRITE64: 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; break;
case GS_RINGTYPE_FREEZE: case GS_RINGTYPE_FREEZE:
@ -562,16 +397,15 @@ void mtgsThreadObject::_RingbufferLoop()
case GS_RINGTYPE_MODECHANGE: case GS_RINGTYPE_MODECHANGE:
_gs_ChangeTimings( tag.data[0], tag.data[1] ); _gs_ChangeTimings( tag.data[0], tag.data[1] );
break; break;
case GS_RINGTYPE_CRC:
GSsetGameCRC( tag.data[0], 0 );
break;
case GS_RINGTYPE_STARTTIME: case GS_RINGTYPE_STARTTIME:
m_iSlowStart += tag.data[0]; m_iSlowStart += tag.data[0];
break; 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 #ifdef PCSX2_DEVBUILD
default: default:
Console::Error("GSThreadProc, bad packet (%x) at m_RingPos: %x, m_WritePos: %x", tag.command, m_RingPos, m_WritePos); 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; uint newringpos = m_RingPos + ringposinc;
wxASSERT( newringpos <= m_RingBufferSize ); wxASSERT( newringpos <= RingBufferSize );
newringpos &= m_RingBufferMask; newringpos &= RingBufferMask;
AtomicExchange( m_RingPos, newringpos ); AtomicExchange( m_RingPos, newringpos );
} }
AtomicExchange( m_RingBufferIsBusy, 0 ); m_RingBufferIsBusy = false;
} }
pthread_cleanup_pop( true ); 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; 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. // Waits for the GS to empty out the entire ring buffer contents.
// Used primarily for plugin startup/shutdown. // Used primarily for plugin startup/shutdown.
void mtgsThreadObject::WaitGS() 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(); SetEvent();
while( volatize(m_RingPos) != volatize(m_WritePos) ) while( volatize(m_RingPos) != volatize(m_WritePos) ) Timeslice();
{
Timeslice();
//SpinWait();
}
} }
// Sets the gsEvent flag and releases a timeslice. // Sets the gsEvent flag and releases a timeslice.
@ -665,7 +476,7 @@ void mtgsThreadObject::PostEventWait() const
u8* mtgsThreadObject::GetDataPacketPtr() 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). // 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 ); jASSUME( m_packet_size != 0 );
uint temp = m_packet_ringpos + m_packet_size; uint temp = m_packet_ringpos + m_packet_size;
jASSUME( temp <= m_RingBufferSize ); jASSUME( temp <= RingBufferSize );
temp &= m_RingBufferMask; temp &= RingBufferMask;
if( IsDebugBuild ) if( IsDebugBuild )
{ {
if( m_packet_ringpos + m_packet_size < m_RingBufferSize ) if( m_packet_ringpos + m_packet_size < RingBufferSize )
{ {
uint readpos = volatize(m_RingPos); uint readpos = volatize(m_RingPos);
if( readpos != m_WritePos ) if( readpos != m_WritePos )
@ -742,10 +553,6 @@ static u32 ringtx_inf[32][32];
static u32 ringtx_inf_s[32]; static u32 ringtx_inf_s[32];
#endif #endif
#ifdef PCSX2_GSRING_SAMPLING_STATS
static u32 GSRingBufCopySz = 0;
#endif
// returns the amount of giftag data not processed (in simd128 values). // 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 // Return value is used by VU1 XGKICK to hack-fix data packets which are too
// large for VU1 memory. // large for VU1 memory.
@ -810,13 +617,13 @@ int mtgsThreadObject::PrepDataPacket( GIF_PATH pathidx, const u8* srcdata, u32 s
jASSUME( m_packet_size == 0 ); jASSUME( m_packet_size == 0 );
// Sanity checks! (within the confines of our ringbuffer please!) // Sanity checks! (within the confines of our ringbuffer please!)
jASSUME( size < m_RingBufferSize ); jASSUME( size < RingBufferSize );
jASSUME( writepos < m_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. size = m_packet_size + 1; // takes into account our command qword.
if( writepos + size < m_RingBufferSize ) if( writepos + size < RingBufferSize )
{ {
// generic gs wait/stall. // generic gs wait/stall.
// if the writepos is past the readpos then we're safe. // 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(); PostEventWait();
} }
} }
else if( writepos + size > m_RingBufferSize ) else if( writepos + size > RingBufferSize )
{ {
// If the incoming packet doesn't fit, then start over from // If the incoming packet doesn't fit, then start over from
// the start of the ring buffer (it's a lot easier than trying // 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 // Command qword: Low word is the command, and the high word is the packet
// length in SIMDs (128 bits). // length in SIMDs (128 bits).
PacketTagType& tag = (PacketTagType&)m_RingBuffer[m_WritePos]; PacketTagType& tag = (PacketTagType&)RingBuffer[m_WritePos];
tag.command = pathidx+1; tag.command = pathidx+1;
tag.data[0] = m_packet_size; tag.data[0] = m_packet_size;
m_packet_ringpos = m_WritePos + 1; m_packet_ringpos = m_WritePos + 1;
@ -935,9 +742,9 @@ __forceinline uint mtgsThreadObject::_PrepForSimplePacket()
#endif #endif
uint future_writepos = m_WritePos+1; 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) ) if( future_writepos == volatize(m_RingPos) )
{ {
@ -963,7 +770,7 @@ void mtgsThreadObject::SendSimplePacket( GS_RINGTYPE type, int data0, int data1,
//ScopedLock locker( m_PacketLocker ); //ScopedLock locker( m_PacketLocker );
const uint thefuture = _PrepForSimplePacket(); const uint thefuture = _PrepForSimplePacket();
PacketTagType& tag = (PacketTagType&)m_RingBuffer[m_WritePos]; PacketTagType& tag = (PacketTagType&)RingBuffer[m_WritePos];
tag.command = type; tag.command = type;
tag.data[0] = data0; tag.data[0] = data0;
@ -978,7 +785,7 @@ void mtgsThreadObject::SendPointerPacket( GS_RINGTYPE type, u32 data0, void* dat
//ScopedLock locker( m_PacketLocker ); //ScopedLock locker( m_PacketLocker );
const uint thefuture = _PrepForSimplePacket(); const uint thefuture = _PrepForSimplePacket();
PacketTagType& tag = (PacketTagType&)m_RingBuffer[m_WritePos]; PacketTagType& tag = (PacketTagType&)RingBuffer[m_WritePos];
tag.command = type; tag.command = type;
tag.data[0] = data0; tag.data[0] = data0;
@ -987,60 +794,39 @@ void mtgsThreadObject::SendPointerPacket( GS_RINGTYPE type, u32 data0, void* dat
_FinishSimplePacket( thefuture ); _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. // Waits for the GS to empty out the entire ring buffer contents.
// Used primarily for plugin startup/shutdown. // Used primarily for plugin startup/shutdown.
void mtgsWaitGS() 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_EROM psER
#define PS2MEM_SCRATCH psS #define PS2MEM_SCRATCH psS
#define PS2MEM_GS g_RealGSMem
#define PS2GS_BASE(mem) (g_RealGSMem+(mem&0x13ff))
// Various useful locations // Various useful locations
#define spr0 ((DMACh*)&PS2MEM_HW[0xD000]) #define spr0 ((DMACh*)&PS2MEM_HW[0xD000])
#define spr1 ((DMACh*)&PS2MEM_HW[0xD400]) #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 ipu0dma ((DMACh *)&PS2MEM_HW[0xb000])
#define ipu1dma ((DMACh *)&PS2MEM_HW[0xb400]) #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 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]) #define psHs8(mem) (*(s8 *)&PS2MEM_HW[(mem) & 0xffff])

View File

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

View File

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

View File

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

View File

@ -263,6 +263,20 @@ void SysClearExecutionCache()
void SysLoadState( const wxString& srcfile ) void SysLoadState( const wxString& srcfile )
{ {
SafeArray<u8> buf; 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. memLoadingState joe( buf ); // this could throw n StateLoadError.
// we perform a full backup to memory first so that we can restore later if the // 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(); SysClearExecutionCache();
cpuReset(); cpuReset();
joe.FreezeAll(); //joe.FreezeAll();
StateRecovery::Recover();
} }
// Maps a block of memory for use as a recompiled code buffer, and ensures that the // 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 "PrecompiledHeader.h"
#include "Common.h" #include "Common.h"
#include "System.h" #include "System.h"
#include "SysThreads.h"
#include "SaveState.h" #include "SaveState.h"
#include "Elfheader.h" #include "Elfheader.h"
#include "Plugins.h" #include "Plugins.h"
#include "CoreEmuThread.h"
#include "R5900.h" #include "R5900.h"
#include "R3000A.h" #include "R3000A.h"
@ -27,6 +27,248 @@
static __threadlocal SysCoreThread* tls_coreThread = NULL; 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() SysCoreThread& SysCoreThread::Get()
{ {
wxASSERT_MSG( tls_coreThread != NULL, L"This function must be called from the context of a running SysCoreThread." ); 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() void SysCoreThread::CpuInitializeMess()
{ {
m_plugins.Open();
cpuReset(); cpuReset();
SysClearExecutionCache(); SysClearExecutionCache();
m_plugins.Open();
if( StateRecovery::HasState() ) if( StateRecovery::HasState() )
{ {
@ -86,8 +326,6 @@ void SysCoreThread::CpuInitializeMess()
loadElfFile( elf_file ); loadElfFile( elf_file );
} }
} }
GSsetGameCRC( ElfCRC, 0 );
} }
// special macro which disables inlining on functions that require their own function stackframe. // special macro which disables inlining on functions that require their own function stackframe.
@ -119,11 +357,7 @@ sptr SysCoreThread::ExecuteTask()
SetName( "EE Core" ); SetName( "EE Core" );
tls_coreThread = this; tls_coreThread = this;
while( m_ExecMode != ExecMode_Running ) StateCheck();
{
m_ResumeEvent.WaitGui();
}
CpuInitializeMess(); CpuInitializeMess();
StateCheck(); StateCheck();
CpuExecute(); CpuExecute();
@ -131,170 +365,22 @@ sptr SysCoreThread::ExecuteTask()
return 0; return 0;
} }
void SysCoreThread::StateCheck() void SysCoreThread::OnSuspendInThread()
{ {
ScopedLock locker( m_lock_ExecMode ); if( !m_shortSuspend )
m_plugins.Close();
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;
}
} }
SysCoreThread::SysCoreThread( PluginManager& plugins ) : void SysCoreThread::OnResumeInThread()
m_ExecMode( ExecMode_NoThreadYet )
, m_lock_ExecMode()
, m_resetRecompilers( false )
, m_resetProfilers( false )
, m_plugins( plugins )
, m_ResumeEvent()
, m_SuspendEvent()
{ {
m_plugins.Open();
} }
// Invoked by the pthread_exit or pthread_cancel // Invoked by the pthread_exit or pthread_cancel
void SysCoreThread::DoThreadCleanup() void SysCoreThread::DoThreadCleanup()
{ {
m_plugins.Shutdown(); m_plugins.Shutdown();
PersistentThread::DoThreadCleanup(); _parent::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();
} }

View File

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

View File

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

View File

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

View File

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

View File

@ -141,7 +141,7 @@ namespace Implementations
{ {
g_Pcsx2Recording ^= 1; 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); if( SPU2setupRecording != NULL ) SPU2setupRecording(g_Pcsx2Recording, NULL);
} }
@ -157,6 +157,10 @@ namespace Implementations
// -------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------
// CommandDeclarations table // 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[] = 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. // returns true if the new state was loaded, or false if nothing happened.
void States_Load( const wxString& file ) void States_Load( const wxString& file )
{ {
SysSuspend();
try try
{ {
SysLoadState( file ); SysLoadState( file );

View File

@ -15,58 +15,4 @@
#pragma once #pragma once
#include "Utilities/Threading.h" #include "SysThreads.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();
};

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

View File

@ -6,7 +6,7 @@
> >
<Tool <Tool
Name="VCCLCompilerTool" 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" PreprocessorDefinitions="__i386__;TIXML_USE_STL;_SCL_SECURE_NO_WARNINGS"
RuntimeTypeInfo="false" RuntimeTypeInfo="false"
PrecompiledHeaderFile="$(IntDir)/pcsx2.pch" PrecompiledHeaderFile="$(IntDir)/pcsx2.pch"

View File

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

View File

@ -1974,10 +1974,10 @@ void VU1XGKICK_MTGSTransfer(u32 *pMem, u32 addr)
u32 size; u32 size;
u8* data = ((u8*)pMem + (addr&0x3fff)); 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 ); jASSUME( size > 0 );
u8* pmem = mtgsThread->GetDataPacketPtr(); u8* pmem = mtgsThread.GetDataPacketPtr();
if((size << 4) > (0x4000-(addr&0x3fff))) 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); memcpy_aligned(pmem, (u8*)pMem+addr, size<<4);
} }
mtgsThread->SendDataPacket(); mtgsThread.SendDataPacket();
} }
//------------------------------------------------------------------ //------------------------------------------------------------------