OpenGL的内存模型(一):线程和上下文环境

OpenGL的内存模型(一):线程和上下文环境

内存模型(Memory model)定义了一些规则,在这些规则下,对各种存储的对象数据的写操作在以后对该数据的读操作中变得可见。例如,内存模型定义当对附加到Framebuffer对象的纹理的写操作对该纹理的后续读操作可见时。

在正常情况下,OpenGL严格执行一个一致性内存模型。通常,如果您写入一个对象,以后发出的任何命令都会看到最新值。因为OpenGL的行为“好像”所有操作都在特定的序列中发生,所以由OpenGL实现来确保在发出读操作时之前的写操作已经完成。

例如,如果触发一个渲染命令,OpenGL在函数返回时还没有完成该命令的执行。如果您发出一个glReadPixels操作(不执行异步读取)来从framebuffer读取数据,那么OpenGL的实现就会与所有未完成的写操作同步。因此,OpenGL将暂停发出读的CPU线程,直到所有渲染完成,然后执行读并返回。

后面将列出“写入数据后触发的读操作将看到新入写的数据”这一基本规则的“例外”情况。

1 多线程和多上下文环境

在OpenGL中可以使用多个CPU线程。这会引起很多关于线程之间的同步和状态可见性的问题。

OpenGL使用的多线程模型基于一个事实:同一个OpenGL上下文(context)不能同时在多个线程中同时运行。虽然可以在多个线程中有多个OpenGL context,但是不能在两个线程中同时操作同一个context。

当然,这增加了在并发上下文上出现竞态条件(race condition)的可能性。也就是说,一个线程正在操作一个上下文,而另一个线程使它处于当前状态。谁会赢呢?

正确的答案是你输了,你应该把你的Application设计成不会出现这种情况。避免当前上下文中的竞争条件是OpenGL应用程序应负的责任。这应该通过使用适当的同步原语(synchronization primitive)来实现,这些原语应该可以从您选择的语言或线程库中获得。

1.1 对象可见性

OpenGL上下文可以共享对象。这意味着一个线程中的对象在另一个线程中使用时可能被使用。

在同步方面,OpenGL声明只有在这些命令完成之后才会发生更改。由于每个上下文都有自己的命令流(command stream),因此需要使用同步对象(Sync Object)或glFinish来确保命令已经执行。但是还必须与另一个线程通信,让其它线程知道数据已经更新。这需要使用适当的语言/库提供的线程间通信特性(互斥、原子等)。

但是,这还不足以确保更改在使用线程中可见。OpenGL规范有许多复杂的规则,这些规则决定一个线程中的对象更新何时对其他线程可见。

规则的一般要点是,每个使用上下文必须在更改可见之前将对象绑定到其上下文。绑定可以是直接绑定(使用glBind*调用),也可以是间接绑定,方法是通过绑定包含需要更改对象引用的容器对象。

即使该对象已经在线程2中绑定,当在线程1中发生更改时,线程2也必须重新绑定该对象,以确保更新数据的可见性。然而,重新绑定不需要将对象重新绑定到与之关联的每个可能的绑定或附着点。它们中的任何一个都将导致对象的新数据在当前线程上可见。

请注意,即使在单线程情况下,上述情况也是正确的。如果在一个线程中有多个共享对象的上下文,就会发生这种情况。如果在上下文1中更改对象的数据,然后使上下文2成为当前的,则对象的数据只有在重新绑定时才会在新上下文中可见。

1.1.1 同步对象

唯一不能以这种方式工作的对象是同步对象。具体来说,当多个上下文在同一个同步对象上被阻塞时。当同步对象在一个上下文中变为有信号状态时,它在当前被该对象阻塞的所有上下文中都变为有信号状态。

OpenGL的内存模型(一):线程和上下文环境


分享到:


相關文章: