Which way to synchronize vkQueueSubmit() to use?

Hello

I have a function that copies data from one buffer to another, I need to synchronize its execution.

I have such a bad option:

void MainWindow::copyBuffer(VkBuffer srcBuffer, VkBuffer dstBuffer, VkDeviceSize size)
{
	VkCommandBuffer commandBuffer;
	vkAllocateCommandBuffers(logicalDevice, &allocInfo, &commandBuffer);

	//Start recording
	vkBeginCommandBuffer(commandBuffer, &beginInfo);
	vkCmdCopyBuffer(commandBuffer, srcBuffer, dstBuffer, 1, &copyRegion);
	vkEndCommandBuffer(commandBuffer);

	//Run command buffer
	vkQueueSubmit(graphicsQueue, 1, &submitInfo, VK_NULL_HANDLE);
	//Waiting for completion
	vkQueueWaitIdle(graphicsQueue);

	vkFreeCommandBuffers(logicalDevice, commandPool, 1, &commandBuffer);
}

This option is bad because if I want to execute the copyBuffer() function several times, then all the buffers will be copied strictly one at a time.

I want to use a fence for each function call so that multiple calls can run in parallel.

So far, I have only such a solution:

void MainWindow::copyBuffer(VkBuffer srcBuffer, VkBuffer dstBuffer, VkDeviceSize size)
{
	VkCommandBuffer commandBuffer;
	vkAllocateCommandBuffers(logicalDevice, &allocInfo, &commandBuffer);
	
	//Create fence
	VkFenceCreateInfo fenceInfo{};
	fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
	fenceInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT;

	VkFence executionCompleteFence = VK_NULL_HANDLE;
	if (vkCreateFence(logicalDevice, &fenceInfo, VK_NULL_HANDLE, &executionCompleteFence) != VK_SUCCESS) {
		throw MakeErrorInfo("Failed to create fence");
	}

	//Start recording
	vkBeginCommandBuffer(commandBuffer, &beginInfo);
	vkCmdCopyBuffer(commandBuffer, srcBuffer, dstBuffer, 1, &copyRegion);

	vkEndCommandBuffer(commandBuffer);

	//Run command buffer
	vkQueueSubmit(graphicsQueue, 1, &submitInfo, VK_NULL_HANDLE);
	
	vkWaitForFences(logicalDevice, 1, &executionCompleteFence, VK_TRUE, UINT64_MAX);
	vkResetFences(logicalDevice, 1, &executionCompleteFence);

	vkFreeCommandBuffers(logicalDevice, commandPool, 1, &commandBuffer);
	vkDestroyFence(logicalDevice, executionCompleteFence, VK_NULL_HANDLE);
}

Which of these options is better?

Is the second option written correctly?

second

no, [vkQueueSubmit(3)]

//Run command buffer
	vkQueueSubmit(graphicsQueue, 1, &submitInfo, executionCompleteFence);

Also, you don’t need to recreate VkCommandBuffer and VkFence. Reuse cVkCommandBuffer for next copy operations, using
VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT

1 Like

Do you suggest creating a global command buffer for copy operations and using it all the time?
How can I synchronize such a solution so that there are no problems when running multiple copyBuffer functions?

yes, why not ?

I guess you can use a global VkFence for synchronization - It works.
For multithreaded updating you should use separate VkCommandPool, VkCommandBuffer and VkFence for each thread.

1 Like