在 Vulkan 中,由于驱动层不会隐式地进行任何自动同步,因此所有的资源读写、执行顺序等操作都需要手动地进行管理。 Vulkan 核心的同步原语有四种:Fence(栅栏)、Semaphore(信号量)、Event(事件) 和 Pipeline Barrier(管线屏障)。
1. Fence(栅栏):CPU 与 GPU 之间的同步#
Fence 主要用于让 CPU 等待 GPU 完成某些操作。
- 特点: 它有两种状态:Signaled(触发/绿灯)和 Unsignaled(未触发/红灯)。通常由 GPU 在执行完某个 Queue 的命令后将其置为 Signaled,而 CPU 在另一端阻塞等待。
- 常见用途: 帧率控制(避免 CPU 提交命令的速度远超 GPU 渲染速度)。例如,在开始录制新一帧的 Command Buffer 之前,CPU 必须等待上一帧对应的 Fence 被触发。
- 核心 API: *
vkQueueSubmit(传入 Fence,当 Queue 中的任务执行完毕时,GPU 会自动将该 Fence 设为 Signaled)
vkWaitForFences(CPU 端阻塞等待)vkResetFences(使用前必须手动重置为 Unsignaled)
2. Semaphore(信号量):GPU Queue 之间的同步#
Semaphore 用于 GPU 内部不同 Queue 之间 或者 同一 Queue 内不同提交(Submit)之间 的粗粒度同步。CPU 无法直接等待或触发它。
- Binary Semaphore(二元信号量): 只有两种状态(Signaled/Unsignaled)。常用于交换链图像获取与渲染的同步。
- Timeline Semaphore(时间轴信号量,Vulkan 1.2+): 包含一个单调递增的 64 位整数值。当它的值大于或等于指定的等待值时,同步完成。它允许更灵活的粗粒度依赖管理(甚至支持 CPU 等待或增加其数值)。
- 常见用途:
vkAcquireNextImageKHR(获取交换链图像后触发 Semaphore) $\rightarrow$vkQueueSubmit(等待该 Semaphore 产生后才开始渲染渲染管线)。 - 核心机制: 在
vkQueueSubmit的VkSubmitInfo结构体中,分别填入pWaitSemaphores(执行前需要等待谁)和pSignalSemaphores(执行完后去触发谁)。
3. Pipeline Barrier(管线屏障):GPU 内部 / 管线阶段之间的同步#
Pipeline Barrier 是 Vulkan 中最频繁、最精细、也最复杂的同步手段。它用于 同一 Queue 内部的不同命令之间 的同步,解决管线内各阶段(Pipeline Stages)的读写冲突,并负责处理内存可见性(Memory Visibility)和图像布局转换(Layout Transition)。
当你在 Command Buffer 中插入一个 Barrier 时,需要显式指定以下几个要素:
- 规定了哪些先前的指令(Src)必须在哪些后续指令(Dst)开始之前执行完毕。例如:等待顶点着色器(
VS)写完,才允许片段着色器(FS)去读。
- 规定了内存的读写类型,解决 RAW(写后读)、WAR(读后写)、WAW(写后写)冲突。
- Execution Dependency(执行依赖): 确保 Dst 阶段在 Src 阶段结束后才执行。
- Memory Dependency(内存依赖): 确保 Src 阶段写入 L1/L2 Cache 的数据刷新到全局内存(Make Available),并让 Dst 阶段能正确从内存读取(Make Visible)。
- GPU 对不同用途的 Image 有不同的内部瓦片(Tiling)或压缩优化。在将一个 Image 从“渲染目标(Color Attachment)”变成“采样纹理(Shader Read Only)”时,必须通过 Image Memory Barrier 进行布局转换,否则会导致硬件读取错误。
4. Event(事件):GPU 内部的细粒度延迟同步#
Event 类似于 Pipeline Barrier 的“拆分版”,它允许异步的、非阻塞的在 GPU 管线中标记进度。
- 特点: Barrier 是立刻在命令流中划定一道分界线,而 Event 允许你在前面的命令走到某个阶段时“设置(Set)”它,在很久之后的另一个地方再去“等待(Wait)”它。
- 常见用途: 减少 GPU 的管线气泡(Bubble)。例如,你在管线早期生成了一段数据,只要在该数据真正被使用的后继阶段之前进行等待即可,中途的其他管线阶段可以并发执行。
- 核心 API:
vkCmdSetEvent和vkCmdWaitEvents。
总结与对比#
为了理清它们的关系,可以通过它们在流水线中的协作层次来区分:
原文:同步原语