Lime3DS/src/core/perf_stats.cpp
Yuri Kunde Schlesner fb1979d7e2 Core: Re-write frame limiter
Now based on std::chrono, and also works in terms of emulated time
instead of frames, so we can in the future frame-limit even when the
display is disabled, etc.

The frame limiter can also be enabled along with v-sync now, which
should be useful for those with displays running at more than 60 Hz.
2017-02-26 17:22:04 -08:00

105 lines
3.3 KiB
C++

// Copyright 2017 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <chrono>
#include <mutex>
#include <thread>
#include "common/math_util.h"
#include "core/hw/gpu.h"
#include "core/perf_stats.h"
#include "core/settings.h"
using namespace std::chrono_literals;
using DoubleSecs = std::chrono::duration<double, std::chrono::seconds::period>;
using std::chrono::duration_cast;
using std::chrono::microseconds;
namespace Core {
void PerfStats::BeginSystemFrame() {
std::lock_guard<std::mutex> lock(object_mutex);
frame_begin = Clock::now();
}
void PerfStats::EndSystemFrame() {
std::lock_guard<std::mutex> lock(object_mutex);
auto frame_end = Clock::now();
accumulated_frametime += frame_end - frame_begin;
system_frames += 1;
previous_frame_length = frame_end - previous_frame_end;
previous_frame_end = frame_end;
}
void PerfStats::EndGameFrame() {
std::lock_guard<std::mutex> lock(object_mutex);
game_frames += 1;
}
PerfStats::Results PerfStats::GetAndResetStats(u64 current_system_time_us) {
std::lock_guard<std::mutex> lock(object_mutex);
auto now = Clock::now();
// Walltime elapsed since stats were reset
auto interval = duration_cast<DoubleSecs>(now - reset_point).count();
auto system_us_per_second =
static_cast<double>(current_system_time_us - reset_point_system_us) / interval;
Results results{};
results.system_fps = static_cast<double>(system_frames) / interval;
results.game_fps = static_cast<double>(game_frames) / interval;
results.frametime = duration_cast<DoubleSecs>(accumulated_frametime).count() /
static_cast<double>(system_frames);
results.emulation_speed = system_us_per_second / 1'000'000.0;
// Reset counters
reset_point = now;
reset_point_system_us = current_system_time_us;
accumulated_frametime = Clock::duration::zero();
system_frames = 0;
game_frames = 0;
return results;
}
double PerfStats::GetLastFrameTimeScale() {
std::lock_guard<std::mutex> lock(object_mutex);
constexpr double FRAME_LENGTH = 1.0 / GPU::SCREEN_REFRESH_RATE;
return duration_cast<DoubleSecs>(previous_frame_length).count() / FRAME_LENGTH;
}
void FrameLimiter::DoFrameLimiting(u64 current_system_time_us) {
// Max lag caused by slow frames. Can be adjusted to compensate for too many slow frames. Higher
// values increases time needed to limit frame rate after spikes.
constexpr microseconds MAX_LAG_TIME_US = 25ms;
if (!Settings::values.toggle_framelimit) {
return;
}
auto now = Clock::now();
frame_limiting_delta_err += microseconds(current_system_time_us - previous_system_time_us);
frame_limiting_delta_err -= duration_cast<microseconds>(now - previous_walltime);
frame_limiting_delta_err =
MathUtil::Clamp(frame_limiting_delta_err, -MAX_LAG_TIME_US, MAX_LAG_TIME_US);
if (frame_limiting_delta_err > microseconds::zero()) {
std::this_thread::sleep_for(frame_limiting_delta_err);
auto now_after_sleep = Clock::now();
frame_limiting_delta_err -= duration_cast<microseconds>(now_after_sleep - now);
now = now_after_sleep;
}
previous_system_time_us = current_system_time_us;
previous_walltime = now;
}
} // namespace Core