https://developer.apple.com/library/ios/documentation/3DDrawing/Conceptual/OpenGLES_ProgrammingGuide/WorkingwithOpenGLESContexts/WorkingwithOpenGLESContexts.html
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.
This chapter details how to create and configure contexts on iOS.
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.
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.
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.
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.
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.
Your app decides which version of OpenGL ES to support when it creates and initializes the 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.
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-1 demonstrates how to do this.
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.
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.
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.
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.
Two contexts sharing OpenGL ES objectsSharegroups 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.
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.
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.
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.
https://developer.apple.com/library/ios/qa/qa1612/_index.html
A: In my multithreading OpenGL ES application, I load textures (or vertices) on a secondary thread then draw them onto screen on the main thread. Occasionally I see blank images or application freezes. What could be causing this?
It is very likely that the main and background threads are accessing the same OpenGL ES context simultaneously.
OpenGL ES commands for a specific context are not reentrant. You should never have more than one thread accessing a single context at the same time. It is highly recommended that you use only one thread per context.
Listing 1 demonstrates how to create and make current a context contextB
using the sharegroup
property (an EAGLSharegroup
object) obtained from another already-allocated context contextA
. Such contexts that belong to the same sharegroup can share resources, for example, textures, FBOs, etc. So you may use contextB
on the secondary thread to create textures, and contextA
on the main thread to draw the textures onto screen.
Creating a context belonging to the same sharegroup
contextB = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES1 sharegroup:contextA.sharegroup]; if (!contextB || ![EAGLContext setCurrentContext:contextB]) { // Handle errors here } |
Important: The only way to get a valid EAGLSharegroup
object is to query it from an existing EAGLContext
object. Any other method, such as explicitly creating it, will result in crashes or undefined behavior.
When you want to modify a resource on one context, you must first make sure it is not currently in use on another, even if you are accessing them from different threads. Once you modify it, its state is undefined until flushing and binding the object have been completed. You should never use an undefined object, whether you access it from a different context or not.
Take a texture as an example. Listing 2 shows the correct way to deal with multiple contexts. Any other ordering will not ensure consistent and correct behavior. It may result in data corruption, leaks, or crashes otherwise.
Modifying a texture on context B prior to using it on context A
// 1. Ensure context A is not bound to the texture [EAGLContext setCurrentContext:contextA]; glBindTexture(GL_TEXTURE_2D, 0); // 2. Call flush on context A glFlush(); // 3. Modify the texture on context B [EAGLContext setCurrentContext:contextB]; glBindTexture(GL_TEXTURE_2D, texId); // // Modify the texture data here // // 4. Call flush on context B glFlush(); // 5. Rebind the texture on context A [EAGLContext setCurrentContext:contextA]; glBindTexture(GL_TEXTURE_2D, texId); // 6. Use the texture as normal on context A |
For more information on the EAGLContext/-initWithAPI:sharegroup:
API and the sharegroup
property, see the EAGLContext Class Reference