diff --git a/desmume/src/frontend/cocoa/ClientDisplayView.cpp b/desmume/src/frontend/cocoa/ClientDisplayView.cpp index c5c2bfcc0..c63bbfa7b 100644 --- a/desmume/src/frontend/cocoa/ClientDisplayView.cpp +++ b/desmume/src/frontend/cocoa/ClientDisplayView.cpp @@ -1292,7 +1292,7 @@ void ClientDisplayViewInterface::FlushView(void *userData) this->_viewNeedsFlush = false; } -void ClientDisplayViewInterface::FinalizeFlush(void *userData) +void ClientDisplayViewInterface::FinalizeFlush(void *userData, uint64_t outputTime) { // Do nothing. This is implementation dependent. } @@ -1300,7 +1300,7 @@ void ClientDisplayViewInterface::FinalizeFlush(void *userData) void ClientDisplayViewInterface::FlushAndFinalizeImmediate() { this->FlushView(NULL); - this->FinalizeFlush(NULL); + this->FinalizeFlush(NULL, 0); } // Touch screen input handling diff --git a/desmume/src/frontend/cocoa/ClientDisplayView.h b/desmume/src/frontend/cocoa/ClientDisplayView.h index 36f6fc68e..1151ec0b4 100644 --- a/desmume/src/frontend/cocoa/ClientDisplayView.h +++ b/desmume/src/frontend/cocoa/ClientDisplayView.h @@ -356,7 +356,7 @@ public: virtual void SetAllowViewFlushes(bool allowFlushes); virtual void FlushView(void *userData); - virtual void FinalizeFlush(void *userData); + virtual void FinalizeFlush(void *userData, uint64_t outputTime); virtual void FlushAndFinalizeImmediate(); // Touch screen input handling diff --git a/desmume/src/frontend/cocoa/cocoa_GPU.h b/desmume/src/frontend/cocoa/cocoa_GPU.h index 78415abc6..a7142e9d3 100644 --- a/desmume/src/frontend/cocoa/cocoa_GPU.h +++ b/desmume/src/frontend/cocoa/cocoa_GPU.h @@ -105,9 +105,8 @@ typedef std::map DisplayLinkFlushTimeLimitMap; - (void) decrementViewsUsingDirectToCPUFiltering; - (void) pushVideoDataToAllDisplayViews; -- (void) flushAllDisplaysOnDisplayLink:(CVDisplayLinkRef)displayLink timeStamp:(const CVTimeStamp *)timeStamp; -- (void) flushMultipleViews:(const std::vector &)cdvFlushList; -- (void) finalizeFlushMultipleViews:(const std::vector &)cdvFlushList; +- (void) flushAllDisplaysOnDisplayLink:(CVDisplayLinkRef)displayLink timeStampNow:(const CVTimeStamp *)timeStampNow timeStampOutput:(const CVTimeStamp *)timeStampOutput; +- (void) flushMultipleViews:(const std::vector &)cdvFlushList timeStampNow:(const CVTimeStamp *)timeStampNow timeStampOutput:(const CVTimeStamp *)timeStampOutput; - (void) displayLinkStartUsingID:(CGDirectDisplayID)displayID; - (void) displayLinkListUpdate; diff --git a/desmume/src/frontend/cocoa/cocoa_GPU.mm b/desmume/src/frontend/cocoa/cocoa_GPU.mm index f7c999446..56ed69e64 100644 --- a/desmume/src/frontend/cocoa/cocoa_GPU.mm +++ b/desmume/src/frontend/cocoa/cocoa_GPU.mm @@ -1382,7 +1382,7 @@ public: } } -- (void) flushAllDisplaysOnDisplayLink:(CVDisplayLinkRef)displayLink timeStamp:(const CVTimeStamp *)timeStamp +- (void) flushAllDisplaysOnDisplayLink:(CVDisplayLinkRef)displayLink timeStampNow:(const CVTimeStamp *)timeStampNow timeStampOutput:(const CVTimeStamp *)timeStampOutput { pthread_rwlock_t *currentRWLock = _rwlockOutputList; CGDirectDisplayID displayID = CVDisplayLinkGetCurrentCGDisplay(displayLink); @@ -1415,8 +1415,7 @@ public: if (listSize > 0) { - [self flushMultipleViews:cdvFlushList]; - [self finalizeFlushMultipleViews:cdvFlushList]; + [self flushMultipleViews:cdvFlushList timeStampNow:timeStampNow timeStampOutput:timeStampOutput]; didFlushOccur = true; } @@ -1428,15 +1427,15 @@ public: if (didFlushOccur) { // Set the new time limit to 8 seconds after the current time. - _displayLinkFlushTimeList[displayID] = timeStamp->videoTime + (timeStamp->videoTimeScale * VIDEO_FLUSH_TIME_LIMIT_OFFSET); + _displayLinkFlushTimeList[displayID] = timeStampNow->videoTime + (timeStampNow->videoTimeScale * VIDEO_FLUSH_TIME_LIMIT_OFFSET); } - else if (timeStamp->videoTime > _displayLinkFlushTimeList[displayID]) + else if (timeStampNow->videoTime > _displayLinkFlushTimeList[displayID]) { CVDisplayLinkStop(displayLink); } } -- (void) flushMultipleViews:(const std::vector &)cdvFlushList +- (void) flushMultipleViews:(const std::vector &)cdvFlushList timeStampNow:(const CVTimeStamp *)timeStampNow timeStampOutput:(const CVTimeStamp *)timeStampOutput { const size_t listSize = cdvFlushList.size(); @@ -1445,16 +1444,11 @@ public: ClientDisplay3DView *cdv = (ClientDisplay3DView *)cdvFlushList[i]; cdv->FlushView(NULL); } -} - -- (void) finalizeFlushMultipleViews:(const std::vector &)cdvFlushList -{ - const size_t listSize = cdvFlushList.size(); for (size_t i = 0; i < listSize; i++) { ClientDisplay3DView *cdv = (ClientDisplay3DView *)cdvFlushList[i]; - cdv->FinalizeFlush(NULL); + cdv->FinalizeFlush(NULL, timeStampOutput->hostTime); } } @@ -1768,7 +1762,7 @@ CVReturn MacDisplayLinkCallback(CVDisplayLinkRef displayLink, void *displayLinkContext) { MacClientSharedObject *sharedData = (MacClientSharedObject *)displayLinkContext; - [sharedData flushAllDisplaysOnDisplayLink:displayLink timeStamp:inNow]; + [sharedData flushAllDisplaysOnDisplayLink:displayLink timeStampNow:inNow timeStampOutput:inOutputTime]; return kCVReturnSuccess; } diff --git a/desmume/src/frontend/cocoa/userinterface/DisplayWindowController.mm b/desmume/src/frontend/cocoa/userinterface/DisplayWindowController.mm index 9b6b2c9e0..97b7b7d89 100644 --- a/desmume/src/frontend/cocoa/userinterface/DisplayWindowController.mm +++ b/desmume/src/frontend/cocoa/userinterface/DisplayWindowController.mm @@ -2142,13 +2142,13 @@ static std::unordered_map _screenMap; // [self setLayer:localLayer]; [self setWantsLayer:YES]; - if (isMetalLayer) + if (cdv->GetRenderToCALayer()) { - cdv->FlushAndFinalizeImmediate(); + [localLayer setNeedsDisplay]; } else { - [localLayer setNeedsDisplay]; + cdv->FlushAndFinalizeImmediate(); } } diff --git a/desmume/src/frontend/cocoa/userinterface/MacMetalDisplayView.h b/desmume/src/frontend/cocoa/userinterface/MacMetalDisplayView.h index 5a32358ff..5599145b1 100644 --- a/desmume/src/frontend/cocoa/userinterface/MacMetalDisplayView.h +++ b/desmume/src/frontend/cocoa/userinterface/MacMetalDisplayView.h @@ -36,7 +36,7 @@ #endif #define METAL_FETCH_BUFFER_COUNT 3 -#define RENDER_BUFFER_COUNT 6 +#define RENDER_BUFFER_COUNT 12 class MacMetalFetchObject; class MacMetalDisplayPresenter; @@ -166,8 +166,7 @@ typedef DisplayViewShaderProperties DisplayViewShaderProperties; - (void) fetchNativeDisplayByID:(const NDSDisplayID)displayID bufferIndex:(const u8)bufferIndex blitCommandEncoder:(id)bce; - (void) fetchCustomDisplayByID:(const NDSDisplayID)displayID bufferIndex:(const u8)bufferIndex blitCommandEncoder:(id)bce; -- (void) flushMultipleViews:(const std::vector &)cdvFlushList;; -- (void) finalizeFlushMultipleViews:(const std::vector &)cdvFlushList; +- (void) flushMultipleViews:(const std::vector &)cdvFlushList timeStampNow:(const CVTimeStamp *)timeStampNow timeStampOutput:(const CVTimeStamp *)timeStampOutput; @end @@ -262,18 +261,23 @@ typedef DisplayViewShaderProperties DisplayViewShaderProperties; MacDisplayLayeredView *_cdv; MacMetalDisplayPresenterObject *presenterObject; dispatch_semaphore_t _semDrawable; - id layerDrawable; + id _currentDrawable; + id layerDrawable0; + id layerDrawable1; + id layerDrawable2; + MetalTexturePair _displayTexturePair; - size_t _displaySequenceNumber; } @property (readonly, nonatomic) MacMetalDisplayPresenterObject *presenterObject; -@property (retain) id layerDrawable; +@property (retain) id layerDrawable0; +@property (retain) id layerDrawable1; +@property (retain) id layerDrawable2; - (id) initWithDisplayPresenterObject:(MacMetalDisplayPresenterObject *)thePresenterObject; - (void) setupLayer; - (void) renderToDrawableUsingCommandBuffer:(id)cb; -- (void) presentDrawableWithCommandBuffer:(id)cb; +- (void) presentDrawableWithCommandBuffer:(id)cb outputTime:(uint64_t)outputTime; - (void) renderAndPresentDrawableImmediate; @end @@ -368,7 +372,7 @@ public: // Client view interface virtual void FlushView(void *userData); - virtual void FinalizeFlush(void *userData); + virtual void FinalizeFlush(void *userData, uint64_t outputTime); virtual void FlushAndFinalizeImmediate(); }; diff --git a/desmume/src/frontend/cocoa/userinterface/MacMetalDisplayView.mm b/desmume/src/frontend/cocoa/userinterface/MacMetalDisplayView.mm index 59e44fde8..f0deb1e52 100644 --- a/desmume/src/frontend/cocoa/userinterface/MacMetalDisplayView.mm +++ b/desmume/src/frontend/cocoa/userinterface/MacMetalDisplayView.mm @@ -428,10 +428,10 @@ } } - id cce = [cb computeCommandEncoder]; - if (currentDisplayInfo.needApplyMasterBrightness[NDSDisplayID_Main] || currentDisplayInfo.needApplyMasterBrightness[NDSDisplayID_Touch]) { + id cce = [cb computeCommandEncoder]; + if (currentDisplayInfo.colorFormat == NDSColorFormat_BGR555_Rev) { [cce setComputePipelineState:_fetch555Pipeline]; @@ -505,9 +505,12 @@ targetTexPair.touch = _texDisplayPostprocessCustom[NDSDisplayID_Touch][index]; } } + + [cce endEncoding]; } else if (currentDisplayInfo.colorFormat != NDSColorFormat_BGR888_Rev) { + id cce = [cb computeCommandEncoder]; bool isPipelineStateSet = false; if (currentDisplayInfo.colorFormat == NDSColorFormat_BGR555_Rev) @@ -569,9 +572,9 @@ } } } + + [cce endEncoding]; } - - [cce endEncoding]; } return targetTexPair; @@ -580,7 +583,6 @@ - (void) fetchFromBufferIndex:(const u8)index { id cb = [_fetchCommandQueue commandBufferWithUnretainedReferences]; - [cb enqueue]; semaphore_wait([self semaphoreFramebufferPageAtIndex:index]); [self setFramebufferState:ClientDisplayBufferState_Reading index:index]; @@ -591,16 +593,6 @@ [self setBceFetch:nil]; [bce endEncoding]; - [cb addCompletedHandler:^(id block) { - [self setFramebufferState:ClientDisplayBufferState_Idle index:index]; - semaphore_signal([self semaphoreFramebufferPageAtIndex:index]); - }]; - - [cb commit]; - - cb = [_fetchCommandQueue commandBufferWithUnretainedReferences]; - [cb enqueue]; - const MetalTexturePair newTexPair = [self setFetchTextureBindingsAtIndex:index commandBuffer:cb]; [newTexPair.main retain]; [newTexPair.touch retain]; @@ -610,6 +602,9 @@ [self setTexPairFetch:newTexPair]; [oldTexPair.main release]; [oldTexPair.touch release]; + + [self setFramebufferState:ClientDisplayBufferState_Idle index:index]; + semaphore_signal([self semaphoreFramebufferPageAtIndex:index]); }]; [cb commit]; @@ -656,32 +651,37 @@ destinationOrigin:MTLOriginMake(0, 0, 0)]; } -- (void) flushMultipleViews:(const std::vector &)cdvFlushList +- (void) flushMultipleViews:(const std::vector &)cdvFlushList timeStampNow:(const CVTimeStamp *)timeStampNow timeStampOutput:(const CVTimeStamp *)timeStampOutput { const size_t listSize = cdvFlushList.size(); - id cb = [commandQueue commandBufferWithUnretainedReferences]; - for (size_t i = 0; i < listSize; i++) + @autoreleasepool { - ClientDisplay3DView *cdv = (ClientDisplay3DView *)cdvFlushList[i]; - cdv->FlushView(cb); + id cbFlush = [commandQueue commandBufferWithUnretainedReferences]; + id cbFinalize = [commandQueue commandBufferWithUnretainedReferences]; + + for (size_t i = 0; i < listSize; i++) + { + ClientDisplay3DView *cdv = (ClientDisplay3DView *)cdvFlushList[i]; + cdv->FlushView(cbFlush); + } + + for (size_t i = 0; i < listSize; i++) + { + ClientDisplay3DView *cdv = (ClientDisplay3DView *)cdvFlushList[i]; + cdv->FinalizeFlush(cbFinalize, timeStampOutput->hostTime); + } + + [cbFlush enqueue]; + [cbFinalize enqueue]; + + [cbFlush commit]; + [cbFinalize commit]; + +#ifdef DEBUG + [commandQueue insertDebugCaptureBoundary]; +#endif } - - [cb commit]; -} - -- (void) finalizeFlushMultipleViews:(const std::vector &)cdvFlushList -{ - const size_t listSize = cdvFlushList.size(); - id cb = [commandQueue commandBufferWithUnretainedReferences]; - - for (size_t i = 0; i < listSize; i++) - { - ClientDisplay3DView *cdv = (ClientDisplay3DView *)cdvFlushList[i]; - cdv->FinalizeFlush(cb); - } - - [cb commit]; } @end @@ -2082,7 +2082,9 @@ @synthesize _cdv; @synthesize presenterObject; -@synthesize layerDrawable; +@synthesize layerDrawable0; +@synthesize layerDrawable1; +@synthesize layerDrawable2; - (id) initWithDisplayPresenterObject:(MacMetalDisplayPresenterObject *)thePresenterObject { @@ -2094,8 +2096,10 @@ _cdv = NULL; _semDrawable = dispatch_semaphore_create(3); - layerDrawable = nil; - _displaySequenceNumber = 0; + _currentDrawable = nil; + layerDrawable0 = nil; + layerDrawable1 = nil; + layerDrawable2 = nil; _displayTexturePair.bufferIndex = 0; _displayTexturePair.fetchSequenceNumber = 0; @@ -2116,7 +2120,9 @@ - (void)dealloc { - [self setLayerDrawable:nil]; + [self setLayerDrawable0:nil]; + [self setLayerDrawable1:nil]; + [self setLayerDrawable2:nil]; dispatch_release(_semDrawable); [_displayTexturePair.main release]; @@ -2136,82 +2142,133 @@ - (void) renderToDrawableUsingCommandBuffer:(id)cb { - @autoreleasepool - { - const MetalTexturePair texProcess = [presenterObject texPairProcess]; - - if (texProcess.fetchSequenceNumber >= _displayTexturePair.fetchSequenceNumber) - { - id oldTexMain = _displayTexturePair.main; - id oldTexTouch = _displayTexturePair.touch; - - _displayTexturePair.bufferIndex = texProcess.bufferIndex; - _displayTexturePair.fetchSequenceNumber = texProcess.fetchSequenceNumber; - _displayTexturePair.main = [texProcess.main retain]; - _displayTexturePair.touch = [texProcess.touch retain]; - - [oldTexMain release]; - [oldTexTouch release]; - } - - // Now that everything is set up, go ahead and draw everything. - dispatch_semaphore_wait(_semDrawable, DISPATCH_TIME_FOREVER); - id drawable = [self nextDrawable]; - - if (drawable != nil) - { - [[presenterObject colorAttachment0Desc] setTexture:[drawable texture]]; - - const MetalRenderFrameInfo mrfi = [presenterObject renderFrameInfo]; - - [presenterObject renderForCommandBuffer:cb - outputPipelineState:[presenterObject outputDrawablePipeline] - hudPipelineState:[[presenterObject sharedData] hudPipeline] - texDisplays:_displayTexturePair - mrfi:mrfi - doYFlip:NO]; - - [cb addScheduledHandler:^(id block) { - [presenterObject setRenderBufferState:ClientDisplayBufferState_Reading index:mrfi.renderIndex]; - }]; - - [cb addCompletedHandler:^(id block) { - [presenterObject renderFinishAtIndex:mrfi.renderIndex]; - }]; - - [self setLayerDrawable:drawable]; - } - else - { - dispatch_semaphore_signal(_semDrawable); - } - } -} - -- (void) presentDrawableWithCommandBuffer:(id)cb -{ - id drawable = [self layerDrawable]; + dispatch_semaphore_wait(_semDrawable, DISPATCH_TIME_FOREVER); + + id drawable = [self nextDrawable]; if (drawable == nil) { + _currentDrawable = nil; + dispatch_semaphore_signal(_semDrawable); + return; + } + else + { + if ([self layerDrawable0] == nil) + { + [self setLayerDrawable0:drawable]; + } + else if ([self layerDrawable1] == nil) + { + [self setLayerDrawable1:drawable]; + } + else if ([self layerDrawable2] == nil) + { + [self setLayerDrawable2:drawable]; + } + } + + id texDrawable = [drawable texture]; + [[presenterObject colorAttachment0Desc] setTexture:texDrawable]; + + const MetalTexturePair texProcess = [presenterObject texPairProcess]; + id oldTexMain = _displayTexturePair.main; + id oldTexTouch = _displayTexturePair.touch; + + _displayTexturePair.bufferIndex = texProcess.bufferIndex; + _displayTexturePair.fetchSequenceNumber = texProcess.fetchSequenceNumber; + _displayTexturePair.main = [texProcess.main retain]; + _displayTexturePair.touch = [texProcess.touch retain]; + + [oldTexMain release]; + [oldTexTouch release]; + + const MetalRenderFrameInfo mrfi = [presenterObject renderFrameInfo]; + + [presenterObject renderForCommandBuffer:cb + outputPipelineState:[presenterObject outputDrawablePipeline] + hudPipelineState:[[presenterObject sharedData] hudPipeline] + texDisplays:_displayTexturePair + mrfi:mrfi + doYFlip:NO]; + + [cb addScheduledHandler:^(id block) { + [presenterObject setRenderBufferState:ClientDisplayBufferState_Reading index:mrfi.renderIndex]; + }]; + + [cb addCompletedHandler:^(id block) { + [presenterObject renderFinishAtIndex:mrfi.renderIndex]; + }]; + + _currentDrawable = drawable; +} + +- (void) presentDrawableWithCommandBuffer:(id)cb outputTime:(uint64_t)outputTime +{ + id drawable = _currentDrawable; + if (drawable == nil) + { + printf("Metal: No drawable was assigned!\n"); return; } - [cb presentDrawable:drawable]; - [cb addCompletedHandler:^(id block) { - [self setLayerDrawable:nil]; + // Apple's documentation might seem to suggest that [MTLCommandBuffer presentDrawable:atTime:] + // and [MTLDrawable presentAtTime:] inside of a [MTLCommandBuffer addScheduledHandler:] block + // are equivalent. However, much testing has shown that this is NOT the case. + // + // So rather than using [MTLCommandBuffer presentDrawable:atTime:], which causes Metal to + // present the drawable whenever it pleases, we manually call [MTLDrawable presentAtTime] so + // that we can synchronously force the presentation order of the drawables. If we don't do + // this, then Metal may start presenting the drawables in some random order, causing some + // really nasty microstuttering. + + [cb addScheduledHandler:^(id block) { + @autoreleasepool + { + [drawable presentAtTime:(CFTimeInterval)outputTime / 1000000000.0]; + + if (drawable == [self layerDrawable0]) + { + [self setLayerDrawable0:nil]; + } + else if (drawable == [self layerDrawable1]) + { + [self setLayerDrawable1:nil]; + } + else if (drawable == [self layerDrawable2]) + { + [self setLayerDrawable2:nil]; + } + } + dispatch_semaphore_signal(_semDrawable); }]; } - (void) renderAndPresentDrawableImmediate { - id cb = [presenterObject newCommandBuffer]; - _cdv->FlushView(cb); - [cb commit]; - - cb = [presenterObject newCommandBuffer]; - _cdv->FinalizeFlush(cb); - [cb commit]; + @autoreleasepool + { + id cbFlush = [presenterObject newCommandBuffer]; + id cbFinalize = [presenterObject newCommandBuffer]; + + _cdv->FlushView(cbFlush); + _cdv->FinalizeFlush(cbFinalize, 0); + + [cbFlush enqueue]; + [cbFinalize enqueue]; + + [cbFlush commit]; + [cbFinalize commit]; + +#ifdef DEBUG + [[[presenterObject sharedData] commandQueue] insertDebugCaptureBoundary]; +#endif + } +} + +- (void) display +{ + [self renderAndPresentDrawableImmediate]; } @end @@ -2541,15 +2598,23 @@ void MacMetalDisplayView::SetViewNeedsFlush() return; } - // For every update, ensure that the CVDisplayLink is started so that the update - // will eventually get flushed. - this->SetAllowViewFlushes(true); - - this->_presenter->UpdateLayout(); - - OSSpinLockLock(&this->_spinlockViewNeedsFlush); - this->_viewNeedsFlush = true; - OSSpinLockUnlock(&this->_spinlockViewNeedsFlush); + if (this->GetRenderToCALayer()) + { + this->_presenter->UpdateLayout(); + [this->_caLayer setNeedsDisplay]; + [CATransaction flush]; + } + else + { + // For every update, ensure that the CVDisplayLink is started so that the update + // will eventually get flushed. + this->SetAllowViewFlushes(true); + this->_presenter->UpdateLayout(); + + OSSpinLockLock(&this->_spinlockViewNeedsFlush); + this->_viewNeedsFlush = true; + OSSpinLockUnlock(&this->_spinlockViewNeedsFlush); + } } void MacMetalDisplayView::SetAllowViewFlushes(bool allowFlushes) @@ -2568,9 +2633,9 @@ void MacMetalDisplayView::FlushView(void *userData) [(DisplayViewMetalLayer *)this->_caLayer renderToDrawableUsingCommandBuffer:(id)userData]; } -void MacMetalDisplayView::FinalizeFlush(void *userData) +void MacMetalDisplayView::FinalizeFlush(void *userData, uint64_t outputTime) { - [(DisplayViewMetalLayer *)this->_caLayer presentDrawableWithCommandBuffer:(id)userData]; + [(DisplayViewMetalLayer *)this->_caLayer presentDrawableWithCommandBuffer:(id)userData outputTime:outputTime]; } void MacMetalDisplayView::FlushAndFinalizeImmediate() diff --git a/desmume/src/frontend/cocoa/userinterface/MacOGLDisplayView.h b/desmume/src/frontend/cocoa/userinterface/MacOGLDisplayView.h index 67ee00c81..006ac63c6 100644 --- a/desmume/src/frontend/cocoa/userinterface/MacOGLDisplayView.h +++ b/desmume/src/frontend/cocoa/userinterface/MacOGLDisplayView.h @@ -134,7 +134,7 @@ public: // Client view interface virtual void FlushView(void *userData); - virtual void FinalizeFlush(void *userData); + virtual void FinalizeFlush(void *userData, uint64_t outputTime); }; #endif // _MAC_OGLDISPLAYOUTPUT_H_ diff --git a/desmume/src/frontend/cocoa/userinterface/MacOGLDisplayView.mm b/desmume/src/frontend/cocoa/userinterface/MacOGLDisplayView.mm index 419e11cab..d49eec3b8 100644 --- a/desmume/src/frontend/cocoa/userinterface/MacOGLDisplayView.mm +++ b/desmume/src/frontend/cocoa/userinterface/MacOGLDisplayView.mm @@ -552,7 +552,7 @@ void MacOGLDisplayView::FlushView(void *userData) CGLUnlockContext(context); } -void MacOGLDisplayView::FinalizeFlush(void *userData) +void MacOGLDisplayView::FinalizeFlush(void *userData, uint64_t outputTime) { CGLContextObj context = ((MacOGLDisplayPresenter *)this->_presenter)->GetContext();