From c4e4fa53d9200bbac34a498d55cde381e3112764 Mon Sep 17 00:00:00 2001 From: emmauss Date: Wed, 7 Dec 2016 07:33:19 +1200 Subject: [PATCH] Implement Frame rate limiter (#2223) * implement frame limiter * fixes --- src/citra/config.cpp | 2 ++ src/citra/default_ini.h | 4 ++++ src/citra_qt/config.cpp | 2 ++ src/citra_qt/configure_graphics.cpp | 2 ++ src/citra_qt/configure_graphics.ui | 7 ++++++ src/core/hw/gpu.cpp | 33 +++++++++++++++++++++++++++++ src/core/settings.cpp | 1 + src/core/settings.h | 1 + src/video_core/video_core.cpp | 1 + src/video_core/video_core.h | 1 + 10 files changed, 54 insertions(+) diff --git a/src/citra/config.cpp b/src/citra/config.cpp index fe74fcd72..29462c982 100644 --- a/src/citra/config.cpp +++ b/src/citra/config.cpp @@ -66,6 +66,8 @@ void Config::ReadValues() { Settings::values.use_scaled_resolution = sdl2_config->GetBoolean("Renderer", "use_scaled_resolution", false); Settings::values.use_vsync = sdl2_config->GetBoolean("Renderer", "use_vsync", false); + Settings::values.toggle_framelimit = + sdl2_config->GetBoolean("Renderer", "toggle_framelimit", true); Settings::values.bg_red = (float)sdl2_config->GetReal("Renderer", "bg_red", 1.0); Settings::values.bg_green = (float)sdl2_config->GetReal("Renderer", "bg_green", 1.0); diff --git a/src/citra/default_ini.h b/src/citra/default_ini.h index b98dc4d83..001b18ac2 100644 --- a/src/citra/default_ini.h +++ b/src/citra/default_ini.h @@ -64,6 +64,10 @@ use_vsync = # 0 (default): Default Top Bottom Screen, 1: Single Screen Only, 2: Large Screen Small Screen layout_option = +#Whether to toggle frame limiter on or off. +# 0: Off , 1 (default): On +toggle_framelimit = + # Swaps the prominent screen with the other screen. # For example, if Single Screen is chosen, setting this to 1 will display the bottom screen instead of the top screen. # 0 (default): Top Screen is prominent, 1: Bottom Screen is prominent diff --git a/src/citra_qt/config.cpp b/src/citra_qt/config.cpp index 3cdfe6443..06a4e9d25 100644 --- a/src/citra_qt/config.cpp +++ b/src/citra_qt/config.cpp @@ -47,6 +47,7 @@ void Config::ReadValues() { Settings::values.use_scaled_resolution = qt_config->value("use_scaled_resolution", false).toBool(); Settings::values.use_vsync = qt_config->value("use_vsync", false).toBool(); + Settings::values.toggle_framelimit = qt_config->value("toggle_framelimit", true).toBool(); Settings::values.bg_red = qt_config->value("bg_red", 1.0).toFloat(); Settings::values.bg_green = qt_config->value("bg_green", 1.0).toFloat(); @@ -152,6 +153,7 @@ void Config::SaveValues() { qt_config->setValue("use_shader_jit", Settings::values.use_shader_jit); qt_config->setValue("use_scaled_resolution", Settings::values.use_scaled_resolution); qt_config->setValue("use_vsync", Settings::values.use_vsync); + qt_config->setValue("toggle_framelimit", Settings::values.toggle_framelimit); // Cast to double because Qt's written float values are not human-readable qt_config->setValue("bg_red", (double)Settings::values.bg_red); diff --git a/src/citra_qt/configure_graphics.cpp b/src/citra_qt/configure_graphics.cpp index 29834e11b..36f10c8d7 100644 --- a/src/citra_qt/configure_graphics.cpp +++ b/src/citra_qt/configure_graphics.cpp @@ -23,6 +23,7 @@ void ConfigureGraphics::setConfiguration() { ui->toggle_shader_jit->setChecked(Settings::values.use_shader_jit); ui->toggle_scaled_resolution->setChecked(Settings::values.use_scaled_resolution); ui->toggle_vsync->setChecked(Settings::values.use_vsync); + ui->toggle_framelimit->setChecked(Settings::values.toggle_framelimit); ui->layout_combobox->setCurrentIndex(static_cast(Settings::values.layout_option)); ui->swap_screen->setChecked(Settings::values.swap_screen); } @@ -32,6 +33,7 @@ void ConfigureGraphics::applyConfiguration() { Settings::values.use_shader_jit = ui->toggle_shader_jit->isChecked(); Settings::values.use_scaled_resolution = ui->toggle_scaled_resolution->isChecked(); Settings::values.use_vsync = ui->toggle_vsync->isChecked(); + Settings::values.toggle_framelimit = ui->toggle_framelimit->isChecked(); Settings::values.layout_option = static_cast(ui->layout_combobox->currentIndex()); Settings::values.swap_screen = ui->swap_screen->isChecked(); diff --git a/src/citra_qt/configure_graphics.ui b/src/citra_qt/configure_graphics.ui index af16a4292..964aa0bbd 100644 --- a/src/citra_qt/configure_graphics.ui +++ b/src/citra_qt/configure_graphics.ui @@ -50,6 +50,13 @@ + + + + Limit framerate + + + diff --git a/src/core/hw/gpu.cpp b/src/core/hw/gpu.cpp index 45dedea68..cfba82e51 100644 --- a/src/core/hw/gpu.cpp +++ b/src/core/hw/gpu.cpp @@ -8,7 +8,10 @@ #include "common/color.h" #include "common/common_types.h" #include "common/logging/log.h" +#include "common/math_util.h" #include "common/microprofile.h" +#include "common/thread.h" +#include "common/timer.h" #include "common/vector_math.h" #include "core/core_timing.h" #include "core/hle/service/gsp_gpu.h" @@ -35,6 +38,14 @@ const u64 frame_ticks = 268123480ull / 60; static int vblank_event; /// Total number of frames drawn static u64 frame_count; +/// Start clock for frame limiter +static u32 time_point; +/// Total delay caused by slow frames +static float time_delay; +constexpr float FIXED_FRAME_TIME = 1000.0f / 60; +// 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 float MAX_LAG_TIME = 18; template inline void Read(T& var, const u32 raw_addr) { @@ -512,6 +523,21 @@ template void Write(u32 addr, const u32 data); template void Write(u32 addr, const u16 data); template void Write(u32 addr, const u8 data); +static void FrameLimiter() { + time_delay += FIXED_FRAME_TIME; + time_delay = MathUtil::Clamp(time_delay, -MAX_LAG_TIME, MAX_LAG_TIME); + s32 desired_time = static_cast(time_delay); + s32 elapsed_time = static_cast(Common::Timer::GetTimeMs() - time_point); + + if (elapsed_time < desired_time) { + Common::SleepCurrentThread(desired_time - elapsed_time); + } + + u32 frame_time = Common::Timer::GetTimeMs() - time_point; + + time_delay -= frame_time; +} + /// Update hardware static void VBlankCallback(u64 userdata, int cycles_late) { frame_count++; @@ -528,6 +554,12 @@ static void VBlankCallback(u64 userdata, int cycles_late) { // Check for user input updates Service::HID::Update(); + if (!Settings::values.use_vsync && Settings::values.toggle_framelimit) { + FrameLimiter(); + } + + time_point = Common::Timer::GetTimeMs(); + // Reschedule recurrent event CoreTiming::ScheduleEvent(frame_ticks - cycles_late, vblank_event); } @@ -563,6 +595,7 @@ void Init() { framebuffer_sub.active_fb = 0; frame_count = 0; + time_point = Common::Timer::GetTimeMs(); vblank_event = CoreTiming::RegisterEvent("GPU::VBlankCallback", VBlankCallback); CoreTiming::ScheduleEvent(frame_ticks, vblank_event); diff --git a/src/core/settings.cpp b/src/core/settings.cpp index 05f41f798..626e06cd9 100644 --- a/src/core/settings.cpp +++ b/src/core/settings.cpp @@ -21,6 +21,7 @@ void Apply() { VideoCore::g_hw_renderer_enabled = values.use_hw_renderer; VideoCore::g_shader_jit_enabled = values.use_shader_jit; VideoCore::g_scaled_resolution_enabled = values.use_scaled_resolution; + VideoCore::g_toggle_framelimit_enabled = values.toggle_framelimit; if (VideoCore::g_emu_window) { auto layout = VideoCore::g_emu_window->GetFramebufferLayout(); diff --git a/src/core/settings.h b/src/core/settings.h index 7470fdbeb..db4c8fada 100644 --- a/src/core/settings.h +++ b/src/core/settings.h @@ -90,6 +90,7 @@ struct Values { bool use_shader_jit; bool use_scaled_resolution; bool use_vsync; + bool toggle_framelimit; LayoutOption layout_option; bool swap_screen; diff --git a/src/video_core/video_core.cpp b/src/video_core/video_core.cpp index 83e33dfc2..8db882f59 100644 --- a/src/video_core/video_core.cpp +++ b/src/video_core/video_core.cpp @@ -21,6 +21,7 @@ std::atomic g_hw_renderer_enabled; std::atomic g_shader_jit_enabled; std::atomic g_scaled_resolution_enabled; std::atomic g_vsync_enabled; +std::atomic g_toggle_framelimit_enabled; /// Initialize the video core bool Init(EmuWindow* emu_window) { diff --git a/src/video_core/video_core.h b/src/video_core/video_core.h index e2d725ab1..c397c1974 100644 --- a/src/video_core/video_core.h +++ b/src/video_core/video_core.h @@ -38,6 +38,7 @@ extern EmuWindow* g_emu_window; ///< Emu window extern std::atomic g_hw_renderer_enabled; extern std::atomic g_shader_jit_enabled; extern std::atomic g_scaled_resolution_enabled; +extern std::atomic g_toggle_framelimit_enabled; /// Start the video core void Start();