Teris
Teris

Reputation: 610

vkCmdCopyBuffer has no effect for transfer of staging buffer

for a vulkan renderer using vulkan-hpp and Vulkan Memory Allocator I am trying to transfer vertex data to the gpu using a staging buffer. Without using an intermediary staging buffer the transfer works and the meshes can be rendered with no problems (just a single buffer created with VMA_MEMORY_USAGE_CPU_TO_GPU). But with the call to vkCmdCopyBuffer the resulting vertexBuffer only contains 0s (checked with NSight). Thinking that synchronisation using barriers might be required I tried the following code, but still have the same problem.

Maybe there is a need to synchronize the command buffer doing the copy with the later command buffer binding and sourcing from the vertex buffer? Although I didnt see this done or mentioned in any tutorials I came across.

// vulkan initialization
// VmaAllocator initialization

std::vector<Vertex> vertices = { /*filled*/ };
auto vertexBufferSize = vertices.size() * sizeof(Vertex);

vk::Buffer stagingBuffer;
auto ci = vk::BufferCreateInfo().
    setUsage(vk::BufferUsageFlagBits::eTransferSrc).
    setSharingMode(vk::SharingMode::eExclusive).
    setSize(vertexBufferSize );
VmaAllocationCreateInfo allocationCI = {};
allocationCI.usage = VMA_MEMORY_USAGE_CPU_ONLY;
vmaCreateBuffer(allocator, (VkBufferCreateInfo*)&ci,
    &allocationCI, (VkBuffer*)&stagingBuffer, &allocation, nullptr);
void* pVBufMem = nullptr;
vmaMapMemory(allocator, allocation, &pVBufMem);
std::memcpy(pVBufMem, vertices.size(), vertexBufferSize );
vmaUnmapMemory(allocator, allocation);

vk::Buffer vertexBuffer;
ci = vk::BufferCreateInfo().
    setUsage(vk::BufferUsageFlagBits::eTransferDst | vk::BufferUsageFlagBits::eVertexBuffer).
    setSharingMode(vk::SharingMode::eExclusive).
    setSize(vertexBufferSize);
allocationCI = {};
allocationCI.usage = VMA_MEMORY_USAGE_GPU_ONLY;
vmaCreateBuffer(allocator, (VkBufferCreateInfo*)&ci,
    &allocationCI, (VkBuffer*)&vertexBuffer, &allocation, nullptr);

// copy staging buffer to vertex buffer
auto allocCI = vk::CommandBufferAllocateInfo().
    setCommandBufferCount(1).
    setCommandPool(pool).
    setLevel(vk::CommandBufferLevel::ePrimary);
auto cmd = device.allocateCommandBuffers(allocCI)[0];
cmd.begin({ vk::CommandBufferUsageFlagBits::eOneTimeSubmit });

// barrier host write -> copyBuffer read
auto bufferMemoryBarrier = vk::BufferMemoryBarrier()
.setBuffer(stagingBuffer)
.setSize(vertexBufferSize)
.setSrcAccessMask(vk::AccessFlagBits::eMemoryWrite)
.setDstAccessMask(vk::AccessFlagBits::eTransferRead)
.setSrcQueueFamilyIndex(VK_QUEUE_FAMILY_IGNORED)
.setDstQueueFamilyIndex(VK_QUEUE_FAMILY_IGNORED);
cmd.pipelineBarrier(
    vk::PipelineStageFlagBits::eAllCommands, vk::PipelineStageFlagBits::eAllCommands, {},
    { }, { bufferMemoryBarrier }, { });

cmd.copyBuffer(stagingBuffer, vertexBuffer, { 0, 0, vertexBufferSize });

// copyBuffer write -> vertex read
bufferMemoryBarrier = vk::BufferMemoryBarrier()
.setBuffer(vertexBuffer)
.setSize(vertexBufferSize)
.setSrcAccessMask(vk::AccessFlagBits::eTransferWrite)
.setDstAccessMask(vk::AccessFlagBits::eVertexAttributeRead)
.setSrcQueueFamilyIndex(VK_QUEUE_FAMILY_IGNORED)
.setDstQueueFamilyIndex(VK_QUEUE_FAMILY_IGNORED);
cmd.pipelineBarrier(
    vk::PipelineStageFlagBits::eAllCommands, vk::PipelineStageFlagBits::eAllCommands, {},
    { }, { bufferMemoryBarrier }, { });

cmd.end();
auto submitInfo = vk::SubmitInfo().
    setCommandBufferCount(1).
    setPCommandBuffers(&cmdBuffer);
queue.submit({ submitInfo }, {});
queue.waitIdle();

Upvotes: 0

Views: 1221

Answers (1)

Teris
Teris

Reputation: 610

It is fixed. The third argument to copybuffer was not being correctly initialized. The correct call should be like this:

cmd.copyBuffer(stagingBuffer, vertexBuffer, {{ 0, 0, vertexBufferSize }});

The line

cmd.copyBuffer(stagingBuffer, vertexBuffer, { 0, 0, vertexBufferSize });

actually creates three distinct copy regions.

Upvotes: 1

Related Questions