1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-12-23 20:29:42 +00:00

Eliminates hard-coded concept of timer jitter.

This commit is contained in:
Thomas Harte 2020-06-25 23:59:44 -04:00
parent 478d081095
commit 3ddc1a1722
3 changed files with 50 additions and 6 deletions

View File

@ -11,24 +11,38 @@
#include "TimeTypes.hpp"
#include <cmath>
#include <cstdio>
namespace Time {
/*!
For platforms that provide no avenue into vsync tracking other than block-until-sync,
this class tracks: (i) how long frame draw takes; and (ii) the apparent frame period; in order
to suggest when you should next start drawing.
this class tracks: (i) how long frame draw takes; (ii) the apparent frame period; and
(iii) optionally, timer jitter; in order to suggest when you should next start drawing.
*/
class VSyncPredictor {
public:
/*!
Announces to the predictor that the work of producing an output frame has begun.
*/
void begin_redraw() {
redraw_begin_time_ = nanos_now();
}
/*!
Announces to the predictor that the work of producing an output frame has ended;
the predictor will use the amount of time between each begin/end pair to modify
its expectations as to how long it takes to draw a frame.
*/
void end_redraw() {
redraw_period_.post(nanos_now() - redraw_begin_time_);
}
/*!
Informs the predictor that a block-on-vsync has just ended, i.e. that the moment this
machine calls retrace is now. The predictor uses these notifications to estimate output
frame rate.
*/
void announce_vsync() {
const auto vsync_time = nanos_now();
if(last_vsync_) {
@ -37,16 +51,35 @@ class VSyncPredictor {
last_vsync_ = vsync_time;
}
/*!
Adds a record of how much jitter was experienced in scheduling; these values will be
factored into the @c suggested_draw_time if supplied.
A positive number means the timer occurred late. A negative number means it occurred early.
*/
void add_timer_jitter(Time::Nanos jitter) {
timer_jitter_.post(jitter);
}
/*!
Announces to the vsync predictor that output is now paused. This ends frame period
calculations until the next announce_vsync() restarts frame-length counting.
*/
void pause() {
last_vsync_ = 0;
}
/*!
@return The time at which redrawing should begin, given the predicted frame period, how
long it appears to take to draw a frame and how much jitter there is in scheduling
(if those figures are being supplied).
*/
Nanos suggested_draw_time() {
const auto mean = (vsync_period_.mean() - redraw_period_.mean()) / 1;
const auto variance = (vsync_period_.variance() + redraw_period_.variance()) / 1;
const auto mean = vsync_period_.mean() - redraw_period_.mean() - timer_jitter_.mean();
const auto variance = vsync_period_.variance() + redraw_period_.variance() + timer_jitter_.variance();
// Permit three standard deviations from the mean, to cover 99.9% of cases.
const auto period = mean + Nanos(3.0f * sqrt(float(variance)));
const auto period = mean - Nanos(3.0f * sqrt(float(variance)));
return last_vsync_ + period;
}
@ -94,6 +127,7 @@ class VSyncPredictor {
VarianceCollector vsync_period_{1'000'000'000 / 60}; // 60Hz: seems like a good first guess.
VarianceCollector redraw_period_{1'000'000'000 / 60}; // A less convincing first guess.
VarianceCollector timer_jitter_{0}; // Seed at 0 in case this feature isn't used by the owner.
};
}

View File

@ -17,6 +17,11 @@ void ScanTargetWidget::initializeGL() {
}
void ScanTargetWidget::paintGL() {
if(requested_redraw_time_) {
vsyncPredictor.add_timer_jitter(Time::nanos_now() - requested_redraw_time_);
requested_redraw_time_ = 0;
}
glClear(GL_COLOR_BUFFER_BIT);
// Gmynastics ahoy: if a producer has been specified or previously connected then:
@ -60,10 +65,12 @@ void ScanTargetWidget::vsync() {
vsyncPredictor.announce_vsync();
const auto time_now = Time::nanos_now();
const auto delay_time = ((vsyncPredictor.suggested_draw_time() - time_now) / 1'000'000) - 5; // TODO: the extra 5 is a random guess.
requested_redraw_time_ = vsyncPredictor.suggested_draw_time();
const auto delay_time = (requested_redraw_time_ - time_now) / 1'000'000;
if(delay_time > 0) {
QTimer::singleShot(delay_time, this, SLOT(repaint()));
} else {
requested_redraw_time_ = 0;
repaint();
}
}
@ -83,6 +90,7 @@ void ScanTargetWidget::stop() {
isConnected = false;
setDefaultClearColour();
vsyncPredictor.pause();
requested_redraw_time_ = 0;
}
void ScanTargetWidget::setDefaultClearColour() {

View File

@ -34,6 +34,8 @@ class ScanTargetWidget : public QOpenGLWidget
GLuint framebuffer = 0;
MachineTypes::ScanProducer *producer = nullptr;
Time::Nanos requested_redraw_time_ = 0;
void setDefaultClearColour();
private slots: