OpenGL ES 编程指南二:配置OpenGL ES上下文


Configuring OpenGL ES Contexts

Every implementation of OpenGL ES provides a way to create rendering contexts to manage the state required by the OpenGL ES specification. By placing this state in a context, multiple apps can easily share the graphics hardware without interfering with the other’s state.

所有实现OpenGL ES的都提供创建渲染上下文并让它管理OpenGL ES规范所需的状态。因为状态被放入上下文,所以多个程序之间可以共享图形硬件而互不干扰。

This chapter details how to create and configure contexts on iOS.本章详细介绍了如何在iOS上创建和配置上下文。

EAGL Is the iOS Implementation of an OpenGL ES Rendering Context(EAGLE实现iOS OpenGL ES渲染上下文)

Before your app can call any OpenGL ES functions, it must initialize an EAGLContext object. The EAGLContext class also provides methods used to integrate OpenGL ES content with Core Animation.

在调用OpenGL ES方法之前,我们必须初始化一个EAGLContext对象。EAGLContext类还提供了将OpenGL ES内容与Core Animation集成的方法。

The Current Context Is the Target for OpenGL ES Function Calls(当前上下文是OpenGL ES函数调用的目标)

Every thread in an iOS app has a current context; when you call an OpenGL ES function, this is the context whose state is changed by the call.

iOS应用程序中的每个线程都具有当前上下文, 我们可以调用OpenGL ES修改上下文的状态。

To set a thread’s current context, call the EAGLContext class method setCurrentContext: when executing on that thread.


[EAGLContext setCurrentContext: myContext];

Call the EAGLContext class method currentContext to retrieve a thread’s current context.


Note: If your app actively switches between two or more contexts on the same thread, call the glFlush function before setting a new context as the current context. This ensures that previously submitted commands are delivered to the graphics hardware in a timely fashion.

注意:如果您的应用程序主动在同一线程上的两个或多个上下文之间切换,请在设置新的上下文作为当前上下文之前调用glFlush函数。 这样可以确保先前提交的命令及时传递给图形硬件。

OpenGL ES holds a strong reference to the EAGLContext object corresponding to the current context. (If you are using manual reference counting, OpenGL ES retains this object.) When you call the setCurrentContext: method to change the current context, OpenGL ES no longer references the previous context. (If you are using manual reference counting, OpenGL ES releases the EAGLContext object.) To prevent EAGLContext objects from being deallocated when not the current context, your app must keep strong references to (or retain) these objects.

OpenGL ES对与当前上下文相对应的EAGLContext对象有很强的引用。 (如果使用手动引用计数,OpenGL ES会保留此对象。)当您调用setCurrentContext:方法来更改当前上下文时,OpenGL ES不再引用上一个上下文。 (如果您使用手动引用计数,则OpenGL ES将释放EAGLContext对象。)为防止当不在当前上下文时EAGLContext对象被释放,您的应用程序必须保持强制引用(或保留)这些对象。

Every Context Targets a Specific Version of OpenGL ES(每个上下文都有特定版本的OpenGL ES)

An EAGLContext object supports only one version of OpenGL ES. For example, code written for OpenGL ES 1.1 is not compatible with an OpenGL ES 2.0 or 3.0 context. Code using core OpenGL ES 2.0 features is compatible with a OpenGL ES 3.0 context, and code designed for OpenGL ES 2.0 extensions can often be used in an OpenGL ES 3.0 context with minor changes. Many new OpenGL ES 3.0 features and increased hardware capabilities require an OpenGL ES 3.0 context.

EAGLContext对象只支持一个版本的OpenGL ES。例如,为OpenGL ES 1.1编写的代码与OpenGL ES 2.0或3.0上下文不兼容。使用核心OpenGL ES 2.0功能的代码与OpenGL ES 3.0上下文兼容,为OpenGL ES 2.0扩展设计的代码通常可以在OpenGL ES 3.0上下文中进行微小更改。许多新的OpenGL ES 3.0功能和增加的硬件功能需要OpenGL ES 3.0上下文。

Your app decides which version of OpenGL ES to support when it creates and initializesthe EAGLContext object. If the device does not support the requested version of OpenGL ES, the initWithAPI: method returns nil. Your app must test to ensure that a context was initialized successfully before using it.

当您创建并初始化EAGLContext对象时,您的应用程序会决定支持哪种版本的OpenGL ES。如果设备不支持所需版本的OpenGL ES,initWithAPI:方法返回nil。您的应用程序必须进行测试,以确保在使用之前成功初始化上下文。

To support multiple versions of OpenGL ES as rendering options in your app, you should first attempt to initialize a rendering context of the newest version you want to target. If the returned object is nil, initialize a context of an older version instead. Listing 2-1demonstrates how to do this.

为了支持OpenGL ES的多个版本作为应用程序中的渲染选项,您应该首先尝试初始化要定位的最新版本的渲染上下文。如果返回的对象为nil,则初始化旧版本的上下文。清单2-1显示了如何做到这一点。

Listing 2-1 Supporting multiple versions of OpenGL ES in the same app

EAGLContext* CreateBestEAGLContext()
   EAGLContext *context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3];
   if (context == nil) {
      context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
   return context;

A context’s API property states which version of OpenGL ES the context supports. Your app should test the context’s API property and use it to choose the correct rendering path. A common pattern for implementing this behavior is to create a class for each rendering path. Your app tests the context and creates a renderer once, on initialization.

上下文的API属性指出上下文支持的OpenGL ES版本。您的应用程序应测试上下文的API属性,并使用它来选择正确的渲染路径。实现此行为的常见模式是为每个渲染路径创建一个类。您的应用程序在初始化时测试上下文并创建一次渲染器。

An EAGL Sharegroup Manages OpenGL ES Objects for the Context(EAGL共享组管理上下文的OpenGL ES对象)

Although the context holds the OpenGL ES state, it does not directly manage OpenGL ES objects. Instead, OpenGL ES objects are created and maintained by an EAGLSharegroup object. Every context contains an EAGLSharegroup object that it delegates object creation to.

尽管上下文保持OpenGL ES状态,但它并不直接管理OpenGL ES对象。 相反,OpenGL ES对象由EAGLSharegroup对象创建和维护。 每个上下文都包含一个被委托创建对象的EAGLSharegroup对象。

The advantage of a sharegroup becomes obvious when two or more contexts refer to the same sharegroup, as shown in Figure 2-1. When multiple contexts are connected to a common sharegroup, OpenGL ES objects created by any context are available on all contexts; if you bind to the same object identifier on another context than the one that created it, you reference the same OpenGL ES object. Resources are often scarce on mobile devices; creating multiple copies of the same content on multiple contexts is wasteful. Sharing common resources makes better use of the available graphics resources on the device.

当两个或多个上下文引用相同的共享组时,共享组的优点变得明显,如图2-1所示。当多个上下文连接到公共共享组时,由任何上下文创建的OpenGL ES对象可在所有上下文中使用;如果绑定到另一个上下文上与创建它相同的对象标识符,则引用同一个OpenGL ES对象。移动设备上的资源通常很少;在多个上下文中创建相同内容的多个副本是浪费的。共享共享资源可以更好地利用设备上可用的图形资源。

A sharegroup is an opaque object; it has no methods or properties that your app can call. Contexts that use the sharegroup object keep a strong reference to it.


Figure 2-1 Two contexts sharing OpenGL ES objects

Sharegroups are most useful under two specific scenarios:

  • When most of the resources shared between the contexts are unchanging.
  • When you want your app to be able to create new OpenGL ES objects on a thread other than the main thread for the renderer. In this case, a second context runs on a separate thread and is devoted to fetching data and creating resources. After the resource is loaded, the first context can bind to the object and use it immediately. The GLKTextureLoader class uses this pattern to provide asynchronous texture loading.


  • 当上下文之间共享的大部分资源是不变的。
  • 当您希望您的应用程序能够在除渲染器的主线程之外的线程上创建新的OpenGL ES对象。 在这种情况下,第二个上下文在单独的线程上运行,专门用于获取数据和创建资源。 在资源被加载之后,第一个上下文可以绑定到对象并立即使用它。 GLKTextureLoader类使用此模式提供异步纹理加载。

To create multiple contexts that reference the same sharegroup, the first context is initialized by calling initWithAPI:; a sharegroup is automatically created for the context. The second and later contexts are initialized to use the first context’s sharegroup by calling the initWithAPI:sharegroup: method instead. Listing 2-2 shows how this would work. The first context is created using the convenience function defined in Listing 2-1. The second context is created by extracting the API version and sharegroup from the first context.


Important: All contexts associated with the same sharegroup must use the same version of the OpenGL ES API as the initial context.

重要提示:与同一共享组相关联的所有上下文都必须使用与初始上下文相同版本的OpenGL ES API。

Listing 2-2 Creating two contexts with a common sharegroup

EAGLContext* firstContext = CreateBestEAGLContext();

EAGLContext* secondContext = [[EAGLContext alloc] initWithAPI:[firstContext API] sharegroup: [firstContext sharegroup]];

It is your app’s responsibility to manage state changes to OpenGL ES objects when the sharegroup is shared by multiple contexts. Here are the rules:

  • Your app may access the object across multiple contexts simultaneously provided the object is not being modified.
  • While the object is being modified by commands sent to a context, the object must not be read or modified on any other context.
  • After an object has been modified, all contexts must rebind the object to see the changes. The contents of the object are undefined if a context references it before binding it.

当共享组由多个上下文共享时,应用程序有责任管理OpenGL ES对象的状态更改。 这是规则:

  • 如果对象未被修改,您的应用程序可以同时访问多个上下文中的对象。
  • 当对象被发送到上下文的命令修改时,对象不得在任何其他上下文中被读取或修改。
  • 修改对象后,所有上下文都必须重新绑定对象以查看更改。 如果上下文在绑定之前引用它,则对象的内容是未定义的。

Here are the steps your app should follow to update an OpenGL ES object:

  • Call glFlush on every context that may be using the object.
  • On the context that wants to modify the object, call one or more OpenGL ES functions to change the object.
  • Call glFlush on the context that received the state-modifying commands.
  • On every other context, rebind the object identifier.

以下是应用程序应该更新OpenGL ES对象的步骤:

  • 在可能使用对象的每个上下文上调用glFlush。
  • 在要修改对象的上下文中,调用一个或多个OpenGL ES函数来更改对象。
  • 在接收到状态修改命令的上下文中调用glFlush。
  • 在其他上下文中,重新绑定对象标识符。

Note: Another way to share objects is to use a single rendering context, but multiple destination framebuffers. At rendering time, your app binds the appropriate framebuffer and renders its frames as needed. Because all of the OpenGL ES objects are referenced from a single context, they see the same OpenGL ES data. This pattern uses less resources, but is only useful for single-threaded apps where you can carefully control the state of the context.

注意:共享对象的另一种方法是使用单个渲染上下文,但是使用多个目标帧缓冲区。 在渲染时,您的应用程序会绑定相应的帧缓冲区,并根据需要呈现其帧。 因为所有OpenGL ES对象都是从单个上下文引用的,所以它们会看到相同的OpenGL ES数据。 此模式使用较少的资源,但仅适用于可以仔细控制上下文状态的单线程应用程序。