1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-11-30 04:50:08 +00:00

Moves drawing into the next timer tick after retrace if sync locked.

... which should mean it occurs within 1/600th of a second of announced retrace, which I assume always will be less than the retrace period. So: does the frame buffer update during retrace.

This should completely eliminate tearing for machines that can be synced to the native output rate.
This commit is contained in:
Thomas Harte 2020-02-08 18:07:13 -05:00
parent 7c0f3bb237
commit b76a5870b3
2 changed files with 20 additions and 12 deletions

View File

@ -67,7 +67,7 @@
</Testables> </Testables>
</TestAction> </TestAction>
<LaunchAction <LaunchAction
buildConfiguration = "Debug" buildConfiguration = "Release"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
enableASanStackUseAfterReturn = "YES" enableASanStackUseAfterReturn = "YES"

View File

@ -157,6 +157,7 @@ struct ActivityObserver: public Activity::Observer {
int64_t _syncTime; int64_t _syncTime;
int64_t _timeDiff; int64_t _timeDiff;
double _refreshPeriod; double _refreshPeriod;
BOOL _isSyncLocking;
std::unique_ptr<Outputs::Display::OpenGL::ScanTarget> _scanTarget; std::unique_ptr<Outputs::Display::OpenGL::ScanTarget> _scanTarget;
} }
@ -712,6 +713,7 @@ struct ActivityObserver: public Activity::Observer {
const auto timeNow = std::chrono::high_resolution_clock::now().time_since_epoch().count(); const auto timeNow = std::chrono::high_resolution_clock::now().time_since_epoch().count();
CGSize pixelSize = view.backingSize; CGSize pixelSize = view.backingSize;
BOOL isSyncLocking;
@synchronized(self) { @synchronized(self) {
// Store a means to map from CVTimeStamp.hostTime to std::chrono::high_resolution_clock; // Store a means to map from CVTimeStamp.hostTime to std::chrono::high_resolution_clock;
// there is an extremely dodgy assumption here that both are in the same units (and, below, that both as in ns). // there is an extremely dodgy assumption here that both are in the same units (and, below, that both as in ns).
@ -727,12 +729,17 @@ struct ActivityObserver: public Activity::Observer {
// Set the current refresh period. // Set the current refresh period.
_refreshPeriod = double(now->videoRefreshPeriod) / double(now->videoTimeScale); _refreshPeriod = double(now->videoRefreshPeriod) / double(now->videoTimeScale);
// Determine where responsibility lies for drawing.
isSyncLocking = _isSyncLocking;
} }
// Draw the current output. (TODO: do this within the timer if either raster racing or, at least, sync matching). // Draw the current output. (TODO: do this within the timer if either raster racing or, at least, sync matching).
if(!isSyncLocking) {
[self.view performWithGLContext:^{ [self.view performWithGLContext:^{
self->_scanTarget->draw((int)pixelSize.width, (int)pixelSize.height); self->_scanTarget->draw((int)pixelSize.width, (int)pixelSize.height);
} flushDrawable:YES]; } flushDrawable:YES];
}
} }
#define TICKS 600 #define TICKS 600
@ -766,6 +773,7 @@ struct ActivityObserver: public Activity::Observer {
splitAndSync = ratio <= maximumAdjustment && ratio >= 1 / maximumAdjustment; splitAndSync = ratio <= maximumAdjustment && ratio >= 1 / maximumAdjustment;
} }
} }
self->_isSyncLocking = splitAndSync;
// If the time window is being split, run up to the split, then check out machine speed, possibly // If the time window is being split, run up to the split, then check out machine speed, possibly
// adjusting multiplier, then run after the split. // adjusting multiplier, then run after the split.
@ -804,19 +812,19 @@ struct ActivityObserver: public Activity::Observer {
// a concluding draw. Implicit assumption here: whatever is left to be done in the final window // a concluding draw. Implicit assumption here: whatever is left to be done in the final window
// can be done within the retrace period. // can be done within the retrace period.
auto wasUpdating = self->_isUpdating.test_and_set(); auto wasUpdating = self->_isUpdating.test_and_set();
// if(wasUpdating && splitAndSync) { if(wasUpdating && splitAndSync) {
// while(self->_isUpdating.test_and_set()); while(self->_isUpdating.test_and_set());
// wasUpdating = false; wasUpdating = false;
// } }
if(!wasUpdating) { if(!wasUpdating) {
dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INTERACTIVE, 0), ^{ dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INTERACTIVE, 0), ^{
[self.view performWithGLContext:^{ [self.view performWithGLContext:^{
self->_scanTarget->update((int)pixelSize.width, (int)pixelSize.height); self->_scanTarget->update((int)pixelSize.width, (int)pixelSize.height);
// if(splitAndSync) { if(splitAndSync) {
// self->_scanTarget->draw((int)pixelSize.width, (int)pixelSize.height); self->_scanTarget->draw((int)pixelSize.width, (int)pixelSize.height);
// } }
} flushDrawable:NO]; } flushDrawable:splitAndSync];
self->_isUpdating.clear(); self->_isUpdating.clear();
}); });
} }