Fix automatic dynamic frame skipping

The previously used algorithm was too aggressive. This fixes the issue
by using the last second data and using a more gentle adjustment curve
to prevent huge changes in frame skipping.
This commit is contained in:
Fabrice de Gans 2023-02-15 00:15:38 -08:00 committed by Rafael Kitover
parent d1733c4505
commit f1d3f631d2
6 changed files with 42 additions and 27 deletions

View File

@ -76,7 +76,7 @@ extern void systemPossibleCartridgeRumble(bool);
extern void updateRumbleFrame();
extern bool systemCanChangeSoundQuality();
extern void systemShowSpeed(int);
extern void system10Frames(int);
extern void system10Frames();
extern void systemFrame();
extern void systemGbBorderOn();
extern void Sm60FPS_Init();

View File

@ -5016,7 +5016,7 @@ void gbEmulate(int ticksToStop)
gbSoundTick(soundTicks);
if ((gbFrameCount % 10) == 0)
system10Frames(60);
system10Frames();
if (gbFrameCount >= 60) {
uint32_t currentTime = systemGetClock();
@ -5224,7 +5224,7 @@ void gbEmulate(int ticksToStop)
gbSoundTick(soundTicks);
if ((gbFrameCount % 10) == 0)
system10Frames(60);
system10Frames();
if (gbFrameCount >= 60) {
uint32_t currentTime = systemGetClock();

View File

@ -3859,7 +3859,7 @@ void CPULoop(int ticks)
systemFrame();
if ((count % 10) == 0) {
system10Frames(60);
system10Frames();
}
if (count == 60) {
uint32_t time = systemGetClock();

View File

@ -1896,7 +1896,7 @@ void systemShowSpeed(int)
{
}
void system10Frames(int)
void system10Frames()
{
}

View File

@ -2091,7 +2091,7 @@ void systemFrame()
{
}
void system10Frames(int rate)
void system10Frames()
{
uint32_t time = systemGetClock();
if (!wasPaused && autoFrameSkip) {
@ -2099,7 +2099,7 @@ void system10Frames(int rate)
int speed = 100;
if (diff)
speed = (1000000 / rate) / diff;
speed = (1000000 / 60) / diff;
if (speed >= 98) {
frameskipadjust++;

View File

@ -1,12 +1,15 @@
#include "../common/SoundSDL.h"
#include "config/game-control.h"
#include "config/option-proxy.h"
#include "wxvbam.h"
#include "SDL.h"
#include <algorithm>
#include <wx/ffile.h>
#include <wx/generic/prntdlgg.h>
#include <wx/print.h>
#include <wx/printdlg.h>
#include <SDL.h>
#include "../common/SoundSDL.h"
#include "config/game-control.h"
#include "config/option-proxy.h"
#include "wxvbam.h"
// These should probably be in vbamcore
int systemVerbose;
@ -469,43 +472,55 @@ void systemShowSpeed(int speed)
int systemSaveUpdateCounter = SYSTEM_SAVE_NOT_UPDATED;
void system10Frames(int rate)
{
void system10Frames() {
GameArea* panel = wxGetApp().frame->GetPanel();
int fs = frameSkip;
if (fs < 0) {
// I don't know why this algorithm isn't in common somewhere
// as is, I copied it from SDL
// We keep a rolling mean of the last second and use this value to
// adjust the systemFrameSkip value dynamically.
// Target time in ms for 10 frames at 60 FPS.
constexpr int kTarget = 10 * 1000 / 60;
static uint32_t prevclock = 0;
static int speedadj = 0;
uint32_t t = systemGetClock();
static int last_second[6] = {kTarget, kTarget, kTarget,
kTarget, kTarget, kTarget};
static size_t last_index = 0;
if (!panel->was_paused && prevclock && (t - prevclock) != (uint32_t)(10000 / rate)) {
int speed = t == prevclock ? 100 * 10000 / rate - (t - prevclock) : 100;
const uint32_t timestamp = systemGetClock();
if (!panel->was_paused && prevclock) {
last_second[last_index] = systemGetClock() - prevclock;
last_index = (last_index + 1) % 6;
int average = 0;
for (size_t i = 0; i < 6; i++) {
average += last_second[i];
}
average /= 6;
const int speed = (kTarget * 100) / average;
// why 98??
if (speed >= 98)
speedadj++;
else if (speed < 80)
speedadj -= (90 - speed) / 5;
speedadj -= (90 - speed) / 10;
else
speedadj--;
if (speedadj >= 3) {
speedadj = 0;
if (systemFrameSkip > 0)
systemFrameSkip--;
systemFrameSkip = std::max(systemFrameSkip - 1, 0);
} else if (speedadj <= -2) {
speedadj += 2;
if (systemFrameSkip < 9)
systemFrameSkip++;
systemFrameSkip = std::min(systemFrameSkip + 1, 9);
}
}
prevclock = t;
prevclock = timestamp;
panel->was_paused = false;
}