在OSX下的OpenGL应用程序中,呈现代码通常运行在与主线程分离的DisplayLink线程上。
在后台执行任务(例如加载GL资产)时,同步线程是很重要的,因此呈现线程并不试图从被后台线程主动更改的模型中提取。
当呈现发生在主线程上时,我一直在使用GCD将后台任务的关键部分分派到主分派队列,如下所示:
dispatch_async(dispatch_get_main_queue(), ^{ [self doCriticalThing]; });但是当呈现发生在DisplayLink线程上时,这是行不通的,因为在DisplayLink试图呈现时,我在试图运行关键任务的主线程之间会出现线程冲突,这并不奇怪。
是否可以使用GCD将任务分派给DisplayLink线程,而不是主线程?
还是我需要恢复到使用这样的东西:
performSelector:onThread:withObject:waitUntilDone:将任务直接分配给DisplayLink线程?
发布于 2013-11-23 15:19:17
似乎没有任何内置的API来实现这一点.尽管如此,烹饪还是相当简单的。看起来可能是这样的:
static CVReturn DispatchDisplayLinkOneshotCallback(CVDisplayLinkRef displayLink, const CVTimeStamp *inNow, const CVTimeStamp *inOutputTime, CVOptionFlags flagsIn, CVOptionFlags *flagsOut, void *displayLinkContext);
void dispatch_async_displaylink(CVDisplayLinkRef dl, dispatch_block_t block)
{
if (!block || !dl)
return;
CVDisplayLinkRef privateDisplayLink = nil;
if (kCVReturnSuccess != CVDisplayLinkCreateWithCGDisplay(CVDisplayLinkGetCurrentCGDisplay(dl), &privateDisplayLink) || !privateDisplayLink)
{
NSLog(@"Couldn't create display link for dispatch");
return;
}
CVDisplayLinkSetOutputCallback(privateDisplayLink, DispatchDisplayLinkOneshotCallback, (void*)CFBridgingRetain([block copy]));
CVDisplayLinkStart(privateDisplayLink);
}
static CVReturn DispatchDisplayLinkOneshotCallback(CVDisplayLinkRef displayLink, const CVTimeStamp *inNow, const CVTimeStamp *inOutputTime, CVOptionFlags flagsIn, CVOptionFlags *flagsOut, void *displayLinkContext)
{
dispatch_block_t block = (dispatch_block_t)CFBridgingRelease(displayLinkContext);
block();
CVDisplayLinkStop(displayLink);
CVDisplayLinkSetOutputCallback(displayLink, NULL, NULL);
CVDisplayLinkRelease(displayLink);
return kCVReturnSuccess;
}我确实注意到,根据经验,使用这种方法排队的块的最终执行顺序是不被维护的,所以如果这对您来说很重要,您可能想要采取不同的方法,但是如果您想要的只是“在显示链接回调上调用这个块”--这应该可以做到。
而且,FWIW,这种方法在技术上并不是在您要传递给它的确切的CVDisplayLink回调上执行,而是在为显示查询那个回调函数,然后制作一个新的“一次性”CVDisplayLink,因此如果您需要对使用确切的CVDisplayLink回调进行强有力的保证,那么您还需要为此添加一些额外的机器。
-performSelector:onThread:withObject:waitUntilDone:是基于runloop的,并且CVDisplayLink线程没有出现运行环(他们有一些东西,但它看起来不像CFRunLoop)编辑:显然这是有效的。不知道那是关于什么的,但这是一个选择。
编辑:好的,我无法抗拒。下面是另一个示例,它维护顺序,并使用您在init时传递给它的特定CVDisplayLink。如果您有一个也需要被调用的“主”回调,那么您必须在init时传递该回调(以及它的上下文)(因为一旦设置了CVDisplayLink,就没有API从它中提取现有的回调,并且我们需要设置回调以服务我们的队列)。
typedef CVReturn (^CVDisplayLinkBlock)(CVDisplayLinkRef displayLink, const CVTimeStamp *inNow, const CVTimeStamp *inOutputTime, CVOptionFlags flagsIn, CVOptionFlags *flagsOut);
@interface MYDisplayLinkDispatcher : NSObject
- (instancetype)initWithDisplayLink: (CVDisplayLinkRef)dl primaryCallback: (CVDisplayLinkOutputCallback)primaryCallback primaryCallbackContext: (void*)pcc;
- (instancetype)initWithDisplayLink: (CVDisplayLinkRef)dl; // No primary callback
- (void)performDisplayLinkBlock: (CVDisplayLinkBlock)block;
- (void)performBlock: (dispatch_block_t)block;
@end
@implementation MYDisplayLinkDispatcher
{
CVDisplayLinkRef _displayLink;
NSMutableArray* _performQueue;
dispatch_queue_t _guardQueue;
CVDisplayLinkOutputCallback _primaryCallback;
void* _primaryCallbackContext;
BOOL _didStartLink;
}
static CVReturn MYDisplayLinkDispatcherOutputCallback(CVDisplayLinkRef displayLink, const CVTimeStamp *inNow, const CVTimeStamp *inOutputTime, CVOptionFlags flagsIn, CVOptionFlags *flagsOut, void *displayLinkContext);
- (instancetype)initWithDisplayLink: (CVDisplayLinkRef)dl primaryCallback: (CVDisplayLinkOutputCallback)primaryCallback primaryCallbackContext: (void*)pcc
{
if (self = [super init])
{
if (!dl)
{
return nil;
}
_displayLink = CVDisplayLinkRetain(dl);
_performQueue = [NSMutableArray array];
_guardQueue = dispatch_queue_create("", DISPATCH_QUEUE_SERIAL);
_primaryCallback = primaryCallback;
_primaryCallbackContext = pcc;
if (kCVReturnSuccess != CVDisplayLinkSetOutputCallback(_displayLink, MYDisplayLinkDispatcherOutputCallback, (__bridge void *)(self)))
{
NSLog(@"Couldn't set output callback on displayLink");
return nil;
}
}
return self;
}
- (instancetype)initWithDisplayLink: (CVDisplayLinkRef)dl
{
return [self initWithDisplayLink: dl primaryCallback: NULL primaryCallbackContext: NULL];
}
- (void)dealloc
{
// If we started it, we should stop it too...
if (_didStartLink) CVDisplayLinkStop(_displayLink);
CVDisplayLinkSetOutputCallback(_displayLink, NULL, NULL);
CVDisplayLinkRelease(_displayLink);
}
- (void)performDisplayLinkBlock: (CVDisplayLinkBlock)block
{
if (block)
{
block = ^CVReturn(CVDisplayLinkRef displayLink, const CVTimeStamp *inNow, const CVTimeStamp *inOutputTime, CVOptionFlags flagsIn, CVOptionFlags *flagsOut)
{
// Capture self so self is assured to stick around through the execution of the block.
id localSelf __attribute__ ((objc_precise_lifetime)) = self;
return block(displayLink, inNow, inOutputTime, flagsIn, flagsOut);
#pragma unused(localSelf)
};
// If it's not running we need to start it, otherwise this block will never run.
const BOOL isRunning = CVDisplayLinkIsRunning(_displayLink);
dispatch_sync(self->_guardQueue, ^{
[_performQueue insertObject: [block copy] atIndex: 0];
_didStartLink = _didStartLink || !isRunning;
});
if (!isRunning)
{
CVDisplayLinkStart(_displayLink);
}
}
}
- (void)performBlock: (dispatch_block_t)block
{
if (block)
{
[self performDisplayLinkBlock:^CVReturn(CVDisplayLinkRef displayLink, const CVTimeStamp *inNow, const CVTimeStamp *inOutputTime, CVOptionFlags flagsIn, CVOptionFlags *flagsOut) {
block();
return kCVReturnSuccess;
}];
}
}
static CVReturn MYDisplayLinkDispatcherOutputCallback(CVDisplayLinkRef displayLink, const CVTimeStamp *inNow, const CVTimeStamp *inOutputTime, CVOptionFlags flagsIn, CVOptionFlags *flagsOut, void *displayLinkContext)
{
MYDisplayLinkDispatcher* self = (__bridge MYDisplayLinkDispatcher *)(displayLinkContext);
// Always do the primary callback first.
if (self->_primaryCallback)
{
CVReturn result = self->_primaryCallback(displayLink, inNow, inOutputTime, flagsIn, flagsOut, self->_primaryCallbackContext);
if (kCVReturnSuccess != result)
{
return result;
}
}
// Then service the queue until its empty or until we get an error condition.
__block CVDisplayLinkBlock block = nil;
do
{
dispatch_sync(self->_guardQueue, ^{
block = [self->_performQueue lastObject];
[self->_performQueue removeLastObject];
});
if (block)
{
CVReturn result = block(displayLink, inNow, inOutputTime, flagsIn, flagsOut);
if (kCVReturnSuccess != result)
{
return result;
}
}
} while (block);
return kCVReturnSuccess;
}
@end那么,要使用它,您可以这样做:
CVDisplayLinkRef dl = nil;
if (kCVReturnSuccess != CVDisplayLinkCreateWithActiveCGDisplays(&dl))
{
NSLog(@"Problem");
}
MYDisplayLinkDispatcher* dld = [[MYDisplayLinkDispatcher alloc] initWithDisplayLink: dl];
for (NSUInteger i = 0; i < 100; i++)
{
[dld performBlock:^{
NSLog(@"Whee! Hello from the display link thread! i: %@", @(i));
}];
}https://stackoverflow.com/questions/20105476
复制相似问题