【问题标题】:Vulkan Device - Host - Device synchronization with VkEventVulkan 设备 - 主机 - 与 VkEvent 的设备同步
【发布时间】:2018-08-01 18:58:16
【问题描述】:

我正在尝试将主机阶段同步到我的管道中,我基本上在设备上执行命令缓冲区期间编辑主机上的一些数据。通过阅读规范,我认为我正在执行正确的同步、执行/内存依赖和可用性/可见性操作,但它既不适用于 NV 也不适用于 AMD 硬件。这甚至可能吗?如果是这样,我在同步方面做错了什么?

总的来说,我正在做以下事情:

  • [D] 将设备缓冲区 (VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) 复制到主机可见且连贯的缓冲区 (VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT)。
  • [D] 已设置第一个事件。
  • [D] 等待第二个事件。
  • [H] 同时主机等待第一个事件。
  • [H] 设置后,它会增加主机可见缓冲区中的数字。
  • [H] 然后设置第二个事件。
  • [D] 然后设备继续将主机可见缓冲区复制回设备本地缓冲区。

会发生什么?

在 NV 上,第一部分起作用,正确的数据到达主机端,但更改后的数据永远不会到达设备端。在 AMD 上,甚至第一部分都不起作用,而且我已经没有在主机上获得数据。

命令缓冲区记录:

// ...
VkMemoryBarrier barrier = {};
barrier.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER;
barrier.srcAccessMask = ...;
barrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
vkCmdPipelineBarrier(command_buffer, ..., VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 1, &barrier, 0, nullptr, 0, nullptr);

copyWholeBuffer(command_buffer, host_buffer, device_buffer);

barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
barrier.dstAccessMask = VK_ACCESS_HOST_READ_BIT;
vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_HOST_BIT, 0, 1, &barrier, 0, nullptr, 0, nullptr);

vkCmdSetEvent(command_buffer, device_to_host_sync_event, VK_PIPELINE_STAGE_TRANSFER_BIT);

barrier.srcAccessMask = VK_ACCESS_HOST_WRITE_BIT;
barrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
vkCmdWaitEvents(command_buffer, 1, &host_to_device_sync_event, VK_PIPELINE_STAGE_HOST_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 1, &barrier, 0, nullptr, 0, nullptr);

copyWholeBuffer(command_buffer, device_buffer, host_buffer);

barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
barrier.dstAccessMask = ...;
vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_TRANSFER_BIT, ..., 0, 1, &barrier, 0, nullptr, 0, nullptr);
// ...

执行

vkQueueSubmit(queue, 1, &submitInfo, VK_NULL_HANDLE);

while(vkGetEventStatus(device, device_to_host_sync_event) != VK_EVENT_SET)
    std::this_thread::sleep_for(std::chrono::microseconds(10));

void* data;

vkMapMemory(device, host_buffer, 0, BUFFER_SIZE, 0, &data);

// read and write parts of the memory

vkUnmapMemory(device, host_buffer);

vkSetEvent(device, host_to_device_sync_event);

vkDeviceWaitIdle(device);

我上传了一个工作示例:https://gist.github.com/neXyon/859b2e52bac9a5a56b804d8a9d5fa4a5

有趣的部分从第 292 行开始!请看看它是否适合您?

【问题讨论】:

  • 为什么不将其拆分为 2 个 cmd 缓冲区并使用栅栏进行同步?
  • 如果这根本不起作用,这是替代方案。这应该是更优化的版本:只有一个队列提交和命令缓冲区中的其他命令可以保持并行运行,两个命令缓冲区当然必须暂时分离命令。您可以在提交完成位置之外的另一个线程中运行主机代码。此外,这个版本将允许我的管道更好的软件设计。
  • 你真的在某个地方重置了你的事件吗?
  • 在这个例子中,整个代码只运行一次,正如标准所说的“当创建时,事件对象处于未发出信号的状态。”在我尝试使用它的应用程序中,我也在每次命令缓冲区提交之前重置它们。
  • 我上传了一个工作示例:gist.github.com/neXyon/859b2e52bac9a5a56b804d8a9d5fa4a5 有趣的部分从第 292 行开始!请看看它是否适合你?

标签: vulkan


【解决方案1】:

我在github上开了一个issue:https://github.com/KhronosGroup/Vulkan-Docs/issues/755

在那里进行了一些讨论后,结论是设备到主机的同步不可能通过事件进行,并且必须使用栅栏。

【讨论】:

    猜你喜欢
    • 2012-12-08
    • 2020-08-20
    • 2012-11-22
    • 1970-01-01
    • 1970-01-01
    • 2017-09-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多