移动设备中的CPU和GPU已经变得很强大,到处都是配备一个或多个高分辨率屏幕的设备,需要使用带有图形驱动器的复杂交互也日益增加。在这篇博客文章中,我将讨论多线程和多窗口渲染对开发人员来讲意味着什么,同时我将介绍将这些技术应用您设计当中的条件和时机。
什么是多线程渲染?
传统上,OpenGL ES应用程序只从一个线程渲染到一个图层。然而,由于3D渲染引擎的复杂性有所增加,图形API操作的CPU开销已经成为瓶颈—尤其是加载资源时。这就使得多线程渲染引起关注。
渲染线程是与图形环境关联的CPU线程。默认情况下,每个图形环境将无法访问另一个环境中的资源(纹理、着色器和顶点缓冲区)。因此,需要使用共享环境,造成一个或多个后台负载线程可访问主线程的资源。这种渲染模式相当有效的原因有两个:
1. 主线程不会阻塞
从根本上说,一直到应用程序和驱动程序内存之间的传输完成之前,上传数据的图形API调用一定会被阻塞。此外,在许多显卡驱动程序中着色器编译就是阻塞型操作。这种阻塞造成开销较大,导致GPU无法运行。将所有上传操作迁移至后台线程,主线程可以维持统一帧率
在多核CPU上进行并行任务分配
由于图形驱动程序在CPU上运行,将这项运行分配至多个渲染线程,使得操作系统向多个CPU内核并行发布指令。这就导致与单个渲染线程相比,驱动程序的工作负载能够处理的更为迅速
使用多线程渲染?
OpenGL ES数据上传—未经优化
OpenGL ES数据上传—经优化
OpenGL ES数据上传—未经优化
多线程渲染最适合于编译着色器或上传数据至显卡驱动器时CPU资源有限的应用程序。多线程渲染(完成之后效果明显)能够更好地分配驱动程序的任务,并使得应用程序保持统一帧率。
上述简单示例当中,游戏当中从一级升至二级需要上传增加的纹理、 VBO和着色器程序。假设需要完成无缝升级(即启动画面、视频等无法降低上传开销) ,仍在渲染一级时游戏程序必须向驱动程序上传新资源。
在未经优化的情况下,向驱动程序发布调用指令时,由于增加了上传/编译操作负载,每帧所用时间并不一致。提交帧所增加的时间将会造成无法同步刷新,帧率不一致,会感觉游戏很卡。
经过优化的情况下,第二线程用于上传资源。这就使得主线程维持统一的调用递交时间,保证帧率一致。
最佳实践
在实现最佳性能时,应在启动程序时创建渲染线程。主线程应用于所有渲染。增加的线程(在共享环境中创建)应只用于着色器编译和缓存数据上传。后台线程的数量应保持在最低限度(如每个CPU内核一个线程) 。创建线程过多会导致难以维护,代码无法调试。
调用eglMakeCurrent()应保持在最低限度以降低开销(EGL specification规定在上下文一定要开启新线程之前,必须刷新所有未执行的操作)。
什么情况下不应使用多线程渲染?
不受CPU资源限制或不涉及加载次数时
如果运行显卡驱动程序时,CPU资源足够,就应该避免多线程渲染。它会增加渲染引擎的复杂性,如果处理得不好,甚至可能降低性能。
试图“简化”渲染引擎时
最糟的使用实例是不断将单一图形环境绑定至不同线程(使用eglmakeCurrent()) 。这样很糟糕,原因有两个:
The cost of context binding 上下文开销
正如以上所述,调用eglMakeCurrent()迫使驱动程序取消所有未完成的操作
API calls are serialized API调用串行化
由于图形环境在任何时点只能绑定到一个CPU线程,所有API调用将被串行提交
因此,API调用与单线程渲染的开销一致(API调用提交呈串行化),但上行文转换时需要额外开销......也就是说与单线程渲染相比,性能会较差
似乎这是较好的设计,但以这种方式渲染会导致代码复杂凌乱,提交顺序不清(甚至更难以调试! ) 。
不要这样做!
这是多窗口渲染?
多窗口渲染将一个应用程序渲染在多个窗口表面。通过操作系统窗口合成器(例如,Android系统的Surface Flinger或Linux发行版的X11)将这些窗口图层进行合成,以提交至设备屏幕。
在多窗口应用程序中,CPU线程和图形环境呈一对一映射。每个图形环境用于渲染到各自的窗口图层。
什么时候应该使用多窗口渲染?
多窗口渲染最适合用于应用程序需要渲染一个以上屏幕时,例如,当电视机作为第二屏时。
什么时候不应该使用多窗口渲染?
合成层
多层合成—未经优化
多层合成—经优化
在上述未经优化的实例中,在单个图层渲染游戏场景、触摸控件和迷你地图。应用程序利用操作系统合成器将这些图层组合成可以显示的图层。由于必须要为多个图层分配内存,因此这种方法较浪费资源,该合成器将处理未完全使用的透明像素和GPU的隐藏面消除(HSR),(即被不透明UI元素覆盖的片段冗余着色)
。
在经优化的情况下,游戏场景第一次渲染,然后触摸控件和迷你地图直接渲染于同一图层。在应用程序中FBO用于执行合成的情况下,这种方法并不适合。例如,游戏场景可以被渲染至较低分辨率FBO,将位图传输至应用程序窗口表面,UI元素可以按原始分辨率被驱动至顶点(这项技术通常用于在渲染游戏场景时增加每个像素的性能)
。
PVRTrace当中的多线程多窗口支持
自IMAGINATION发布 PowerVR Graphics 3.2 SDK以来 ,PVRTrace(OpenGL
ES捕获和分析工具)支持需要执行这些复杂图形驱动程序交互的应用程序。其中还包括Call View 和Frame
Selector中的每线程状态检查器、每线程过滤,线程使用时间轴图。所有这些功能组合使得多线程OpenGL ES更加便于调试。此外,我们的 PVRVFrameOpenGL ES模拟器的多线程支持已经得到显著改善。
自IMAGINATION发布 PowerVR Graphics 3.2 SDK以来 ,PVRTrace(OpenGL
ES捕获和分析工具)支持需要执行这些复杂图形驱动程序交互的应用程序。其中还包括Call View 和Frame
Selector中的每线程状态检查器、每线程过滤,线程使用时间轴图。所有这些功能组合使得多线程OpenGL ES更加便于调试。此外,我们的
PVRVFrameOpenGL ES模拟器的多线程支持已经得到显著改善。
结论
多线程多窗口渲染当中创建复杂难以调试的渲染引擎很容易您陷入困境。然而,如果使用正确也会帮助系统实现较多功能,提高灵活性。如果你按照本篇博客指南设计,将会提升资源负载的性能,而不会带来不必要的麻烦。
原文链接:
http://blog.imgtec.com/powervr/understanding-opengl-es-multi-thread-mult...