0

I have 2 queues for which I submit commands. The first one is the graphics queue and the other one is the compute queue. They are also getting executed in this order. (graphics -> compute -> present)

In my rendering stage I'm using an image which has to be transitioned to VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL in order to use and in the compute stage it has to be transitioned again to VK_IMAGE_LAYOUT_GENERAL.

After around 5 to 10 frames it's complaining abt that the image cannot be transitioned anymore, my first guess is, that I have messed up the synchronization somewhere but I cannot find it! I have tried for 3 days now and still the error persists.

Can somebody spot an issue?

And before anybody asks, the initial layout has been properly set to VK_IMAGE_LAYOUT_GENERAL, so that, when the rendering happens first, it can be properly transitioned for the first frame.

Here is the error that occurs after a few frames:

Validation Error: [ VUID-vkCmdDraw-None-09600 ] Object 0: handle = 0x22225086530, type = VK_OBJECT_TYPE_COMMAND_BUFFER; Object 1: handle = 0x1983b0000000008a, type = VK_OBJECT_TYPE_IMAGE; | MessageID = 0x46582f7b | vkQueueSubmit(): pSubmits[0].pCommandBuffers[0] command buffer VkCommandBuffer 0x22225086530[] expects VkImage 0x1983b0000000008a[] (subresource: aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, mipLevel = 0, arrayLayer = 0) to be in layout VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL--instead, current layout is VK_IMAGE_LAYOUT_GENERAL.

Here is my complete render loop:

void vulkan_renderer_draw(transform_t* transform, camera_t* camera)
{
    VkFence fences[] = { s_vulkan_renderer_graphics_fences[s_vulkan_renderer_frame_index], s_vulkan_renderer_compute_fences[s_vulkan_renderer_frame_index] };

    vkWaitForFences(g_vulkan_context_device, 2, fences, 1, UINT64_MAX);
    vkResetFences(g_vulkan_context_device, 2, fences);

    vkResetCommandBuffer(s_vulkan_renderer_graphics_command_buffers[s_vulkan_renderer_frame_index], VK_COMMAND_BUFFER_RESET_RELEASE_RESOURCES_BIT);
    vkResetCommandBuffer(s_vulkan_renderer_compute_command_buffers[s_vulkan_renderer_frame_index], VK_COMMAND_BUFFER_RESET_RELEASE_RESOURCES_BIT);

    vkAcquireNextImageKHR(
        g_vulkan_context_device,
        g_vulkan_context_swap_chain,
        UINT64_MAX,
        s_vulkan_renderer_image_available_semaphores[s_vulkan_renderer_frame_index],
        0,
        &s_vulkan_renderer_image_index
    );

    vulkan_renderer_update_uniform_buffers(transform, camera);

    vulkan_renderer_record_graphics_commands();

    {
        VkPipelineStageFlags graphics_wait_stages[] = { VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT };

        VkSubmitInfo graphics_submit_info = { 0 };
        graphics_submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
        graphics_submit_info.pWaitSemaphores = &s_vulkan_renderer_image_available_semaphores[s_vulkan_renderer_frame_index];
        graphics_submit_info.waitSemaphoreCount = 1;
        graphics_submit_info.pSignalSemaphores = &s_vulkan_renderer_graphics_complete_semaphores[s_vulkan_renderer_frame_index];
        graphics_submit_info.signalSemaphoreCount = 1;
        graphics_submit_info.pCommandBuffers = &s_vulkan_renderer_graphics_command_buffers[s_vulkan_renderer_frame_index];
        graphics_submit_info.commandBufferCount = 1;
        graphics_submit_info.pWaitDstStageMask = graphics_wait_stages;

        vkQueueSubmit(g_vulkan_context_graphics_queue, 1, &graphics_submit_info, s_vulkan_renderer_graphics_fences[s_vulkan_renderer_frame_index]);
    }

    vulkan_renderer_record_compute_commands();

    {
        VkPipelineStageFlags compute_wait_stages[] = { VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT };

        VkSubmitInfo compute_submit_info = { 0 };
        compute_submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
        compute_submit_info.pWaitSemaphores = &s_vulkan_renderer_graphics_complete_semaphores[s_vulkan_renderer_frame_index];
        compute_submit_info.waitSemaphoreCount = 1;
        compute_submit_info.pSignalSemaphores = &s_vulkan_renderer_compute_complete_semaphores[s_vulkan_renderer_frame_index];
        compute_submit_info.signalSemaphoreCount = 1;
        compute_submit_info.pCommandBuffers = &s_vulkan_renderer_compute_command_buffers[s_vulkan_renderer_frame_index];
        compute_submit_info.commandBufferCount = 1;
        compute_submit_info.pWaitDstStageMask = compute_wait_stages;

        vkQueueSubmit(g_vulkan_context_compute_queue, 1, &compute_submit_info, s_vulkan_renderer_compute_fences[s_vulkan_renderer_frame_index]);
    }

    {
        VkPresentInfoKHR present_info = { 0 };
        present_info.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
        present_info.pWaitSemaphores = &s_vulkan_renderer_compute_complete_semaphores[s_vulkan_renderer_frame_index];
        present_info.waitSemaphoreCount = 1;
        present_info.pSwapchains = &g_vulkan_context_swap_chain;
        present_info.swapchainCount = 1;
        present_info.pImageIndices = &s_vulkan_renderer_image_index;

        vkQueuePresentKHR(g_vulkan_context_present_queue, &present_info);
    }

    s_vulkan_renderer_frame_index = (s_vulkan_renderer_frame_index + 1) % VULKAN_RENDERER_FRAMES_IN_FLIGHT;
}

In the "vulkan_renderer_record_graphics_commands" function, I simply record the command buffer, but the first thing I do before rendering is to transition my image layout like this:

VkImageMemoryBarrier chunk_image_memory_barrier = { 0 };
chunk_image_memory_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
chunk_image_memory_barrier.oldLayout = VK_IMAGE_LAYOUT_GENERAL;
chunk_image_memory_barrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
chunk_image_memory_barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; // TODO
chunk_image_memory_barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; // TODO
chunk_image_memory_barrier.image = s_vulkan_renderer_chunk_image[s_vulkan_renderer_frame_index];
chunk_image_memory_barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
chunk_image_memory_barrier.subresourceRange.baseMipLevel = 0;
chunk_image_memory_barrier.subresourceRange.levelCount = 1;
chunk_image_memory_barrier.subresourceRange.baseArrayLayer = 0;
chunk_image_memory_barrier.subresourceRange.layerCount = 1;
chunk_image_memory_barrier.srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT | VK_ACCESS_MEMORY_WRITE_BIT;
chunk_image_memory_barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_MEMORY_READ_BIT;

vkCmdPipelineBarrier(
    s_vulkan_renderer_graphics_command_buffers[s_vulkan_renderer_frame_index],
    VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
    VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
    0, 0, 0, 0, 0, 1, &chunk_image_memory_barrier
);

In the "vulkan_renderer_record_compute_commands" function, I do the exact opposite, transitioning the layout back for the compute shader to use it:

VkImageMemoryBarrier chunk_image_memory_barrier = { 0 };
chunk_image_memory_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
chunk_image_memory_barrier.oldLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
chunk_image_memory_barrier.newLayout = VK_IMAGE_LAYOUT_GENERAL;
chunk_image_memory_barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; // TODO
chunk_image_memory_barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; // TODO
chunk_image_memory_barrier.image = s_vulkan_renderer_chunk_image[s_vulkan_renderer_frame_index];
chunk_image_memory_barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
chunk_image_memory_barrier.subresourceRange.baseMipLevel = 0;
chunk_image_memory_barrier.subresourceRange.levelCount = 1;
chunk_image_memory_barrier.subresourceRange.baseArrayLayer = 0;
chunk_image_memory_barrier.subresourceRange.layerCount = 1;
chunk_image_memory_barrier.srcAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_MEMORY_READ_BIT;
chunk_image_memory_barrier.dstAccessMask = VK_ACCESS_SHADER_WRITE_BIT | VK_ACCESS_MEMORY_WRITE_BIT;

vkCmdPipelineBarrier(
    s_vulkan_renderer_compute_command_buffers[s_vulkan_renderer_frame_index],
    VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
    VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
    0, 0, 0, 0, 0, 1, &chunk_image_memory_barrier
);

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.