From e3276828f622c0b442c6049cf158b18410d893de Mon Sep 17 00:00:00 2001 From: nitsuja Date: Wed, 11 Nov 2009 17:47:18 +0000 Subject: [PATCH] new logic for autoframeskip, and new options to limit the maximum consecutive frames it's allowed to skip. the old logic would skip frames in an arbitrary pattern, the new logic prefers to keep the skipping rate steady. --- desmume/src/windows/main.cpp | 88 ++++++++-------- desmume/src/windows/resource.h | 10 +- desmume/src/windows/resources.rc | Bin 443706 -> 449002 bytes desmume/src/windows/throttle.cpp | 176 ++++++++++++++++++++++--------- desmume/src/windows/throttle.h | 9 +- 5 files changed, 186 insertions(+), 97 deletions(-) diff --git a/desmume/src/windows/main.cpp b/desmume/src/windows/main.cpp index 9aa24add8..702d47904 100644 --- a/desmume/src/windows/main.cpp +++ b/desmume/src/windows/main.cpp @@ -1463,8 +1463,6 @@ static struct MainLoopData u64 lastticks; u64 curticks; u64 diffticks; - u32 framecount; - u64 onesecondticks; u64 fpsticks; HWND hwnd; int fps; @@ -1618,40 +1616,26 @@ static void StepRunLoop_Throttle(bool allowSleep = true, int forceFrameSkip = -1 if (mainLoopData.framestoskip < 1) mainLoopData.framestoskip += ffSkipRate; } - else if((autoframeskipenab || FrameLimit) && allowSleep) + else if((/*autoframeskipenab ||*/ FrameLimit) && allowSleep) { - while(SpeedThrottle()) - { - } + SpeedThrottle(); } if (autoframeskipenab) { - mainLoopData.framecount++; - - if (mainLoopData.framecount > 60) + if(!frameAdvance && !continuousframeAdvancing) { - mainLoopData.framecount = 1; - mainLoopData.onesecondticks = 0; + AutoFrameSkip_NextFrame(); + if (mainLoopData.framestoskip < 1) + mainLoopData.framestoskip += AutoFrameSkip_GetSkipAmount(0,skipRate); } - - QueryPerformanceCounter((LARGE_INTEGER *)&mainLoopData.curticks); - mainLoopData.diffticks = mainLoopData.curticks - mainLoopData.lastticks; - - if(ThrottleIsBehind() && (mainLoopData.framesskipped < ffSkipRate)) - { - mainLoopData.skipnextframe = 1; - mainLoopData.framestoskip = 1; - } - - mainLoopData.onesecondticks += mainLoopData.diffticks; - mainLoopData.lastticks = mainLoopData.curticks; } else { if (mainLoopData.framestoskip < 1) mainLoopData.framestoskip += skipRate; } + if (frameAdvance && allowSleep) { frameAdvance = false; @@ -2424,17 +2408,20 @@ int _main() GetPrivateProfileString("Video", "FrameSkip", "0", text, 80, IniName); - if (strcmp(text, "AUTO") == 0) + if(!strncmp(text, "AUTO", 4)) { autoframeskipenab=1; - frameskiprate=0; - MainWindow->checkMenu(IDC_FRAMESKIPAUTO, true); + if(!text[4]) + frameskiprate=9; + else + frameskiprate=atoi(text+4); + if(frameskiprate < 1) + frameskiprate = 9; } else { autoframeskipenab=0; frameskiprate=atoi(text); - MainWindow->checkMenu(frameskiprate + IDC_FRAMESKIP0, true); } if (KeyInDelayMSec == 0) { @@ -3573,8 +3560,15 @@ LRESULT CALLBACK WindowProcedure (HWND hwnd, UINT message, WPARAM wParam, LPARAM MainWindow->checkMenu(IDC_FRAMELIMIT, FrameLimit); //Frame Skip - MainWindow->checkMenu(IDC_FRAMESKIPAUTO, ((autoframeskipenab))); - + MainWindow->checkMenu(IDC_FRAMESKIPAUTO1, (autoframeskipenab && frameskiprate==1) ); + MainWindow->checkMenu(IDC_FRAMESKIPAUTO2, (autoframeskipenab && frameskiprate==2) ); + MainWindow->checkMenu(IDC_FRAMESKIPAUTO3, (autoframeskipenab && frameskiprate==3) ); + MainWindow->checkMenu(IDC_FRAMESKIPAUTO4, (autoframeskipenab && frameskiprate==4) ); + MainWindow->checkMenu(IDC_FRAMESKIPAUTO5, (autoframeskipenab && frameskiprate==5) ); + MainWindow->checkMenu(IDC_FRAMESKIPAUTO6, (autoframeskipenab && frameskiprate==6) ); + MainWindow->checkMenu(IDC_FRAMESKIPAUTO7, (autoframeskipenab && frameskiprate==7) ); + MainWindow->checkMenu(IDC_FRAMESKIPAUTO8, (autoframeskipenab && frameskiprate==8) ); + MainWindow->checkMenu(IDC_FRAMESKIPAUTO9, (autoframeskipenab && frameskiprate==9) ); MainWindow->checkMenu(IDC_FRAMESKIP0, (!autoframeskipenab && frameskiprate==0) ); MainWindow->checkMenu(IDC_FRAMESKIP1, (!autoframeskipenab && frameskiprate==1) ); MainWindow->checkMenu(IDC_FRAMESKIP2, (!autoframeskipenab && frameskiprate==2) ); @@ -4740,7 +4734,23 @@ LRESULT CALLBACK WindowProcedure (HWND hwnd, UINT message, WPARAM wParam, LPARAM if(tpaused) NDS_UnPause(); } return 0; - case IDC_FRAMESKIPAUTO: + case IDC_FRAMESKIPAUTO1: + case IDC_FRAMESKIPAUTO2: + case IDC_FRAMESKIPAUTO3: + case IDC_FRAMESKIPAUTO4: + case IDC_FRAMESKIPAUTO5: + case IDC_FRAMESKIPAUTO6: + case IDC_FRAMESKIPAUTO7: + case IDC_FRAMESKIPAUTO8: + case IDC_FRAMESKIPAUTO9: + { + char text[80]; + autoframeskipenab = 1; + frameskiprate = LOWORD(wParam) - (IDC_FRAMESKIPAUTO1 - 1); + sprintf(text, "AUTO%d", frameskiprate); + WritePrivateProfileString("Video", "FrameSkip", text, IniName); + } + return 0; case IDC_FRAMESKIP0: case IDC_FRAMESKIP1: case IDC_FRAMESKIP2: @@ -4752,19 +4762,11 @@ LRESULT CALLBACK WindowProcedure (HWND hwnd, UINT message, WPARAM wParam, LPARAM case IDC_FRAMESKIP8: case IDC_FRAMESKIP9: { - if(LOWORD(wParam) == IDC_FRAMESKIPAUTO) - { - autoframeskipenab = 1; - WritePrivateProfileString("Video", "FrameSkip", "AUTO", IniName); - } - else - { - char text[80]; - autoframeskipenab = 0; - frameskiprate = LOWORD(wParam) - IDC_FRAMESKIP0; - sprintf(text, "%d", frameskiprate); - WritePrivateProfileString("Video", "FrameSkip", text, IniName); - } + char text[80]; + autoframeskipenab = 0; + frameskiprate = LOWORD(wParam) - IDC_FRAMESKIP0; + sprintf(text, "%d", frameskiprate); + WritePrivateProfileString("Video", "FrameSkip", text, IniName); } return 0; case IDC_NEW_LUA_SCRIPT: diff --git a/desmume/src/windows/resource.h b/desmume/src/windows/resource.h index 371dc761c..3bdbd995e 100644 --- a/desmume/src/windows/resource.h +++ b/desmume/src/windows/resource.h @@ -77,7 +77,6 @@ #define IDC_SAVETYPE4 185 #define IDC_SAVETYPE5 186 #define IDC_SAVETYPE6 187 -#define IDC_FRAMESKIPAUTO 190 #define IDC_FRAMESKIP0 191 #define IDC_FRAMESKIP1 192 #define IDC_FRAMESKIP2 193 @@ -97,6 +96,15 @@ #define IDC_LANGDANISH 212 #define IDM_MGPU 213 #define IDM_SGPU 214 +#define IDC_FRAMESKIPAUTO1 221 +#define IDC_FRAMESKIPAUTO2 222 +#define IDC_FRAMESKIPAUTO3 223 +#define IDC_FRAMESKIPAUTO4 224 +#define IDC_FRAMESKIPAUTO5 225 +#define IDC_FRAMESKIPAUTO6 226 +#define IDC_FRAMESKIPAUTO7 227 +#define IDC_FRAMESKIPAUTO8 228 +#define IDC_FRAMESKIPAUTO9 229 #define IDC_8_BIT 302 #define IDC_16_BIT 303 #define IDC_32_BIT 304 diff --git a/desmume/src/windows/resources.rc b/desmume/src/windows/resources.rc index d8ff882cab1faa1a48b0b75d6c41d71c3074c7da..783a432893380725fe09d40f1d5ef33b628291f8 100644 GIT binary patch delta 3248 zcmdnhCH-o%^add>M$YLEcd{x^e!wOs&&dFS0Sx{?916q=3`z`Y3~mgW3>6Hi3@Ov! z7BeYMZeZh>o)E^QHu-_C45Qlgg}zLtlNb0%O<(YVRSqWR0Tr_;V}gkZ<}vGn^n%rH z_y|)wSy2q6-2$R>@55M z3bgltAJnkvg$JQJAAEr;5rjFnriw`n?4ASqT(a0a{;F|BkOt8Ej*~y?OTv83#|lcD=(BM{Vi`TyG8(0jS5A1L0XN$0GJ9i81jI5r3{FRfV^UcY+&vw0J=wd zSY{Gn_FyPwC}GG)DI1`h=Ng<^h|D+B4MLa%w(qH8dh}p4$3SX9;;WU>95d{5j1;i8(b&GL zib;-n@&Z+v_ID}FK+LlJT?*@>uF)Jbnq!a~iAXInoaOSc&oP*7GjJU<9azV3Y~S#e Hwd*_p`UfER delta 747 zcmaF0S$bEO^adfX$yIy;lN;DLCI_&Iu)Jzq5j4HPklALkLy+9$A{m3}MbB6*rhjN+ zlAHc3jahE`hl{LY(+hN%?IsI&i%s6(EjPJAGz7$+&X>U~H%SJ z&lkcskOlLnugL_OrJ&CoGF>5y*<|{M049jW16dG_(*q2c9VRFEn@mr*&I*>A6d*R; zA%Iz<*(|i(ER=D(StwJD3e4@(H^eZ@Om}hzy8A+a)Sz_tcCA8YIp*yO9ZbjW4k2f^ c1Dy@TEI{Y8-sv4e4xbKmIM?k diff --git a/desmume/src/windows/throttle.cpp b/desmume/src/windows/throttle.cpp index 0a296829c..785d671c8 100644 --- a/desmume/src/windows/throttle.cpp +++ b/desmume/src/windows/throttle.cpp @@ -1,5 +1,6 @@ -//THIS SPEED THROTTLE IS TAKEN FROM FCEUX. +//THIS SPEED THROTTLE WAS TAKEN FROM FCEUX. //Copyright (C) 2002 Xodnizel +//(the code might look quite different by now, though...) #include "../common.h" #include "../types.h" @@ -8,9 +9,10 @@ #include int FastForward=0; -static u64 tmethod,tfreq; +static u64 tmethod,tfreq,afsfreq; static const u64 core_desiredfps = 3920763; //59.8261 static u64 desiredfps = core_desiredfps; +static float desiredspf = 65536.0f / core_desiredfps; static int desiredFpsScalerIndex = 2; static u64 desiredFpsScalers [] = { 1024, @@ -32,6 +34,7 @@ void IncreaseSpeed(void) { desiredFpsScalerIndex--; u64 desiredFpsScaler = desiredFpsScalers[desiredFpsScalerIndex]; desiredfps = core_desiredfps * desiredFpsScaler / 256; + desiredspf = 65536.0f / desiredfps; printf("Throttle fps scaling increased to: %f\n",desiredFpsScaler/256.0); } @@ -41,6 +44,7 @@ void DecreaseSpeed(void) { desiredFpsScalerIndex++; u64 desiredFpsScaler = desiredFpsScalers[desiredFpsScalerIndex]; desiredfps = core_desiredfps * desiredFpsScaler / 256; + desiredspf = 65536.0f / desiredfps; printf("Throttle fps scaling decreased to: %f\n",desiredFpsScaler/256.0); } @@ -49,78 +53,150 @@ static u64 GetCurTime(void) if(tmethod) { u64 tmp; - - /* Practically, LARGE_INTEGER and u64 differ only by signness and name. */ QueryPerformanceCounter((LARGE_INTEGER*)&tmp); - - return(tmp); + return tmp; } else - return((u64)GetTickCount()); - + { + return (u64)GetTickCount(); + } } void InitSpeedThrottle(void) { tmethod=0; - if(QueryPerformanceFrequency((LARGE_INTEGER*)&tfreq)) - { + if(QueryPerformanceFrequency((LARGE_INTEGER*)&afsfreq)) tmethod=1; - } else - tfreq=1000; - tfreq<<=16; /* Adjustment for fps returned from FCEUI_GetDesiredFPS(). */ + afsfreq=1000; + tfreq = afsfreq << 16; } -static bool behind=false; -bool ThrottleIsBehind() { - return behind; -} - -int SpeedThrottle(void) +void SpeedThrottle() { - static u64 ttime,ltime; - - if(FastForward) - return (0); - - behind = false; + static u64 ttime, ltime; waiter: + if(FastForward) + return; - ttime=GetCurTime(); + ttime = GetCurTime(); - - if( (ttime-ltime) < (tfreq/desiredfps) ) + if((ttime - ltime) < (tfreq / desiredfps)) { u64 sleepy; - sleepy=(tfreq/desiredfps)-(ttime-ltime); - sleepy*=1000; - if(tfreq>=65536) - sleepy/=tfreq>>16; + sleepy = (tfreq / desiredfps) - (ttime - ltime); + sleepy *= 1000; + if(tfreq >= 65536) + sleepy /= afsfreq; else - sleepy=0; - if(sleepy>100) - { - // block for a max of 100ms to - // keep the gui responsive - Sleep(100); - return 1; - } - Sleep((DWORD)sleepy); + sleepy = 0; + if(sleepy >= 10) + Sleep((DWORD)(sleepy / 2)); // reduce it further beacuse Sleep usually sleeps for more than the amount we tell it to + else if(sleepy > 0) // spin for <1 millisecond waits + SwitchToThread(); // limit to other threads on the same CPU core for other short waits goto waiter; } if( (ttime-ltime) >= (tfreq*4/desiredfps)) ltime=ttime; else - { ltime+=tfreq/desiredfps; - - if( (ttime-ltime) >= (tfreq/desiredfps) ) // Oops, we're behind! - { - behind = true; - return 0; - } - } - return(0); } + + +// auto frameskip + +static u64 beginticks=0, endticks=0, diffticks=0; +static std::vector diffs; +static const int SLIDING_WINDOW_SIZE = 32; +static float fSkipFrames = 0; +static float fSkipFramesError = 0; +static int minSkip = 0, maxSkip = 9; + +static float maxDiff(const std::vector& diffs) +{ + float maximum = 0; + for(int i = 0; i < diffs.size(); i++) + if(maximum < diffs[i]) + maximum = diffs[i]; + return maximum; +} + +static void addDiff(float diff, std::vector& diffs) +{ + // limit to about double the maximum, to reduce the impact of the occasional huge diffs + // (using the last or average entry is a bad idea because some games do their own frameskipping) + float maximum = maxDiff(diffs); + if(!diffs.empty() && diff > maximum * 2 + 0.0001f) + diff = maximum * 2 + 0.0001f; + + diffs.push_back(diff); + + if(diffs.size() > SLIDING_WINDOW_SIZE) + diffs.erase(diffs.begin()); +} + +static float average(const std::vector& diffs) +{ + float avg = 0; + for(int i = 0; i < diffs.size(); i++) + avg += diffs[i]; + if(diffs.size()) + avg /= diffs.size(); + return avg; +} + +void AutoFrameSkip_NextFrame() +{ + endticks = GetCurTime(); + diffticks = endticks - beginticks; + + float diff = (float)diffticks / afsfreq; + addDiff(diff,diffs); + + float avg = average(diffs); + float overby = (avg - (desiredspf + (fSkipFrames + 2) * 0.000025f)) * 8; + // try to avoid taking too long to catch up to the game running fast again + if(overby < 0 && fSkipFrames > 4) + overby = -fSkipFrames / 4; + else if(avg < desiredspf) + overby *= 8; + + fSkipFrames += overby; + if(fSkipFrames < minSkip-1) + fSkipFrames = minSkip-1; + if(fSkipFrames > maxSkip+1) + fSkipFrames = maxSkip+1; + + //printf("avg = %g, overby = %g, skipframes = %g\n", avg, overby, fSkipFrames); + + beginticks = GetCurTime(); +} + +int AutoFrameSkip_GetSkipAmount(int min, int max) +{ + int rv = (int)fSkipFrames; + fSkipFramesError += fSkipFrames - rv; + while(fSkipFramesError >= 1.0f) + { + fSkipFramesError -= 1.0f; + rv++; + } + while(fSkipFramesError <= -1.0f) + { + fSkipFramesError += 1.0f; + rv--; + } + if(rv < min) + rv = min; + if(rv > max) + rv = max; + minSkip = min; + maxSkip = max; + + //printf("SKIPPED: %d\n", rv); + + return rv; +} + + diff --git a/desmume/src/windows/throttle.h b/desmume/src/windows/throttle.h index b2d3103e3..53a792606 100644 --- a/desmume/src/windows/throttle.h +++ b/desmume/src/windows/throttle.h @@ -1,11 +1,14 @@ #ifndef _THROTTLE_H_ #define _THROTTLE_H_ -void InitSpeedThrottle(); -int SpeedThrottle(); -bool ThrottleIsBehind(); extern int FastForward; void IncreaseSpeed(); void DecreaseSpeed(); +void InitSpeedThrottle(); +void SpeedThrottle(); + +void AutoFrameSkip_NextFrame(); +int AutoFrameSkip_GetSkipAmount(int min=0, int max=9); + #endif