mirror of
https://github.com/TomHarte/CLK.git
synced 2024-11-25 16:31:42 +00:00
Attempts to be more rigorous in vsync prediction.
This commit is contained in:
parent
4417f81014
commit
9d4b49bbb5
@ -10,6 +10,7 @@
|
|||||||
#define VSyncPredictor_hpp
|
#define VSyncPredictor_hpp
|
||||||
|
|
||||||
#include "TimeTypes.hpp"
|
#include "TimeTypes.hpp"
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
namespace Time {
|
namespace Time {
|
||||||
|
|
||||||
@ -25,19 +26,13 @@ class VSyncPredictor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void end_redraw() {
|
void end_redraw() {
|
||||||
const auto redraw_length = nanos_now() - redraw_begin_time_;
|
redraw_period_.post(nanos_now() - redraw_begin_time_);
|
||||||
redraw_period_ =
|
|
||||||
((redraw_period_ * 9) +
|
|
||||||
((redraw_length) * 1)) / 10;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void announce_vsync() {
|
void announce_vsync() {
|
||||||
const auto vsync_time = nanos_now();
|
const auto vsync_time = nanos_now();
|
||||||
if(last_vsync_) {
|
if(last_vsync_) {
|
||||||
// Use an IIR to try to converge on frame times.
|
vsync_period_.post(vsync_time - last_vsync_);
|
||||||
vsync_period_ =
|
|
||||||
((vsync_period_ * 9) +
|
|
||||||
((vsync_time - last_vsync_) * 1)) / 10;
|
|
||||||
}
|
}
|
||||||
last_vsync_ = vsync_time;
|
last_vsync_ = vsync_time;
|
||||||
}
|
}
|
||||||
@ -47,19 +42,58 @@ class VSyncPredictor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Nanos suggested_draw_time() {
|
Nanos suggested_draw_time() {
|
||||||
// TODO: this is a very simple version of how this calculation
|
const auto mean = (vsync_period_.mean() - redraw_period_.mean()) / 1;
|
||||||
// should be made. It's tracking the average amount of time these
|
const auto variance = (vsync_period_.variance() + redraw_period_.variance()) / 1;
|
||||||
// things take, therefore will often be wrong. Deviations need to
|
|
||||||
// be accounted for.
|
// Permit three standard deviations from the mean, to cover 99.9% of cases.
|
||||||
return last_vsync_ + vsync_period_ - redraw_period_;
|
const auto period = mean + Nanos(3.0f * sqrt(float(variance)));
|
||||||
|
|
||||||
|
return last_vsync_ + period;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
class VarianceCollector {
|
||||||
|
public:
|
||||||
|
VarianceCollector(Time::Nanos default_value) {
|
||||||
|
sum_ = default_value * 128;
|
||||||
|
for(int c = 0; c < 128; ++c) {
|
||||||
|
history_[c] = default_value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void post(Time::Nanos value) {
|
||||||
|
sum_ -= history_[write_pointer_];
|
||||||
|
sum_ += value;
|
||||||
|
history_[write_pointer_] = value;
|
||||||
|
write_pointer_ = (write_pointer_ + 1) & 127;
|
||||||
|
}
|
||||||
|
|
||||||
|
Time::Nanos mean() {
|
||||||
|
return sum_ / 128;
|
||||||
|
}
|
||||||
|
|
||||||
|
Time::Nanos variance() {
|
||||||
|
// I haven't yet come up with a better solution that calculating this
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
return variance / (128 * 128 * 128);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
Time::Nanos sum_;
|
||||||
|
Time::Nanos history_[128];
|
||||||
|
size_t write_pointer_ = 0;
|
||||||
|
};
|
||||||
|
|
||||||
Nanos redraw_begin_time_ = 0;
|
Nanos redraw_begin_time_ = 0;
|
||||||
Nanos last_vsync_ = 0;
|
Nanos last_vsync_ = 0;
|
||||||
|
|
||||||
Nanos vsync_period_ = 1'000'000'000 / 60; // Seems like a good first guess.
|
VarianceCollector vsync_period_{1'000'000'000 / 60}; // 60Hz: seems like a good first guess.
|
||||||
Nanos redraw_period_ = 0;
|
VarianceCollector redraw_period_{1'000'000'000 / 60}; // A less convincing first guess.
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -49,6 +49,7 @@ void ScanTargetWidget::paintGL() {
|
|||||||
vsyncPredictor.begin_redraw();
|
vsyncPredictor.begin_redraw();
|
||||||
scanTarget->update(width(), height());
|
scanTarget->update(width(), height());
|
||||||
scanTarget->draw(width(), height());
|
scanTarget->draw(width(), height());
|
||||||
|
glFinish(); // Make sure all costs are properly accounted for in the vsync predictor.
|
||||||
vsyncPredictor.end_redraw();
|
vsyncPredictor.end_redraw();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user