mirror of
https://github.com/hrydgard/ppsspp.git
synced 2025-04-02 11:01:50 -04:00
Merge pull request #6433 from unknownbrackets/frame-timing
Maintain timing of at least 30 vblanks per second if possible
This commit is contained in:
commit
f0cb70d9fb
1 changed files with 70 additions and 51 deletions
|
@ -105,8 +105,12 @@ static int width;
|
|||
static int height;
|
||||
static bool wasPaused;
|
||||
|
||||
// 1.001f to compensate for the classic 59.94 NTSC framerate that the PSP seems to have.
|
||||
static const double timePerVblank = 1.001f / 60.0f;
|
||||
|
||||
// Don't include this in the state, time increases regardless of state.
|
||||
static double curFrameTime;
|
||||
static double lastFrameTime;
|
||||
static double nextFrameTime;
|
||||
static int numVBlanksSinceFlip;
|
||||
|
||||
|
@ -184,7 +188,7 @@ void __DisplayInit() {
|
|||
framebuf.pspFramebufLinesize = 480; // ??
|
||||
lastFlipCycles = 0;
|
||||
lastFlipsTooFrequent = 0;
|
||||
wasPaused = 0;
|
||||
wasPaused = false;
|
||||
|
||||
enterVblankEvent = CoreTiming::RegisterEvent("EnterVBlank", &hleEnterVblank);
|
||||
leaveVblankEvent = CoreTiming::RegisterEvent("LeaveVBlank", &hleLeaveVblank);
|
||||
|
@ -199,6 +203,7 @@ void __DisplayInit() {
|
|||
hCountBase = 0;
|
||||
curFrameTime = 0.0;
|
||||
nextFrameTime = 0.0;
|
||||
lastFrameTime = 0.0;
|
||||
|
||||
flips = 0;
|
||||
fps = 0.0;
|
||||
|
@ -466,38 +471,21 @@ enum {
|
|||
FPS_LIMIT_CUSTOM = 1,
|
||||
};
|
||||
|
||||
// Start out assuming we'll go at 59.94 NTSC.
|
||||
static float timestepSmooth[8] = {
|
||||
1.001f / 60.0f, 1.001f / 60.0f, 1.001f / 60.0f, 1.001f / 60.0f,
|
||||
1.001f / 60.0f, 1.001f / 60.0f, 1.001f / 60.0f, 1.001f / 60.0f,
|
||||
};
|
||||
static int timestepNext = 0;
|
||||
|
||||
static float CalculateSmoothTimestep(float lastTimestep) {
|
||||
// Straight up ignore timesteps that would cause sub-10 fps speeds.
|
||||
if (lastTimestep < 1.001f / 10.0f) {
|
||||
timestepSmooth[timestepNext] = lastTimestep;
|
||||
}
|
||||
timestepNext = (timestepNext + 1) % ARRAY_SIZE(timestepSmooth);
|
||||
|
||||
float summed = 0.0f;
|
||||
for (size_t i = 0; i < ARRAY_SIZE(timestepSmooth); ++i) {
|
||||
summed += timestepSmooth[i];
|
||||
}
|
||||
return summed / (float)ARRAY_SIZE(timestepSmooth);
|
||||
}
|
||||
|
||||
void __DisplaySetWasPaused() {
|
||||
wasPaused = true;
|
||||
}
|
||||
|
||||
static bool FrameTimingThrottled() {
|
||||
if (PSP_CoreParameter().fpsLimit == FPS_LIMIT_CUSTOM && g_Config.iFpsLimit == 0) {
|
||||
return false;
|
||||
}
|
||||
return !PSP_CoreParameter().unthrottle;
|
||||
}
|
||||
|
||||
// Let's collect all the throttling and frameskipping logic here.
|
||||
void DoFrameTiming(bool &throttle, bool &skipFrame, float lastTimestep) {
|
||||
float timestep = CalculateSmoothTimestep(lastTimestep);
|
||||
void DoFrameTiming(bool &throttle, bool &skipFrame, float timestep) {
|
||||
int fpsLimiter = PSP_CoreParameter().fpsLimit;
|
||||
throttle = !PSP_CoreParameter().unthrottle;
|
||||
if (fpsLimiter == FPS_LIMIT_CUSTOM && g_Config.iFpsLimit == 0)
|
||||
throttle = false;
|
||||
throttle = FrameTimingThrottled();
|
||||
skipFrame = false;
|
||||
|
||||
// Check if the frameskipping code should be enabled. If neither throttling or frameskipping is on,
|
||||
|
@ -513,18 +501,28 @@ void DoFrameTiming(bool &throttle, bool &skipFrame, float lastTimestep) {
|
|||
return;
|
||||
}
|
||||
|
||||
|
||||
if (!throttle && !doFrameSkip)
|
||||
return;
|
||||
|
||||
time_update();
|
||||
|
||||
curFrameTime = time_now_d();
|
||||
if (nextFrameTime == 0.0 || wasPaused) {
|
||||
nextFrameTime = time_now_d() + timestep;
|
||||
|
||||
float scaledTimestep = timestep;
|
||||
if (fpsLimiter == FPS_LIMIT_CUSTOM && g_Config.iFpsLimit != 0) {
|
||||
scaledTimestep *= 60.0f / g_Config.iFpsLimit;
|
||||
}
|
||||
|
||||
if (lastFrameTime == 0.0 || wasPaused) {
|
||||
nextFrameTime = time_now_d() + scaledTimestep;
|
||||
if (wasPaused)
|
||||
wasPaused = false;
|
||||
} else {
|
||||
// Advance lastFrameTime by a constant amount each frame,
|
||||
// but don't let it get too far behind as things can get very jumpy.
|
||||
const double maxFallBehindFrames = 5.5;
|
||||
|
||||
nextFrameTime = std::max(lastFrameTime + scaledTimestep, time_now_d() - maxFallBehindFrames * scaledTimestep);
|
||||
}
|
||||
curFrameTime = time_now_d();
|
||||
|
||||
// Auto-frameskip automatically if speed limit is set differently than the default.
|
||||
if (g_Config.bAutoFrameSkip || (g_Config.iFrameSkip == 0 && fpsLimiter == FPS_LIMIT_CUSTOM && g_Config.iFpsLimit > 60)) {
|
||||
|
@ -543,8 +541,8 @@ void DoFrameTiming(bool &throttle, bool &skipFrame, float lastTimestep) {
|
|||
|
||||
if (curFrameTime < nextFrameTime && throttle) {
|
||||
// If time gap is huge just jump (somebody unthrottled)
|
||||
if ((nextFrameTime - curFrameTime > 2*timestep) && fpsLimiter == FPS_LIMIT_NORMAL) {
|
||||
nextFrameTime = curFrameTime + timestep;
|
||||
if (nextFrameTime - curFrameTime > 2*scaledTimestep) {
|
||||
nextFrameTime = curFrameTime;
|
||||
} else {
|
||||
// Wait until we've caught up.
|
||||
while (time_now_d() < nextFrameTime) {
|
||||
|
@ -554,16 +552,39 @@ void DoFrameTiming(bool &throttle, bool &skipFrame, float lastTimestep) {
|
|||
}
|
||||
curFrameTime = time_now_d();
|
||||
}
|
||||
// Advance lastFrameTime by a constant amount each frame,
|
||||
// but don't let it get too far behind as things can get very jumpy.
|
||||
const double maxFallBehindFrames = 5.5;
|
||||
|
||||
// 3 states of fps limiter
|
||||
if (fpsLimiter == FPS_LIMIT_NORMAL) {
|
||||
nextFrameTime = std::max(nextFrameTime + timestep, time_now_d() - maxFallBehindFrames * timestep);
|
||||
} else if (fpsLimiter == FPS_LIMIT_CUSTOM) {
|
||||
double customLimiter = (g_Config.iFpsLimit / 60.0f) / timestep;
|
||||
nextFrameTime = std::max(nextFrameTime + 1.0 / customLimiter, time_now_d() - maxFallBehindFrames / customLimiter);
|
||||
lastFrameTime = nextFrameTime;
|
||||
}
|
||||
|
||||
void DoFrameIdleTiming() {
|
||||
if (!FrameTimingThrottled() || wasPaused) {
|
||||
return;
|
||||
}
|
||||
|
||||
time_update();
|
||||
|
||||
double dist = time_now_d() - lastFrameTime;
|
||||
// Ignore if the distance is just crazy. May mean wrap or pause.
|
||||
if (dist < 0.0 || dist >= 15 * timePerVblank) {
|
||||
return;
|
||||
}
|
||||
|
||||
float scaledVblank = timePerVblank;
|
||||
if (PSP_CoreParameter().fpsLimit == FPS_LIMIT_CUSTOM) {
|
||||
// 0 is handled in FrameTimingThrottled().
|
||||
scaledVblank *= 60.0f / g_Config.iFpsLimit;
|
||||
}
|
||||
|
||||
// If we have over at least a vblank of spare time, maintain at least 30fps in delay.
|
||||
// This prevents fast forward during loading screens.
|
||||
const double thresh = lastFrameTime + (numVBlanksSinceFlip - 1) * scaledVblank;
|
||||
if (numVBlanksSinceFlip >= 2 && time_now_d() < thresh) {
|
||||
// Give a little extra wiggle room in case the next vblank does more work.
|
||||
const double goal = lastFrameTime + numVBlanksSinceFlip * scaledVblank - 0.001;
|
||||
while (time_now_d() < goal) {
|
||||
sleep_ms(1);
|
||||
time_update();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -637,8 +658,7 @@ void hleEnterVblank(u64 userdata, int cyclesLate) {
|
|||
gpuStats.numFlips++;
|
||||
|
||||
bool throttle, skipFrame;
|
||||
// 1.001f to compensate for the classic 59.94 NTSC framerate that the PSP seems to have.
|
||||
DoFrameTiming(throttle, skipFrame, (float)numVBlanksSinceFlip * (1.001f / 60.0f));
|
||||
DoFrameTiming(throttle, skipFrame, (float)numVBlanksSinceFlip * timePerVblank);
|
||||
|
||||
int maxFrameskip = 8;
|
||||
if (throttle) {
|
||||
|
@ -662,6 +682,9 @@ void hleEnterVblank(u64 userdata, int cyclesLate) {
|
|||
// place to do housekeeping.
|
||||
CoreTiming::ScheduleEvent(0 - cyclesLate, afterFlipEvent, 0);
|
||||
numVBlanksSinceFlip = 0;
|
||||
} else {
|
||||
// Okay, there's no new frame to draw. But audio may be playing, so we need to time still.
|
||||
DoFrameIdleTiming();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -689,18 +712,14 @@ void hleLagSync(u64 userdata, int cyclesLate) {
|
|||
// Our normal timing is very "stop and go". This is efficient, but causes real world lag.
|
||||
// This event (optionally) runs every 1ms to sync with the real world.
|
||||
|
||||
if (PSP_CoreParameter().unthrottle) {
|
||||
if (!FrameTimingThrottled()) {
|
||||
lagSyncScheduled = false;
|
||||
return;
|
||||
}
|
||||
|
||||
float scale = 1.0f;
|
||||
if (PSP_CoreParameter().fpsLimit == FPS_LIMIT_CUSTOM) {
|
||||
if (g_Config.iFpsLimit == 0) {
|
||||
lagSyncScheduled = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// 0 is handled in FrameTimingThrottled().
|
||||
scale = 60.0f / g_Config.iFpsLimit;
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue