Javin Yang
Javin Yang

Reputation: 305

vulkan: VkImageMemoryBarrier

I don't quite understand here.: https://github.com/SaschaWillems/Vulkan/blob/master/examples/computeshader/computeshader.cpp

void draw()
{
    VulkanExampleBase::prepareFrame();

    submitInfo.commandBufferCount = 1;
    submitInfo.pCommandBuffers = &drawCmdBuffers[currentBuffer];
    VK_CHECK_RESULT(vkQueueSubmit(queue, 1, &submitInfo, VK_NULL_HANDLE));

    VulkanExampleBase::submitFrame();

    // Submit compute commands
    // Use a fence to ensure that compute command buffer has finished executin before using it again
    vkWaitForFences(device, 1, &compute.fence, VK_TRUE, UINT64_MAX);
    vkResetFences(device, 1, &compute.fence);

    VkSubmitInfo computeSubmitInfo = vks::initializers::submitInfo();
    computeSubmitInfo.commandBufferCount = 1;
    computeSubmitInfo.pCommandBuffers = &compute.commandBuffer;

    VK_CHECK_RESULT(vkQueueSubmit(compute.queue, 1, &computeSubmitInfo, compute.fence));
}

drawCmdBuffers[currentBuffer] runs before compute.commandBuffer, but the consumer drawCmdBuffers[currentBuffer] requires the textureComputeTarget produced by the producer compute.commandBuffer.

  1. I don't understand why drawCmdBuffers[currentBuffer] is called before compute.commandBuffer.

In the following code, only the first frame is rendered, while the right picture does not get the textureComputeTarget, so it is rendered with a blue background.

void draw()

{
    VulkanExampleBase::prepareFrame();

    submitInfo.commandBufferCount = 1;
    submitInfo.pCommandBuffers = &drawCmdBuffers[currentBuffer];
    VK_CHECK_RESULT(vkQueueSubmit(queue, 1, &submitInfo, VK_NULL_HANDLE));

    VulkanExampleBase::submitFrame();

    // Submit compute commands
    // Use a fence to ensure that compute command buffer has finished executin before using it again
    vkWaitForFences(device, 1, &compute.fence, VK_TRUE, UINT64_MAX);
    vkResetFences(device, 1, &compute.fence);

    VkSubmitInfo computeSubmitInfo = vks::initializers::submitInfo();
    computeSubmitInfo.commandBufferCount = 1;
    computeSubmitInfo.pCommandBuffers = &compute.commandBuffer;

    VK_CHECK_RESULT(vkQueueSubmit(compute.queue, 1, &computeSubmitInfo, compute.fence));

       sleep(1000) // <-------- Wait
}

Executed when calling vkQueueSubmit(queue, 1, &submitInfo, VK_NULL_HANDLE):

VkImageMemoryBarrier imageMemoryBarrier = {};
imageMemoryBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
// We won't be changing the layout of the image
imageMemoryBarrier.oldLayout = VK_IMAGE_LAYOUT_GENERAL;
imageMemoryBarrier.newLayout = VK_IMAGE_LAYOUT_GENERAL;
imageMemoryBarrier.image = textureComputeTarget.image;
imageMemoryBarrier.subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 };
imageMemoryBarrier.srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT;
imageMemoryBarrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
vkCmdPipelineBarrier(
    drawCmdBuffers[i],
    VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
    VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
    VK_FLAGS_NONE,
    0, nullptr,
    0, nullptr,
    1, &imageMemoryBarrier);
vkCmdBeginRenderPass(drawCmdBuffers[i], &renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE);
  1. Wait for VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, but this phase has not been executed before, why is the pipeline not stuck? Is it because there is no pipeline before, so there is no need to wait?

In section 6.6 Pipeline Barriers vkCmdPipelineBarrier is a synchronization command that inserts a dependency between commands submitted to the same queue, or between commands in the same subpass.

void draw()
{
    printf("%p, %p\n", queue, compute.queue);

    VulkanExampleBase::prepareFrame();

    submitInfo.commandBufferCount = 1;
    submitInfo.pCommandBuffers = &drawCmdBuffers[currentBuffer];
    VK_CHECK_RESULT(vkQueueSubmit(queue, 1, &submitInfo, VK_NULL_HANDLE));

    VulkanExampleBase::submitFrame();

    // Submit compute commands
    // Use a fence to ensure that compute command buffer has finished executin before using it again
    vkWaitForFences(device, 1, &compute.fence, VK_TRUE, UINT64_MAX);
    vkResetFences(device, 1, &compute.fence);

    VkSubmitInfo computeSubmitInfo = vks::initializers::submitInfo();
    computeSubmitInfo.commandBufferCount = 1;
    computeSubmitInfo.pCommandBuffers = &compute.commandBuffer;

    VK_CHECK_RESULT(vkQueueSubmit(compute.queue, 1, &computeSubmitInfo, compute.fence));
    sleep(1000);
}

Print results: 0x6000039c4a20, 0x6000039c4a20 The current queue and compute.queue are the same queue.But it is possible that the above code may generate different queue.

  1. Can VkImageMemoryBarrier be synchronized in multiple queues?

  2. vkCmdPipelineBarrier is a synchronization command that inserts a dependency between commands submitted to the same queue, or between commands in the same subpass. why use "or", why not use "and"?

Upvotes: 0

Views: 888

Answers (1)

krOoze
krOoze

Reputation: 13246

  1. I don't understand why drawCmdBuffers[currentBuffer] is called before compute.commandBuffer.

Dunno, it is an example. Author was probably not awfully woried what happens in the first frame. It would simply be drawn with one frame delay. Swapping the compute before draw should also work with some effort.

  1. Wait for VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, but this phase has not been executed before, why is the pipeline not stuck? Is it because there is no pipeline before, so there is no need to wait?

Because that is not how pipeline and dependencies work. vkCmdPipelineBarrier makes sure any command\operation in queue before the barrier reaches (and finishes) at least the srcStage stage (i.e. COMPUTE) before any command\op recorded after it reach dstStage.

Such dependency is satisfied even if there are no commands recorded before. I.e. by definition of "nothing", there are no commands that have not reached COMPUTE stage yet.

  1. Can VkImageMemoryBarrier be synchronized in multiple queues?

Yes, with the help of a Semaphore.

For VK_SHARING_MODE_EXCLUSIVE and different queue family it is called Queue Family Ownership Transfer (QFOT).

Otherwisely, a Semaphore already performs a memory dependency and a VkImageMemoryBarrier is not needed.

  1. vkCmdPipelineBarrier is a synchronization command that inserts a dependency between commands submitted to the same queue, or between commands in the same subpass. why use "or", why not use "and"?

vkCmdPipelineBarrier is either outside subpass, then it forms a dependency with commands recorded before and after in the queue.

Or vkCmdPipelineBarrier is inside a subpass, in which case it is called "subpass self-dependency" and its scope is limited only to that subpass (among other restrictions).

Upvotes: 2

Related Questions