简体   繁体   中英

Vulkan - Multiple frames in flight

I'm working a on a Vulkan project and have gotten through the draw cube progression. I'm brand new to Vulkan and have had to relearn everything. I'm having some trouble.

I want to have multiple frames in-flight (1 recording, 1 executing). My render loop is based on https://vulkan-tutorial.com/Drawing_a_triangle/Drawing/Rendering_and_presentation because I couldn't understand what was going on when I had a second frame in-flight. Right now, it doesn't crash but also it doesn't seem to work properly as 'currentFrame' and 'imageIndex' are the same. If I change either currentFrame or imageIndex to be different, nothing works. I should note that I create N VkCommandPools and 1 command buffer per pool for now if that matters.

INIT:

VkFenceCreateInfo fence_info;
fence_info.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
fence_info.pNext = NULL;
fence_info.flags = 0;

VkFence *inFlightFences = malloc(sizeof *inFlightFences * NUM_SWAPCHAIN_IMAGES);

for(i = 0; i < NUM_SWAPCHAIN_IMAGES; i++) {
    result = vkCreateFence(device.logical, &fence_info, NULL, &inFlightFences[i]);
    assert(result == VK_SUCCESS && "vkCreateFence");
}
VkFence imagesInFlight[2] = {NULL, NULL};
unsigned int imageIndex, currentFrame = 0;

MAIN LOOP:

    vkAcquireNextImageKHR(device.logical, swapchain.handle, UINT64_MAX, imageAvailableSemaphores[currentFrame], VK_NULL_HANDLE, &imageIndex);

    printf("%u %u\n", currentFrame, imageIndex); // both equal always
    
    if(imagesInFlight[imageIndex] != VK_NULL_HANDLE) {
        vkWaitForFences(device.logical, 1, &imagesInFlight[imageIndex], VK_TRUE, UINT64_MAX);
    }
    
    imagesInFlight[imageIndex] = inFlightFences[currentFrame];
    
    VkSemaphore waitSemaphores[] = {imageAvailableSemaphores[currentFrame]};
    VkSemaphore signalSemaphores[] = {renderFinishedSemaphores[currentFrame]};

    /* begin rendering */
    
    VkCommandBufferBeginInfo begin_info = {0};
    begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
    
    vkBeginCommandBuffer(main_cmdbuffs[imageIndex], &begin_info);
    
    VkImageMemoryBarrier acquire_barrier;
    acquire_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
    acquire_barrier.pNext = NULL;
    acquire_barrier.srcAccessMask = 0;
    acquire_barrier.dstAccessMask = VK_ACCESS_MEMORY_WRITE_BIT;
    acquire_barrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
    acquire_barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
    acquire_barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
    acquire_barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
    acquire_barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
    acquire_barrier.subresourceRange.baseMipLevel = 0;
    acquire_barrier.subresourceRange.levelCount = 1;
    acquire_barrier.subresourceRange.baseArrayLayer = 0;
    acquire_barrier.subresourceRange.layerCount = 1;
    acquire_barrier.image = swapchain.images[imageIndex];
    
    vkCmdPipelineBarrier(main_cmdbuffs[imageIndex],
                         VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
                         VK_PIPELINE_STAGE_TRANSFER_BIT,
                         0, 0, NULL, 0, NULL, 1, &acquire_barrier);        

    VkImageMemoryBarrier present_barrier = {};
    present_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
    present_barrier.pNext = NULL;
    present_barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
    present_barrier.dstAccessMask = VK_ACCESS_MEMORY_READ_BIT;
    present_barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
    present_barrier.newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
    present_barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
    present_barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
    present_barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
    present_barrier.subresourceRange.baseMipLevel = 0;
    present_barrier.subresourceRange.levelCount = 1;
    present_barrier.subresourceRange.baseArrayLayer = 0;
    present_barrier.subresourceRange.layerCount = 1;
    present_barrier.image = swapchain.images[imageIndex];
    
    vkCmdPipelineBarrier(main_cmdbuffs[imageIndex], VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0, 0, NULL,
                        0, NULL, 1, &present_barrier);
    vkEndCommandBuffer(main_cmdbuffs[imageIndex]);

    VkPipelineStageFlags wait_dst_stage_mask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
    VkSubmitInfo submit_info = {0};
    
    submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
    submit_info.pWaitDstStageMask = &wait_dst_stage_mask;
    submit_info.commandBufferCount = 1;
    submit_info.pCommandBuffers = &main_cmdbuffs[imageIndex];
    submit_info.waitSemaphoreCount = 1;
    submit_info.signalSemaphoreCount = 1;
    submit_info.pWaitSemaphores = waitSemaphores;
    submit_info.pSignalSemaphores = signalSemaphores;
    
    vkResetFences(device.logical, 1, &inFlightFences[currentFrame]);
    
    vkQueueSubmit(device.graphics_queue, 1, &submit_info, inFlightFences[imageIndex]);
    
    VkPresentInfoKHR present_info = {0};
    present_info.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
    present_info.waitSemaphoreCount = 1;
    present_info.pWaitSemaphores = &renderFinishedSemaphores[currentFrame];
    present_info.swapchainCount = 1;
    present_info.pSwapchains = &swapchain.handle;
    present_info.pImageIndices = &currentFrame;
    
    vkQueuePresentKHR(device.graphics_queue, &present_info);

There are several reasons why this could fail - it is impossible to tell with the amount of information provided. Most important thing to do is to activate validation layers, try to understand the error message and get rid of it (would also be helpful to post validation layer errors here.).

Some points where you could further investigate:

From the source code given, it appears that you are re-recording the command buffers every frame. If you are doing so, then you'll have to ensure that:

commandBuffer must have been allocated from a pool that was created with the VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT

(citing the specification). And there must be a call to vkResetCommandBuffer call somewhere. What Vulkan Tutorial is doing is: record command buffers once (one for each frame in flight) and reuse those pre-recorded command buffers every frame.

Regarding currentFrame and imageIndex : In the optimal case, they would be in sync because then the

vkWaitForFences(device.logical, 1, &imagesInFlight[imageIndex], VK_TRUE, UINT64_MAX);

would never have to wait---that is, IF you have previously waited for the inFlightFences[currentFrame] which is what Vulkan Tutorial does, but it is not obvious if you are doing so as well.

You can't really control which imageIndex you get, because that is returned from vkAcquireNextImageKHR . However, you have to advance the currentFrame which -- again -- is not obvious if you are doing so from the code you have posted.

Create one VkCommandPool for every VkCommandBuffer that you are creating (or put differently: allocating only one command buffer from a command pool) is a suboptimal strategy. I think, there's nothing that speaks against just using one VkCommandPool if you only have only one type of command buffers (ie one with the VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT bit set, if you are really going to reset command buffers). If you are going to create different types of command buffers (eg resettable ones and non-resettable ones), then different pools might make sense.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM