Skip to content

同步 (Synchronization)

同步是确保 OpenGL 渲染管线已发出或执行完所提交命令的过程。

OpenGL 规范定义命令按给定顺序执行,后续命令行为如同所有先前命令已完成。但这是规范行为,实际实现可能不同。

异步执行 (Asynchronous Action)

OpenGL 渲染命令是异步执行的。调用 glDraw* 函数时,不保证渲染已完成,甚至可能尚未开始。OpenGL 允许实现自行决定何时执行命令。

优势

异步执行允许优化:驱动程序可缓存多个命令,然后一次性提交给 GPU,减少 CPU 模式切换开销。

OpenGL API 定义为同步:每个命令行为如同所有先前命令已完成。若命令依赖先前命令结果,实现会阻止执行重叠。

涉及客户端内存的函数(如 glReadPixelsglBufferSubDataglTexSubImage2D)返回时必须完成对客户端内存的操作。

缓冲区对象优势

使用缓冲区对象(Buffer Object)比客户端内存更高效,因为驱动程序无需担心用户后续修改内存,可直接将命令写入命令流。

命令状态

命令有三种概念状态:

  1. 未发出(Unissued):已提交给驱动程序,但尚未发送给硬件
  2. 已发出未完成(Issued but not complete):已发送给硬件,但结果未就绪
  3. 已完成(Complete):已完全离开管线,效果已写入输出

同步选项 (Synchronization Options)

显式同步

OpenGL 提供两个显式同步机制:

glFinish:阻塞当前 CPU 线程,直到所有渲染命令完成。

glFlush:阻塞当前 CPU 线程,直到所有命令已添加到 GPU 命令队列。

使用场景

如果在短时间内提交大量命令后不执行其他 OpenGL 调用,GPU 命令队列可能为空。调用 glFlush 可确保命令被推送到 GPU。

隐式同步

某些操作会隐式触发同步:

  • 交换缓冲区:可能触发同步,等待影响默认帧缓冲区的命令完成
  • 读取帧缓冲区到 CPU 内存:等待所有影响该帧缓冲区的渲染命令完成
  • 写入缓冲区对象glBufferSubData 或映射):等待使用该缓冲区的渲染命令完成

避免阻塞

在写入缓冲区对象前使其无效(invalidate),实现可分配新存储并孤立旧数据,避免等待。

使用 GL_MAP_UNSYNCHRONIZED_BIT 标志调用 glMapBufferRange 可跳过同步,但需自行处理可能的冲突。

使用像素缓冲区对象(Pixel Buffer Object)修改纹理数据不会强制同步,传输会延迟到纹理不再使用时。

同步对象 (Sync Objects)

glFinish 是粗粒度同步。更精细的同步可使用围栏对象(Fence):

围栏是命令流中的标记,可测试其是否已完成。若围栏已完成,则其之前发出的所有命令也已完成。

详见 同步对象

基于 OpenGL Wiki 的本地化项目