[英]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);
这可能会失败有几个原因 - 无法通过提供的信息量来判断。 要做的最重要的事情是激活验证层,尝试理解错误消息并摆脱它(在此处发布验证层错误也将有所帮助。)。
您可以进一步调查的一些要点:
从给出的源代码来看,您似乎每帧都在重新记录命令缓冲区。 如果您这样做,那么您必须确保:
commandBuffer 必须是从使用 VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT 创建的池中分配的
(引用规范)。 并且必须在某处调用vkResetCommandBuffer
。 Vulkan Tutorial正在做的是:记录一次命令缓冲区(飞行中的每一帧一个)并在每一帧重用那些预先记录的命令缓冲区。
关于currentFrame
和imageIndex
:在最佳情况下,它们将是同步的,因为那时
vkWaitForFences(device.logical, 1, &imagesInFlight[imageIndex], VK_TRUE, UINT64_MAX);
永远不必等待——也就是说,如果您之前等待过inFlightFences[currentFrame]
,这正是 Vulkan 教程所做的,但您是否也这样做并不明显。
您无法真正控制获得的imageIndex
,因为它是从vkAcquireNextImageKHR
返回的。 但是,您必须推进currentFrame
,如果您从您发布的代码中这样做,这 - 再一次 - 并不明显。
为您正在创建的每个VkCommandBuffer
创建一个VkCommandPool
(或者换一种说法:从命令池中仅分配一个命令缓冲区)是一种次优策略。 我认为,如果您只有一种类型的命令缓冲区(即设置了VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT
位的命令缓冲区,如果您真的要重置命令缓冲区),则没有什么可以反对仅使用一个VkCommandPool
。 如果您要创建不同类型的命令缓冲区(例如可重置的和不可重置的),那么不同的池可能有意义。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.