Vulkan:用于数据一致性的 vkCmdPipelineBarrier

Vulkan: vkCmdPipelineBarrier for data coherence

我的问题分为两部分:

  1. 内存可用/可见有什么区别?
  2. 我正在通过本教程学习 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.

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

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

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

它的目的是缓存的一致性。规范试图避免 "cache" 这个词更抽象。

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

据我所知,计划重写同步规范,术语可能会更改。