Why does dispatch_semaphore_wait() return YES all

2019-08-05 07:35发布

Brad Larson delivered a solution for the CADisplayLink freeze issue when scroll views are scrolling.

My OpenGL ES draw method is called by a CADisplayLink, and I tried Brad's technique but can't make it work. The core problem is that my OpenGL ES view is hosted by a UIScrollView, and when the UIScrollView scrolls, the CADisplayLink stops firing.

The technique Brad described is supposed to let the CADisplayLink continue to fire even during scrolling (by adding it to NSRunLoopCommonModes instead of the default runloop mode), and using a fancy semaphore trick the rendering callback is supposed to ensure that it doesn't render when UIKit is too occupied.

The problem is though that the semaphore trick prevents the rendering callback from drawing, no matter what.

First, I create the serial GCD queue and semaphore in the initWithFrame method of my OpenGL ES view like this (on the main thread):

frameRenderingQueue = dispatch_queue_create("com.mycompany.crw", DISPATCH_QUEUE_SERIAL);
frameRenderingSemaphore = dispatch_semaphore_create(1);

The display link is created and added to NSRunLoopCommonModes:

CADisplayLink *dl = [[UIScreen mainScreen] displayLinkWithTarget:self selector:@selector(renderFrame)];
[dl addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];

The render callback performs Brad's technique:

- (void)renderFrame {
    if (dispatch_semaphore_wait(frameRenderingSemaphore, DISPATCH_TIME_NOW) != 0) {
        NSLog(@"return"); // Gets called ALWAYS!
        return;
    }

    dispatch_async(drawingQueue, ^{
        @autoreleasepool {

            // OpenGL ES drawing code

            dispatch_semaphore_signal(frameRenderingSemaphore);
        }
    });
}

The dispatch_semaphore_wait function always returns YES and thus the render callback never renders. Even when I'm not scrolling.

I suppose that I missed something important here. Can someone point it out?

Edit: It seems to work only when I call dispatch_sync instead of dispatch_async, but according to Brad dispatch_async would give better performance here.

1条回答
相关推荐>>
2楼-- · 2019-08-05 08:12

I had to change the structure of the code to this:

- (void)renderFrame {
    dispatch_async(drawingQueue, ^{
        if (dispatch_semaphore_wait(frameRenderingSemaphore, DISPATCH_TIME_NOW) != 0) {
            return;
        }

        @autoreleasepool {
            // Drawing code...
        }

        dispatch_semaphore_signal(frameRenderingSemaphore);
    });
}

After I restructured it this way, the dispatch_semaphore_wait call stopped returning YES all the time. I am not sure if this is effectively just disabling Brad's semaphore wait trick or not. But it works.

查看更多
登录 后发表回答