Vulkan:用于数据一致性的 vkCmdPipelineBarrier
Vulkan: vkCmdPipelineBarrier for data coherence
我的问题分为两部分:
- 内存可用/可见有什么区别?
我正在通过本教程学习 Vulkan (https://vulkan-tutorial.com),目前正在偷偷摸摸地寻找一种不同的方法来将统一数据(简单的 model/view/projection 矩阵)上传到设备本地内存。矩阵用于顶点着色器。
在本教程中,矩阵得到更新并复制到暂存缓冲区(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 部分的问题是缺少同步;暂存缓冲区在渲染之前的帧期间被读取之前(部分)被更新。 (感谢您阐明内存可用/可见之间的区别)。
如果暂存缓冲区内存不是主机一致的,那么您还需要 vkFlushMappedMemoryRanges
在 memcpy 之后(内存可以保持映射)。如果不这样做,则无法保证数据对 gpu 确实可见。
第一道屏障(host to transfer)其实并不需要;提交时存在隐式障碍。
我看到的另一个问题是您只有一个暂存缓冲区,这意味着您需要等待前一帧完成才能上传新数据。
如果提到 "destroying" 意味着您按帧分配...首先,您必须等待销毁,直到所有提交的命令缓冲区使用完成,其次不要这样做。 GPU 端分配很昂贵,而不是分配一次并使用环形缓冲区。
广告 1.
Available 和 visible 是内存依赖的两半。几乎两者都必须发生才能成为有效的内存依赖性。
您可以将其视为状态序列:
资源由 src
编写 → 由 src
提供 → 对 dst
可见 → 由 dst
使用。
它的目的是缓存的一致性。规范试图避免 "cache" 这个词更抽象。
您负责提供和可见。可以执行其中一些操作的是障碍、事件、连贯映射内存或 flush
和 invalidate
以及其他一些...
据我所知,计划重写同步规范,术语可能会更改。
我的问题分为两部分:
- 内存可用/可见有什么区别?
我正在通过本教程学习 Vulkan (https://vulkan-tutorial.com),目前正在偷偷摸摸地寻找一种不同的方法来将统一数据(简单的 model/view/projection 矩阵)上传到设备本地内存。矩阵用于顶点着色器。
在本教程中,矩阵得到更新并复制到暂存缓冲区(
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 部分的问题是缺少同步;暂存缓冲区在渲染之前的帧期间被读取之前(部分)被更新。 (感谢您阐明内存可用/可见之间的区别)。
如果暂存缓冲区内存不是主机一致的,那么您还需要 vkFlushMappedMemoryRanges
在 memcpy 之后(内存可以保持映射)。如果不这样做,则无法保证数据对 gpu 确实可见。
第一道屏障(host to transfer)其实并不需要;提交时存在隐式障碍。
我看到的另一个问题是您只有一个暂存缓冲区,这意味着您需要等待前一帧完成才能上传新数据。
如果提到 "destroying" 意味着您按帧分配...首先,您必须等待销毁,直到所有提交的命令缓冲区使用完成,其次不要这样做。 GPU 端分配很昂贵,而不是分配一次并使用环形缓冲区。
广告 1.
Available 和 visible 是内存依赖的两半。几乎两者都必须发生才能成为有效的内存依赖性。
您可以将其视为状态序列:
资源由 src
编写 → 由 src
提供 → 对 dst
可见 → 由 dst
使用。
它的目的是缓存的一致性。规范试图避免 "cache" 这个词更抽象。
您负责提供和可见。可以执行其中一些操作的是障碍、事件、连贯映射内存或 flush
和 invalidate
以及其他一些...
据我所知,计划重写同步规范,术语可能会更改。