Vulkan 设备 - 主机 - 设备与 VkEvent 同步
Vulkan Device - Host - Device synchronization with VkEvent
我正在尝试将主机阶段同步到我的管道中,我基本上是在设备上执行命令缓冲区期间在主机上编辑一些数据。通过阅读规范,我认为我正在执行正确的同步、execution/memory 依赖项和 availability/visibility 操作,但它既不适用于 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 行开始!请看看它是否适合你?
我在 github 上开了一个问题:https://github.com/KhronosGroup/Vulkan-Docs/issues/755
在那里经过一些讨论后,结论是设备到主机的同步不可能与事件一起使用,必须使用栅栏。
我正在尝试将主机阶段同步到我的管道中,我基本上是在设备上执行命令缓冲区期间在主机上编辑一些数据。通过阅读规范,我认为我正在执行正确的同步、execution/memory 依赖项和 availability/visibility 操作,但它既不适用于 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 行开始!请看看它是否适合你?
我在 github 上开了一个问题:https://github.com/KhronosGroup/Vulkan-Docs/issues/755
在那里经过一些讨论后,结论是设备到主机的同步不可能与事件一起使用,必须使用栅栏。