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:
nitsuja 2009-11-11 17:47:18 +00:00
parent bbc0a1d6d6
commit e3276828f6
5 changed files with 186 additions and 97 deletions

View File

@ -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:

View File

@ -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.

View File

@ -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;
}

View File

@ -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