配置OpenGL ES上下文
OpenGL ES的每个实现都提供了一种创建渲染上下文以管理OpenGL ES规范所需状态的方法。通过在上下文中放置此状态,多个应用程序可以轻松共享图形硬件,而不会干扰对方的状态。
本章详细介绍了如何在iOS上创建和配置上下文。
EAGL是iOS实现的OpenGL ES渲染上下文
在您的应用程序可以调用任何OpenGL ES函数之前,必须初始化一个EAGLContext对象。 EAGLContext类还提供了将OpenGL ES内容与Core Animation集成的方法。
当前上下文是OpenGL ES函数调用的目标
iOS应用程序中的每个线程都具有当前上下文;当您调用OpenGL ES函数时,这是通过调用更改其状态的上下文。
要设置线程的当前上下文,请在该线程上执行时调用EAGLContext类方法setCurrentContext。
[EAGLContext setCurrentContext: myContext];
调用EAGLContext类方法currentContext以检索线程的当前上下文。
注意:如果您的应用程序主动在同一线程上的两个或多个上下文之间切换,请在设置新的上下文作为当前上下文之前调用glFlush函数。这样可以确保先前提交的命令及时传递给图形硬件。
OpenGL ES持有对当前上下文对应的EAGLContext对象的强烈引用。 (如果使用手动引用计数,OpenGL ES会保留此对象。)当调用setCurrentContext:方法来更改当前上下文时,OpenGL ES不再引用上一个上下文。 (如果您使用手动引用计数,OpenGL ES将释放EAGLContext对象。)为防止当不在当前上下文时EAGLContext对象被释放,您的应用程序必须保持强制引用(或保留)这些对象。
每个上下文都针对特定版本的OpenGL ES
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上下文。
当您创建并初始化EAGLContext对象时,您的应用程序将决定要支持的OpenGL ES版本。如果设备不支持所需版本的OpenGL ES,initWithAPI:方法返回nil。您的应用程序必须进行测试,以确保上下文在使用之前已初始化成功。
为了支持OpenGL ES的多个版本作为应用程序中的渲染选项,您应该首先尝试初始化要定位的最新版本的渲染上下文。如果返回的对象为nil,则初始化旧版本的上下文。清单2-1演示了如何做到这一点。
EAGLContext* CreateBestEAGLContext()
{
EAGLContext *context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3];
if (context == nil) {
context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
}
return context;
}
上下文的API属性指出上下文支持的OpenGL ES版本。您的应用程序应测试上下文的API属性并使用它来选择正确的渲染路径。实现此行为的常见模式是为每个渲染路径创建一个类。您的应用程序在初始化时测试上下文并创建一次渲染器。
EAGL共享组管理上下文的OpenGL ES对象
尽管上下文保持OpenGL ES状态,但它并不直接管理OpenGL ES对象。相反,OpenGL ES对象由EAGLSharegroup对象创建和维护。每个上下文都包含一个EAGLSharegroup对象,它将对象创建委托给。
当两个或多个上下文引用相同的共享组时,共享组的优点变得明显,如图2-1所示。当多个上下文连接到公共共享组时,由任何上下文创建的OpenGL ES对象可用于所有上下文;如果绑定到与创建它相同的上下文上的相同对象标识符,则引用同一个OpenGL ES对象。移动设备上的资源通常很少;在多个上下文中创建相同内容的多个副本是浪费的。共享共享资源可以更好地利用设备上可用的图形资源。
一个共享组是一个不透明的对象;它没有您的应用可以调用的方法或属性。使用sharegroup对象的上下文保持强有力的参考。
2-1.png在两种具体情况下,共享组最有用
- 当上下文之间共享的大部分资源是不变的。
- 当您希望您的应用程序能够在除渲染器的主线程之外的线程上创建新的OpenGL ES对象。在这种情况下,第二个上下文在单独的线程上运行,专门用于获取数据和创建资源。在资源被加载之后,第一个上下文可以绑定到对象并立即使用它。 GLKTextureLoader类使用此模式提供异步纹理加载。
要创建引用同一个共享组的多个上下文,首先通过调用initWithAPI:将为上下文自动创建一个共享组。第二个和更晚的上下文通过调用initWithAPI:sharegroup:方法来初始化为使用第一个上下文的共享组。清单2-2显示了如何工作。第一个上下文使用清单2-1中定义的方便函数创建。第二个上下文通过从第一个上下文提取API版本和共享组来创建。
重要提示:与同一共享组相关联的所有上下文都必须使用与初始上下文相同版本的OpenGL ES API
EAGLContext* firstContext = CreateBestEAGLContext();
EAGLContext* secondContext = [[EAGLContext alloc] initWithAPI:[firstContext API] sharegroup: [firstContext sharegroup]];
当共享组由多个上下文共享时,应用程序有责任管理OpenGL ES对象的状态更改。这是规则:
- 如果对象未被修改,您的应用程序可以同时访问跨多个上下文的对象。
- 当对象被发送到上下文的命令修改时,对象不得在任何其他上下文中被读取或修改。
- 修改对象后,所有上下文都必须重新绑定对象以查看更改。如果上下文在绑定它之前引用它,则对象的内容是未定义的。
以下是您的应用程序应该更新OpenGL ES对象的步骤:
- 在可能使用对象的每个上下文上调用glFlush。
- 在要修改对象的上下文中,调用一个或多个OpenGL ES函数来更改对象。
- 在接收到状态修改命令的上下文中调用glFlush。
- 在其他上下文中,重新绑定对象标识符。
注意:共享对象的另一种方法是使用单个渲染上下文,但使用多个目标帧缓冲区。在渲染时,您的应用程序会绑定相应的帧缓冲区,并根据需要呈现其帧。因为所有OpenGL ES对象都是从单个上下文中引用的,所以它们会看到相同的OpenGL ES数据。此模式使用较少的资源,但仅适用于可以仔细控制上下文状态的单线程应用程序
网友评论