Clean up framerate handling
Also stop saving the framerate (it is not meaningful to persist between sessions).
This commit is contained in:
parent
363e7c7a3c
commit
c9e05a607a
4 changed files with 70 additions and 96 deletions
|
@ -3,6 +3,19 @@
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
* 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();
|
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.
|
* Freeze time.
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
#include <sys/time.h>
|
#include <sys/time.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
#include <limits>
|
||||||
|
|
||||||
#define HISTORY_FRAMES 10
|
#define HISTORY_FRAMES 10
|
||||||
|
|
||||||
|
@ -25,10 +26,11 @@ namespace
|
||||||
bool time_frozen = true;
|
bool time_frozen = true;
|
||||||
uint64_t frame_number = 0;
|
uint64_t frame_number = 0;
|
||||||
uint64_t frame_start_times[HISTORY_FRAMES];
|
uint64_t frame_start_times[HISTORY_FRAMES];
|
||||||
double nominal_rate = 100;
|
//Framerate.
|
||||||
bool target_nominal = true;
|
double nominal_framerate = 60;
|
||||||
double target_fps = 100.0;
|
double multiplier_framerate = 1;
|
||||||
bool target_infinite = false;
|
mutex_class framerate_lock;
|
||||||
|
bool turboed = false;
|
||||||
|
|
||||||
uint64_t get_time(uint64_t curtime, bool update)
|
uint64_t get_time(uint64_t curtime, bool update)
|
||||||
{
|
{
|
||||||
|
@ -58,86 +60,19 @@ namespace
|
||||||
frame_number++;
|
frame_number++;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct setting_targetfps : public setting
|
std::pair<bool, double> read_fps()
|
||||||
{
|
{
|
||||||
setting_targetfps() throw(std::bad_alloc)
|
double n, m;
|
||||||
: setting(lsnes_set, "targetfps")
|
|
||||||
{
|
{
|
||||||
|
umutex_class h(framerate_lock);
|
||||||
|
n = nominal_framerate;
|
||||||
|
m = multiplier_framerate;
|
||||||
}
|
}
|
||||||
|
if(m == std::numeric_limits<double>::infinity())
|
||||||
bool blank(bool really) throw(std::bad_alloc, std::runtime_error)
|
return std::make_pair(true, 0);
|
||||||
{
|
else
|
||||||
if(!really)
|
return std::make_pair(false, n * m);
|
||||||
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<bool, double> 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;
|
|
||||||
|
|
||||||
function_ptr_command<> tturbo(lsnes_cmd, "toggle-turbo", "Toggle turbo",
|
function_ptr_command<> tturbo(lsnes_cmd, "toggle-turbo", "Toggle turbo",
|
||||||
"Syntax: toggle-turbo\nToggle turbo mode.\n",
|
"Syntax: toggle-turbo\nToggle turbo mode.\n",
|
||||||
|
@ -161,6 +96,21 @@ namespace
|
||||||
inverse_bind turbot(lsnes_mapper, "toggle-turbo", "Speed‣Turbo toggle");
|
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)
|
void freeze_time(uint64_t curtime)
|
||||||
{
|
{
|
||||||
get_time(curtime, true);
|
get_time(curtime, true);
|
||||||
|
@ -176,12 +126,14 @@ void unfreeze_time(uint64_t curtime)
|
||||||
|
|
||||||
void set_nominal_framerate(double fps) throw()
|
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()
|
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()
|
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())
|
if(!frame_number || target.first || turboed || graphics_driver_is_dummy())
|
||||||
return 0;
|
return 0;
|
||||||
uint64_t lintime = get_time(usec, true);
|
uint64_t lintime = get_time(usec, true);
|
||||||
uint64_t frame_lasted = lintime - frame_start_times[0];
|
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)
|
if(frame_lasted >= frame_should_last)
|
||||||
return 0; //We are late.
|
return 0; //We are late.
|
||||||
uint64_t history_frames = min(frame_number, static_cast<uint64_t>(HISTORY_FRAMES));
|
uint64_t history_frames = min(frame_number, static_cast<uint64_t>(HISTORY_FRAMES));
|
||||||
uint64_t history_lasted = lintime - frame_start_times[history_frames - 1];
|
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)
|
if(history_lasted >= history_should_last)
|
||||||
return 0;
|
return 0;
|
||||||
return min(history_should_last - history_lasted, frame_should_last - frame_lasted);
|
return min(history_should_last - history_lasted, frame_should_last - frame_lasted);
|
||||||
|
|
|
@ -298,7 +298,7 @@ void update_movie_state()
|
||||||
_status.erase("Saveslot");
|
_status.erase("Saveslot");
|
||||||
{
|
{
|
||||||
std::ostringstream x;
|
std::ostringstream x;
|
||||||
x << get_framerate();
|
x << 100 * get_realized_multiplier();
|
||||||
_status.set("SPD%", x.str());
|
_status.set("SPD%", x.str());
|
||||||
}
|
}
|
||||||
do_watch_memory();
|
do_watch_memory();
|
||||||
|
|
|
@ -265,11 +265,10 @@ namespace
|
||||||
|
|
||||||
void set_speed(double target)
|
void set_speed(double target)
|
||||||
{
|
{
|
||||||
std::string v = (stringfmt() << target).str();
|
|
||||||
if(target < 0)
|
if(target < 0)
|
||||||
lsnes_set.set("targetfps", "infinite");
|
set_speed_multiplier(std::numeric_limits<double>::infinity());
|
||||||
else
|
else
|
||||||
lsnes_set.set("targetfps", v);
|
set_speed_multiplier(target / 100);
|
||||||
}
|
}
|
||||||
|
|
||||||
class autohold_menu;
|
class autohold_menu;
|
||||||
|
@ -1165,10 +1164,20 @@ void wxwin_mainwindow::handle_menu_click_cancelable(wxCommandEvent& e)
|
||||||
}
|
}
|
||||||
case wxID_SET_SPEED: {
|
case wxID_SET_SPEED: {
|
||||||
bool bad = false;
|
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<double>::infinity()))
|
||||||
|
value = (stringfmt() << (100 * val)).str();
|
||||||
value = pick_text(this, "Set speed", "Enter percentage speed (or \"infinite\"):", value);
|
value = pick_text(this, "Set speed", "Enter percentage speed (or \"infinite\"):", value);
|
||||||
try {
|
try {
|
||||||
lsnes_set.set("targetfps", value);
|
if(value == "infinite")
|
||||||
|
set_speed_multiplier(std::numeric_limits<double>::infinity());
|
||||||
|
else {
|
||||||
|
double v = parse_value<double>(value) / 100;
|
||||||
|
if(v <= 0.0001)
|
||||||
|
throw 42;
|
||||||
|
set_speed_multiplier(v);
|
||||||
|
}
|
||||||
} catch(...) {
|
} catch(...) {
|
||||||
wxMessageBox(wxT("Invalid speed"), _T("Error"), wxICON_EXCLAMATION | wxOK, this);
|
wxMessageBox(wxT("Invalid speed"), _T("Error"), wxICON_EXCLAMATION | wxOK, this);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue