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 d8ff882ca..783a43289 100644 Binary files a/desmume/src/windows/resources.rc and b/desmume/src/windows/resources.rc differ 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