Opengl es 如何将OpenGL图形与UIKit更新同步

Opengl es 如何将OpenGL图形与UIKit更新同步,opengl-es,uikit,core-animation,Opengl Es,Uikit,Core Animation,在我们的应用程序中,CaeAglayer上方有UIScrollView。UIScrollView包含一些UIView(红色矩形)。在CaeAglayer中,我们绘制白色矩形。白色矩形的中心与红色矩形的中心相同。当UIScrollView滚动时,我们更新CaeAglayer中白色矩形的位置并渲染它们 我们得到了预期的结果:白色矩形的中心始终与红色矩形的中心相同 但我们不能将CaeAglayer的更新和UIScrollView中视图的移动同步。 我们有一些误解——红色矩形落后于白色矩形 粗略地说,我

在我们的应用程序中,CaeAglayer上方有UIScrollView。UIScrollView包含一些UIView(红色矩形)。在CaeAglayer中,我们绘制白色矩形。白色矩形的中心与红色矩形的中心相同。当UIScrollView滚动时,我们更新CaeAglayer中白色矩形的位置并渲染它们

我们得到了预期的结果:白色矩形的中心始终与红色矩形的中心相同

但我们不能将CaeAglayer的更新和UIScrollView中视图的移动同步。 我们有一些误解——红色矩形落后于白色矩形

粗略地说,我们真的想让Caeaglayer与UISCOLLIEW一起滞后

我们已经准备好了示例代码。在设备上运行它并滚动,您将看到白色矩形(由OpenGL绘制)比红色矩形(常规UIView)移动得更快。OpenGL正在scrollViewDidScroll:委托调用中更新

即使在iOS模拟器中,其行为也相同,只需看一下视频:

红色=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:


示例项目基本上在OpenGL中绘制一组白色正方形并设置其动画,并对一组红色的UIView执行相同的操作。在红色和白色方块之间可以很容易地看到滞后现象。

由于缺乏正确的答案,我想与大家分享我的想法:

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

为了实现这一点,您的应用程序的流程序列化图层层次结构和对其属性的更改,并将数据传递给backboardd,backboardd反过来呈现和合成图层,并使结果可见

将OpenGL与UIKit混合时,CaeAglayer的渲染(在应用程序中完成)必须与其他核心动画层合成。我猜CaeAglayer使用的渲染缓冲区是以某种方式与BackboardDD共享的,以便以一种快速的方式传输应用程序的渲染。此机制不一定必须与核心动画的更新同步


为了解决您的问题,有必要找到一种方法,将OpenGL渲染与核心动画层序列化同步,并传输到BackboardDD。很抱歉无法提供解决方案,但我希望这些想法和猜测有助于您理解问题的原因。

为了同步UIKit和OpenGL绘图,您应该尝试在核心动画希望您绘制的地方进行渲染,例如:

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

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

在仔细研究了一下之后,我想扩展一下我之前的答案:

OpenGL渲染将立即从
scrollViewDidScroll:
方法中完成,而UIKit绘图将在稍后从runloop进行常规CATTransaction更新期间执行

要将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];
}

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

理想情况下,这可以通过访问核心动画的最终合成GL上下文来解决。但是我们不能访问私有API

另一种方法是将GL结果传输到CA。我尝试通过读取帧缓冲区像素发送到CALayer,这太慢了。应传输大约超过200MB/s的数据。现在我正试图优化这个。因为这是我最后一次尝试

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

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

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


这里是相关的雷达:

在iOS 9
CaeAglayer
中有
presentsWithTransaction
属性,用于同步这两个属性。

+1用于优秀(简化)示例项目。使用Xcode 6+,要使示例项目正常工作,需要:
在ViewController中导入
。谢谢帮助!我们已经找到了这个解决方案,并且图形是同步的。但是性能非常差(我说的是我们的应用程序,不是示例代码)。不幸的是,这不是我们的方式。@NikolaiRuhe这太棒了,谢谢你发布它。我们需要将OpenGL视图与CA视图同步,这非常有效。我不能评论性能-在我们的情况下,性能不是问题,因为不经常需要同步抽签。性能也有问题。通过刷新除scrollViewDidScroll之外的所有内容修复了此问题,因此它是用于缩放的视图CrollView、GestureRecognitizer、GestureRecognitizer应该开始,GestureRecognitizerCaeglayer不支持调用setNeedsDisplay。目前(iOS 6.0)它支持调用
setNeedsDisplay
并正确地发送
-displayLayer:
消息,但是GL渲染仍然比CA渲染早。Eonil,如果您知道任何允许存档结果的私有API,请告诉我。@beefon很遗憾,我也不知道。这只是我的g