版本记录
版本号 | 时间 |
---|---|
V1.0 | 2017.10.02 |
前言
OpenGL ES是一个强大的图形库,是跨平台的图形API,属于OpenGL的一个简化版本。iOS系统可以利用OpenGL ES将图像数据直接送入到GPU进行渲染,这样避免了从CPU进行计算再送到显卡渲染带来的性能的高消耗,能带来来更好的视频效果和用户体验。接下来几篇就介绍下iOS 系统的 OpenGL ES框架。感兴趣的可以看上面几篇。
1. OpenGL ES 框架详细解析(一) —— 基本概览
2. OpenGL ES 框架详细解析(二) —— 关于OpenGL ES
3. OpenGL ES 框架详细解析(三) —— 构建用于iOS的OpenGL ES应用程序的清单
4. OpenGL ES 框架详细解析(四) —— 配置OpenGL ES的上下文
5. OpenGL ES 框架详细解析(五) —— 使用OpenGL ES和GLKit进行绘制
6. OpenGL ES 框架详细解析(六) —— 绘制到其他渲染目的地
7. OpenGL ES 框架详细解析(七) —— 多任务,高分辨率和其他iOS功能
8. OpenGL ES 框架详细解析(八) —— OpenGL ES 设计指南
9. OpenGL ES 框架详细解析(九) —— 调整您的OpenGL ES应用程序
10. OpenGL ES 框架详细解析(十) —— 使用顶点数据的最佳做法
Concurrency and OpenGL ES - 并发和OpenGL ES
在计算中,并发通常是指同时在多个处理器上执行任务。 通过并行执行工作,任务尽快完成,应用程序对用户的响应更快。 精心设计的OpenGL ES应用程序在GPU上的应用程序处理和GPU上的OpenGL ES处理之间已经展现出一种特定的并发形式的并发性。OpenGL ES Design Guidelines 中引入的许多技术专门用于创建展示出优秀CPU-GPU并行性的OpenGL应用程序。 设计并发应用程序意味着将工作分解为子任务,并确定哪些任务可以并行安全运行,哪些任务必须顺序执行,即哪些任务依赖于其他任务使用的资源或从这些任务返回的结果。
iOS中的每个进程都由一个或多个线程组成。 线程是运行流程的执行流。 苹果提供传统线程和一个名为Grand Central Dispatch(GCD)的功能。 使用Grand Central Dispatch
,您可以将任务分解为子任务,而无需手动管理线程。 GCD根据设备上可用的核心数量分配线程,并自动将任务调度到这些线程。
在更高层次上,Cocoa Touch提供NSOperation和NSOperationQueue,以提供用于创建和调度工作单元的Objective-C抽象。
本章不详细介绍这些技术。 在考虑如何向OpenGL ES应用程序添加并发之前,请参阅 Concurrency Programming Guide。 如果您打算手动管理线程,请参阅Threading Programming Guide。 无论使用哪种技术,在多线程系统上调用OpenGL ES时还有其他限制。 本章帮助您了解多线程程序如何提高OpenGL ES应用程序的性能,OpenGL ES对多线程应用程序的限制以及可用于在OpenGL ES应用程序中实现并发性的常见设计策略。
Deciding Whether You Can Benefit from Concurrency - 决定是否可以从并发中受益
创建多线程应用程序需要在设计,实施和测试应用程序方面付出巨大的努力。 线程也增加了复杂性和开销。 您的应用程序可能需要复制数据,以便将其传递给工作线程,或者多个线程可能需要同步访问相同的资源。 在尝试在OpenGL ES应用程序中实现并发之前,请使用OpenGL ES Design Guidelines中描述的技术在单线程环境中优化OpenGL ES代码。 重点是首先实现优秀的CPU-GPU并行性,然后评估并发编程是否可以提供额外的性能。
一个好的候选结果具有以下特征之一或两者:
- 该应用程序在独立于OpenGL ES渲染的CPU上执行许多任务。 游戏,例如,模拟游戏世界,从计算机控制的对手计算人工智能,并播放声音。 您可以在这种情况下利用并行性,因为许多这些任务不依赖于您的OpenGL ES绘图代码。
- 分析您的应用程序已显示您的OpenGL ES渲染代码在CPU中花费了大量时间。 在这种情况下,GPU是空闲的,因为您的应用程序不能足够快地给它命令。 如果您的CPU限制代码已经被优化,您可以通过将工作分解为并发执行的任务来进一步提高其性能。
如果您的应用程序被阻止等待GPU,并且没有工作,它可以与其OpenGL ES绘图并行执行,那么并不是并发好的选择。 如果CPU和GPU都处于空闲状态,那么您的OpenGL ES需要很简单,无需进一步调整。
OpenGL ES Restricts Each Context to a Single Thread - OpenGL ES将每个上下文限制为单个线程
iOS中的每个线程都有一个当前的OpenGL ES呈现上下文。 每当您的应用程序调用OpenGL ES函数时,OpenGL ES会隐式查找与当前线程相关联的上下文,并修改与该上下文关联的状态或对象。
OpenGL ES不可重入 如果同时从多个线程修改相同的上下文,则结果是不可预测的。 您的应用程式可能会崩溃,否则可能会造成错误。 如果由于某种原因,您决定设置多个线程来定位相同的上下文,那么您必须通过将所有OpenGL ES调用的互斥体放置在上下文中来同步线程。 阻塞的OpenGL ES命令(如glFinish
)不同步线程。
GCD和NSOperationQueue对象可以在他们选择的线程上执行你的任务。 他们可以专门为该任务创建一个线程,或者它们可以重用现有的线程。 但是无论如何,您不能保证哪个线程执行任务。 对于OpenGL ES应用程序,这意味着:
- 每个任务必须在执行任何OpenGL ES命令之前设置上下文。
- 访问相同上下文的两个任务可能永远不会同时执行。
- 每个任务应该在退出之前清除线程的上下文。
Strategies for Implementing Concurrency in OpenGL ES Apps - 在OpenGL ES应用程序中实现并发策略
并发OpenGL ES应用程序应该专注于CPU并行性,以便OpenGL ES可以为GPU提供更多的工作。 以下是在OpenGL ES应用程序中实现并发性的几个策略:
- 将您的应用程序分解为可以并发执行的OpenGL ES和非OpenGL ES任务。 您的OpenGL ES绘图代码作为单个任务执行,因此它仍然在单个线程中执行。 当您的应用程序具有需要重要CPU处理的其他任务时,此策略效果最好。
- 如果性能分析显示您的应用程序在OpenGL中花费了大量CPU时间,可以通过为OpenGL ES上下文启用多线程,将一些处理移至另一个线程。 优点是简单; 启用多线程需要一行代码。 请参阅Multithreaded OpenGL ES。
- 如果您的应用程序花费大量CPU时间准备数据发送到OpenGL ES,则将准备渲染数据的任务和将渲染命令的任务之间的工作分配给OpenGL ES。 请参阅 Perform OpenGL ES Computations in a Worker Task。
- 如果您的应用程序具有多个场景,它可以同时渲染或者可以在多个上下文中执行,它可以创建多个任务,每个任务具有一个OpenGL ES上下文。 如果上下文需要访问相同的资源,请使用共享组在上下文之间共享OpenGL ES对象。 请参阅 Use Multiple OpenGL ES Contexts。
Multithreaded OpenGL ES - 多线程OpenGL ES
每当您的应用程序调用OpenGL ES函数时,OpenGL ES将处理参数,使其以硬件所理解的格式。 处理这些命令所需的时间取决于输入是否已经是硬件友好的格式,但在为硬件准备命令时总是有开销。
如果您的应用程序花费大量时间在OpenGL ES中执行计算,并且您已经采取措施选择理想的数据格式,则通过为OpenGL ES上下文启用多线程,您的应用程序可能会获得额外的好处。 多线程OpenGL ES上下文自动创建一个工作线程,并将其一些计算转移到该线程。 在多核设备上,启用多线程功能可以使CPU上执行的内部OpenGL ES计算与应用程序并行运行,从而提高性能。 同步函数继续阻止调用线程。
要启用OpenGL ES多线程,请将EAGLContext
对象的multiThreaded
属性的值设置为YES。
注意:启用或禁用多线程执行会导致OpenGL ES刷新以前的命令,并导致设置附加线程的开销。 在初始化函数中而不是在渲染循环中启用或禁用多线程。
启用多线程意味着OpenGL ES必须复制参数以将其传输到工作线程。 由于这种开销,始终使用和不使用多线程来测试应用程序,以确定是否提供了显着的性能改进。 您可以通过在多线程应用程序中实现自己的x OpenGL ES使用策略来最小化此开销,如本章的其余部分所述。
Perform OpenGL ES Computations in a Worker Task - 在工作任务中执行OpenGL ES计算
某些应用程序在将数据传递到OpenGL ES之前对其数据执行大量计算。 例如,应用程序可能会创建新的几何体或对现有的几何体进行动画。 在可能的情况下,这样的计算应该在OpenGL ES中执行。 这充分利用GPU内部更大的并行性,并减少了在应用程序和OpenGL ES之间复制结果的开销。
Figure 6-6 中描述的方法在更新OpenGL ES对象和执行使用这些对象的渲染命令之间进行交替。 OpenGL ES在GPU上呈现与应用程序在CPU上运行的更新的并行。 如果在CPU上执行的计算所花费的时间比GPU上的处理时间更长,那么GPU花费更多的空闲时间。 在这种情况下,您可以在具有多个CPU的系统上利用并行性。 将OpenGL ES渲染代码分解为单独的计算和处理任务,并且并行运行它们。 一个任务会生成第二个要使用的数据并将其提交给OpenGL。
为获得最佳性能,请避免在任务之间复制数据 不是在一个任务中计算数据并将其复制到另一个任务中的顶点缓冲对象中,而是将顶点缓冲区对象映射到设置代码中,并将指针直接传递给工作任务。
如果您可以进一步将修改任务分解为子任务,则可能会看到更好的收益。 例如,假设两个或多个顶点缓冲区对象,每个顶点缓冲区对象在提交绘图命令之前都需要更新。 每个都可以独立于其他的重新计算。 在这种情况下,对每个缓冲区的修改成为一个操作,使用NSOperationQueue
对象来管理工作:
- 设置当前上下文。
- 映射第一个缓冲区。
- 创建一个NSOperation对象,其任务是填充该缓冲区。
- 在操作队列上进行队列排队。
- 对其他缓冲区执行步骤2到4。
- 在运行队列中调用
waitUntilAllOperationsAreFinished
。 - 取消映射缓冲区。
- 执行渲染命令。
Use Multiple OpenGL ES Contexts - 使用多个OpenGL ES上下文
使用多个上下文的一个常见方法是具有更新OpenGL ES对象的一个上下文,而另一个上下文消耗这些资源,每个上下文在单独的线程上运行。 因为每个上下文在一个单独的线程上运行,所以它的动作很少被其他上下文阻止。 要实现这一点,您的应用程序将创建两个上下文和两个线程; 每个线程控制一个上下文。 此外,您的应用程序要在第二个线程上更新的任何OpenGL ES对象必须是双缓冲的; 消耗的线程可能不会访问OpenGL ES对象,而另一个线程正在修改它。 An EAGL Sharegroup Manages OpenGL ES Objects for the Context中详细描述了上下文之间的更改同步过程。
GLKTextureLoader类实现了这种策略来提供纹理数据的异步加载。 (请参阅Use the GLKit Framework to Load Texture Data)
Guidelines for Threading OpenGL ES Apps - 线程OpenGL ES应用指南
遵循以下准则,以确保在使用OpenGL ES的应用程序中成功执行线程:
- 每个上下文只使用一个线程。 特定上下文的OpenGL ES命令不是线程安全的。 不要有多个线程同时访问单个上下文。
- 使用GCD时,使用专用的串行队列将命令分派给OpenGL ES; 这可以用来代替常规的互斥模式。
- 跟踪当前上下文。 切换线程时,很容易无意中切换上下文,这会对图形命令的执行造成不可预见的影响。 在切换到新创建的线程之前,您必须设置当前上下文,并在离开线程之前清除当前上下文。
后记
未完,待续~~~
网友评论