Vulkan:用于数据一致性的vkCmdPipelineBarrier

时间:2016-11-13 18:03:41

标签: c++ vulkan barrier

我的问题有两部分:

  1. 可用/可见的内存有什么区别?
  2. 我正在学习本教程中的Vulkan(https://vulkan-tutorial.com),目前正在寻找一种将统一数据(简单模型/视图/投影矩阵)上传到设备本地内存的不同方法。矩阵在顶点着色器中使用。

    在本教程中,矩阵会更新并复制到临时缓冲区(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;
    

    我正在使用vkcpphttps://github.com/KhronosGroup/Vulkan-Hpp)。

    这种非流体动画的原因是否缺少数据连贯性 - 我是否在尝试实现这一目标时遇到了错误?

  3. 提前致谢!

    编辑:第2部分的问题是缺少同步;在渲染之前的帧期间,在读取之前(部分)更新了暂存缓冲区。 (感谢您明确了可用/可见内存之间的区别。)

2 个答案:

答案 0 :(得分:0)

如果登台缓冲区内存不是主机一致的,那么在memcpy之后你还需要vkFlushMappedMemoryRanges(内存可以保持映射)。如果您不这样做,则无法保证数据实际上对gpu可见。

实际上并不需要第一道屏障(主机转移);提交时存在隐含的障碍。

我看到的另一个问题是你有一个临时缓冲区,这意味着你需要等到上一帧完成才能上传新数据。

如果提到&#34;摧毁&#34;意味着你每帧分配...首先你必须等待销毁,直到所有提交的命令缓冲区都完成,第二次不要这样做。 GPU端分配很昂贵,而是更喜欢分配一次并使用环形缓冲区。

答案 1 :(得分:0)

ad 1。

可用可见是内存依赖的两半。几乎必须要发生它才能成为有效的内存依赖。

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

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

它的目的是缓存的一致性。规范试图避免这个词&#34; cache&#34;更加抽象。

您负责制作并且可见。可以执行其中一些操作的操作是障碍,事件,相干映射内存或flushinvalidate以及其他一些......

AFAIK计划进行同步规范重写,命名法可能会改变。