Fix the speed throttle
This new algorithm shouldn't give ridiculously low framerates nor be prone to oscillation.
This commit is contained in:
parent
2e0886b284
commit
308412e502
4 changed files with 90 additions and 24 deletions
|
@ -25,7 +25,21 @@ void set_nominal_framerate(double fps) throw();
|
||||||
double get_framerate() throw();
|
double get_framerate() throw();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Acknowledge frame start for timing purposes.
|
* Freeze time.
|
||||||
|
*
|
||||||
|
* Parameter usec: Current time in microseconds.
|
||||||
|
*/
|
||||||
|
void freeze_time(uint64_t usec);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unfreeze time.
|
||||||
|
*
|
||||||
|
* Parameter usec: Current time in microseconds.
|
||||||
|
*/
|
||||||
|
void unfreeze_time(uint64_t usec);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Acknowledge frame start for timing purposes. If time is frozen, it is automatically unfrozen.
|
||||||
*
|
*
|
||||||
* parameter usec: Current time (relative to some unknown epoch) in microseconds.
|
* parameter usec: Current time (relative to some unknown epoch) in microseconds.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -10,18 +10,48 @@
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
#define DEFAULT_NOMINAL_RATE 60
|
#define DEFAULT_NOMINAL_RATE 60
|
||||||
|
#define HISTORY_FRAMES 10
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
|
uint64_t last_time_update = 0;
|
||||||
|
uint64_t time_at_last_update = 0;
|
||||||
|
bool time_frozen = true;
|
||||||
|
uint64_t frame_number = 0;
|
||||||
|
uint64_t frame_start_times[HISTORY_FRAMES];
|
||||||
double nominal_rate = DEFAULT_NOMINAL_RATE;
|
double nominal_rate = DEFAULT_NOMINAL_RATE;
|
||||||
double fps_value = 0;
|
|
||||||
const double exp_factor = 0.97;
|
|
||||||
uint64_t last_frame_usec = 0;
|
|
||||||
bool last_frame_usec_valid = false;
|
|
||||||
bool target_nominal = true;
|
bool target_nominal = true;
|
||||||
double target_fps = DEFAULT_NOMINAL_RATE;
|
double target_fps = DEFAULT_NOMINAL_RATE;
|
||||||
bool target_infinite = false;
|
bool target_infinite = false;
|
||||||
uint64_t wait_duration = 0;
|
|
||||||
|
uint64_t get_time(uint64_t curtime, bool update)
|
||||||
|
{
|
||||||
|
if(curtime < last_time_update || time_frozen)
|
||||||
|
return time_at_last_update;
|
||||||
|
if(update) {
|
||||||
|
time_at_last_update += (curtime - last_time_update);
|
||||||
|
last_time_update = curtime;
|
||||||
|
return time_at_last_update;
|
||||||
|
} else
|
||||||
|
return time_at_last_update + (curtime - last_time_update);
|
||||||
|
}
|
||||||
|
|
||||||
|
double get_realized_fps()
|
||||||
|
{
|
||||||
|
if(frame_number < 2)
|
||||||
|
return 0;
|
||||||
|
if(frame_number >= HISTORY_FRAMES)
|
||||||
|
return (1000000.0 * (HISTORY_FRAMES - 1)) / (frame_start_times[0] - frame_start_times[HISTORY_FRAMES - 1] + 1);
|
||||||
|
return (1000000.0 * (frame_number - 1)) / (frame_start_times[0] - frame_start_times[frame_number - 1] + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void add_frame(uint64_t linear_time)
|
||||||
|
{
|
||||||
|
for(size_t i = HISTORY_FRAMES - 2; i < HISTORY_FRAMES; i--)
|
||||||
|
frame_start_times[i + 1] = frame_start_times[i];
|
||||||
|
frame_start_times[0] = linear_time;
|
||||||
|
frame_number++;
|
||||||
|
}
|
||||||
|
|
||||||
struct setting_targetfps : public setting
|
struct setting_targetfps : public setting
|
||||||
{
|
{
|
||||||
|
@ -67,6 +97,8 @@ namespace
|
||||||
{
|
{
|
||||||
if(target_nominal)
|
if(target_nominal)
|
||||||
return "";
|
return "";
|
||||||
|
else if(target_infinite)
|
||||||
|
return "infinite";
|
||||||
else {
|
else {
|
||||||
std::ostringstream o;
|
std::ostringstream o;
|
||||||
o << target_fps;
|
o << target_fps;
|
||||||
|
@ -77,6 +109,19 @@ namespace
|
||||||
} targetfps;
|
} targetfps;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void freeze_time(uint64_t curtime)
|
||||||
|
{
|
||||||
|
get_time(curtime, true);
|
||||||
|
time_frozen = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void unfreeze_time(uint64_t curtime)
|
||||||
|
{
|
||||||
|
if(time_frozen)
|
||||||
|
last_time_update = curtime;
|
||||||
|
time_frozen = false;
|
||||||
|
}
|
||||||
|
|
||||||
void set_nominal_framerate(double fps) throw()
|
void set_nominal_framerate(double fps) throw()
|
||||||
{
|
{
|
||||||
nominal_rate = fps;
|
nominal_rate = fps;
|
||||||
|
@ -88,31 +133,34 @@ void set_nominal_framerate(double fps) throw()
|
||||||
|
|
||||||
double get_framerate() throw()
|
double get_framerate() throw()
|
||||||
{
|
{
|
||||||
return fps_value;
|
return 100.0 * get_realized_fps() / nominal_rate;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ack_frame_tick(uint64_t usec) throw()
|
void ack_frame_tick(uint64_t usec) throw()
|
||||||
{
|
{
|
||||||
if(!last_frame_usec_valid) {
|
unfreeze_time(usec);
|
||||||
last_frame_usec = usec;
|
add_frame(get_time(usec, true));
|
||||||
last_frame_usec_valid = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
uint64_t frame_usec = usec - last_frame_usec;
|
|
||||||
fps_value = exp_factor * fps_value + (1 - exp_factor) * (1000000.0 / frame_usec);
|
|
||||||
last_frame_usec = usec;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t to_wait_frame(uint64_t usec) throw()
|
uint64_t to_wait_frame(uint64_t usec) throw()
|
||||||
{
|
{
|
||||||
//Very simple algorithm. TODO: Make better one.
|
if(!frame_number || target_infinite)
|
||||||
if(!last_frame_usec_valid || target_infinite)
|
|
||||||
return 0;
|
return 0;
|
||||||
if(get_framerate() < target_fps && wait_duration > 0)
|
uint64_t lintime = get_time(usec, true);
|
||||||
wait_duration -= 1000;
|
uint64_t frame_lasted = lintime - frame_start_times[0];
|
||||||
else if(get_framerate() > target_fps)
|
uint64_t frame_should_last = 1000000 / target_fps;
|
||||||
wait_duration += 1000;
|
if(frame_lasted >= frame_should_last)
|
||||||
return wait_duration;
|
return 0; //We are late.
|
||||||
|
uint64_t maxwait = frame_should_last - frame_lasted;
|
||||||
|
uint64_t history_frames = (frame_number < HISTORY_FRAMES) ? frame_number : HISTORY_FRAMES;
|
||||||
|
uint64_t history_lasted = lintime - frame_start_times[history_frames - 1];
|
||||||
|
uint64_t history_should_last = history_frames * 1000000 / target_fps;
|
||||||
|
if(history_lasted >= history_should_last)
|
||||||
|
return 0;
|
||||||
|
uint64_t history_wait = history_should_last - history_lasted;
|
||||||
|
if(history_wait > maxwait)
|
||||||
|
history_wait = maxwait;
|
||||||
|
return history_wait;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -278,7 +278,7 @@ void update_movie_state()
|
||||||
{
|
{
|
||||||
std::ostringstream x;
|
std::ostringstream x;
|
||||||
x << get_framerate();
|
x << get_framerate();
|
||||||
_status.set("FPS", x.str());
|
_status.set("SPD%", x.str());
|
||||||
}
|
}
|
||||||
do_watch_memory();
|
do_watch_memory();
|
||||||
|
|
||||||
|
|
|
@ -416,12 +416,14 @@ namespace
|
||||||
|
|
||||||
void platform::flush_command_queue() throw()
|
void platform::flush_command_queue() throw()
|
||||||
{
|
{
|
||||||
|
if(modal_pause || normal_pause)
|
||||||
|
freeze_time(get_utime());
|
||||||
init_threading();
|
init_threading();
|
||||||
while(true) {
|
while(true) {
|
||||||
mutex::holder h(*queue_lock);
|
mutex::holder h(*queue_lock);
|
||||||
internal_run_queues(true);
|
internal_run_queues(true);
|
||||||
if(!pausing_allowed)
|
if(!pausing_allowed)
|
||||||
return;
|
break;
|
||||||
uint64_t now = get_utime();
|
uint64_t now = get_utime();
|
||||||
uint64_t waitleft = 0;
|
uint64_t waitleft = 0;
|
||||||
waitleft = (now < continue_time) ? (continue_time - now) : 0;
|
waitleft = (now < continue_time) ? (continue_time - now) : 0;
|
||||||
|
@ -433,6 +435,8 @@ void platform::flush_command_queue() throw()
|
||||||
return;
|
return;
|
||||||
//If we had to wait, check queues at least once more.
|
//If we had to wait, check queues at least once more.
|
||||||
}
|
}
|
||||||
|
if(!modal_pause && !normal_pause)
|
||||||
|
unfreeze_time(get_utime());
|
||||||
}
|
}
|
||||||
|
|
||||||
void platform::set_paused(bool enable) throw()
|
void platform::set_paused(bool enable) throw()
|
||||||
|
|
Loading…
Add table
Reference in a new issue