mirror of
https://github.com/TomHarte/CLK.git
synced 2025-01-11 08:30:55 +00:00
Trusts Qt to supply a refresh rate, and handles retina <-> non-retina window transitions.
This commit is contained in:
parent
b9e117cdcf
commit
a096a09c72
@ -10,6 +10,7 @@
|
||||
#define VSyncPredictor_hpp
|
||||
|
||||
#include "TimeTypes.hpp"
|
||||
#include <cassert>
|
||||
#include <cmath>
|
||||
#include <cstdio>
|
||||
|
||||
@ -44,11 +45,22 @@ class VSyncPredictor {
|
||||
frame rate.
|
||||
*/
|
||||
void announce_vsync() {
|
||||
const auto vsync_time = nanos_now();
|
||||
const auto now = nanos_now();
|
||||
|
||||
if(last_vsync_) {
|
||||
vsync_period_.post(vsync_time - last_vsync_);
|
||||
last_vsync_ += frame_duration_;
|
||||
vsync_jitter_.post(last_vsync_ - now);
|
||||
last_vsync_ = (last_vsync_ + now) >> 1;
|
||||
} else {
|
||||
last_vsync_ = now;
|
||||
}
|
||||
last_vsync_ = vsync_time;
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the frame rate for the target display.
|
||||
*/
|
||||
void set_frame_rate(float rate) {
|
||||
frame_duration_ = Nanos(1'000'000'000.0f / rate);
|
||||
}
|
||||
|
||||
/*!
|
||||
@ -75,12 +87,14 @@ class VSyncPredictor {
|
||||
(if those figures are being supplied).
|
||||
*/
|
||||
Nanos suggested_draw_time() {
|
||||
const auto mean = vsync_period_.mean() - redraw_period_.mean() - timer_jitter_.mean();
|
||||
const auto variance = vsync_period_.variance() + redraw_period_.variance() + timer_jitter_.variance();
|
||||
const auto mean = redraw_period_.mean() - timer_jitter_.mean() - vsync_jitter_.mean();
|
||||
const auto variance = redraw_period_.variance() + timer_jitter_.variance() + vsync_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)));
|
||||
|
||||
assert(abs(period) < 10'000'000'000);
|
||||
|
||||
return last_vsync_ + period;
|
||||
}
|
||||
|
||||
@ -95,6 +109,7 @@ class VSyncPredictor {
|
||||
}
|
||||
|
||||
void post(Time::Nanos value) {
|
||||
assert(abs(value) < 10'000'000'000); // 10 seconds is a very liberal maximum.
|
||||
sum_ -= history_[write_pointer_];
|
||||
sum_ += value;
|
||||
history_[write_pointer_] = value;
|
||||
@ -110,10 +125,10 @@ class VSyncPredictor {
|
||||
// in whole every time, given the way that the mean mutates.
|
||||
Time::Nanos variance = 0;
|
||||
for(int c = 0; c < 128; ++c) {
|
||||
const auto difference = (history_[c] * 128) - sum_;
|
||||
variance += difference * difference;
|
||||
const auto difference = ((history_[c] * 128) - sum_) / 128;
|
||||
variance += (difference * difference);
|
||||
}
|
||||
return variance / (128 * 128 * 128);
|
||||
return variance / 128;
|
||||
}
|
||||
|
||||
private:
|
||||
@ -124,8 +139,9 @@ class VSyncPredictor {
|
||||
|
||||
Nanos redraw_begin_time_ = 0;
|
||||
Nanos last_vsync_ = 0;
|
||||
Nanos frame_duration_ = 1'000'000'000 / 60;
|
||||
|
||||
VarianceCollector vsync_period_{1'000'000'000 / 60}; // 60Hz: seems like a good first guess.
|
||||
VarianceCollector vsync_jitter_{0};
|
||||
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.
|
||||
};
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
#include <QDebug>
|
||||
#include <QOpenGLContext>
|
||||
#include <QScreen>
|
||||
#include <QTimer>
|
||||
|
||||
#include "../../ClockReceiver/TimeTypes.hpp"
|
||||
@ -18,10 +19,18 @@ void ScanTargetWidget::initializeGL() {
|
||||
|
||||
void ScanTargetWidget::paintGL() {
|
||||
if(requested_redraw_time_) {
|
||||
vsyncPredictor.add_timer_jitter(Time::nanos_now() - requested_redraw_time_);
|
||||
const auto now = Time::nanos_now();
|
||||
vsyncPredictor.add_timer_jitter(now - requested_redraw_time_);
|
||||
requested_redraw_time_ = 0;
|
||||
}
|
||||
|
||||
const float newOutputScale = float(window()->screen()->devicePixelRatio());
|
||||
if(outputScale != newOutputScale) {
|
||||
outputScale = newOutputScale;
|
||||
resize();
|
||||
}
|
||||
vsyncPredictor.set_frame_rate(float(window()->screen()->refreshRate()));
|
||||
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
|
||||
// Gmynastics ahoy: if a producer has been specified or previously connected then:
|
||||
@ -52,11 +61,8 @@ void ScanTargetWidget::paintGL() {
|
||||
}
|
||||
|
||||
vsyncPredictor.begin_redraw();
|
||||
const int devicePixelRatio = QPaintDevice::devicePixelRatio();
|
||||
const int outputWidth = width()*devicePixelRatio;
|
||||
const int outputHeight = height()*devicePixelRatio;
|
||||
scanTarget->update(outputWidth, outputHeight);
|
||||
scanTarget->draw(outputWidth, outputHeight);
|
||||
scanTarget->update(scaledWidth, scaledHeight);
|
||||
scanTarget->draw(scaledWidth, scaledHeight);
|
||||
glFinish(); // Make sure all costs are properly accounted for in the vsync predictor.
|
||||
vsyncPredictor.end_redraw();
|
||||
}
|
||||
@ -79,8 +85,22 @@ void ScanTargetWidget::vsync() {
|
||||
}
|
||||
|
||||
void ScanTargetWidget::resizeGL(int w, int h) {
|
||||
const int devicePixelRatio = QPaintDevice::devicePixelRatio();
|
||||
glViewport(0, 0, w*devicePixelRatio, h*devicePixelRatio);
|
||||
if(width != w || height != h) {
|
||||
width = w;
|
||||
height = h;
|
||||
resize();
|
||||
}
|
||||
}
|
||||
|
||||
void ScanTargetWidget::resize() {
|
||||
const int newScaledWidth = int(float(width) * outputScale);
|
||||
const int newScaledHeight = int(float(height) * outputScale);
|
||||
|
||||
if(newScaledWidth != scaledWidth || newScaledHeight != scaledHeight) {
|
||||
scaledWidth = newScaledWidth;
|
||||
scaledHeight = newScaledHeight;
|
||||
glViewport(0, 0, scaledWidth, scaledHeight);
|
||||
}
|
||||
}
|
||||
|
||||
void ScanTargetWidget::setScanProducer(MachineTypes::ScanProducer *producer) {
|
||||
|
@ -38,6 +38,11 @@ class ScanTargetWidget : public QOpenGLWidget
|
||||
|
||||
void setDefaultClearColour();
|
||||
|
||||
int width = 0, height = 0;
|
||||
int scaledWidth = 0, scaledHeight = 0;
|
||||
float outputScale = 1.0f;
|
||||
void resize();
|
||||
|
||||
private slots:
|
||||
void vsync();
|
||||
};
|
||||
|
Loading…
x
Reference in New Issue
Block a user