Memory Barrier 和 Semaphore 是Vulkan中用于同步操作的两种机制,主要区别在于它们的应用范围和使用场景。

Memory Barrier (内存屏障)

作用:

  • 用于在单个命令缓冲区内,或者同一队列中的多个命令之间同步内存访问。
  • 确保在执行某些操作(如计算着色器写入数据)之后,内存中的数据能够被后续操作正确读取。

使用场景:

  • 适用于单个队列内的命令同步,例如在计算着色器和后续读取这些数据的命令之间。
  • 常用于细粒度的同步,例如确保数据写入和读取的正确顺序。

设计原因:

  • 完成内存屏障需要指定源阶段(source stage)和目的阶段(destination stage),以及涉及的内存访问类型。这可以细致地控制同步操作的执行顺序和范围,提高效率。

示例:

VkMemoryBarrier memoryBarrier = {};
memoryBarrier.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER;
memoryBarrier.srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT;
memoryBarrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
vkCmdPipelineBarrier(commandBuffer, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_VERTEX_SHADER_BIT, 0, 1, &memoryBarrier, 0, nullptr, 0, nullptr);

上述代码在计算着色器写入数据后,保证顶点着色器可以正确读取这些数据。

Semaphore (信号量)

作用:

  • 用于跨队列的任务同步以及多个命令缓冲区之间的操作协调。
  • 确保一个队列中的操作完成后,另一个队列中的操作才能开始。

使用场景:

  • 适用于跨队列同步,例如计算队列和图形队列之间的同步,或者图形队列和传输队列之间的同步。
  • 常用于粗粒度的同步,例如帧缓冲区渲染完成后,信号传输队列开始传输纹理数据。

设计原因:

  • 信号量在队列提交时使用,综合了硬件和驱动调度机制,适合较高层次的任务同步。
  • 与 fence 不同的是,信号量主要用于GPU-GPU的同步,而 fence 多用于CPU-GPU的同步。

示例:

VkSemaphoreCreateInfo semaphoreInfo = {};
semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
VkSemaphore semaphore;
vkCreateSemaphore(device, &semaphoreInfo, nullptr, &semaphore);

// 在计算队列提交任务后信号信号量
VkSubmitInfo computeSubmitInfo = {};
computeSubmitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
computeSubmitInfo.signalSemaphoreCount = 1;
computeSubmitInfo.pSignalSemaphores = &semaphore;
vkQueueSubmit(computeQueue, 1, &computeSubmitInfo, VK_NULL_HANDLE);

// 在图形队列等待信号量再提交任务
VkPipelineStageFlags waitStages[] = {VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT};
VkSubmitInfo graphicsSubmitInfo = {};
graphicsSubmitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
graphicsSubmitInfo.waitSemaphoreCount = 1;
graphicsSubmitInfo.pWaitSemaphores = &semaphore;
graphicsSubmitInfo.pWaitDstStageMask = waitStages;
vkQueueSubmit(graphicsQueue, 1, &graphicsSubmitInfo, VK_NULL_HANDLE);

上述代码展示了在计算队列和图形队列之间使用信号量进行同步。

总结

  • 内存屏障 提供了细粒度的控制,适合在同一队列内短时间内的同步操作,确保数据在各个阶段的正确性。
  • 信号量 提供了粗粒度的控制,适合跨队列或者跨命令缓冲区的同步操作,可以高效地协调复杂的任务调度和数据传递。

更多推荐