Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to synchronize OpenGL drawing with UIKit updates

In our app we have UIScrollView above CAEAGLLayer. UIScrollView contains some UIViews (red rectangles). In CAEAGLLayer we draw white rectangles. Centers of white rectangles are the same as the centers of red rectangles. When UIScrollView scrolls, we update positions of white rectangles in CAEAGLLayer and render them.

We are getting the expected result: centers of white rectangles are always the same as centers of red rectangles.

But we can't synchronize updates of the CAEAGLLayer with the movement of the views inside UIScrollView. We have some kind of mistiming – red rectangles lag behind white rectangles.

Speaking roughly, we really want to make CAEAGLLayer lag together with the UIScollView.

We have prepared sample code. Run it on the device and scroll, and you will see that white rectangles (drawn by OpenGL) are moving faster than red ones (regular UIViews). OpenGL is being updated within scrollViewDidScroll: delegate call.

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

It behaves the same even in iOS Simulator, just take a look at the video: http://www.youtube.com/watch?v=1T9hsAVrEXw

Red = UIKit, White = OpenGL

Code is:

- (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(...)];
}

Edit:

The same issue can easily be demonstrated in a much simplified sample. The working xcodeproj can be find at:

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

The sample project basically draws and animates a set of WHITE squares in OpenGL and does the same for a RED set of UIViews. The lagging can easily be seen between the red and white squares.

like image 420
beefon Avatar asked Oct 19 '12 04:10

beefon


3 Answers

In iOS 9 CAEAGLLayer has presentsWithTransaction property that synchronizes the two.

like image 181
Bartosz Ciechanowski Avatar answered Oct 23 '22 21:10

Bartosz Ciechanowski


In fact, you can't synchronize them using current APIs. MobileMaps.app and Apple Map Kit use private property on CAEAGLLayer asynchronous to workaround this issue.

Here is related radar: http://openradar.appspot.com/radar?id=3118401

like image 24
Roman Busygin Avatar answered Oct 23 '22 21:10

Roman Busygin


In lack of a proper answer I'd like to share my thoughts:

There are two ways of drawing involved: Core Animation (UIKit) and OpenGL. In the end, all drawing is done by OpenGL but the Core Animation part is rendered in backboardd (or Springboard.app, before iOS 6) which serves as a kind of window server.

To make this work your app's process serializes the layer hierarchy and changes to its properties and passes the data over to backboardd which in turn renders and composes the layers and makes the result visible.

When mixing OpenGL with UIKit, the CAEAGLLayer's rendering (which is done in your app) has to be composited with the rest of the Core Animation layers. I'm guessing that the render buffer used by the CAEAGLLayer is somehow shared with backboardd to have a fast way of transferring your application's rendering. This mechanism does not necessarily has to be synchronized with the updates from Core Animation.

To solve your issue it would be necessary to find a way of synchronizing the OpenGL rendering with the Core Animation layer serialization and transfer to backboardd. Sorry for not being able to present a solution but I hope these thoughts and guesses help you to understand the reason for the problem.

like image 5
Nikolai Ruhe Avatar answered Oct 23 '22 20:10

Nikolai Ruhe