本章主要描述了多个OpenGL上下文之间共享对象的特殊注意事项,包括删除行为以及如何在不同上下文间传播对共享对象所做的更改。
可以在多个上下文之间共享的对象类型包括:缓冲区对象、程序和着色器对象、渲染缓冲对象、采样器对象、同步对象以及纹理对象(除了名称为零的纹理对象)。
其中一些对象可能包含其他对象数据存储部分或全部的不同视图。例如,纹理缓冲对象包含对缓冲对象数据存储的视图,而纹理视图则包含对另一个纹理对象数据存储的视图。这些视图被视为对被查看对象的引用。
包含对其他对象引用的对象包括帧缓冲对象、程序管道对象、变换反馈对象和顶点数组对象。这类对象被称为容器对象,它们是不可共享的。
实现可能会允许在实现不同版本OpenGL或同一版本不同配置文件之间的上下文间进行共享(参见附录E)。然而,当共享对象的某些方面和/或行为在一个以上版本或配置文件中不适用或未被明确描述时,可能会导致依赖于实现的行为差异。这意味着,在跨版本或跨配置文件共享对象时,需要格外注意与特定版本或配置文件相关的功能限制和行为一致性问题。
对象删除行为 Object Deletion Behavior
共享上下文销毁的副作用
共享列表是一组共享对象的所有上下文集合。如果一个共享对象未被明确删除,那么在该列表中的任何单个上下文被销毁时,除非它是该共享列表中唯一剩下的上下文,否则对这个对象不会产生影响。一旦共享列表中的最后一个上下文被销毁,所有共享对象以及为该上下文或整个共享列表分配的所有其他资源都将在尽可能快的时间内被删除,并由实现重新回收。
删除对象后的自动解绑定
当缓冲区、纹理或渲染缓冲区对象被删除时,它们会从当前上下文中任何与其绑定的绑定点解除绑定,并根据DeleteBuffers
、DeleteTextures
和DeleteRenderbuffers
的描述,从当前上下文中任何已绑定容器对象的附件中分离。如果对象绑定是通过其他相关状态(如BindBufferRange
中的缓冲区范围或FramebufferTexture
或BindImageTexture
中的选定层级和层信息)建立的,那么所有这些相关状态都会通过自动解绑恢复到默认值。其他上下文中的绑定点不受影响。对于未绑定容器对象的附件,比如删除与未绑定到上下文的顶点数组对象关联的缓冲区,这种情况下删除操作不会影响并继续作为对已删除对象的引用,这一点在下一节中有详细说明。
删除对象及其名称的生命期
当一个缓冲区、纹理、采样器、渲染缓冲区、查询或同步对象被删除时,其名称立即变得无效(例如标记为未使用),但底层对象直到不再使用才会被删除。
-
缓冲区、纹理、采样器或渲染缓冲区对象处于使用状态,只要满足以下任一条件:
- 对象附加到任何容器对象上
- 对象绑定到任何上下文的绑定点
- 其他对象包含对该对象数据存储的视图
-
同步对象在有对应栅栏命令尚未完成且未触发同步对象,或者由于
ClientWaitSync
或WaitSync
命令导致有任意GL客户端和/或服务器阻塞在同步对象上时,被认为是正在使用。 -
查询对象在其活跃期间一直处于使用状态,如第4.2节所述。
-
当着色器对象或程序对象被删除时,会被标记为待删除,但其名称依然有效,直至底层对象因不再使用而可以被删除为止。着色器对象在它附着于任何程序对象时被认为正在使用。程序对象在它附着于任何程序管线对象或在任何上下文中是当前程序时被视为正在使用。
应当谨慎处理在对象仍处于使用状态(如上述定义)时对其进行删除。在删除后,该对象的名称可能通过Gen*
或Create*
命令返回。对于这样一个已被删除但仍处于使用状态的对象,其底层对象的状态和数据仍可被GL读写,但无法通过名称访问。支持已删除对象的底层存储空间只有在从容器对象附件点、上下文绑定点、视图、栅栏命令、活动查询等所有对它的引用被移除后,才能被GL回收。由于名称已标记为未使用,再次绑定该名称将创建具有相同名称的新对象,尝试将其附加到其他对象上将会生成错误。
同步对象和多个上下文 Sync Objects and Multiple Contexts
当多个GL客户端和/或服务器在同一同步对象上阻塞,并且该同步对象被触发(即信号状态变为已触发),所有这些阻塞都将被解除。不同客户端和服务器的阻塞解除顺序取决于实现,即具体由OpenGL实现决定,不保证特定的解除顺序。一旦同步对象接收到完成信号,所有等待它的线程或者上下文都可以继续执行后续的操作。
传播对象内容的变化 Propagating Changes to Objects
在OpenGL中,对象包含两类信息:数据和状态。这两者统称为对象的内容。为了按照下文描述的方式传播对象内容的变化,数据和状态会被一致对待。
-
数据是指OpenGL实现无需检查且对操作无直接影响的信息。目前,数据主要包括:
- 帧缓冲区中的像素。
- 缓冲区对象、渲染缓冲区和纹理的数据存储内容。
-
状态决定了渲染管线的配置,并且OpenGL实现必须检查它以确定其影响。
在硬件加速的OpenGL实现中,状态通常保存在GPU寄存器中,而数据则通常存储在GPU内存中。
当对象T的内容发生改变时,这些变化并不总是立即可见,也不一定立即影响涉及该对象的GL操作。变化可能通过以下任意方式产生:
- 状态设置命令,如TexParameter。
- 数据设置命令,如TexSubImage*或BufferData。
- 通过向附着于帧缓冲对象的渲染缓冲区或纹理进行渲染来设置数据。
- 通过变换反馈操作以及EndTransformFeedback命令后设置数据。
- 影响状态和数据的命令,如TexImage*和BufferData。
- 修改映射缓冲区数据后调用UnmapBuffer或FlushMappedBufferRange等命令。
- 触发着色器调用的渲染命令,在着色器内部执行图像变量存储、缓冲区变量存储、原子操作,或者内置的原子计数器函数。
当T为纹理时,T的内容被理解为包括了T的数据存储的内容,即使T的数据存储是通过不同的数据视图修改的。
确定对象内容更改的完成
当如第5.3节所述命令完成后,对象T的内容被认为是发生了变化。可以通过调用Finish确定命令1的完成,或者通过调用FenceSync并在相关同步对象上调用WaitSync来确定。第二种方法不需要往返于GL服务器,可能更为高效,特别是在一个上下文中对T的更改必须在另一个上下文中执行依赖这些更改的命令之前被确认完成的情况下。如果建立了反馈循环(参见第8.6.1、8.14.2.1和9.3节以及下文5.3.3节中关于规则1的讨论),则可能导致对象的最终内容未定义。
定义
在本节剩余部分,将使用以下术语:
-
对象T直接附加到当前上下文是指它已被绑定到上下文的一个绑定点上。例如但不限于:已绑定的纹理、已绑定的帧缓冲区、已绑定的顶点数组和当前程序。
-
T间接附加到当前上下文,若其附着于另一个对象C,称为容器对象,并且C本身是直接或间接附加的。例如但不限于:渲染缓冲区或纹理附着于帧缓冲区;缓冲区附着于顶点数组;着色器附着于程序。
-
直接附加到当前上下文的对象T可以通过在同一绑定点重新绑定T来进行重新附加。间接附加到当前上下文的对象T可以通过重新附着与之相连的容器对象C来进行重新附加。
推论:将C重新绑定到当前上下文会重新附着C及其包含的对象层次结构。
规则
所有OpenGL实现必须遵守以下规则:
规则1:如果在当前上下文中,当对象T直接或间接附着时,其内容发生更改,则当前上下文中对T的所有操作都将使用新内容。
注释:此规则旨在处理单个上下文中的变更。多上下文情况由其他规则处理。
注释:“通过渲染或变换反馈进行的更新”与通过GL命令进行的更新一致处理。一旦发出EndTransformFeedback命令,在同一上下文中任何后续使用变换反馈操作结果的命令都将看到这些结果。如果在渲染和变换反馈之间设置了反馈循环(参见第13.3.3节),结果将是未定义的。
规则2:当容器对象C被绑定时,在当前上下文中对C的附件内容所做的任何更改都保证可见。要确保在其他上下文中对附着于C的对象所做的更改在当前上下文中可见,这些更改必须在该其他上下文中先完成(参见第5.3.1节),并且在C被当前上下文绑定之前完成。在其他上下文中做出但未按照第5.3.1节描述完成的更改,或在C被当前上下文绑定后做出的更改,不保证可见。
规则3:共享对象的内容更改不会自动在上下文间传播。如果在一个不同于当前上下文的上下文中更改了共享对象T的内容,并且T已经直接或间接附着到当前上下文,那么当前上下文中通过那些附着关系涉及T的任何操作都不保证使用其新内容。
规则4:如果在一个不同于当前上下文的上下文中更改了对象T的内容,为了确保新内容在当前上下文中可见,T必须至少附加或重新附加到当前上下文中的一个绑定点,或者当前已绑定容器对象C的一个附件点。
注释:“附加或重新附加”意味着要么将对象附着到它尚未附着的绑定点,要么再次将对象附着到它已附着的绑定点。
示例:如果一个纹理图像被绑定到多个纹理绑定点,并且在另一个上下文中更改了纹理,只需在任一纹理绑定点上重新绑定纹理,就足以使更改在所有纹理绑定点可见。