OpenGL ES的每个实现都提供了一种创建渲染上下文的方法,渲染上下文用来管理OpenGL ES规范所需的状态。通过将此状态置于上下文中,多个应用程序可以轻松共享图形硬件而不会干扰到对方的状态。本章详细介绍了如何在iOS中创建和配置上下文。
在您的应用程序调用任何OpenGL ES函数之前,必须初始化 EAGLContext 对象。 EAGLContext
类还提供了用于将OpenGL ES内容与Core Animation集成的方法。
iOS应用中的每个线程都有一个当前上下文; 当您调用OpenGL ES函数时,将会修改这个上下文中的状态信息。若要设置线程的当前上下文,请在该线程上调用EAGLContext的类方法 setCurrentContext:。
[EAGLContext setCurrentContext: myContext];
调用EAGLContext
类的currentContext方法则可以获取线程的当前上下文。
注意: 如果您的应用程序在同一个线程上切换上下文,请在设置新的当前上下文之前调用glFlush函数。 这可以确保先前提交的命令能及时的传递到图形硬件。
OpenGL ES会对EAGLContext
对象进行强引用。 (如果使用手动引用计数,OpenGL ES将retains此对象。)当您调用 setCurrentContext: 方法更改当前上下文时,OpenGL ES将不再引用先前的上下文。(如果使用手动引用计数,OpenGL ES将释放EAGLContext
对象。)为防止EAGLContext
对象在不是当前上下文时被释放,您的应用必须确保持有这些对象。
每个 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属性,并使用它来选择正确的渲染路径。 通常做法是为每个渲染路径创建一个类。 您的应用程序在初始化时检查上下文对象并创建一次渲染器。
虽然上下文保存着OpenGL ES的状态,但它不直接管理OpenGL ES对象。 相反,OpenGL ES对象由 EAGLSharegroup 对象创建和维护。 Every context contains an EAGLSharegroup object that it delegates object creation to。
当两个或多个上下文引用相同的sharegroup时,sharegroup的优势就会很明显,如图2-1所示。 当多个上下文连接到一个公共sharegroup时,任何上下文创建的OpenGL ES对象都可在所有上下文中使用; 如果在一个上下文中绑定到另一个上下文所创建的对象标识符,那么这两个上下文引用的OpenGL ES对象是相同的。 移动设备上的资源往往很少; 在多个上下文中创建相同内容的多个副本是很浪费的。 共享公共资源可以更好地利用设备上的可用图像资源。
sharegroup是一个不透明对象; 它没有您的应用可以调用的方法或属性。 使用了sharegroup对象的上下文将会对它进行强引用。
Figure 2-1 Two contexts sharing OpenGL ES objects
Sharegroup 在两个场景下比较常见:
要创建多个引用同一个sharegroup的上下文,可以通过调用 initWithAPI: 来初始化第一个上下文, 接着这个上下文就会自动创建sharegroup对象。 之后创建上下文时,通过调用 initWithAPI:sharegroup: 来引用第一个上下文的sharegroup。
Listing 2-2 进行了演示。 第一个上下文是使用Listing 2-1中定义的便捷函数创建的。 通过从第一个上下文中提取API版本和共享组来创建第二个上下文。
要点:所有引用同一个sharegroup的上下文必须使用相同版本的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]];
当多个上下文共享sharegroup时,您的应用程序负责管理OpenGL ES对象的状态更改。 以下是规则:
以下是应用程序更新OpenGL ES对象时应遵循的步骤:
注意:共享对象的另一种方法是使用单个渲染上下文和使用多个目标帧缓冲区。 在渲染时,您的应用程序绑定合适的帧缓冲区并根据需要渲染其帧。 由于所有OpenGL ES对象都是从单个上下文引用的,因此它们会看到相同的OpenGL ES数据。 此模式占用资源较少,但仅适用于可以仔细控制上下文状态的单线程应用程序。