Vulkan:用于数据一致性的 vkCmdPipelineBarrier
Posted
技术标签:
【中文标题】Vulkan:用于数据一致性的 vkCmdPipelineBarrier【英文标题】:Vulkan: vkCmdPipelineBarrier for data coherence 【发布时间】:2017-03-27 09:35:48 【问题描述】:我的问题分为两部分:
-
内存可用/可见之间有什么区别?
我正在从本教程 (https://vulkan-tutorial.com) 中学习 Vulkan,目前正在寻找一种不同的方法来将统一数据(简单模型/视图/投影矩阵)上传到设备本地内存。矩阵用于顶点着色器。
在教程中,矩阵得到更新并被复制到暂存缓冲区(vkMapMemory
等),然后通过创建命令缓冲区、记录 vkCmdCopy
、提交并销毁缓冲。我尝试在绘图的强制性命令缓冲区中执行最后一步。
虽然教程的方式可以带来流畅的动画,但我的实验错过了这个功能。我尝试安装 2 个 bufferBarriers 以确保完成副本(这似乎是问题),但这并没有帮助。资源已正确创建和绑定 - 运行良好。
//update uniform buffer and copy it to the staging buffer
//(called every frame)
Tools::UniformBufferObject ubo;
//set the matrices
void* data;
data = device.mapMemory( uniformStagingMemory, 0, sizeof( ubo ), (vk::MemoryMapFlagBits) 0 );
memcpy( data, &ubo, sizeof( ubo ));
device.unmapMemory( uniformStagingMemory );
//once: create a command buffer for each framebuffer of the swapchain
//queueFamily struct members set properly
//1st barrier: make transfer from host memory to staging buffer available / visible
vk::BufferMemoryBarrier bufMemBarrierStaging;
bufMemBarrierStaging.srcAccessMask = vk::AccessFlagBits::eHostWrite;
bufMemBarrierStaging.dstAccessMask = vk::AccessFlagBits::eTransferRead;
bufMemBarrierStaging.buffer = uniformStagingBuffer;
bufMemBarrierStaging.offset = 0;
bufMemBarrierStaging.size = sizeof( Tools::UniformBufferObject );
//2nd barrier: make transfer from staging buffer to device local buffer available / visible
vk::BufferMemoryBarrier bufMemBarrier;
bufMemBarrier.srcAccessMask = vk::AccessFlagBits::eTransferWrite;
bufMemBarrier.dstAccessMask = vk::AccessFlagBits::eUniformRead | vk::AccessFlagBits::eShaderRead;
bufMemBarrier.buffer = dataBuffer;
bufMemBarrier.offset = dataBufferOffsets[2];
bufMemBarrier.size = sizeof( Tools::UniformBufferObject );
for( size_t i = 0; i < cmdBuffers.size(); i++ )
//begin command buffer
cmdBuffers[i].pipelineBarrier(
vk::PipelineStageFlagBits::eHost, //srcPipelineStage
vk::PipelineStageFlagBits::eTransfer, //dstPipelineStage
(vk::DependencyFlagBits) 0,
nullptr, //memBarrier
bufMemBarrierStaging,
nullptr //imgBarrier
);
vk::BufferCopy copyRegion; //filled appropriate
cmdBuffers[i].copyBuffer( uniformStagingBuffer, dataBuffer, copyRegion );
cmdBuffers[i].pipelineBarrier(
vk::PipelineStageFlagBits::eTransfer, //srcPipelineStage
vk::PipelineStageFlagBits::eVertexShader, //dstPipelineStage
(vk::DependencyFlagBits) 0,
nullptr, //memBarrier
bufMemBarrier,
nullptr //imgBarrier
);
//renderpass stuff and drawing etc.
与
namespace Tools
struct UniformBufferObject
glm::mat4 model;
glm::mat4 view;
glm::mat4 proj;
;
;
vk::Buffer uniformStagingBuffer;
vk::DeviceMemory uniformStagingMemory;
//dataBuffer also contains the vertex and index data, is device local
vk::Buffer dataBuffer;
vk::DeviceMemory dataBufferMemory;
vk::vector<vk::DeviceSize> dataBufferOffsets;
std::vector<vk::CommandBuffer> cmdBuffers;
我正在使用vkcpp
(https://github.com/KhronosGroup/Vulkan-Hpp)。
这种非流体动画缺少数据连贯性的原因是 - 我试图实现这一点是否有误?
提前致谢!
编辑:第 2 部分的问题是缺少同步;在渲染之前的帧期间读取暂存缓冲区之前(部分)更新了暂存缓冲区。 (感谢您明确内存可用/可见之间的区别)。
【问题讨论】:
“内存可用/可见之间有什么区别?”不清楚你所说的“可用”是什么意思。 “并销毁缓冲区” 为什么要销毁缓冲区?你到底是什么意思? 顺便说一句,您不需要第一个内存屏障。内存屏障HOST_WRITE_BIT / HOST_STAGE是通过command buffer的提交隐式完成的。 @NicolBolas 第一个问题是指规范的第 6.4 节,关于内存依赖的段落。我看不出有什么区别。破坏缓冲区意味着,我调用vkFreeCommandBuffers
。由于这是教程中的代码,因此选择了最简单的方法,而不是好的方法。
【参考方案1】:
如果暂存缓冲内存不是主机一致的,那么您还需要在 memcpy 之后添加vkFlushMappedMemoryRanges
(内存可以保持映射)。如果不这样做,则无法保证数据实际上对 gpu 可见。
实际上并不需要第一个屏障(要传输的主机);提交时存在隐式障碍。
我看到的另一个问题是您只有一个暂存缓冲区,这意味着您需要等待前一帧完成才能上传新数据。
如果提到“销毁”意味着您按帧分配...首先您必须等待销毁,直到所有提交的命令缓冲区使用完成,其次不要这样做。 GPU 端分配代价高昂,而是更喜欢分配一次并使用环形缓冲区。
【讨论】:
【参考方案2】:广告 1.
Available 和 visible 是内存依赖的两半。几乎两者都必须发生才能成为有效的内存依赖项。
您可以将其视为状态序列:
资源由src
编写→ 由src
提供→ 对dst
可见→ 由dst
使用。
这样做的目的是缓存的一致性。规范试图避免“缓存”这个词更加抽象。
您负责使可用和可见。可以执行其中一些操作的操作是障碍、事件、连贯映射内存或flush
和invalidate
以及其他一些...
AFAIK 计划重写同步规范,并且命名法可能会更改。
【讨论】:
以上是关于Vulkan:用于数据一致性的 vkCmdPipelineBarrier的主要内容,如果未能解决你的问题,请参考以下文章
Vulkan Shader&Resources:为什么统一而不是Const资源