diff --git a/include/core/framerate.hpp b/include/core/framerate.hpp index 92edd91c..8016da59 100644 --- a/include/core/framerate.hpp +++ b/include/core/framerate.hpp @@ -3,6 +3,19 @@ #include +/** + * Set the target speed multiplier. + * + * Parameter multiplier: The multiplier target. May be INFINITE. + */ +void set_speed_multiplier(double multiplier) throw(); + +/** + * Get the target speed multiplier. + * + * Returns: The multiplier. May be INFINITE. + */ +double get_speed_multiplier() throw(); /** * Sets the nominal frame rate. Framerate limiting tries to maintain the nominal framerate when there is no other @@ -11,11 +24,11 @@ void set_nominal_framerate(double fps) throw(); /** - * Returns the current realized framerate. + * Returns the current realized framerate multiplier. * - * returns: The framerate the system is currently archiving. + * returns: The framerate multiplier the system is currently archiving. */ -double get_framerate() throw(); +double get_realized_multiplier() throw(); /** * Freeze time. diff --git a/src/core/framerate.cpp b/src/core/framerate.cpp index 531629e1..b3f739cb 100644 --- a/src/core/framerate.cpp +++ b/src/core/framerate.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #define HISTORY_FRAMES 10 @@ -25,10 +26,11 @@ namespace bool time_frozen = true; uint64_t frame_number = 0; uint64_t frame_start_times[HISTORY_FRAMES]; - double nominal_rate = 100; - bool target_nominal = true; - double target_fps = 100.0; - bool target_infinite = false; + //Framerate. + double nominal_framerate = 60; + double multiplier_framerate = 1; + mutex_class framerate_lock; + bool turboed = false; uint64_t get_time(uint64_t curtime, bool update) { @@ -58,86 +60,19 @@ namespace frame_number++; } - struct setting_targetfps : public setting + std::pair read_fps() { - setting_targetfps() throw(std::bad_alloc) - : setting(lsnes_set, "targetfps") + double n, m; { + umutex_class h(framerate_lock); + n = nominal_framerate; + m = multiplier_framerate; } - - bool blank(bool really) throw(std::bad_alloc, std::runtime_error) - { - if(!really) - return true; - target_nominal = true; - target_infinite = false; - target_fps = 100.0; - return true; - } - - bool is_set() throw() - { - return !target_nominal; - } - - virtual void set(const std::string& value) throw(std::bad_alloc, std::runtime_error) - { - double tmp; - const char* s; - char* e; - if(value == "infinite") { - target_infinite = true; - target_nominal = false; - return; - } - s = value.c_str(); - tmp = strtod(s, &e); - if(*e) - throw std::runtime_error("Invalid frame rate"); - if(tmp < 0.001) - throw std::runtime_error("Target frame rate must be at least 0.001fps"); - target_fps = tmp; - target_infinite = false; - target_nominal = false; - } - - virtual std::string get() throw(std::bad_alloc) - { - if(target_nominal) - return ""; - else if(target_infinite) - return "infinite"; - else { - std::ostringstream o; - o << target_fps; - return o.str(); - } - } - - std::pair read() throw() - { - lock_holder lck(this); - return std::make_pair(target_infinite, target_fps); - } - - void set_nominal_framerate(double fps) - { - lock_holder(this); - nominal_rate = fps; - if(target_nominal) { - target_fps = nominal_rate; - target_infinite = false; - } - } - - double get_framerate() - { - lock_holder(this); - return 100.0 * get_realized_fps() / nominal_rate; - } - } targetfps; - - bool turboed = false; + if(m == std::numeric_limits::infinity()) + return std::make_pair(true, 0); + else + return std::make_pair(false, n * m); + } function_ptr_command<> tturbo(lsnes_cmd, "toggle-turbo", "Toggle turbo", "Syntax: toggle-turbo\nToggle turbo mode.\n", @@ -161,6 +96,21 @@ namespace inverse_bind turbot(lsnes_mapper, "toggle-turbo", "Speedā€£Turbo toggle"); } + +//Set the speed multiplier. Note that INFINITE is a valid multiplier. +void set_speed_multiplier(double multiplier) throw() +{ + umutex_class h(framerate_lock); + multiplier_framerate = multiplier; +} + +//Get the speed multiplier. Note that this may be INFINITE. +double get_speed_multiplier() throw() +{ + umutex_class h(framerate_lock); + return multiplier_framerate; +} + void freeze_time(uint64_t curtime) { get_time(curtime, true); @@ -176,12 +126,14 @@ void unfreeze_time(uint64_t curtime) void set_nominal_framerate(double fps) throw() { - targetfps.set_nominal_framerate(fps); + umutex_class h(framerate_lock); + nominal_framerate = fps; } -double get_framerate() throw() +double get_realized_multiplier() throw() { - return targetfps.get_framerate(); + umutex_class h(framerate_lock); + return get_realized_fps() / nominal_framerate; } void ack_frame_tick(uint64_t usec) throw() @@ -192,17 +144,17 @@ void ack_frame_tick(uint64_t usec) throw() uint64_t to_wait_frame(uint64_t usec) throw() { - auto target = targetfps.read(); + auto target = read_fps(); if(!frame_number || target.first || turboed || graphics_driver_is_dummy()) return 0; uint64_t lintime = get_time(usec, true); uint64_t frame_lasted = lintime - frame_start_times[0]; - uint64_t frame_should_last = 1000000 / (target.second * nominal_rate / 100); + uint64_t frame_should_last = 1000000 / target.second; if(frame_lasted >= frame_should_last) return 0; //We are late. uint64_t history_frames = min(frame_number, static_cast(HISTORY_FRAMES)); uint64_t history_lasted = lintime - frame_start_times[history_frames - 1]; - uint64_t history_should_last = history_frames * 1000000 / (target.second * nominal_rate / 100); + uint64_t history_should_last = history_frames * 1000000 / target.second; if(history_lasted >= history_should_last) return 0; return min(history_should_last - history_lasted, frame_should_last - frame_lasted); diff --git a/src/core/mainloop.cpp b/src/core/mainloop.cpp index 8a7f3050..f42a3426 100644 --- a/src/core/mainloop.cpp +++ b/src/core/mainloop.cpp @@ -298,7 +298,7 @@ void update_movie_state() _status.erase("Saveslot"); { std::ostringstream x; - x << get_framerate(); + x << 100 * get_realized_multiplier(); _status.set("SPD%", x.str()); } do_watch_memory(); diff --git a/src/platform/wxwidgets/mainwindow.cpp b/src/platform/wxwidgets/mainwindow.cpp index 5e5976e6..f7daecf4 100644 --- a/src/platform/wxwidgets/mainwindow.cpp +++ b/src/platform/wxwidgets/mainwindow.cpp @@ -265,11 +265,10 @@ namespace void set_speed(double target) { - std::string v = (stringfmt() << target).str(); if(target < 0) - lsnes_set.set("targetfps", "infinite"); + set_speed_multiplier(std::numeric_limits::infinity()); else - lsnes_set.set("targetfps", v); + set_speed_multiplier(target / 100); } class autohold_menu; @@ -1165,10 +1164,20 @@ void wxwin_mainwindow::handle_menu_click_cancelable(wxCommandEvent& e) } case wxID_SET_SPEED: { bool bad = false; - std::string value = lsnes_set.is_set("targetfps") ? lsnes_set.get("targetfps") : ""; + std::string value = "infinite"; + double val = get_speed_multiplier(); + if(!(val == std::numeric_limits::infinity())) + value = (stringfmt() << (100 * val)).str(); value = pick_text(this, "Set speed", "Enter percentage speed (or \"infinite\"):", value); try { - lsnes_set.set("targetfps", value); + if(value == "infinite") + set_speed_multiplier(std::numeric_limits::infinity()); + else { + double v = parse_value(value) / 100; + if(v <= 0.0001) + throw 42; + set_speed_multiplier(v); + } } catch(...) { wxMessageBox(wxT("Invalid speed"), _T("Error"), wxICON_EXCLAMATION | wxOK, this); }