Визуализация CADisplayLink OpenGL нарушает поведение UIScrollView - PullRequest
24 голосов
/ 10 мая 2011

Есть несколько похожих вопросов по SO (ссылки в конце), но ни один из них не позволил мне решить мою проблему, так что вот так:

Я использую рендеринг OpenGL, чтобы создать библиотеку листов и кеширования изображений для использования в игровом проекте, и я хочу перехватить физику UIScrollView, чтобы позволить пользователю перемещаться по изображениям (так как он имеет хорошее поведение отскока с тем же успехом Итак, у меня есть UIScrollView, который я использую, чтобы получить вид рендеринга для моих текстур, но есть проблема - перемещение по представлению прокрутки не позволяет CADisplayLink запускаться до тех пор, пока пользователь не закончит прокрутку (что выглядит ужасно). Одним из временных исправлений было использование NSRunLoopCommonModes вместо режима запуска по умолчанию, но, к сожалению, это нарушает некоторые аспекты поведения просмотра прокрутки на некоторых телефонах, на которых я тестирую (3GS и симулятор, кажется, работают нормально, в то время как iPhone4 и 3G не работают «т).

Кто-нибудь знает, как я мог обойти это столкновение между CADisplayLink и UIScrollView, или знает, как исправить UIScrollView, работающий в других режимах выполнения? Заранее спасибо:)

Обещанные ссылки на похожие вопросы: UIScrollView не работает и останавливает прокрутку с помощью рендеринга OpenGL (связанный CADisplayLink, NSRunLoop)

Анимация в представлении OpenGL ES останавливается при перетаскивании UIScrollView на iPhone

Ответы [ 4 ]

43 голосов
/ 11 мая 2011

Возможно, что медленное обновление основного потока, запускаемое CADisplayLink, является тем, что нарушает поведение прокрутки UIScrollView здесь. Ваш рендеринг OpenGL ES может занять достаточно много времени для каждого кадра, чтобы отбросить синхронизацию UIScrollView при использовании NSRunLoopCommonModes для CADisplayLink.

Одним из способов решения этой проблемы является выполнение действий рендеринга OpenGL ES в фоновом потоке с использованием последовательной очереди Grand Central Dispatch. Я сделал это в своем недавнем обновлении до Molecules (исходный код которого можно найти по этой ссылке), и при тестировании с использованием NSRunLoopCommonModes на моем CADisplayLink я не вижу никакого прерывания нативного поведение прокрутки табличного представления, которое отображается на экране одновременно с рендерингом.

Для этого вы можете создать очередь последовательной отправки GCD и использовать ее для всех ваших обновлений рендеринга в конкретный контекст OpenGL ES, чтобы избежать двух действий, одновременно записывающих контекст. Затем в вашем обратном вызове CADisplayLink вы можете использовать код, подобный следующему:

if (dispatch_semaphore_wait(frameRenderingSemaphore, DISPATCH_TIME_NOW) != 0)
{
    return;
}

dispatch_async(openGLESContextQueue, ^{

    [EAGLContext setCurrentContext:context];

    // Render here

    dispatch_semaphore_signal(frameRenderingSemaphore);
});

, где frameRenderingSemaphore создан ранее следующим образом:

frameRenderingSemaphore = dispatch_semaphore_create(1);

Этот код будет добавлять новое действие рендеринга кадров в очередь, только если он не находится в середине выполнения. Таким образом, CADisplayLink может запускаться непрерывно, но он не будет перегружать очередь ожидающими действиями рендеринга, если обработка кадра занимает более 1/60 секунды.

Опять же, я попробовал это на своем iPad здесь и не обнаружил нарушения прокрутки в представлении таблицы, только небольшое замедление, поскольку рендеринг OpenGL ES потреблял циклы графического процессора.

4 голосов
/ 02 сентября 2011

Мое простое решение - уменьшить частоту рендеринга вдвое, когда цикл выполнения находится в режиме отслеживания. Все мои UIScrollViews теперь работают гладко.

Вот фрагмент кода:

- (void) drawView: (CADisplayLink*) displayLink
{
    if (displayLink != nil) 
    {
        self.tickCounter++;

        if(( [[ NSRunLoop currentRunLoop ] currentMode ] == UITrackingRunLoopMode ) && ( self.tickCounter & 1 ))
        {
            return;
        }

        /*** Rendering code goes here ***/
     }
}
3 голосов
/ 01 февраля 2013

Ответ в следующем посте работает очень хорошо для меня (кажется, что он очень похож на ответ Тилля):

UIScrollView приостанавливает NSTimer до завершения прокрутки

Подводя итог: отключите цикл рендеринга CADisplayLink или GLKViewController, когда появится UIScrollView, и запустите NSTimer для выполнения цикла обновления / рендеринга с желаемой частотой кадров.Когда UIScrollView отклонен / удален из иерархии представления, повторно включите цикл displayLink / GLKViewController.

В подклассе GLKViewController я использую следующий код

при появлении UIScrollView:

// disable GLKViewController update/render loop, it will be interrupted
// by the UIScrollView of the MPMediaPicker
self.paused = YES;
updateAndRenderTimer = [NSTimer timerWithTimeInterval:1.0f/60.0f target:self selector:@selector(updateAndRender) userInfo:nil repeats:YES];
[[NSRunLoop mainRunLoop] addTimer:updateAndRenderTimer forMode:NSRunLoopCommonModes];

при закрытии UIScrollView:

// enable the GLKViewController update/render loop and cancel our own.
// UIScrollView wont interrupt us anymore
self.paused = NO;
[updateAndRenderTimer invalidate];
updateAndRenderTimer = nil;

Просто и эффективно.Я не уверен, может ли это вызвать какие-либо артефакты / разрывы, поскольку рендеринг отделен от обновлений экрана, но использование CADisplayLink с NSRunLoopCommonModes полностью нарушает UIScrollView в нашем случае.Использование NSTimer прекрасно выглядит для нашего приложения и определенно намного лучше, чем отсутствие рендеринга.

0 голосов
/ 10 мая 2011

Несмотря на то, что это не идеальное решение, оно все равно может работать в качестве обходного пути; Вы можете игнорировать доступность отображаемой ссылки и использовать NSTimer для обновления вашего GL-слоя.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...