OpenGL同步对象 Sync Objects

同步对象(Sync Objects)

同步对象(Sync Objects)在OpenGL中扮演着同步原语的角色,它们是对事件完成状态的一种表示,可以被测试或等待其完成。同步对象主要用于以下几种同步场景:

  1. 与OpenGL状态机操作的同步:当需要确保一组图形命令已经完成执行,或者特定条件满足(例如,所有渲染操作已完成、特定查询对象的结果可用等)时,可以创建并使用同步对象。通过这种方式,应用程序可以在继续进行后续图形操作之前等待前序操作彻底完成。

  2. 与图形管线内部操作同步:同步对象可以用于跟踪管线中的某些阶段是否结束,比如等待所有的片段着色和混合操作完成,或者等待深度/模板测试及多重采样过程结束。

  3. 多个图形上下文之间的同步:在多线程应用或者跨不同OpenGL上下文之间共享资源的情况下,同步对象能够帮助实现安全的数据交换和依赖关系管理,确保一个上下文的绘制操作完成后,另一个上下文才能访问相关的OpenGL资源或开始自己的绘图命令序列。

  4. 同步与其他API或系统事件:虽然主要设计用于OpenGL内部同步,同步对象也可以间接用于与其他图形API或者其他操作系统级别的事件同步,前提是这些事件可以通过某种方式触发OpenGL的状态变化。

在OpenGL中,同步对象通常通过glFenceSync函数创建,并通过glClientWaitSyncglWaitSync来等待其完成。此外,还可以使用glGetSynciv函数检查同步对象的状态信息。这样有助于防止数据竞争、避免视觉异常,并保证图形流水线操作按预期顺序执行。

同步对象和栅栏 (Sync Objects and Fences)

同步对象具有两种可能的状态:signaled和unsignaled。每个同步对象都关联有事件,当创建时,其状态被设置为unsignaled。一旦相关事件发生,同步对象就会变为signaled状态。可以要求OpenGL等待某个同步对象变为signaled状态。

最初定义的一种特定类型的同步对象是栅栏同步对象,其关联的事件由放置在OpenGL命令流中的栅栏命令触发。栅栏同步对象提供了一种比Finish更灵活的方式来等待GL命令流的部分完成。

sync glFenceSync( enum condition, bitfield flags );

命令用于创建一个新的栅栏同步对象,并在OpenGL命令流中插入一个栅栏命令,并将其与该同步对象关联。该命令返回一个非零名称,对应于创建的同步对象。

  • condition参数指定了同步对象的条件。必须为SYNC_GPU_COMMANDS_COMPLETE,表示同步对象将在GPU命令完全执行完成后被标记为已完成。

  • flags参数必须设置为零。

当满足栅栏命令对应的同步对象以及同一命令流中所有先前命令的条件时,同步对象被标记为signaled状态。在此之前,任何等待该同步对象的阻塞命令都将保持阻塞状态。

栅栏同步对象用于在OpenGL命令流的执行过程中等待某些操作的完成,是一种灵活的控制机制。

void glDeleteSync(sync sync)

删除指定的同步对象(sync object)。当调用此函数并传入同步对象的名称时:

  1. 如果与该同步对象关联的栅栏命令(fence command)已完成执行,并且没有其他glClientWaitSyncglWaitSync命令在等待这个同步对象,则该同步对象会立即被删除。

  2. 如果仍有相关的栅栏命令未完成,或者有阻塞在此同步对象上的等待命令,那么系统不会立即删除该同步对象,而是将其标记为待删除状态。当所有依赖于它的栅栏命令完成,且不再有等待它的命令时,系统会在适当的时候自动删除该同步对象。

  3. 调用 glDeleteSync 函数后,无论同步对象是否立即删除,传入的同步对象名称都将变得无效,此后不能再用来引用对应的同步对象。

  4. 特别地,如果传给 glDeleteSync 的参数 sync 为零(NULL),函数将静默忽略,不会删除任何同步对象,也不会引发错误。

enum glClientWaitSync( sync sync, bitfield flags, uint64 timeout )

命令用于在OpenGL中阻塞当前线程,直到指定的同步对象(sync)为signaled,或者达到指定的超时时间为止。超时时间以纳秒为单位。

  • timeout 参数指定了等待的最大时间长度,但实际的超时精度可能受实现依赖,并且可能会显著大于一纳秒,甚至可能超过请求的时间段。

  • 如果在调用 glClientWaitSync 时同步对象已经处于signaled状态,则该函数会立即返回。

  • 若在调用 glClientWaitSync 时同步对象为unsignaled状态,则函数将会阻塞并最多等待 timeout 纳秒,直到同步对象变为signaled状态。

  • flags 参数控制了命令刷新行为,可以设置为 SYNC_FLUSH_COMMANDS_BIT

  • glClientWaitSync 函数返回四种状态值之一:

    • ALREADY_SIGNALED 表示在调用 glClientWaitSync 时同步对象已经处于signaled状态。
    • TIMEOUT_EXPIRED 表示在指定的超时时间到达之前,同步对象仍为unsignaled状态。
    • CONDITION_SATISFIED 表明在超时到期前,同步对象变为signaled状态。
    • 如果发生错误,除了生成相应的OpenGL错误外,glClientWaitSync 将立即返回 WAIT_FAILED,不会进行阻塞。
  • timeout 的值设为零时,glClientWaitSync 不会阻塞,而只是检查同步对象当前的状态。如果此时同步对象为unsignaled状态,返回 TIMEOUT_EXPIRED;为signaled状态,返回ALREADY_SIGNALED

如果应用程序阻塞在某个可能永远无法被信号化的同步对象上(例如,之前发出但尚未刷新到图形管线中的关联栅栏命令),那么ClientWaitSync可能会无限期地挂起。为了防止这种情况的发生:

当满足以下条件时,OpenGL 会采取一种类似调用 Flush 的行为:

  • flags 参数中设置了 SYNC_FLUSH_COMMANDS_BIT 标志位;
  • 在调用 ClientWaitSync 时,同步对象未被signaled;
  • 调用 ClientWaitSync 和 FenceSync 的上下文是同一个。

在这种情况下,OpenGL 将会在创建 sync 对象之后立即表现出如同执行了 Flush 操作的效果,从而尽量确保栅栏命令能够及时提交给 GPU 执行,并使对应的同步对象可以适时变为signaled状态。

void glWaitSync( sync sync, bitfield flags, uint64 timeout )

这个命令与 glClientWaitSync 类似,都是用于等待同步对象(sync)变为signaled状态。然而,glWaitSync 并不会阻塞应用程序的当前线程并等待同步对象变为signaled状态后才返回;相反,它会立即返回,并导致 OpenGL 服务器端进行阻塞直到同步对象被信号化。

  • 同步对象 sync 的含义与 glClientWaitSync 中的一致,即代表要等待的同步对象。

  • timeout 必须为 TIMEOUT_IGNORED,并且实际上不使用这个参数来控制超时时间。glWaitSync 命令总是等待不超过实现依赖的最大超时时间。这个超时时间以纳秒为单位,可以通过调用 GetInteger64v 函数并指定 pname 为 MAX_SERVER_WAIT_TIMEOUT 来查询其具体数值。

  • 当前并没有方法可以确定 glWaitSync 是由于超时到期还是因为等待的同步对象被信号化而解除阻塞。

  • flags 参数必须设置为零。

  • 如果在执行过程中发生错误,glWaitSync 将会生成相应的 OpenGL 错误,并且不会使 OpenGL 服务器端进行阻塞。

当多个OpenGL客户端和/或服务器都在等待同一个同步对象(sync object),并且该同步对象被信号化时,所有这些阻塞都将被解除。各个阻塞的释放顺序取决于具体实现。

void glGetSynciv( sync sync, enum pname, sizei count, sizei *length, int *values )

该命令用于获取指定同步对象的各项属性值,结果将通过 lengthvalues 参数返回。

成功执行时,glGetSynciv 函数会用所查询对象对应的属性值替换 values 数组中的最多 count 个整数。实际替换的整数数量会被存储在 *length 中。如果 length 是 NULL,则不会返回长度信息。

  • pname 设置为 OBJECT_TYPE 时,单个表示同步对象具体类型的值会被放入 values 中。目前唯一支持的类型是 SYNC_FENCE(栅栏同步)。

  • pname 设为 SYNC_STATUS,则代表同步对象状态(SIGNALEDUNSIGNALED)的单个值会被存入 values 中。

  • 如果 pnameSYNC_CONDITION,则表示同步对象条件的单个值会被写入 values。当前唯一支持的条件是 SYNC_GPU_COMMANDS_COMPLETE,即GPU命令已完成。

  • pname 被设置为 SYNC_FLAGS 时,创建同步对象时使用的标志位会被以单个值的形式存入 values 中。目前不支持任何标志位。

boolean glIsSync( sync sync )

如果sync是一个sync对象的名称,返回TRUE。如果sync不是sync对象的名称,或者发生错误条件,IsSync返回FALSE(注意,零不是sync对象的名称)。

在调用DeleteSync之后,sync对象名称立即变为无效,如4.1和5.2节所述,但底层sync对象直到不再与任何fence命令相关联并且不再阻塞任何*WaitSync命令时才会被删除。

你可能感兴趣的:(OpenGL,图形渲染)