在我们的应用程序中,我们在CAEAGLLayer上面有UIScrollView。 UIScrollView包含一些UIViews(红色矩形)。在CAEAGLLayer中,我们绘制白色矩形。白色矩形的中心与红色矩形的中心相同。当UIScrollView滚动时,我们更新CAEAGLLayer中白色矩形的位置并渲染它们。
我们得到了预期的结果:白色矩形的中心总是与红色矩形的中心相同。
但是我们无法将CAEAGLLayer的更新与UIScrollView中的视图移动同步。 我们有一些错误 - 红色矩形落后于白色矩形。
粗略地说,我们真的想让CAEAGLLayer与UIScollView一起滞后。
我们准备了示例代码。在设备上运行并滚动,您将看到白色矩形(由OpenGL绘制)比红色矩形(常规UIViews)移动得更快。在scrollViewDidScroll:委托调用中正在更新OpenGL。
https://www.dropbox.com/s/kzybsyu10825ikw/ios-opengl-scrollview-test.zip
即使在iOS模拟器中也是如此,只需看一下视频: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
示例项目基本上在OpenGL中绘制并动画一组WHITE正方形,并对一组RED UIViews执行相同操作。红色和白色方块之间很容易看到滞后。
答案 0 :(得分:11)
在iOS 9中,CAEAGLLayer
具有presentsWithTransaction
属性,可同步两者。
答案 1 :(得分:8)
实际上,您无法使用当前API同步它们。 MobileMaps.app和Apple Map Kit使用CAEAGLLayer asynchronous
上的私有属性来解决此问题。
答案 2 :(得分:5)
稍微挖了一下后,我想扩展我以前的答案:
您的OpenGL渲染会立即从scrollViewDidScroll:
方法中完成,而UIKit绘图将在稍后执行,在来自runloop的正常CATransaction更新期间执行。
要将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];
}
答案 3 :(得分:2)
由于缺乏正确答案,我想分享一下我的想法:
有两种绘图方式:核心动画(UIKit)和OpenGL。最后,所有绘图都是由OpenGL完成的,但Core Animation部分是在backboardd(或者是iOS 6之前的Springboard.app)中渲染的,它是一种窗口服务器。
为了使这项工作正常,您的应用程序的流程会序列化层次结构并更改其属性,并将数据传递给backboardd,后者又会渲染和组合图层并使结果可见。
将OpenGL与UIKit混合时,CAEAGLLayer的渲染(在您的应用程序中完成)必须与剩余的Core Animation图层合成。我猜测CAEAGLLayer使用的渲染缓冲区以某种方式与backboardd共享,以便快速传输应用程序的渲染。此机制不一定必须与Core Animation的更新同步。
要解决您的问题,有必要找到一种方法将OpenGL渲染与Core Animation层序列化同步并转移到Backboardd。很抱歉无法提供解决方案,但我希望这些想法和猜测可以帮助您了解问题的原因。
答案 4 :(得分:0)
我正在努力解决完全相同的问题。我尝试了上面描述的所有方法,但没有什么是可以接受的。
理想情况下,这可以通过访问Core Animation的最终合成GL上下文来解决。但我们无法访问私有API。
另一种方法是将GL结果传输到CA.我尝试通过读取帧缓冲区像素来调度到CALayer,这太慢了。应传输大约200MB /秒以上的数据。现在我正在努力优化这一点。因为这是我可以尝试的最后一种方式。
仍然很重,但读取帧缓冲区并将其设置为CALayer.content运行良好,并在我的iPhone 4S上显示可接受的性能。无论如何,超过70%的CPU负载仅用于此读取和设置操作。特别是glReadPixels
我其中大多数只是等待从GPU内存中读取数据的时间。
我放弃了最后一种方法。它的成本几乎完全是像素传输,但仍然太慢。我决定在GL中绘制每个动态对象。只会使用CA绘制一些静态弹出视图。
答案 5 :(得分:-1)
为了同步UIKit和OpenGL绘图,您应该尝试渲染Core Animation期望您绘制的位置,例如:
- (void)displayLayer:(CALayer *)layer
{
[self drawFrame];
}
- (void)updateVisibleRect:(CGRect)rect {
_visibleRect = rect;
[self.layer setNeedsDisplay];
}