如何使用 Vulkan 上的暂存缓冲区解决网格损坏 Api
How to solve mesh corruption with staging buffer on Vulkan Api
我在我的代码中发现了一个错误,在某些情况下使用暂存缓冲区会导致网格数据损坏。我有:
临时网格数据
具有一定大小的暂存缓冲区,由命令缓冲区和 memcpy 同时使用,但一次不是同一个段。
缓冲区分配器,提供合适的顶点索引缓冲区的一部分,其中网格数据通过 vkCmdCopyBuffer 从分段传输。缓冲区包含许多段,针对不同的网格给出。
当我通过command buffer和memcpy同时使用staging buffer时,网格数据写入不正确(变成overwritten/corrupted)甚至严重会导致VK_ERROR_DEVICE_LOST的问题。
https://imgur.com/8p53SUW "correct mesh"
https://imgur.com/plJ8V0v "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;
}
如果我不能像这样使用暂存缓冲区,那是为什么。
如果我有错误,请指出错误所在。
问题是
staging_offset = staging_offset == 0 ? mesh_size : 0;
需要改变
staging_offset = staging_offset == 0 ? TransferStagingData::BUFFER_SIZE - mesh_size : 0;
更改后一切正常。
我在我的代码中发现了一个错误,在某些情况下使用暂存缓冲区会导致网格数据损坏。我有:
临时网格数据
具有一定大小的暂存缓冲区,由命令缓冲区和 memcpy 同时使用,但一次不是同一个段。
缓冲区分配器,提供合适的顶点索引缓冲区的一部分,其中网格数据通过 vkCmdCopyBuffer 从分段传输。缓冲区包含许多段,针对不同的网格给出。
当我通过command buffer和memcpy同时使用staging buffer时,网格数据写入不正确(变成overwritten/corrupted)甚至严重会导致VK_ERROR_DEVICE_LOST的问题。
https://imgur.com/8p53SUW "correct mesh"
https://imgur.com/plJ8V0v "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;
}
如果我不能像这样使用暂存缓冲区,那是为什么。
如果我有错误,请指出错误所在。
问题是
staging_offset = staging_offset == 0 ? mesh_size : 0;
需要改变
staging_offset = staging_offset == 0 ? TransferStagingData::BUFFER_SIZE - mesh_size : 0;
更改后一切正常。