Vulkan - 飞行中的多个帧
Vulkan - Multiple frames in flight
我正在做一个 Vulkan 项目,并且已经完成了绘制立方体的进度。我是 Vulkan 的新手,必须重新学习所有内容。我遇到了一些麻烦。
我想在飞行中有多个帧(1 个记录,1 个执行)。我的渲染循环是基于 https://vulkan-tutorial.com/Drawing_a_triangle/Drawing/Rendering_and_presentation 的,因为我无法理解当我有第二帧时发生了什么。现在,它没有崩溃,但它似乎也不能正常工作,因为 'currentFrame' 和 'imageIndex' 是一样的。如果我将 currentFrame 或 imageIndex 更改为不同,则没有任何效果。我应该注意,如果重要的话,我现在为每个池创建 N VkCommandPools 和 1 个命令缓冲区。
初始化:
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;
主循环:
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 = ¤tFrame;
vkQueuePresentKHR(device.graphics_queue, &present_info);
这可能会失败的原因有多种 - 所提供的信息量无法判断。最重要的事情是激活验证层,尝试理解错误消息并摆脱它(也有助于 post 此处的验证层错误!)。
您可以进一步调查的一些要点:
从给出的源代码来看,您似乎是 re-recording 命令缓冲每一帧。如果您这样做,则必须确保:
commandBuffer must have been allocated from a pool that was created with the VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT
(引用规范)。并且必须调用 vkResetCommandBuffer
call somewhere. What Vulkan Tutorial 正在做的是:记录一次命令缓冲区(一个用于飞行中的每一帧)并在每一帧重复使用那些 pre-recorded 命令缓冲区。
关于 currentFrame
和 imageIndex
:在最佳情况下,它们会同步,因为
vkWaitForFences(device.logical, 1, &imagesInFlight[imageIndex], VK_TRUE, UINT64_MAX);
永远不必等待 --- 也就是说,如果您之前已经等待 inFlightFences[currentFrame]
,这是 Vulkan 教程所做的,但如果您也这样做并不明显。
您无法真正控制得到哪个 imageIndex
,因为它是从 vkAcquireNextImageKHR
返回的。但是,您必须推进 currentFrame
,如果您从 posted.
的代码中这样做的话,这又是不明显的
为您正在创建的每个 VkCommandBuffer
创建一个 VkCommandPool
(或换句话说:从命令池中仅分配一个命令缓冲区)是一种次优策略。我认为,如果您只有一种类型的命令缓冲区(即设置了 VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT
位的命令缓冲区,如果您真的要重置命令缓冲区,则没有什么可以反对仅使用一个 VkCommandPool
)。如果您要创建不同类型的命令缓冲区(例如可重置的和 non-resettable 的),那么不同的池可能是有意义的。
我正在做一个 Vulkan 项目,并且已经完成了绘制立方体的进度。我是 Vulkan 的新手,必须重新学习所有内容。我遇到了一些麻烦。
我想在飞行中有多个帧(1 个记录,1 个执行)。我的渲染循环是基于 https://vulkan-tutorial.com/Drawing_a_triangle/Drawing/Rendering_and_presentation 的,因为我无法理解当我有第二帧时发生了什么。现在,它没有崩溃,但它似乎也不能正常工作,因为 'currentFrame' 和 'imageIndex' 是一样的。如果我将 currentFrame 或 imageIndex 更改为不同,则没有任何效果。我应该注意,如果重要的话,我现在为每个池创建 N VkCommandPools 和 1 个命令缓冲区。
初始化:
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;
主循环:
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 = ¤tFrame;
vkQueuePresentKHR(device.graphics_queue, &present_info);
这可能会失败的原因有多种 - 所提供的信息量无法判断。最重要的事情是激活验证层,尝试理解错误消息并摆脱它(也有助于 post 此处的验证层错误!)。
您可以进一步调查的一些要点:
从给出的源代码来看,您似乎是 re-recording 命令缓冲每一帧。如果您这样做,则必须确保:
commandBuffer must have been allocated from a pool that was created with the VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT
(引用规范)。并且必须调用 vkResetCommandBuffer
call somewhere. What Vulkan Tutorial 正在做的是:记录一次命令缓冲区(一个用于飞行中的每一帧)并在每一帧重复使用那些 pre-recorded 命令缓冲区。
关于 currentFrame
和 imageIndex
:在最佳情况下,它们会同步,因为
vkWaitForFences(device.logical, 1, &imagesInFlight[imageIndex], VK_TRUE, UINT64_MAX);
永远不必等待 --- 也就是说,如果您之前已经等待 inFlightFences[currentFrame]
,这是 Vulkan 教程所做的,但如果您也这样做并不明显。
您无法真正控制得到哪个 imageIndex
,因为它是从 vkAcquireNextImageKHR
返回的。但是,您必须推进 currentFrame
,如果您从 posted.
为您正在创建的每个 VkCommandBuffer
创建一个 VkCommandPool
(或换句话说:从命令池中仅分配一个命令缓冲区)是一种次优策略。我认为,如果您只有一种类型的命令缓冲区(即设置了 VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT
位的命令缓冲区,如果您真的要重置命令缓冲区,则没有什么可以反对仅使用一个 VkCommandPool
)。如果您要创建不同类型的命令缓冲区(例如可重置的和 non-resettable 的),那么不同的池可能是有意义的。