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.
This commit is contained in:
parent
bbc0a1d6d6
commit
e3276828f6
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
|
Binary file not shown.
|
@ -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 <windows.h>
|
||||
|
||||
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<float> 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<float>& 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<float>& 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<float>& 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;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue