Подтвердить что ты не робот

Как синхронизировать рисунок OpenGL с обновлениями UIKit

В нашем приложении UIScrollView над CAEAGLLayer. UIScrollView содержит некоторые UIViews (красные прямоугольники). В CAEAGLLayer мы рисуем белые прямоугольники. Центры белых прямоугольников такие же, как центры красных прямоугольников. Когда прокручивается UIScrollView, мы обновляем позиции белых прямоугольников в CAEAGLLayer и визуализируем их.

Мы получаем ожидаемый результат: центры белых прямоугольников всегда совпадают с центрами красных прямоугольников.

Но мы не можем синхронизировать обновления CAEAGLLayer с перемещением представлений внутри UIScrollView. У нас есть какая-то ошибка: красные прямоугольники отстают от белых прямоугольников.

Говоря грубо, мы действительно хотим сделать задержку CAEAGLLayer вместе с UIScollView.

Мы подготовили образец кода. Запустите его на устройстве и прокрутите, и вы увидите, что белые прямоугольники (нарисованные OpenGL) движутся быстрее, чем красные (обычные UIViews). OpenGL обновляется в scrollViewDidScroll: делегировать вызов.

https://www.dropbox.com/s/kzybsyu10825ikw/ios-opengl-scrollview-test.zip

Он ведет себя одинаково даже в iOS Simulator, просто взгляните на видео: http://www.youtube.com/watch?v=1T9hsAVrEXw

Красный = UIKit, белый = OpenGL

Код:

- (void)scrollViewDidScroll:(UIScrollView *)aScrollView {
    // reuses red squares that are gone outside thw bounds
    [overlayView updateVisibleRect:CGRectMake(...)];
    // draws white squares using OpenGL under the red squares
    [openGlView updateVisibleRect:CGRectMake(...)];
}

Изменить:

Та же проблема может быть легко продемонстрирована в упрощенном примере. Рабочий xcodeproj можно найти по адресу:

https://www.dropbox.com/s/vznzccibhlf8bon/simple_sample.zip

Пример проекта в основном рисует и анимирует набор квадратов WHITE в OpenGL и делает то же самое для RED-набора UIViews. Отставание можно легко увидеть между красным и белым квадратами.

4b9b3361

Ответ 1

Фактически вы не можете синхронизировать их с использованием существующих API. MobileMaps.app и Apple Map Kit используют частную собственность в CAEAGLLayer asynchronous, чтобы обойти эту проблему.

Вот связанный радар: http://openradar.appspot.com/radar?id=3118401

Ответ 2

В iOS 9 CAEAGLLayer есть свойство presentsWithTransaction, которое синхронизирует два.

Ответ 3

После небольшого поиска я хотел бы продлить мой предыдущий ответ:

Ваш рендеринг OpenGL немедленно выполняется из метода scrollViewDidScroll:, а чертеж UIKit выполняется позже, во время обычных обновлений CATransaction из runloop.

Чтобы синхронизировать обновления UIKit с рендерингом OpenGL, просто заключайте как в одну явную транзакцию, так и запустите ее, чтобы заставить UIKit немедленно зафиксировать изменения backbaordd:

- (void)scrollViewDidScroll:(UIScrollView *)aScrollView {
    [CATransaction begin];
    [overlayView updateVisibleRect:CGRectMake(...)];
    [openGlView updateVisibleRect:CGRectMake(...)];
    [CATransaction flush]; // trigger a UIKit and Core Animation graphics update
    [CATransaction commit];
}

Ответ 4

В отсутствие надлежащего ответа я хотел бы поделиться своими мыслями:

Существует два способа рисования: Core Animation (UIKit) и OpenGL. В конце концов, все рисунки выполняются OpenGL, но часть Core Animation отображается в backboardd (или Springboard.app, до iOS 6), которая выступает в качестве своего рода оконного сервера.

Чтобы сделать эту работу, ваш процесс приложения выполняет сериализацию иерархии уровней и изменения ее свойств и передает данные на backboardd, который, в свою очередь, отображает и компонует слои и делает результат видимым.

При смешивании OpenGL с UIKit рендеринг CAEAGLLayer (который выполняется в вашем приложении) должен быть составлен вместе с остальными слоями Core Animation. Я предполагаю, что буфер визуализации, используемый CAEAGLLayer, каким-то образом делится с backboardd, чтобы иметь быстрый способ переноса рендеринга вашего приложения. Этот механизм необязательно должен быть синхронизирован с обновлениями из Core Animation.

Чтобы решить вашу проблему, необходимо найти способ синхронизации рендеринга OpenGL с сериализацией уровня Core Animation и переноса на backboardd. Извините за невозможность представить решение, но я надеюсь, что эти мысли и догадки помогут вам понять причину проблемы.

Ответ 5

Я пытаюсь решить точно такую ​​же проблему. Я пробовал весь описанный выше метод, но ничего не было приемлемо.

В идеале это можно решить, обратившись к финальной компоновке GL-контекста Core Animation. Но мы не можем получить доступ к частному API.

Другим способом является передача результата GL в CA. Я попробовал это, читая пиксели фрейма-буфера для отправки в CALayer, который был слишком медленным. Около 200 МБ/с данные должны быть переданы. Теперь я пытаюсь оптимизировать это. Потому что это последний способ, которым я могу попробовать.

Update

По-прежнему тяжелый, но считывающий фрейм-буфер и настройка его на CALayer.content работает хорошо и показывает приемлемую производительность на моем iPhone 4S. В любом случае более 70% загрузки процессора используется только для этой операции чтения и настройки. Особенно glReadPixels Я это большинство из них всего лишь время ожидания для чтения данных из памяти GPU.

Update2

Я отказался от последнего метода. Это почти чистая передача пикселей и все еще слишком медленная. Я решил рисовать все динамические объекты в GL. Только CA будет отображаться только несколько статических всплывающих просмотров.

Ответ 6

Чтобы синхронизировать чертежи UIKit и OpenGL, вы должны попытаться отобразить, где Core Animation ожидает, что вы рисуете, то есть что-то вроде этого:

- (void)displayLayer:(CALayer *)layer
{
    [self drawFrame];
}

- (void)updateVisibleRect:(CGRect)rect {
    _visibleRect = rect;
    [self.layer setNeedsDisplay];
}