Cocoa Port: Yet another attempt at eliminating microstuttering in Metal display views. While it hasn't been completely eliminated yet, it shouldn't be as bad now.

This commit is contained in:
rogerman 2018-11-28 13:36:02 -08:00
parent 1f9b9e02a4
commit 4f543aa8ca
9 changed files with 210 additions and 148 deletions

View File

@ -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

View File

@ -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

View File

@ -105,9 +105,8 @@ typedef std::map<CGDirectDisplayID, int64_t> DisplayLinkFlushTimeLimitMap;
- (void) decrementViewsUsingDirectToCPUFiltering;
- (void) pushVideoDataToAllDisplayViews;
- (void) flushAllDisplaysOnDisplayLink:(CVDisplayLinkRef)displayLink timeStamp:(const CVTimeStamp *)timeStamp;
- (void) flushMultipleViews:(const std::vector<ClientDisplay3DView *> &)cdvFlushList;
- (void) finalizeFlushMultipleViews:(const std::vector<ClientDisplay3DView *> &)cdvFlushList;
- (void) flushAllDisplaysOnDisplayLink:(CVDisplayLinkRef)displayLink timeStampNow:(const CVTimeStamp *)timeStampNow timeStampOutput:(const CVTimeStamp *)timeStampOutput;
- (void) flushMultipleViews:(const std::vector<ClientDisplay3DView *> &)cdvFlushList timeStampNow:(const CVTimeStamp *)timeStampNow timeStampOutput:(const CVTimeStamp *)timeStampOutput;
- (void) displayLinkStartUsingID:(CGDirectDisplayID)displayID;
- (void) displayLinkListUpdate;

View File

@ -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<ClientDisplay3DView *> &)cdvFlushList
- (void) flushMultipleViews:(const std::vector<ClientDisplay3DView *> &)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<ClientDisplay3DView *> &)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;
}

View File

@ -2142,13 +2142,13 @@ static std::unordered_map<NSScreen *, DisplayWindowController *> _screenMap; //
[self setLayer:localLayer];
[self setWantsLayer:YES];
if (isMetalLayer)
if (cdv->GetRenderToCALayer())
{
cdv->FlushAndFinalizeImmediate();
[localLayer setNeedsDisplay];
}
else
{
[localLayer setNeedsDisplay];
cdv->FlushAndFinalizeImmediate();
}
}

View File

@ -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<MTLBlitCommandEncoder>)bce;
- (void) fetchCustomDisplayByID:(const NDSDisplayID)displayID bufferIndex:(const u8)bufferIndex blitCommandEncoder:(id<MTLBlitCommandEncoder>)bce;
- (void) flushMultipleViews:(const std::vector<ClientDisplay3DView *> &)cdvFlushList;;
- (void) finalizeFlushMultipleViews:(const std::vector<ClientDisplay3DView *> &)cdvFlushList;
- (void) flushMultipleViews:(const std::vector<ClientDisplay3DView *> &)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<CAMetalDrawable> layerDrawable;
id<CAMetalDrawable> _currentDrawable;
id<CAMetalDrawable> layerDrawable0;
id<CAMetalDrawable> layerDrawable1;
id<CAMetalDrawable> layerDrawable2;
MetalTexturePair _displayTexturePair;
size_t _displaySequenceNumber;
}
@property (readonly, nonatomic) MacMetalDisplayPresenterObject *presenterObject;
@property (retain) id<CAMetalDrawable> layerDrawable;
@property (retain) id<CAMetalDrawable> layerDrawable0;
@property (retain) id<CAMetalDrawable> layerDrawable1;
@property (retain) id<CAMetalDrawable> layerDrawable2;
- (id) initWithDisplayPresenterObject:(MacMetalDisplayPresenterObject *)thePresenterObject;
- (void) setupLayer;
- (void) renderToDrawableUsingCommandBuffer:(id<MTLCommandBuffer>)cb;
- (void) presentDrawableWithCommandBuffer:(id<MTLCommandBuffer>)cb;
- (void) presentDrawableWithCommandBuffer:(id<MTLCommandBuffer>)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();
};

View File

@ -428,10 +428,10 @@
}
}
id<MTLComputeCommandEncoder> cce = [cb computeCommandEncoder];
if (currentDisplayInfo.needApplyMasterBrightness[NDSDisplayID_Main] || currentDisplayInfo.needApplyMasterBrightness[NDSDisplayID_Touch])
{
id<MTLComputeCommandEncoder> 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<MTLComputeCommandEncoder> 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<MTLCommandBuffer> 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<MTLCommandBuffer> 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<ClientDisplay3DView *> &)cdvFlushList
- (void) flushMultipleViews:(const std::vector<ClientDisplay3DView *> &)cdvFlushList timeStampNow:(const CVTimeStamp *)timeStampNow timeStampOutput:(const CVTimeStamp *)timeStampOutput
{
const size_t listSize = cdvFlushList.size();
id<MTLCommandBuffer> cb = [commandQueue commandBufferWithUnretainedReferences];
for (size_t i = 0; i < listSize; i++)
@autoreleasepool
{
ClientDisplay3DView *cdv = (ClientDisplay3DView *)cdvFlushList[i];
cdv->FlushView(cb);
id<MTLCommandBuffer> cbFlush = [commandQueue commandBufferWithUnretainedReferences];
id<MTLCommandBuffer> 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<ClientDisplay3DView *> &)cdvFlushList
{
const size_t listSize = cdvFlushList.size();
id<MTLCommandBuffer> 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<MTLCommandBuffer>)cb
{
@autoreleasepool
{
const MetalTexturePair texProcess = [presenterObject texPairProcess];
dispatch_semaphore_wait(_semDrawable, DISPATCH_TIME_FOREVER);
if (texProcess.fetchSequenceNumber >= _displayTexturePair.fetchSequenceNumber)
{
id<MTLTexture> oldTexMain = _displayTexturePair.main;
id<MTLTexture> 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<CAMetalDrawable> 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<MTLCommandBuffer> block) {
[presenterObject setRenderBufferState:ClientDisplayBufferState_Reading index:mrfi.renderIndex];
}];
[cb addCompletedHandler:^(id<MTLCommandBuffer> block) {
[presenterObject renderFinishAtIndex:mrfi.renderIndex];
}];
[self setLayerDrawable:drawable];
}
else
{
dispatch_semaphore_signal(_semDrawable);
}
}
}
- (void) presentDrawableWithCommandBuffer:(id<MTLCommandBuffer>)cb
{
id<CAMetalDrawable> drawable = [self layerDrawable];
id<CAMetalDrawable> 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<MTLTexture> texDrawable = [drawable texture];
[[presenterObject colorAttachment0Desc] setTexture:texDrawable];
const MetalTexturePair texProcess = [presenterObject texPairProcess];
id<MTLTexture> oldTexMain = _displayTexturePair.main;
id<MTLTexture> 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<MTLCommandBuffer> block) {
[presenterObject setRenderBufferState:ClientDisplayBufferState_Reading index:mrfi.renderIndex];
}];
[cb addCompletedHandler:^(id<MTLCommandBuffer> block) {
[presenterObject renderFinishAtIndex:mrfi.renderIndex];
}];
_currentDrawable = drawable;
}
- (void) presentDrawableWithCommandBuffer:(id<MTLCommandBuffer>)cb outputTime:(uint64_t)outputTime
{
id<CAMetalDrawable> drawable = _currentDrawable;
if (drawable == nil)
{
printf("Metal: No drawable was assigned!\n");
return;
}
[cb presentDrawable:drawable];
[cb addCompletedHandler:^(id<MTLCommandBuffer> 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<MTLCommandBuffer> 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<MTLCommandBuffer> cb = [presenterObject newCommandBuffer];
_cdv->FlushView(cb);
[cb commit];
@autoreleasepool
{
id<MTLCommandBuffer> cbFlush = [presenterObject newCommandBuffer];
id<MTLCommandBuffer> cbFinalize = [presenterObject newCommandBuffer];
cb = [presenterObject newCommandBuffer];
_cdv->FinalizeFlush(cb);
[cb commit];
_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);
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();
this->_presenter->UpdateLayout();
OSSpinLockLock(&this->_spinlockViewNeedsFlush);
this->_viewNeedsFlush = true;
OSSpinLockUnlock(&this->_spinlockViewNeedsFlush);
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<MTLCommandBuffer>)userData];
}
void MacMetalDisplayView::FinalizeFlush(void *userData)
void MacMetalDisplayView::FinalizeFlush(void *userData, uint64_t outputTime)
{
[(DisplayViewMetalLayer *)this->_caLayer presentDrawableWithCommandBuffer:(id<MTLCommandBuffer>)userData];
[(DisplayViewMetalLayer *)this->_caLayer presentDrawableWithCommandBuffer:(id<MTLCommandBuffer>)userData outputTime:outputTime];
}
void MacMetalDisplayView::FlushAndFinalizeImmediate()

View File

@ -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_

View File

@ -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();