Как синхронизировать рисунок 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. Отставание можно легко увидеть между красным и белым квадратами.
Ответы
Ответ 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];
}
Ответ 7
Обратитесь к следующей документации:
http://developer.apple.com/library/ios/#documentation/QuartzCore/Reference/CALayerDelegate_protocol/Reference/Reference.html#//apple_ref/doc/uid/TP40012871