Proper way to update VkBuffer in different places at the same time


#1

Hi everyone! I’m using one VkBuffer for vertices, indices and color. There is function rhiUpdateBuffer() which I call three times to update data:

rhiBeginCommandList();
rhiUpdateBuffer(constBuffer.get(), constBuffer->getOffset(0), constBuffer->getSize(0), vertices);
rhiUpdateBuffer(constBuffer.get(), constBuffer->getOffset(1), constBuffer->getSize(1), indices);
rhiUpdateBuffer(constBuffer.get(), constBuffer->getOffset(2), constBuffer->getSize(2), color);
rhiWaitCommandListFinished(rhiEndCommandList()); //submit VkCommandBuffer to queue

rhiUpdateBuffer() function:

FStagingBuffer* pStagingBuffer = vulkanRHI_GetStagingManager()->getBuffer(m_size);
pStagingBuffer->update(0, m_size, m_data);

vulkanTransiteBuffer(pCmdList, m_pBuffer, m_offset, m_size, EBufferBarrier::TransferDestination);

VkBufferCopy region = {};
region.dstOffset = m_offset;
region.srcOffset = 0;
region.size = m_size;

vkCmdCopyBuffer(
  pCmdList->getCommandBuffer(EQueueType::Graphics), 
  pStagingBuffer->getBuffer(), 
  m_pBuffer->getVulkanBuffer()->getBuffer(), 
  1, &region);

vulkanTransiteBuffer(pCmdList, m_pBuffer, m_offset, m_size, EBufferBarrier::Optimal);

vulkanTransiteBuffer() function:

VkBufferMemoryBarrier barrier = {};
barrier.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER;
barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier.buffer = pBuffer->getVulkanBuffer()->getBuffer();
barrier.offset = offset;
barrier.size = size;

VkPipelineStageFlags sourceStage;
VkPipelineStageFlags destinationStage;

if (barrierType == EBufferBarrier::Optimal)
{
  barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
  barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
  sourceStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
  destinationStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_VERTEX_SHADER_BIT;
}
else if (barrierType == EBufferBarrier::TransferDestination)
{
  barrier.srcAccessMask = 0;
  barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
  sourceStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
  destinationStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
}

vkCmdPipelineBarrier(
  pCmdList->getCommandBuffer(EQueueType::Graphics), 
  sourceStage, 
  destinationStage, 
  0, 0, nullptr, 1, &barrier, 0, nullptr);

And it doesn’t work correctly. Looks like first call rhiUpdateBuffer(…, vertices) works well but second call rhiUpdateBuffer(…, indices) corrupts data for vertices then third call rhiUpdateBuffer(…, color) corrupts data for indices. But all offsets and sizes are correct.

Main problem why I’m asking for help is that all works fine if I call rhiUpdateBuffer() separately:

rhiBeginCommandList();
rhiUpdateBuffer(constBuffer.get(), constBuffer->getOffset(0), constBuffer->getSize(0), vertices);
rhiWaitCommandListFinished(rhiEndCommandList()); //submit VkCommandBuffer to queue

rhiBeginCommandList();
rhiUpdateBuffer(constBuffer.get(), constBuffer->getOffset(1), constBuffer->getSize(1), indices);
rhiWaitCommandListFinished(rhiEndCommandList()); //submit VkCommandBuffer to queue

rhiBeginCommandList();
rhiUpdateBuffer(constBuffer.get(), constBuffer->getOffset(2), constBuffer->getSize(2), color);
rhiWaitCommandListFinished(rhiEndCommandList()); //submit VkCommandBuffer to queue

I know that I can update whole memory by one call. I just want to understand how things work and what cause error. Validation layer says that all is ok.


#2

It’s solved. Was my fault. Incorrect staging buffer memory management.