使用NSOpenGLLayer从单独的线程绘制

时间:2012-02-25 08:52:25

标签: multithreading cocoa layer nsopenglview nsopengl

我正在开发一款需要使用OpengGL绘制的应用程序,其刷新率至少等于显示器的刷新率。我需要在一个单独的线程中执行绘图,以便绘图永远不会被强烈的UI操作锁定。

实际上我将NSOpenGLViewCVDisplayLink结合使用,我可以毫无问题地达到60-80FPS。

因为我还需要在这个视图的顶部显示一些cocoa控件,所以我试图在LayerBackedOpenGLView Apple示例之后继承NSOpenGLView并使其支持图层。

结果不尽如人意,我收到了很多文物。

因此,我使用单独的NSWindow来托管cocoa控件并将此窗口添加为包含NSOpenGLView的主窗口的子窗口,从而解决了问题。 它工作正常,我可以获得与初始实现完全相同的FPS。

由于我认为这个解决方案非常像一个肮脏的黑客,我正在寻找一种替代和更干净的方式来实现我所需要的。

几天前,我遇到了NSOpenGLLayer,我认为它可以作为我问题的可行解决方案。

最后,在所有这个序言之后,我的问题出现了: 是否可以使用NSOpenGLLayer回调从单独的线程中绘制到CVDisplayLink

到目前为止,我已尝试实现此功能,但我无法从CVDisplayLink回调中进行绘制。我只能-setNeedsDisplay:TRUE来自NSOpenGLLayer回调的CVDisplayLink,然后在{可能被cocoa自动调用时-drawInOpenGLContext:pixelFormat:forLayerTime:displayTime:执行绘图。但是我想这是我从主线程中抽取的,不是吗?

在Google上搜索之后,我甚至找到了this帖子,其中用户声称Lion下的绘图只能在-drawInOpenGLContext:pixelFormat:forLayerTime:displayTime:内发生。

我现在正在使用Snow Leopard,但应用程序应该在Lion上运行完美。

我错过了什么吗?

1 个答案:

答案 0 :(得分:4)

是的,有可能,但不推荐。从您的CVDisplayLink中调用图层上的display。这将导致canDrawInContext:...被调用,如果它返回YES,将调用drawInContext:...并在所谓的display的任何线程上调用所有这些。要使渲染图像在屏幕上可见,您必须调用[CATransaction flush]。这个方法已经在Apple邮件列表上提出了,虽然它不是完全没有问题的(其他视图的显示方法也可能在你的后台线程上调用,而且并非所有视图都支持从后台线程渲染)。

推荐的方法是使图层异步并在主线程上呈现OpenGL上下文。如果你不能以这种方式实现良好的帧速率,因为你的主线程在其他地方忙,所以建议将其他所有东西(几乎是你的整个应用程序逻辑)移动到其他线程(例如使用Grand Central Dispatch)并且只保留用户输入和在主线程上绘制代码。如果你的窗户很大,你可能仍然没有比30 FPS更好的东西(一帧两次刷新屏幕),但事实上,CALayer组合似乎是一个相当昂贵的过程,它已经或多或少地进行了优化静态图层(例如包含图片的图层),而不是用于更新图层的图层60 FPS。

E.g。如果您正在编写3D游戏,建议您不要将CALayers与OpenGL内容混合使用。如果您需要Cocoa UI元素,请将它们与OpenGL内容分开(例如,将窗口水平分割为仅显示OpenGL的部分和仅显示控件的部分)或自己绘制所有控件(这对于游戏来说非常常见)。

最后但并非最不重要的是,两种窗口的方法并不像你想象的那样具有异国情调,这就是VLC(视频播放器)如何在视频图像上绘制控件(也是由Mac上的OpenGL渲染)。