Vulkan迷惑问题-交换链中获取图片vkAcquireNextImageKHR的ImageIndex 与 currentFrame之间的关系
Posted 赵新政
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Vulkan迷惑问题-交换链中获取图片vkAcquireNextImageKHR的ImageIndex 与 currentFrame之间的关系相关的知识,希望对你有一定的参考价值。
在**vulkan**当中,我们遇到了**drawFrame**函数,但是其中有**inFlight**的概念,那么如何理解这个概念,又如何理解在这个概念之下的同步控制(**fence, semaphore**)呢?
遇到的问题
在学习VulkanTutorial的时候,同学们一定会遇到drawFrame这个函数,在这个函数里面,绘制的过程是长得这样子的:
vkWaitForFences(device, 1, &inFlightFences[currentFrame], VK_TRUE, UINT64_MAX);
uint32_t imageIndex;
vkAcquireNextImageKHR(device, swapChain, UINT64_MAX, imageAvailableSemaphores[currentFrame], VK_NULL_HANDLE, &imageIndex);
if (imagesInFlight[imageIndex] != VK_NULL_HANDLE) {
vkWaitForFences(device, 1, &imagesInFlight[imageIndex], VK_TRUE, UINT64_MAX);
}
imagesInFlight[imageIndex] = inFlightFences[currentFrame];
VkSubmitInfo submitInfo{};
submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
VkSemaphore waitSemaphores[] = { imageAvailableSemaphores[currentFrame] };
VkPipelineStageFlags waitStages[] = { VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT };
submitInfo.waitSemaphoreCount = 1;
submitInfo.pWaitSemaphores = waitSemaphores;
submitInfo.pWaitDstStageMask = waitStages;
submitInfo.commandBufferCount = 1;
submitInfo.pCommandBuffers = &commandBuffers[imageIndex];
VkSemaphore signalSemaphores[] = { renderFinishedSemaphores[currentFrame] };
submitInfo.signalSemaphoreCount = 1;
submitInfo.pSignalSemaphores = signalSemaphores;
vkResetFences(device, 1, &inFlightFences[currentFrame]);
if (vkQueueSubmit(graphicsQueue, 1, &submitInfo, inFlightFences[currentFrame]) != VK_SUCCESS) {
throw std::runtime_error("failed to submit draw command buffer!");
}
VkPresentInfoKHR presentInfo{};
presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
presentInfo.waitSemaphoreCount = 1;
presentInfo.pWaitSemaphores = signalSemaphores;
VkSwapchainKHR swapChains[] = { swapChain };
presentInfo.swapchainCount = 1;
presentInfo.pSwapchains = swapChains;
presentInfo.pImageIndices = &imageIndex;
vkQueuePresentKHR(presentQueue, &presentInfo);
currentFrame = (currentFrame + 1) % MAX_FRAMES_IN_FLIGHT
其中我们对代码进行一下简化,把其中的同步设置抽取出来看看:
- 进行了对于Fence的阻塞等待,用了currentFrame作为imageIndex来索引Fence。
- 使用vkAcquireNextImageKHR来获取下一帧可以使用的图片,并且绑定一个Semaphore,用了currentFrame作为index来索引Semaphore。
- 对提交信息等进行设置。
- currentFrame ++。
但是这个里面的ImageIndex与currentFrame之间到底是什么关系呢?
同步控制
这段代码的大前提是,我们希望自己控制最多有多少张图片处于同时被渲染的状态。我们希望多少张,就做出来多少套Semaphore/Fence这种控制资源,然后用做出来的这N套资源,每一次选择一套,分配给第imageIndex张图片
我们可以先观察下所有的同步变量:
inFlightFences:这个数组的大小是MAX_FRAMES_IN_FLIGHT这么多,用来表示当前的Frame所对应的Fence
imagesInFlight:这个数组的大小是SwapChainImageCount这么多,用来表示当前某一个交换链当中图片所对应的Fence
这两个数组其实每一帧都会设置一次对应关系,imagesInFlight数组当中的每一个fence都对应了inFlightFences当中的某一个fence
接下来我们来分析下同步代码;
首先我们走到了当前第currentFrame个帧,那么,就要等待这一帧的命令队列执行完毕,为什么要等待呢?因为我们需要使用这个Fence,为接下即将获取的第imageIndex的图片进行过程控制,如下所示:
vkWaitForFences(device, 1, &inFlightFences[currentFrame], VK_TRUE, UINT64_MAX);
只要这一句代码通过了,那么说明第currentFrame所对应的不管Fence也好、Semaphore也好,都已经使用完毕了,这一套资源对应的过程结束了。
然后我们获取到了一张当前可以用的交换链当中的图片ID
uint32_t **imageIndex**;
vkAcquireNextImageKHR(device, swapChain, UINT64_MAX, imageAvailableSemaphores[currentFrame], VK_NULL_HANDLE, &imageIndex);
那么接下来就要使用交换链当中的第imageIndex个图片进行渲染,所以我们需要给到这张图片一组同步对象,就是当前currentFrame对应的semaphore跟fence, 但是还有一个问题,我们必须等待imageIndex这一帧渲染完毕才能使用,那这一张图片对应的fence就必须执行完毕,除非这一帧没有被执行过,所以就判断一下**imagesInFlight[imageIndex]**是否被赋值过。
if (imagesInFlight[imageIndex] != VK_NULL_HANDLE) {
vkWaitForFences(device, 1, &imagesInFlight[imageIndex], VK_TRUE, UINT64_MAX);
}
imagesInFlight[imageIndex] = inFlightFences[currentFrame];
如上所示,在判断imageIndex对应的一帧已经执行完毕之后,我们把currentFrame对应的fence分配过去了。
接下来我们可以看到:
VkSemaphore waitSemaphores[] = { imageAvailableSemaphores[currentFrame] };
VkPipelineStageFlags waitStages[] = { VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT };
submitInfo.waitSemaphoreCount = 1;
submitInfo.pWaitSemaphores = waitSemaphores;
submitInfo.pWaitDstStageMask = waitStages;
submitInfo.commandBufferCount = 1;
submitInfo.pCommandBuffers = &commandBuffers[imageIndex];
VkSemaphore signalSemaphores[] = { renderFinishedSemaphores[currentFrame] };
submitInfo.signalSemaphoreCount = 1;
submitInfo.pSignalSemaphores = signalSemaphores;
因为我们之前等待了inFlightFences[currentFrame]完成,所以对应的那一次提交所使用的semaphore们也一定已经使用完毕,所以可以直接给这一帧使用,即给到了imageIndex对应的交换链图片。
结论
inFlightFences、imageAvailableSemaphores以及renderFinishedSemaphores都是我们提供的资源,数量就是MAX_FRAMES_IN_FLIGHT这么多,在每一次获取imageIndex的交换链图片的时候,会看看有没有闲置资源,如果有就分配给这一帧,否则就等待currentFrame所对应的资源组空闲。
以上是关于Vulkan迷惑问题-交换链中获取图片vkAcquireNextImageKHR的ImageIndex 与 currentFrame之间的关系的主要内容,如果未能解决你的问题,请参考以下文章