Jackalope
Jackalope

Reputation: 93

How to solve mesh corruption with staging buffer on Vulkan Api

I am found a bug in my code, that cause mesh data corruption in certain situation using staging buffer. I have:

  1. temporary mesh data

  2. staging buffer with certain size, that used simultaneously by command buffer and memcpy, but not same segment at a time.

  3. Buffer allocator, that gives part of suitable vertex-index buffer, where mesh data transfers from staging by vkCmdCopyBuffer. Buffer contains many of segments, given for different meshes.

The issue that when I am using staging buffer simultaneously by command buffer and memcpy, mesh data writes incorrectly (become overwritten/corrupted) and even badly can cause VK_ERROR_DEVICE_LOST .

https://i.sstatic.net/xluPf.jpg "correct mesh"

https://i.sstatic.net/aW949.jpg "broken mesh"

    [[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;
    }

If I can't use staging buffer like this, than why.

If i have an error, idk where.

Upvotes: 1

Views: 254

Answers (1)

Jackalope
Jackalope

Reputation: 93

The issue was

staging_offset = staging_offset == 0 ? mesh_size : 0;

Need to change

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

And after change all works correctly.

Upvotes: 1

Related Questions