OpenGL ES的每种实现,都会有个方法,来创建渲染图形的上下文,从而可以管理Opengl ES指定的状态。通过放置这些状态到上下文中,这样在多任务的情况下,就很容易的共享图像的硬件设备,从而互不影响各自的状态。
这一章就是来详解,如何创建和配置IOS中的图形上下文。一个EAGL上下文就是一个OpenGL ES图形渲染上下文在IOS中的实现。
在你使用Opengl ES的函数之前,你必须初始化EAGLContext对象,并且设置它作为当前上下文。EAGLContext类也为你的应用程序提供了一些方法,这些方法可以使Opengl ES的内容方便的同核心动画进行集成工作。没有这些方法,你的应用程序在协同离屏图像工作时,会被限制很多。
一个线程中的当前上下文是作为Opengl ES函数调用的目标
在IOS应用程序中,每个线程都会维护一个当前上下文。当你的应用程序使用Opengl ES的调用时,线程的上下文就会被那个调用改变。
要设置当前上下文,你可以通过调用EAGLContext类的setCurrentContext:方法。
[EAGLContext setCurrentContext:myContext];
你的应用程序也可以通过EAGLContext类的currentContext方法来获取一个线程的当前上下文。
当你的应用程序设置了一个新的上下文时,EAGL会释放先前的上下文,并且获取新的上下文。
注意:如果你的应用程序需要在同一个线程中转换两个或者更多的上下文时,在设置一个新的上下文作为当前上下文之前,要调用glFlush函数。这样确保先前提交的一些命令可以及时的传递给图像硬件设备。
每个上下文都会指定一个Opengl的版本作为目标
一个EAGLContext对象可以支持Opengl ES1.1和Opengl ES2.0中的任何一个,但是不能同时支持两个。原因归于Opengl ES2.0的设计。Opengl ES 2.0移除了Opengl ES 1.0中所有的处理图像的固定功能流水线(其实也就是对顶点和纹理进行处理的变换引擎)的函数,并且增加了许多新的函数,以便于提供一个更清晰的着色接口。如果你的应用程序在Opengl ES1.1的上下文中尝试调用Opengl ES 2.0的函数,那么结果会是未定义的。
当你的应用程序创建和初始化EAGLContext对象时,可以来决定那种版本的Opengl ES会被支持。创建Opengl ES 2.0上下文时,你的应用程序可以如下初始化:
EAGLContext* myContext = [[EAGLContext alloc]
initWithAPI:kEAGLRenderingAPIOpenGLES2];
如果你的设备不支持相应的Openggl ES的版本,那么initWithAPI:的方法会返回nil。在使用EAGLContext的上下文时,你的应用程序应该测试下,确保上下文是被成功的初始化。
为了支持Opengl ES2.0和Opengl ES1.1的渲染特性,你的应用程序应该首先尝试初始化一个Opengl ES 2.0的渲染上下文。如果返回的对象是nil,那么就应该初始化一个Opengl ES 1.1的上下文来代替。清单2-1 演示了如何去做。
EAGLContext* CreateBestEAGLContext()
{
EAGLContext *context;
context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
if (context == nil)
{ context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES1];
}
return context;
}
清单 2-1 在同一个应用程序中支持Opengl ES 1.1和Opengl ES 2.0
一个上下文的API属性表明了上下文支持那个Opengl ES的版本。你的应用程序应该测试上下文的API属性,并且使用它选择正确的渲染路径。通常的做法是为每一个渲染的路径创建一个类。你的应用程序可以在初始化的时候,一次创建一个渲染器并且测试上下文。
一个EAGL的共享组(sharegroup)管理上下文中的Openggl ES对象。
尽管上下文保存了Openggl ES的状态,但是它不能直接管理Opengl ES的对象。相反,Openggl ES对象是通过EAGLSharegroup对象创建和管理的。每一个上下文都包含了一个EAGLSharegroup对象,由它代笔创建对象。
假如两个或者更多的上下文引用到了同样的共享组,一个共享组的优点就会变得很明显,如图表2-1所示。当多个上下文关联到同一个共享组时,被任意一个上下文创建的Opengl ES对象在所有的上下文中都是可用的。假如你创建了一个对象,而把这个相同的对象绑定到了另一个上下文中时,你就可以引用同样的对象。在移动设备中,资源是非常匮乏的。在多个上下文中创建同样内容的备份是浪费的。共享通用的资源可以更好的利用设备的图形资源。
你的应用程序会把共享组看成一个不透明的对象。也就是你没有方法和属性可以调用,并且它都是通过引用的上下文自动的retain和release的。
图 2-1 两个上下文共享一个Opengl ES对象
共享组最有用的是以下两种情况:
在不同的上下文中,共享的资源不会变化
当你想要应用程序在一个线程中创建新的Opengl ES对象,而在主线程渲染时。在这种情况下,第二个上下文运行在一个独立的线程中,并且致力于获取数据和创建资源。在所有的资源都加载完成后,第一个上下文绑定到这些对象上,然后立即使用它。
创建多个上下文引用到同一个共享组的方法,首先第一个上下文通过调用initWithAPI:初始化,那么一个共享组就会自动的被创建。然后第二个或者之后的上下文初始化时,就可以通过调用initWithAPI:sharegroup:的方法使用第一个上下文创建的贡献组。清单2-2展示了如何工作。在清单2-1中,第一个上下文通过使用快捷方法。第二个上下文是通过一个扩展的API传递了第一个上下文的共享组从而被创建。
重要:共享同一个共享组的所有的上下文,都必须使用同一个版本的Opengl ES API来初始化上下文。
EAGLContext* firstContext = CreateBestEAGLContext();
EAGLContext* secondContext = [[EAGLContext alloc] initWithAPI:[firstContext API]
sharegroup: [firstContext sharegroup]];
清单 2-2 用同一个共享组创建2个上下文
当共享组是被多个上下文共享时,你的应用程序就有义务要管理Opengl ES对象状态的改变。规则如下:
当你的应用程序可能要通过多个上下文进入某个对象的同时,要确保对象没有被同时改变。
当对象要被发给上下文的命令改变时,对象此时不能被另外的上下文读取或者改变。
在一个对象被改变时,必须是被绑定对象的所有的上下文才能看到这些改变。如果一个上下文在绑定之前就引用它,那么这个对象的内容是没有被定义的。
这里有一些步骤,是你在更新Opengl ES对象需要遵循的:
1 在每一个可能使用该对象的上下文中调用glFlush方法
2 在某个上下文想要改变对象的同时,调用一个或者更多的Opengl ES的函数来改变对象
3 在接收到上下文状态改变的通知时,调用glFlush函数
4 在每个其他的上下文中,绑定对象的标志。
注意:共享对象的另一个方法是去使用单一的渲染上下文,但是多个目的缓冲帧。在渲染时,你的应用程序绑定需要的缓冲帧,然后渲染成需要的帧。因为所有的Opengl ES对象都是从单一的上下文中渲染的,所以他们看到的是同样的Opengl ES的数据。这个方式使用的资源很少,但是仅仅对单线程的应用程序有用,在单线程中你可以很好的控制上下文的状态。