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.

Availablevisible 是内存依赖的两半。几乎两者都必须发生才能成为有效的内存依赖项。

您可以将其视为状态序列:

资源由src 编写→ 由src 提供→ 对dst 可见→ 由dst 使用。

这样做的目的是缓存的一致性。规范试图避免“缓存”这个词更加抽象。

您负责使可用和可见。可以执行其中一些操作的操作是障碍、事件、连贯映射内存或flushinvalidate 以及其他一些...

AFAIK 计划重写同步规范,并且命名法可能会更改。

【讨论】:

以上是关于Vulkan:用于数据一致性的 vkCmdPipelineBarrier的主要内容,如果未能解决你的问题,请参考以下文章

Vulkan:一个管道和多个描述符集?

Vulkan Shader&Resources:为什么统一而不是Const资源

Vulkan Tutorial 15 Framebuffers

Vulkan系列教程—VMA教程—用户数据

Vulkan系列教程—VMA教程—用户数据

Vulkan系列教程—VMA教程—用户数据