24

在我们的应用程序中,我们在 CAEAGLLayer 上方有 UIScrollView。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 模拟器中它的行为也是一样的,只需看一下视频: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 中绘制和动画一组白色方块,并为一组红色 UIViews 做同样的事情。在红色和白色方块之间很容易看到滞后。

4

6 回答 6

12

在 iOS 9CAEAGLLayer中具有presentsWithTransaction同步两者的属性。

于 2015-06-09T02:56:26.590 回答
8

事实上,您无法使用当前的 API 同步它们。MobileMaps.app 和 Apple Map Kit 使用 CAEAGLLayer 上的私有属性asynchronous来解决此问题。

这里是相关雷达: http://openradar.appspot.com/radar?id= 3118401

于 2013-06-13T20:54:36.837 回答
5

在挖掘了一下之后,我想扩展我之前的答案:

您的 OpenGL 渲染会立即在该scrollViewDidScroll:方法中完成,而 UIKit 绘图稍后会在运行循环的正常 CATransaction 更新期间执行。

要将 UIKit 更新与 OpenGL 渲染同步,只需将两者包含在一个显式事务中并刷新它以强制 UIKitbackbaordd立即提交更改:

- (void)scrollViewDidScroll:(UIScrollView *)aScrollView {
    [CATransaction begin];
    [overlayView updateVisibleRect:CGRectMake(...)];
    [openGlView updateVisibleRect:CGRectMake(...)];
    [CATransaction flush]; // trigger a UIKit and Core Animation graphics update
    [CATransaction commit];
}
于 2012-11-12T12:58:21.973 回答
2

在缺乏正确答案的情况下,我想分享我的想法:

有两种绘制方式:Core Animation (UIKit) 和 OpenGL。最后,所有的绘图都是由 OpenGL 完成的,但核心动画部分是在 backboardd(或 iOS 6 之前的 Springboard.app)中渲染的,它充当一种窗口服务器。

为了完成这项工作,您的应用程序的进程会序列化层层次结构并更改其属性,并将数据传递给 backboardd,然后由 backboardd 渲染和组合层并使​​结果可见。

在将 OpenGL 与 UIKit 混合时,CAEAGLLayer 的渲染(在您的应用程序中完成)必须与其余的核心动画层合成。我猜测 CAEAGLLayer 使用的渲染缓冲区以某种方式与 backboardd 共享,以便快速传输应用程序的渲染。这种机制不一定必须与来自 Core Animation 的更新同步。

要解决您的问题,有必要找到一种将 OpenGL 渲染与 Core Animation 层序列化同步并传输到 backboardd 的方法。很抱歉无法提出解决方案,但我希望这些想法和猜测能帮助您理解问题的原因。

于 2012-10-24T16:29:21.270 回答
0

我正在尝试解决完全相同的问题。我尝试了上述所有方法,但没有任何方法可以接受。

理想情况下,这可以通过访问 Core Animation 的最终合成 GL 上下文来解决。但是我们无法访问私有 API。

另一种方法是将 GL 结果传输到 CA。我通过读取帧缓冲区像素来分派到太慢的 CALayer 来尝试这个。应该传输大约超过 200MB/秒的数据。现在我正在尝试优化它。因为这是我可以尝试的最后一种方法。

更新

仍然很重,但读取帧缓冲区并将其设置为 CALayer.content 运行良好,并且在我的 iPhone 4S 上显示出可接受的性能。无论如何,超过 70% 的 CPU 负载仅用于此读取和设置操作。尤其是glReadPixels我,他们中的大多数人只是在等待从 GPU 内存读取数据的时间。

更新2

我放弃了最后一种方法。它的成本几乎是纯粹的像素传输,而且仍然太慢。我决定在 GL 中绘制每个动态对象。只有一些静态弹出视图将使用 CA 绘制。

于 2012-12-27T09:28:38.930 回答
-1

为了同步 UIKit 和 OpenGL 的绘制,你应该尝试渲染 Core Animation 期望你绘制的地方,例如:

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

- (void)updateVisibleRect:(CGRect)rect {
    _visibleRect = rect;
    [self.layer setNeedsDisplay];
}
于 2012-10-24T21:55:35.290 回答