呈现
渲染操作执行后,我们需要将渲染的图像返回给交换链进行呈现操作。我们在drawFrame函数的尾部通过VkPresentInfoKHR结构体来配置呈现信息:
VkPresentInfoKHR presentInfo = {};
presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
presentInfo.waitSemaphoreCount = 1;
presentInfo.pWaitSemaphores = signalSemaphores;
waitSemaphoreCount和pWaitSemaphores成员变量用于指定开始呈现操作需要等待的信号量。
VkSwapchainKHR swapChains[] = {swapChain};
presentInfo.swapchainCount = 1;
presentInfo.pSwapchains = swapChains;
presentInfo.pImageIndices = &imageIndex;
接着,我们指定了用于呈现图像的交换链,以及需要呈现的图像在交换链中的索引。
presentInfo.pResults = nullptr; // Optional
我们可以通过pResults成员变量获取每个交换链的呈现操作是否成功的信息。在这里,由于我们只使用了一个交换链,可以直接使用呈现函数的返回值来判断呈现操作是否成功,没有必要使用pResults。
vkQueuePresentKHR(presentQueue, &presentInfo);
调用vkQueuePresentKHR函数可以请求交换链进行图像呈现操作。在下一章节,我们会对vkAcquireNextImageKHR函数和vkQueuePresentKHR函数添加错误处理的代码,应对调用它们失败后的情况。
现在如果编译运行程序,当我们关闭应用程序窗口时,我们的程序直接就奔溃了。如果开启了校验层,我们可以从控制台窗口看到调试回调函数打印的错误信息。
造成这一问题的原因是drawFrame函数中的操作是异步执行的。这意味着我们关闭应用程序窗口跳出主循环时,绘制操作和呈现操作可能仍在继续执行,这与我们紧接着进行的清除操作也是冲突的。
我们应该等待逻辑设备的操作结束执行才能销毁窗口:
void mainLoop() {
while (!glfwWindowShouldClose(window)) {
glfwPollEvents();
drawFrame();
}
vkDeviceWaitIdle(device);
}
我们可以使用vkQueueWaitIdle函数等待一个特定指令队列结束执行。现在再次编译运行程序,关闭应用程序窗口就不会造成程序直接崩溃了。