首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >我可以使用中央调度将任务分派给OSX DisplayLink线程吗?

我可以使用中央调度将任务分派给OSX DisplayLink线程吗?
EN

Stack Overflow用户
提问于 2013-11-20 19:45:09
回答 1查看 1.1K关注 0票数 4

在OSX下的OpenGL应用程序中,呈现代码通常运行在与主线程分离的DisplayLink线程上。

在后台执行任务(例如加载GL资产)时,同步线程是很重要的,因此呈现线程并不试图从被后台线程主动更改的模型中提取。

当呈现发生在主线程上时,我一直在使用GCD将后台任务的关键部分分派到主分派队列,如下所示:

代码语言:javascript
复制
dispatch_async(dispatch_get_main_queue(), ^{ [self doCriticalThing]; });

但是当呈现发生在DisplayLink线程上时,这是行不通的,因为在DisplayLink试图呈现时,我在试图运行关键任务的主线程之间会出现线程冲突,这并不奇怪。

是否可以使用GCD将任务分派给DisplayLink线程,而不是主线程?

还是我需要恢复到使用这样的东西:

代码语言:javascript
复制
performSelector:onThread:withObject:waitUntilDone:

将任务直接分配给DisplayLink线程?

EN

回答 1

Stack Overflow用户

发布于 2013-11-23 15:19:17

似乎没有任何内置的API来实现这一点.尽管如此,烹饪还是相当简单的。看起来可能是这样的:

代码语言:javascript
复制
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从它中提取现有的回调,并且我们需要设置回调以服务我们的队列)。

代码语言:javascript
复制
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

那么,要使用它,您可以这样做:

代码语言:javascript
复制
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));
    }];
}
票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/20105476

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档