繁体   English   中英

如何使用 Vulkan Api 上的暂存缓冲区解决网格损坏问题

[英]How to solve mesh corruption with staging buffer on Vulkan Api

我在我的代码中发现了一个错误,在某些情况下使用暂存缓冲区会导致网格数据损坏。 我有:

  1. 临时网格数据

  2. 具有特定大小的暂存缓冲区,由命令缓冲区和 memcpy 同时使用,但一次不是同一段。

  3. 缓冲区分配器,提供部分合适的顶点索引缓冲区,其中网格数据通过 vkCmdCopyBuffer 从暂存区传输。 缓冲区包含许多段,为不同的网格提供。

当我通过命令缓冲区和 memcpy 同时使用暂存缓冲区时,网格数据写入错误(被覆盖/损坏)甚至严重可能导致 VK_ERROR_DEVICE_LOST 的问题。

https://imgur.com/8p53SUW “正确的网格”

https://imgur.com/plJ8V0v “破网”

    [[nodiscard]] static Result writeMeshBuffer(TransferData &data, GpuMesh &buffer)
    {
        Result result; using namespace vkw;

        auto &mesh = buffer.source;

        size_t vSize = mesh.vertices_count * mesh.vertex_size;
        size_t iSize = mesh.indices_count  * mesh.index_size;
        size_t mesh_size = vSize + iSize;

        auto &staging_offset = data.stagingData.buffer_offset_unused;

        // write data to staging buffer
        {
            // guaranteed that mesh_size will less or equal than staging buffer size
            //FIXME false condition generate broken meshes somehow
            bool is_wait_before = mesh_size > TransferStagingData::BUFFER_SIZE - staging_offset;
            //will work correctly:
            //bool is_wait_before = true;

            if (is_wait_before) // if we need more memory on staging buffer than not used already
            {
                result = data.wait_transfer();
                if (result != VK_SUCCESS)
                    return result;
                staging_offset = 0;
            }

            uint8_t *pMemory = static_cast<uint8_t*>(data.stagingData.pMemory) + staging_offset;

            memcpy(pMemory,         mesh.vertices.pX, vSize);
            memcpy(pMemory + vSize, mesh.indices.pXX, iSize);

            if (not is_wait_before)
            {
                result = data.wait_transfer();
                if (result != VK_SUCCESS)
                    return result;
            }
        }

        // write data from staging buffer to mesh buffer
        {
            auto cmd_cpy_buff = [](CommandBuffer cmd, BufferCopy copy, Offsets offsets, DeviceSizeT size)
            {
                cmd.cmd_copy_buffer(copy, offsets, size);
            };

                                       //       SRC                                 DST
            BufferCopy copy = { data.stagingData.buffer, buffer.info.buffer        };
            Offsets offsets = { staging_offset,          buffer.info.region.offset };
            result = data.transfer.prepare(cmd_cpy_buff, data.transfer.cmd_buffer, copy, offsets, mesh_size);
            if (result != VK_SUCCESS)
                return result;

            data.reset_fence();

            result = data.transfer.submit({&data.transfer.cmd_buffer,1},{}, {}, {}, data.transferFence);
            if (result != VK_SUCCESS)
                return result;
        }

        // save usused offset to  data.stagingData.buffer_offset_unused;
        staging_offset = staging_offset == 0 ? mesh_size : 0;

        return result;
    }

如果我不能像这样使用暂存缓冲区,那么为什么。

如果我有错误,我知道在哪里。

问题是

staging_offset = staging_offset == 0 ? mesh_size : 0;

需要改变

staging_offset = staging_offset == 0 ? TransferStagingData::BUFFER_SIZE - mesh_size : 0;

更改后一切正常。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM