From 61e983b69537a5dcce74cd7bfa000992936f4661 Mon Sep 17 00:00:00 2001 From: "Jake.Stine" Date: Thu, 4 Dec 2008 07:03:05 +0000 Subject: [PATCH] Improved frameskipper when running under MTGS mode, by moving it from the EE thread to the MTGS thread. Also got rid of some highly unnecessary recompiler code in iGS.cpp. Register writes to SMODE1/SMODE2 now fall back on the new modeset code in GS.cpp. (that code is entirely *not* speed critical, and maintaining a bunch of rec code for it wasn't my idea of a good time). git-svn-id: http://pcsx2-playground.googlecode.com/svn/trunk@384 a6443dda-0b58-4228-96e9-037be469359c --- pcsx2/Counters.c | 205 +++++++-------------------------- pcsx2/Counters.h | 2 +- pcsx2/GS.cpp | 248 ++++++++++++++++++++++++++++++++++++---- pcsx2/GS.h | 3 + pcsx2/Misc.c | 46 ++++++-- pcsx2/Misc.h | 3 + pcsx2/R5900.c | 4 +- pcsx2/windows/WinMain.c | 2 +- pcsx2/x86/iGS.cpp | 219 +++-------------------------------- 9 files changed, 330 insertions(+), 402 deletions(-) diff --git a/pcsx2/Counters.c b/pcsx2/Counters.c index ecae946554..36335fdba8 100644 --- a/pcsx2/Counters.c +++ b/pcsx2/Counters.c @@ -37,9 +37,6 @@ Counter counters[6]; u32 nextsCounter; // records the cpuRegs.cycle value of the last call to rcntUpdate() s32 nextCounter; // delta from nextsCounter, in cycles, until the next rcntUpdate() -static void (*s_prevExecuteVU1Block)() = NULL; -LARGE_INTEGER lfreq; - void rcntReset(int index) { counters[index].count = 0; counters[index].sCycleT = cpuRegs.cycle; @@ -111,51 +108,21 @@ void rcntInit() { UpdateVSyncRate(); -#ifdef _WIN32 - QueryPerformanceFrequency(&lfreq); -#endif - for (i=0; i<4; i++) rcntReset(i); cpuRcntSet(); - - assert(Cpu != NULL && Cpu->ExecuteVU1Block != NULL ); - s_prevExecuteVU1Block = Cpu->ExecuteVU1Block; } // debug code, used for stats int g_nCounters[4]; static int iFrame = 0; +long iFrameLimitEnable = 1; #ifndef _WIN32 #include #endif static s64 m_iTicks=0; -static s64 m_iSlowTicks=0; static u64 m_iStart=0; -//static u64 m_iSlowStart=0; - -u64 GetTickFrequency() -{ -#ifdef _WIN32 - return lfreq.QuadPart; -#else - return 1000000; -#endif -} - -u64 GetCPUTicks() -{ -#ifdef _WIN32 - LARGE_INTEGER count; - QueryPerformanceCounter(&count); - return count.QuadPart; -#else - struct timeval t; - gettimeofday(&t, NULL); - return (u64)t.tv_sec*1000000+t.tv_usec; -#endif -} typedef struct { @@ -179,6 +146,9 @@ static __forceinline void vSyncInfoCalc( vSyncTimingInfo* info, u32 framesPerSec // depending on user-set speedhack options, and it can break float/double code // (as in returning infinities and junk) + // NOTE: mgs3 likes a /4 vsync, but many games prefer /2. This seems to indicate a + // problem in the counters vsync gates somewhere. + u64 Frame = ((u64)PS2CLK * 1000000ULL) / framesPerSecond; u64 HalfFrame = Frame / 2; u64 Blank = HalfFrame / 2; // two blanks and renders per frame @@ -220,9 +190,9 @@ static __forceinline void vSyncInfoCalc( vSyncTimingInfo* info, u32 framesPerSec } -void UpdateVSyncRate() +u32 UpdateVSyncRate() { - const char *limiterMsg = "Framelimiter rate updated (UpdateVSyncRate): %.2f fps\n"; + const char *limiterMsg = "Framelimiter rate updated (UpdateVSyncRate): %d.%d fps\n"; // fixme - According to some docs, progressive-scan modes actually refresh slower than // interlaced modes. But I can't fathom how, since the refresh rate is a function of @@ -235,18 +205,12 @@ void UpdateVSyncRate() if(Config.PsxType & 1) { if( vSyncInfo.Framerate != FRAMERATE_PAL ) - { - SysPrintf( "PCSX2: Switching to PAL display timings.\n" ); vSyncInfoCalc( &vSyncInfo, FRAMERATE_PAL, SCANLINES_TOTAL_PAL ); - } } else { if( vSyncInfo.Framerate != FRAMERATE_NTSC ) - { - SysPrintf( "PCSX2: Switching to NTSC display timings.\n" ); vSyncInfoCalc( &vSyncInfo, FRAMERATE_NTSC, SCANLINES_TOTAL_NTSC ); - } } counters[4].CycleT = vSyncInfo.hRender; // Amount of cycles before the counter will be updated @@ -258,7 +222,7 @@ void UpdateVSyncRate() if( m_iTicks != ticks ) { m_iTicks = ticks; - SysPrintf( limiterMsg, (float)Config.CustomFps ); + SysPrintf( limiterMsg, Config.CustomFps, 0 ); } } else @@ -267,35 +231,22 @@ void UpdateVSyncRate() if( m_iTicks != ticks ) { m_iTicks = ticks; - SysPrintf( limiterMsg, (Config.PsxType & 1) ? 50.00 : 59.94 ); + SysPrintf( limiterMsg, vSyncInfo.Framerate/100, vSyncInfo.Framerate%100 ); } } - { - u32 frameSkipThreshold = Config.CustomFrameSkip; - if( Config.CustomFrameSkip == 0) - { - // default: load the frameSkipThreshold with a value roughly 90% of our current framerate - frameSkipThreshold = ( vSyncInfo.Framerate * 228 ) / 256; - } - - m_iSlowTicks = GetTickFrequency() / frameSkipThreshold; - m_iStart = GetCPUTicks(); - - // sanity check against users who set a "minimum" frame that's higher - // than the maximum framerate: - if( m_iSlowTicks < m_iTicks ) m_iSlowTicks = m_iTicks; - } + m_iStart = GetCPUTicks(); + iFrameLimitEnable = 1; cpuRcntSet(); + + return (u32)m_iTicks; } extern u32 CSRw; extern u64 SuperVUGetRecTimes(int clear); extern u32 vu0time; -extern void DummyExecuteVU1Block(void); - void vSyncDebugStuff() { #ifdef EE_PROFILING @@ -378,121 +329,37 @@ void vSyncDebugStuff() { #endif } -static __forceinline void frameLimit() +// Framelimiter - Measures the delta time between calls and stalls until a +// certain amount of time passes if such time hasn't passed yet. +// See the GS FrameSkip function for details on why this is here and not in the GS. +static __forceinline void frameLimit() { - static u8 bOkayToSkip = 0; - static u8 bKeepSkipping = 0; - s64 sDeltaTime; u64 uExpectedEnd; u64 iEnd; if( CHECK_FRAMELIMIT == PCSX2_FRAMELIMIT_NORMAL ) return; + if( Config.CustomFps >= 999 ) return; // means the user would rather just have framelimiting turned off... + if( !iFrameLimitEnable ) // Frameskipper disabled the limiter + { + m_iStart = GetCPUTicks(); + return; + } uExpectedEnd = m_iStart + m_iTicks; iEnd = GetCPUTicks(); sDeltaTime = iEnd - uExpectedEnd; - if( CHECK_FRAMELIMIT == PCSX2_FRAMELIMIT_LIMIT ) + // If the framerate drops too low, reset the expected value. This avoids + // excessive amounts of "fast forward" syndrome which would occur if we + // tried to catch up too much. + + if( (sDeltaTime>>3) > m_iTicks ) { - // Non-skip Compensation: If the framerate drops too low, reset the - // expected value. This avoids excessive amounts of - // "fast forward" syndrome which would occur if we tried to - // catch up too much. - - if( (sDeltaTime>>3) > m_iTicks ) - { - m_iStart = iEnd; - return; - } + m_iStart = iEnd; + return; } - else - { - // FrameSkip and VU-Skip Magic! - // Skips a sequence of consecutive frames after a sequence of rendered frames - - // This is the least number of consecutive frames we will render w/o skipping - #define noSkipFrames (Config.CustomConsecutiveFrames>0) ? Config.CustomConsecutiveFrames : 2 - // This is the number of consecutive frames we will skip - #define yesSkipFrames (Config.CustomConsecutiveSkip>0) ? Config.CustomConsecutiveSkip : 2 - - const s64 uSlowExpectedEnd = m_iStart + m_iSlowTicks; - const s64 sSlowDeltaTime = iEnd - uSlowExpectedEnd; - - if (bOkayToSkip == 0) - { - // -- Standard operation section -- - // Means neither skipping frames nor force-rendering consecutive frames. - - if( sSlowDeltaTime > 0 ) - { - // The game is running below the minimum framerate, so use the SlowExpectedEnd. - // But don't start skipping yet! That would be too sensitive, and wouldn't - // allow compensation for occasional abnormal hiccups. - // So the skipping code is only engaged if the SlowDeltaTime falls behind by a full frame. - - m_iStart = uSlowExpectedEnd; - - if( sSlowDeltaTime > m_iSlowTicks*2 ) - { - //SysPrintf( "Frameskip Initiated!\n" ); - if( CHECK_MULTIGS ) GSRingBufSimplePacket(GS_RINGTYPE_FRAMESKIP, 1, 0, 0); - else GSsetFrameSkip(1); - if( CHECK_FRAMELIMIT == PCSX2_FRAMELIMIT_VUSKIP ) - Cpu->ExecuteVU1Block = DummyExecuteVU1Block; - - bOkayToSkip = noSkipFrames; - bKeepSkipping = yesSkipFrames; - } - return; // don't run the framelimiter. - } - } - else if (bOkayToSkip == noSkipFrames) - { - // -- Frames-a-Skippin' Section -- - - if (bKeepSkipping <= 1) - { - //first set VU1 to enabled THEN unfreeze GS regs - if( CHECK_FRAMELIMIT == PCSX2_FRAMELIMIT_VUSKIP ) - Cpu->ExecuteVU1Block = s_prevExecuteVU1Block; - if( CHECK_MULTIGS ) GSRingBufSimplePacket(GS_RINGTYPE_FRAMESKIP, 0, 0, 0); - else GSsetFrameSkip(0); - bOkayToSkip--; - - // Note: If we lag behind by 8 frames then it's time to give up on the idea - // of catching up. Force the game to drop some frames by resetting iStart to - // something closer iEnd. - - if( sSlowDeltaTime > m_iSlowTicks * 8 ) - { - //SysPrintf( "Frameskip couldn't skip enough -- had to lose some time!\n" ); - m_iStart = iEnd - m_iSlowTicks; - return; - } - } - - m_iStart = uSlowExpectedEnd; - bKeepSkipping--; - return; - } - else - { - // -- Consecutive frames section -- - // Force-render consecutive frames without skipping. - - bOkayToSkip--; - - if( sSlowDeltaTime > 0 ) - { - m_iStart = uSlowExpectedEnd; - return; - } - } - } - - // If we got this far it means the game is running near or above full framerate. // use the expected frame completion time as our starting point. // improves smoothness by making the framelimiter more adaptive to the @@ -501,13 +368,23 @@ static __forceinline void frameLimit() m_iStart = uExpectedEnd; - while( sDeltaTime < 0 ) { + while( sDeltaTime < 0 ) + { _TIMESLICE(); iEnd = GetCPUTicks(); + + if( !(*(volatile long*)&iFrameLimitEnable) ) + { + // Frameskipper turned us off + m_iStart = iEnd; + break; + } + sDeltaTime = iEnd - uExpectedEnd; } } + static __forceinline void VSyncStart(u32 sCycle) // VSync Start { vSyncDebugStuff(); // EE Profiling and Debug code @@ -547,7 +424,6 @@ __forceinline void rcntUpdate_hScanline() iopBranchAction = 1; if (counters[4].mode & MODE_HBLANK) { //HBLANK Start - //hScanlineNextCycle(difference, modeCycles); rcntStartGate(0, counters[4].sCycle); psxCheckStartGate16(0); @@ -557,7 +433,6 @@ __forceinline void rcntUpdate_hScanline() counters[4].mode = MODE_HRENDER; } else { //HBLANK END / HRENDER Begin - //hScanlineNextCycle(difference, modeCycles); if (CSRw & 0x4) GSCSRr |= 4; // signal if (!(GSIMR&0x400)) gsIrq(); if (gates) rcntEndGate(0, counters[4].sCycle); diff --git a/pcsx2/Counters.h b/pcsx2/Counters.h index aba6119ed5..f807a40756 100644 --- a/pcsx2/Counters.h +++ b/pcsx2/Counters.h @@ -94,6 +94,6 @@ u32 rcntRcount(int index); u32 rcntCycle(int index); int rcntFreeze(gzFile f, int Mode); -void UpdateVSyncRate(); +u32 UpdateVSyncRate(); #endif /* __COUNTERS_H__ */ diff --git a/pcsx2/GS.cpp b/pcsx2/GS.cpp index 47ec3f52b4..4f8d9a7be6 100644 --- a/pcsx2/GS.cpp +++ b/pcsx2/GS.cpp @@ -32,9 +32,11 @@ using namespace std; -//#define MTGS_LOG SysPrintf +#ifdef DEBUG +#define MTGS_LOG SysPrintf +#else #define MTGS_LOG 0&& - +#endif #if defined(_WIN32) && !defined(WIN32_PTHREADS) @@ -294,6 +296,59 @@ CRITICAL_SECTION stackLock; static int g_mtgsCopyLock = 0; #endif +// FrameSkipping Stuff + +static s64 m_iSlowTicks=0; +static u64 m_iSlowStart=0; +static void (*s_prevExecuteVU1Block)() = NULL; + +extern "C" void DummyExecuteVU1Block(void); + +static void OnModeChanged( u32 framerate, u32 iTicks ) +{ + m_iSlowStart = GetCPUTicks(); + + u32 frameSkipThreshold = Config.CustomFrameSkip*100; + if( Config.CustomFrameSkip == 0) + { + // default: load the frameSkipThreshold with a value roughly 90% of our current framerate + frameSkipThreshold = ( framerate * 228 ) / 256; + } + + m_iSlowTicks = ( GetTickFrequency() * 100 ) / frameSkipThreshold; + + // sanity check against users who set a "minimum" frame that's higher + // than the maximum framerate: + if( m_iSlowTicks < iTicks ) m_iSlowTicks = iTicks; +} + +extern "C" void gsSetVideoRegionType( u32 isPal ) +{ + u32 framerate; + + if( isPal ) + { + if( Config.PsxType & 1 ) return; + SysPrintf( "PAL Display Mode Initialized.\n" ); + Config.PsxType |= 1; + framerate = FRAMERATE_PAL; + } + else + { + if( !(Config.PsxType & 1 ) ) return; + SysPrintf( "NTSC Display Mode Initialized.\n" ); + Config.PsxType &= ~1; + framerate = FRAMERATE_NTSC; + } + + u32 newTickrate = UpdateVSyncRate(); + if( CHECK_MULTIGS ) + GSRingBufSimplePacket( GS_RINGTYPE_MODECHANGE, framerate, newTickrate, 0 ); + else + OnModeChanged( framerate, newTickrate ); +} + + // Initializes MultiGS ringbuffer and registers. // (does nothing for single threaded GS) void gsInit() @@ -318,11 +373,19 @@ void gsInit() if( GSsetBaseMem != NULL ) GSsetBaseMem(g_MTGSMem); } + + assert(Cpu != NULL && Cpu->ExecuteVU1Block != NULL ); + s_prevExecuteVU1Block = Cpu->ExecuteVU1Block; } // Opens the gsRingbuffer thread. s32 gsOpen() { + OnModeChanged( + (Config.PsxType & 1) ? FRAMERATE_PAL : FRAMERATE_NTSC, + UpdateVSyncRate() + ); + if( !CHECK_MULTIGS ) return GSopen((void *)&pDsp, "PCSX2", 0); @@ -750,17 +813,13 @@ void gsWrite8(u32 mem, u8 value) { GIF_LOG("GS write 8 at %8.8lx with data %8.8lx\n", mem, value); } -extern void UpdateVSyncRate(); - void gsWrite16(u32 mem, u16 value) { GIF_LOG("GS write 16 at %8.8lx with data %8.8lx\n", mem, value); switch (mem) { case 0x12000010: // GS_SMODE1 - if((value & 0x6000) == 0x6000) Config.PsxType |= 1; // PAL - else Config.PsxType &= ~1; // NTSC - UpdateVSyncRate(); + gsSetVideoRegionType( (value & 0x6000) == 0x6000 ); break; case 0x12000020: // GS_SMODE2 @@ -794,10 +853,8 @@ void gsWrite32(u32 mem, u32 value) switch (mem) { case 0x12000010: // GS_SMODE1 - if((value & 0x6000) == 0x6000) Config.PsxType |= 1; // PAL - else Config.PsxType &= ~1; // NTSC - UpdateVSyncRate(); - break; + gsSetVideoRegionType( (value & 0x6000) == 0x6000 ); + break; case 0x12000020: // GS_SMODE2 if(value & 0x1) Config.PsxType |= 2; // Interlaced @@ -826,9 +883,7 @@ void gsWrite64(u32 mem, u64 value) { switch (mem) { case 0x12000010: // GS_SMODE1 - if((value & 0x6000) == 0x6000) Config.PsxType |= 1; // PAL - else Config.PsxType &= ~1; // NTSC - UpdateVSyncRate(); + gsSetVideoRegionType( (value & 0x6000) == 0x6000 ); break; case 0x12000020: // GS_SMODE2 @@ -1591,6 +1646,128 @@ void gifMFIFOInterrupt() cpuRegs.interrupt &= ~(1 << 11); } +extern "C" long iFrameLimitEnable; // used to enable/disable the EE framelimiter. + +// FrameSkipper - Measures delta time between calls and issues frameskips +// it the time is too long. Also regulates the status of the EE's framelimiter. + +// This function does not regulate frame limiting, meaning it does no stalling. +// Stalling functions are performed by the EE: If the MTGS were throtted and not +// the EE, the EE would fill the ringbuffer while the MTGS regulated frames -- +// fine for most situations but could result in literally dozens of frames queued +// up in the ringbuffer durimg some game menu screens; which in turn would result +// in a half-second lag of keystroke actions becoming visible to the user (bad!). + +// Alternative: Instead of this, I could have everything regulated here, and then +// put a framecount limit on the MTGS ringbuffer. But that seems no less complicated +// and would also mean that aforementioned menus would still be laggy by whatever +// frame count threshold. This method is more responsive. + +static __forceinline void frameSkip() +{ + static u8 FramesToRender = 0; + static u8 FramesToSkip = 0; + static bool justSkipped = false; + + if( CHECK_FRAMELIMIT != PCSX2_FRAMELIMIT_SKIP && + CHECK_FRAMELIMIT != PCSX2_FRAMELIMIT_VUSKIP ) return; + + // FrameSkip and VU-Skip Magic! + // Skips a sequence of consecutive frames after a sequence of rendered frames + + // This is the least number of consecutive frames we will render w/o skipping + #define noSkipFrames ((Config.CustomConsecutiveFrames>0) ? Config.CustomConsecutiveFrames : 1) + // This is the number of consecutive frames we will skip + #define yesSkipFrames ((Config.CustomConsecutiveSkip>0) ? Config.CustomConsecutiveSkip : 1) + + const u64 iEnd = GetCPUTicks(); + const s64 uSlowExpectedEnd = m_iSlowStart + m_iSlowTicks; + const s64 sSlowDeltaTime = iEnd - uSlowExpectedEnd; + + m_iSlowStart = uSlowExpectedEnd; + + if( FramesToRender == 0 ) + { + // -- Standard operation section -- + // Means neither skipping frames nor force-rendering consecutive frames. + + if( sSlowDeltaTime > 0 ) + { + // The game is running below the minimum framerate, so use the SlowExpectedEnd. + // But don't start skipping yet! That would be too sensitive. + // So the skipping code is only engaged if the SlowDeltaTime falls behind by + // a full frame, or if we're already skipping (in which case we don't care + // to avoid errant skips). + + if( justSkipped || sSlowDeltaTime > m_iSlowTicks*2 ) + { + //SysPrintf( "Frameskip Initiated! Lateness: %d\n", (int)( sSlowDeltaTime / m_iSlowTicks ) ); + + InterlockedExchange( &iFrameLimitEnable, 0 ); + GSsetFrameSkip(1); + + if( CHECK_FRAMELIMIT == PCSX2_FRAMELIMIT_VUSKIP ) + InterlockedExchangePointer( &Cpu->ExecuteVU1Block, DummyExecuteVU1Block ); + + FramesToRender = noSkipFrames+1; + FramesToSkip = yesSkipFrames; + } + } + else + { + // Running at or above full speed, so reset the StartTime + + InterlockedExchange( &iFrameLimitEnable, 1 ); + m_iSlowStart = iEnd; + } + justSkipped = false; + return; + } + else if( FramesToSkip > 0 ) + { + // -- Frames-a-Skippin' Section -- + + FramesToSkip--; + + if( FramesToSkip == 0 ) + { + // Skipped our last frame, so restore non-skip behavior + + GSsetFrameSkip(0); + + // Note: If we lag behind by 250ms then it's time to give up on the idea + // of catching up. Force the game to slow down by resetting iStart to + // something closer to iEnd. + + if( sSlowDeltaTime > (m_iSlowTicks + ((s64)GetTickFrequency() / 4)) ) + { + //SysPrintf( "Frameskip couldn't skip enough -- had to lose some time!\n" ); + m_iSlowStart = iEnd - m_iSlowTicks; + } + + justSkipped = true; + if( CHECK_FRAMELIMIT == PCSX2_FRAMELIMIT_VUSKIP ) + InterlockedExchangePointer( &Cpu->ExecuteVU1Block, s_prevExecuteVU1Block ); + } + else + return; + } + + //SysPrintf( "Consecutive Frames -- Lateness: %d\n", (int)( sSlowDeltaTime / m_iSlowTicks ) ); + + // -- Consecutive frames section -- + // Force-render consecutive frames without skipping. + // re-enable the frame limiter of the EE if frames render fast. + + FramesToRender--; + + if( sSlowDeltaTime < 0 ) + { + InterlockedExchange( &iFrameLimitEnable, 1 ); + m_iSlowStart = iEnd; + } +} + extern "C" void GSPostVsyncEnd() { *(u32*)(PS2MEM_GS+0x1000) ^= 0x2000; // swap the vsync field @@ -1604,14 +1781,35 @@ extern "C" void GSPostVsyncEnd() GSRingBufSimplePacket(GS_RINGTYPE_VSYNC, (*(u32*)(PS2MEM_GS+0x1000)&0x2000), 0, 0); GS_SETEVENT(); } - else { + else + { GSvsync((*(u32*)(PS2MEM_GS+0x1000)&0x2000)); - // update here on single thread mode *OBSOLETE* - if( PAD1update != NULL ) PAD1update(0); - if( PAD2update != NULL ) PAD2update(1); + + // update here on single thread mode *OBSOLETE* + if( PAD1update != NULL ) PAD1update(0); + if( PAD2update != NULL ) PAD2update(1); + + frameSkip(); } } +static void _resetFrameskip() +{ + InterlockedExchangePointer( &Cpu->ExecuteVU1Block, s_prevExecuteVU1Block ); + InterlockedExchange( &iFrameLimitEnable, 1 ); + GSsetFrameSkip( 0 ); +} + +// Disables the GS Frameskip at runtime without any racy mess... +extern "C" void gsResetFrameSkip() +{ + if( CHECK_MULTIGS ) + GSRingBufSimplePacket(GS_RINGTYPE_FRAMESKIP, 0, 0, 0); + else + _resetFrameskip(); +} + + GS_THREADPROC { g_GsExitCode = GSopen((void *)&pDsp, "PCSX2", 1); @@ -1624,8 +1822,6 @@ GS_THREADPROC u32 prevCmd=0; #endif - SysPrintf("Starting GS thread\n"); - while( !gsHasToExit ) { event_wait( g_hGsEvent ); @@ -1683,6 +1879,9 @@ GS_THREADPROC case GS_RINGTYPE_VSYNC: { GSvsync(*(u32*)(g_pGSRingPos+4)); + + frameSkip(); + if( PAD1update != NULL ) PAD1update(0); if( PAD2update != NULL ) PAD2update(1); @@ -1697,8 +1896,10 @@ GS_THREADPROC } case GS_RINGTYPE_FRAMESKIP: - if( GSsetFrameSkip != NULL ) - GSsetFrameSkip(*(u32*)(g_pGSRingPos+4)); + _resetFrameskip(); + + //if( GSsetFrameSkip != NULL ) + // GSsetFrameSkip(*(u32*)(g_pGSRingPos+4)); break; case GS_RINGTYPE_MEMWRITE8: @@ -1786,6 +1987,9 @@ GS_THREADPROC GSwriteCSR( *(u32*)(g_pGSRingPos+4) ); break; + case GS_RINGTYPE_MODECHANGE: + OnModeChanged( *(u32*)(g_pGSRingPos+4), *(u32*)(g_pGSRingPos+8) ); + break; default: diff --git a/pcsx2/GS.h b/pcsx2/GS.h index bfba5076ee..7ce1a5355e 100644 --- a/pcsx2/GS.h +++ b/pcsx2/GS.h @@ -65,6 +65,7 @@ enum GS_RINGTYPE , GS_RINGTYPE_RESET // issues a GSreset() command. , GS_RINGTYPE_SOFTRESET // issues a soft reset for the GIF , GS_RINGTYPE_WRITECSR +, GS_RINGTYPE_MODECHANGE // for issued mode changes. }; // if returns NULL, don't copy (memory is preserved) @@ -77,6 +78,8 @@ void gsInit(); s32 gsOpen(); void gsShutdown(); void gsReset(); +void gsSetVideoRegionType( u32 isPal ); +void gsResetFrameSkip(); // mem and size are the ones from GSRingBufCopy extern void GSRINGBUF_DONECOPY(const u8 *mem, u32 size); diff --git a/pcsx2/Misc.c b/pcsx2/Misc.c index b88760edb8..2e04a213c5 100644 --- a/pcsx2/Misc.c +++ b/pcsx2/Misc.c @@ -827,7 +827,6 @@ int StatesC = 0; extern void iDumpRegisters(u32 startpc, u32 temp); extern void recExecuteVU0Block(void); extern void recExecuteVU1Block(void); -extern void DummyExecuteVU1Block(void); extern char strgametitle[256]; char* mystrlwr( char* string ) @@ -895,22 +894,16 @@ void ProcessFKeys(int fkey, int shift) Config.Options = (Config.Options&~PCSX2_FRAMELIMIT_MASK)|(((Config.Options&PCSX2_FRAMELIMIT_MASK)+PCSX2_FRAMELIMIT_LIMIT)&PCSX2_FRAMELIMIT_MASK); } + gsResetFrameSkip(); + switch(CHECK_FRAMELIMIT) { case PCSX2_FRAMELIMIT_NORMAL: - if( CHECK_MULTIGS ) GSRingBufSimplePacket(GS_RINGTYPE_FRAMESKIP, 0, 0, 0); - else GSsetFrameSkip(0); - Cpu->ExecuteVU1Block = recExecuteVU1Block; limitMsg = "None/Normal"; break; case PCSX2_FRAMELIMIT_LIMIT: - if( CHECK_MULTIGS ) GSRingBufSimplePacket(GS_RINGTYPE_FRAMESKIP, 0, 0, 0); - else GSsetFrameSkip(0); - Cpu->ExecuteVU1Block = recExecuteVU1Block; - //Quality option, turn off timestretching on the SPU2 plugin limitMsg = "Limit"; break; case PCSX2_FRAMELIMIT_SKIP: - Cpu->ExecuteVU1Block = recExecuteVU1Block; case PCSX2_FRAMELIMIT_VUSKIP: if( GSsetFrameSkip == NULL ) { @@ -1078,6 +1071,41 @@ void injectIRX(char *filename){ rd[i].extInfoSize=0; } +// [TODO] I'd like to move the following functions to their own module eventually. +// It might even be a good idea to just go ahead and move them into Win32/Linux +// specific files since they're all #ifdef'd that way anyways. + +static LARGE_INTEGER lfreq; + +void InitCPUTicks() +{ +#ifdef _WIN32 + QueryPerformanceFrequency(&lfreq); +#endif +} + +u64 GetTickFrequency() +{ +#ifdef _WIN32 + return lfreq.QuadPart; +#else + return 1000000; // unix measures in microseconds +#endif +} + +u64 GetCPUTicks() +{ +#ifdef _WIN32 + LARGE_INTEGER count; + QueryPerformanceCounter(&count); + return count.QuadPart; +#else + struct timeval t; + gettimeofday(&t, NULL); + return ((u64)t.tv_sec*GetTickFrequency())+t.tv_usec; +#endif +} + __forceinline void _TIMESLICE() { #ifdef _WIN32 diff --git a/pcsx2/Misc.h b/pcsx2/Misc.h index b662134a7d..d5e14f3214 100644 --- a/pcsx2/Misc.h +++ b/pcsx2/Misc.h @@ -391,6 +391,9 @@ static __forceinline long InterlockedCompareExchangePointer(PVOID volatile *dest #endif +extern void InitCPUTicks(); +extern u64 GetTickFrequency(); +extern u64 GetCPUTicks(); // Timeslice releaser for those many idle loop spots through out PCSX2. extern void _TIMESLICE(); diff --git a/pcsx2/R5900.c b/pcsx2/R5900.c index b9315cf7d0..571d5cc316 100644 --- a/pcsx2/R5900.c +++ b/pcsx2/R5900.c @@ -626,7 +626,9 @@ void cpuExecuteBios() break; } - UpdateVSyncRate(); + // Set the video mode to user's default request: + gsSetVideoRegionType( Config.PsxType & 1 ); + SysPrintf("* PCSX2 *: ExecuteBios\n"); bExecBIOS = TRUE; diff --git a/pcsx2/windows/WinMain.c b/pcsx2/windows/WinMain.c index 398f593961..acc29e16c8 100644 --- a/pcsx2/windows/WinMain.c +++ b/pcsx2/windows/WinMain.c @@ -652,7 +652,7 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine // [TODO] : Add the other plugin overrides here... - + InitCPUTicks(); if (SysInit() == -1) return 1; #ifdef PCSX2_DEVBUILD diff --git a/pcsx2/x86/iGS.cpp b/pcsx2/x86/iGS.cpp index 5b1298bee9..a0e13bd6c3 100644 --- a/pcsx2/x86/iGS.cpp +++ b/pcsx2/x86/iGS.cpp @@ -89,93 +89,17 @@ void gsConstWrite8(u32 mem, int mmreg) } } -void recSetSMODE1() -{ - iFlushCall(0); - AND32ItoR(EAX, 0x6000); - CMP32ItoR(EAX, 0x6000); - j8Ptr[5] = JNE8(0); - - // PAL - OR32ItoM( (uptr)&Config.PsxType, 1); - j8Ptr[6] = JMP8(0); - - x86SetJ8( j8Ptr[5] ); - - // NTSC - AND32ItoM( (uptr)&Config.PsxType, ~1 ); - - x86SetJ8( j8Ptr[6] ); - CALLFunc((uptr)UpdateVSyncRate); -} - -void recSetSMODE2() -{ - TEST32ItoR(EAX, 1); - j8Ptr[5] = JZ8(0); - - // Interlaced - OR32ItoM( (uptr)&Config.PsxType, 2); - j8Ptr[6] = JMP8(0); - - x86SetJ8( j8Ptr[5] ); - - // Non-Interlaced - AND32ItoM( (uptr)&Config.PsxType, ~2 ); - - x86SetJ8( j8Ptr[6] ); -} - void gsConstWrite16(u32 mem, int mmreg) { switch (mem&~3) { + case 0x12000010: // GS_SMODE1 - assert( !(mem&3)); - _eeMoveMMREGtoR(EAX, mmreg); - _eeWriteConstMem16( (uptr)PS2GS_BASE(mem), mmreg ); - - if( CHECK_MULTIGS ) - _callPushArg(MEM_X86TAG|EAX, 0, X86ARG3); - - recSetSMODE1(); - - if( CHECK_MULTIGS ) { - iFlushCall(0); - - _callPushArg(MEM_CONSTTAG, mem&0x13ff, X86ARG2); - _callPushArg(MEM_CONSTTAG, GS_RINGTYPE_MEMWRITE16, X86ARG1); - CALLFunc((uptr)GSRingBufSimplePacket); -#ifndef __x86_64__ - ADD32ItoR(ESP, 12); -#endif - - } - - break; - case 0x12000020: // GS_SMODE2 - assert( !(mem&3)); - _eeMoveMMREGtoR(EAX, mmreg); - _eeWriteConstMem16( (uptr)PS2GS_BASE(mem), mmreg ); - - if( CHECK_MULTIGS ) - _callPushArg(MEM_X86TAG|EAX, 0, X86ARG3); - - recSetSMODE2(); - - if( CHECK_MULTIGS ) { - iFlushCall(0); - - _callPushArg(MEM_CONSTTAG, mem&0x13ff, X86ARG2); - _callPushArg(MEM_CONSTTAG, GS_RINGTYPE_MEMWRITE16, X86ARG1); - CALLFunc((uptr)GSRingBufSimplePacket); -#ifndef __x86_64__ - ADD32ItoR(ESP, 12); -#endif - } - + // SMODE1 and SMODE2 fall back on the gsWrite library. + iFlushCall(0); + _callFunctionArg2((uptr)gsWrite16, MEM_CONSTTAG, mmreg, mem, 0 ); break; - + case 0x12001000: // GS_CSR assert( !(mem&2) ); @@ -187,7 +111,7 @@ void gsConstWrite16(u32 mem, int mmreg) AND32ItoR(ECX, ~(0xffff<<(mem&2)*8)); OR32ItoR(EAX, ECX); _callFunctionArg1((uptr)CSRwrite, EAX|MEM_X86TAG, 0); - break; + return; // don't write to GSMEM default: _eeWriteConstMem16( (uptr)PS2GS_BASE(mem), mmreg ); @@ -236,49 +160,12 @@ void gsConstWrite32(u32 mem, int mmreg) { switch (mem) { case 0x12000010: // GS_SMODE1 - _eeMoveMMREGtoR(EAX, mmreg); - _eeWriteConstMem32( (uptr)PS2GS_BASE(mem), mmreg ); - - if( CHECK_MULTIGS ) - _callPushArg(MEM_X86TAG|EAX, 0, X86ARG3); - - recSetSMODE1(); - - if( CHECK_MULTIGS ) { - iFlushCall(0); - - _callPushArg(MEM_CONSTTAG, mem&0x13ff, X86ARG2); - _callPushArg(MEM_CONSTTAG, GS_RINGTYPE_MEMWRITE32, X86ARG1); - CALLFunc((uptr)GSRingBufSimplePacket); -#ifndef __x86_64__ - ADD32ItoR(ESP, 12); -#endif - } - - break; - case 0x12000020: // GS_SMODE2 - _eeMoveMMREGtoR(EAX, mmreg); - _eeWriteConstMem32( (uptr)PS2GS_BASE(mem), mmreg ); - - if( CHECK_MULTIGS ) - _callPushArg(MEM_X86TAG|EAX, 0, X86ARG3); - - recSetSMODE2(); - - if( CHECK_MULTIGS ) { - iFlushCall(0); - - _callPushArg(MEM_CONSTTAG, mem&0x13ff, X86ARG2); - _callPushArg(MEM_CONSTTAG, GS_RINGTYPE_MEMWRITE32, X86ARG1); - CALLFunc((uptr)GSRingBufSimplePacket); -#ifndef __x86_64__ - ADD32ItoR(ESP, 12); -#endif - } - + // SMODE1 and SMODE2 fall back on the gsWrite library. + iFlushCall(0); + _callFunctionArg2((uptr)gsWrite16, MEM_CONSTTAG, mmreg, mem, 0 ); break; - + case 0x12001000: // GS_CSR iFlushCall(0); _callFunctionArg1((uptr)CSRwrite, mmreg, 0); @@ -305,47 +192,10 @@ void gsConstWrite64(u32 mem, int mmreg) { switch (mem) { case 0x12000010: // GS_SMODE1 - _eeMoveMMREGtoR(EAX, mmreg); - _eeWriteConstMem64((uptr)PS2GS_BASE(mem), mmreg); - - if( CHECK_MULTIGS ) - _callPushArg(MEM_X86TAG|EAX, 0, X86ARG3); - - recSetSMODE1(); - - if( CHECK_MULTIGS ) { - iFlushCall(0); - - _callPushArg(MEM_CONSTTAG, mem&0x13ff, X86ARG2); - _callPushArg(MEM_CONSTTAG, GS_RINGTYPE_MEMWRITE32, X86ARG1); - CALLFunc((uptr)GSRingBufSimplePacket); -#ifndef __x86_64__ - ADD32ItoR(ESP, 12); -#endif - } - - break; - case 0x12000020: // GS_SMODE2 - _eeMoveMMREGtoR(EAX, mmreg); - _eeWriteConstMem64((uptr)PS2GS_BASE(mem), mmreg); - - if( CHECK_MULTIGS ) - _callPushArg(MEM_X86TAG|EAX, 0, X86ARG3); - - recSetSMODE2(); - - if( CHECK_MULTIGS ) { - iFlushCall(0); - - _callPushArg(MEM_CONSTTAG, mem&0x13ff, X86ARG2); - _callPushArg(MEM_CONSTTAG, GS_RINGTYPE_MEMWRITE32, X86ARG1); - CALLFunc((uptr)GSRingBufSimplePacket); -#ifndef __x86_64__ - ADD32ItoR(ESP, 12); -#endif - } - + // SMODE1 and SMODE2 fall back on the gsWrite library. + iFlushCall(0); + _callFunctionArg2((uptr)gsWrite16, MEM_CONSTTAG, mmreg, mem, 0 ); break; case 0x12001000: // GS_CSR @@ -381,47 +231,10 @@ void gsConstWrite128(u32 mem, int mmreg) { switch (mem) { case 0x12000010: // GS_SMODE1 - _eeMoveMMREGtoR(EAX, mmreg); - _eeWriteConstMem128( (uptr)PS2GS_BASE(mem), mmreg); - - if( CHECK_MULTIGS ) - _callPushArg(MEM_X86TAG|EAX, 0, X86ARG3); - - recSetSMODE1(); - - if( CHECK_MULTIGS ) { - iFlushCall(0); - - _callPushArg(MEM_CONSTTAG, mem&0x13ff, X86ARG2); - _callPushArg(MEM_CONSTTAG, GS_RINGTYPE_MEMWRITE32, X86ARG1); - CALLFunc((uptr)GSRingBufSimplePacket); -#ifndef __x86_64__ - ADD32ItoR(ESP, 12); -#endif - } - - break; - case 0x12000020: // GS_SMODE2 - _eeMoveMMREGtoR(EAX, mmreg); - _eeWriteConstMem128( (uptr)PS2GS_BASE(mem), mmreg); - - if( CHECK_MULTIGS ) - _callPushArg(MEM_X86TAG|EAX, 0, X86ARG3); - - recSetSMODE2(); - - if( CHECK_MULTIGS ) { - iFlushCall(0); - - _callPushArg(MEM_CONSTTAG, mem&0x13ff, X86ARG2); - _callPushArg(MEM_CONSTTAG, GS_RINGTYPE_MEMWRITE32, X86ARG1); - CALLFunc((uptr)GSRingBufSimplePacket); -#ifndef __x86_64__ - ADD32ItoR(ESP, 12); -#endif - } - + // SMODE1 and SMODE2 fall back on the gsWrite library. + iFlushCall(0); + _callFunctionArg2((uptr)gsWrite16, MEM_CONSTTAG, mmreg, mem, 0 ); break; case 0x12001000: // GS_CSR