OpenGL ES编程指南(三)

《OpenGL ES Programming Guide》文档翻译之多任务、高分辨率和其他功能

Posted by Ted on March 22, 2018

本文翻译自苹果官方文档OpenGL ES Programming Guide

六、多任务、高分辨率和其他功能

使用OpenGL ES的许多方面都是平台无关的,但在iOS上使用OpenGL ES的一些细节需要特别注意。 尤其是,使用OpenGL ES的iOS应用程序必须正确处理多任务,否则在转到后台时可能会被终止。 在为iOS设备开发OpenGL ES内容时,您还应该考虑显示分辨率和其他设备功能。

不能在后台工作

OpenGL ES应用程序移到后台时必须做额外的操作。 如果应用程序不正确地处理这些任务,它可能会被iOS终止。 此外,应用程序可能需要释放OpenGL ES资源,以便这些资源可用于前台应用程序。

iOS会阻止后台应用程序访问图形处理器,以便前台的应用程序始终能够为用户提供出色的体验。 您的应用程序如果在后台进行OpenGL ES调用,或者在后台将先前提交的命令刷新到GPU,应用程序将会被终止。 您的应用程序必须确保移动到后台之前让先前提交的所有命令在都已完成执行。

如果您使用GLKit view和view controller,并且只在绘图方法中提交OpenGL ES命令,那么当您的应用移动到背景时,您的应用会自动正确运行。 默认情况下,GLKViewController类会在您的应用程序变为非活动状态时暂停其动画计时器,以确保您的绘图方法未被调用。

如果您不使用GLKit view和view controller,或者如果您在GLKView绘图方法外提交OpenGL ES命令,则必须执行以下步骤以确保您的应用程序不会在后台终止:

  • 在您的应用程序delegate的applicationWillResignActive:方法中,您的应用程序应停止其动画计时器(如果有),将自己置于已知良好状态,然后调用glFinish函数。
  • 在您的应用程序delegate的applicationDidEnterBackground:方法中,您的应用程序可能希望删除其某些OpenGL ES对象,以使内存和资源可用于前台应用程序。 调用glFinish函数以确保立即删除资源。
  • 在您的应用退出其applicationDidEnterBackground:方法后,它不能进行任何新的OpenGL ES调用。 如果它进行OpenGL ES调用,它将被iOS终止。
  • 在您的应用程序delegate的applicationWillEnterForeground:方法中,重新创建任何对象并重新启动您的动画计时器

总之,您的应用程序需要调用glFinish函数以确保所有先前提交的命令从命令缓冲区中排出并由OpenGL ES执行。 进入后台后,必须避免使用OpenGL ES,直到它移回到前台。

在移至后台之前删除易重建资源

在移动到后台时,您的应用永远不需要释放OpenGL ES对象。通常,您的应用应该避免处理其内容。考虑两种情况:

  • 用户正在玩您的游戏并暂时退出以查看日历。当玩家回到游戏时,游戏的资源仍然在记忆中,游戏可以立即恢复。

  • 当用户启动另一个OpenGL ES应用程序时,您的OpenGL ES应用程序处于后台。如果该应用程序需要的内存超过设备上的可用内存,系统将自动终止您的应用程序,而无需执行任何其他工作。

您的目标应该是设计您的应用程序成为一个”好公民”:这意味着尽可能缩短移动到前台所需的时间,同时减少其在后台的内存占用量。

以下是您应该如何处理这两种情况的方法:

  • 您的应用应该将纹理,模型和其他资源保留在内存中;花费很长时间重新创建的资源不应该在您的应用移动到后台时处理。
  • 您的应用程序应该处理可以快速轻松地重新创建的对象。寻找消耗大量内存的对象。

简单的目标是你的应用程序分配的帧缓冲区来保存渲染结果。当您的应用程序位于后台时,它对用户不可见,并且可能不会使用OpenGL ES呈现任何新内容。这意味着您的应用程序的帧缓冲区所消耗的内存已分配,但无用。而且,帧缓冲器的内容是暂时的;大多数应用程序每次渲染新帧时都会重新创建帧缓冲区的内容。这使得渲染缓冲区成为一个可以轻松重新创建的内存密集型资源,成为移动到后台时可以处理的对象的良好候选对象。

如果您使用GLKit视图和视图控制器,则当您的应用移动到后台时,GLKViewController类会自动处理其关联视图的帧缓冲区。如果您为其他用途手动创建帧缓冲区,则应该在应用移动到背景时将其丢弃。无论哪种情况,您还应该考虑当时您的应用可以处理的其他暂时资源。

支持高分辨率显示

默认情况下,GLKit View的contentScaleFactor属性的值与包含它的屏幕的比例相匹配,因此将其关联的帧缓冲区配置为在显示器的全分辨率下呈现。

如果您使用Core Animation图层呈现OpenGL ES内容,则默认情况下其比例因子设置为1.0。 要以Retina显示器的全分辨率绘制,您应该更改CAEAGLLayer对象的比例因子以匹配屏幕的比例因子。

当支持具有高分辨率显示器的设备时,您应该相应地调整应用程序的型号和纹理资源。 在高分辨率设备上运行时,您可能需要选择更详细的模型和纹理以呈现更好的图像。 相反,在标准分辨率设备上,您可以使用较小的模型和纹理。

重要提示:许多OpenGL ES API调用以屏幕像素表示尺寸。 如果使用大于1.0的比例因子,则应在使用glScissorglBlitFramebufferglLineWidthglPointSize函数或gl_PointSize着色器变量时相应地调整尺寸。

确定如何支持高分辨率显示器的一个重要因素是性能。 Retina显示屏上缩放倍数的倍增使像素数量增加了四倍,导致GPU处理四倍的碎片。如果您的应用执行许多每片段计算,则像素增加可能会降低帧速率。如果您发现您的应用在较高比例因素下运行速度显着较慢,请考虑以下选项之一:

  • 使用本文档中的性能调整指导来优化片段着色器的性能。

  • 在你的片段着色器中实现一个更简单的算法。通过这样做,您可以降低单个像素的质量,从而以更高的分辨率呈现整个图像。

  • 使用1.0到和屏幕比例因子之间的分数比例因子。比例因子1.5提供比1.0的比例因子更好的质量,但需要填充比缩放为2.0的图像更少的像素。

  • 为您的GLKView对象的drawableColorFormatdrawableDepthFormat属性使用较低精度的格式。通过这样做,可以减少在底层渲染缓冲区上操作所需的内存带宽。

  • 使用较低的比例因子并启用多重采样。另一个优点是多重采样还可以在不支持高分辨率显示的设备上提供更高的质量。

  • 要为GLKView对象启用多重采样,请更改其drawableMultisample属性的值。如果您未渲染到GLKit视图,则必须手动设置多重采样缓冲区并在呈现最终图像之前解决它们(请参阅使用多重采样来提高图像质量)。

    多重采样不是免费的;需要额外的内存来存储额外的样本,并且将样本解析到解析帧缓冲区需要时间。如果您向应用添加多重采样,请始终测试应用的性能以确保其可接受性。

多方向交互

与任何应用程序一样,OpenGL ES应用程序应支持适合其内容的用户界面方向。您可以在其信息属性列表中为您的应用程序声明支持的界面方向,或者使用其supportedInterfaceOrientations方法为托管OpenGL ES内容的视图控制器声明支持的界面方向。

默认情况下,GLKViewController和GLKView类会自动处理方向更改:当用户将设备旋转到支持的方向时,系统会激活方向更改并更改视图控制器视图的大小。当其大小改变时,GLKView对象相应地调整其帧缓冲区和视口的大小。如果您需要响应此更改,请在您的GLKViewController子类中实现viewWillLayoutSubviews或viewDidLayoutSubviews方法,或者在使用自定义GLKView子类时实现layoutSubviews方法。

如果您使用Core Animation图层绘制OpenGL ES内容,则应用程序仍应包含视图控制器来管理用户界面方向。

其他显示屏上展示

iOS设备可以连接到外部显示器。外部显示器的分辨率及其内容比例因子可能与主屏幕的分辨率和比例因子不同;渲染帧的代码应调整为匹配。

在外部显示器上绘图的步骤与在主屏幕上运行的步骤几乎完全相同。

  • 按照多显示器编程指南for iOS中的步骤在外部显示器上创建一个窗口。
  • 为您的渲染策略添加适当的视图或视图控制器对象。
  • 如果使用GLKit进行渲染,请设置GLKViewController和GLKView(或您的自定义子类)的实例,并使用其rootViewController属性将它们添加到窗口中。
  • 如果渲染到Core Animation图层,请将包含图层的视图添加为窗口的子视图。要使用动画循环进行渲染,请通过检索窗口的屏幕属性并调用其displayLinkWithTarget:selector:方法来创建为外部显示器优化的显示链接对象。