情景
同时跟随 https://www.bilibili.com/video/BV1HHg7ztEwc/?spm_id_from=333.1391.0.0 与 Vulkan 官方教程 的代码,构建基于C++的基本的Vulkan渲染器(另外一提前者的实现中将窗口的创建与 Vulkan 的初始化相耦合了,使用的也是类的设计)。 在教程的“渲染和呈现”小节中,在实现代码与教程完全一致的情况下,渲染窗口时在初始化结束后,进行drawFrame()时验证层报如下信息,随即窗口弹窗崩溃。
△验证层的报错信息
Validation Layer:vkQueueSubmit(): pSubmits[0].pSignalSemaphores[0] (VkSemaphore 0x140000000014) is being signaled by VkQueue 0x296356ead30, but it may still be in use by VkSwapchainKHR 0x30000000003. Here are the most recently acquired image indices: [0], 1, 2. (brackets mark the last use of VkSemaphore 0x140000000014 in a presentation operation) Swapchain image 0 was presented but was not re-acquired, so VkSemaphore 0x140000000014 may still be in use and cannot be safely reused with image index 2. Vulkan insight: One solution is to assign each image its own semaphore. Here are some common methods to ensure that a semaphore passed to vkQueuePresentKHR is not in use and can be safely reused: a) Use a separate semaphore per swapchain image. Index these semaphores using the index of the acquired image. b) Consider the VK_KHR_swapchain_maintenance1 extension. It allows using a VkFence with the presentation operation. The Vulkan spec states: Each binary semaphore element of the pSignalSemaphores member of any element of pSubmits must be unsignaled when the semaphore signal operation it defines is executed on the device (https://vulkan.lunarg.com/doc/view/1.4.321.1/windows/antora/spec/latest/chapters/cmdbuffers.html#VUID-vkQueueSubmit-pSignalSemaphores-00067)
这些消息提示了一个信号量重用的问题。我尝试过将“飞行中的帧”小节的代码也进行应用,但无济于事。然而当我使用另外一份功能上完全相同的参考代码时,却不会触发崩溃(然而它的验证层也会报类似的警告,具体如下),仓库链接为 https://github.com/crd2333/Learning-Vulkan
△参考代码的警告信息
Here are the most recently acquired image indices: [0], 1, 2. (brackets mark the last use of VkSemaphore 0x140000000014 in a presentation operation) Swapchain image 0 was presented but was not re-acquired, so VkSemaphore 0x140000000014 may still be in use and cannot be safely reused with image index 2. Vulkan insight: One solution is to assign each image its own semaphore. Here are some common methods to ensure that a semaphore passed to vkQueuePresentKHR is not in use and can be safely reused: a) Use a separate semaphore per swapchain image. Index these semaphores using the index of the acquired image. b) Consider the VK_KHR_swapchain_maintenance1 extension. It allows using a VkFence with the presentation operation. The Vulkan spec states: Each binary semaphore element of the pSignalSemaphores member of any element of pSubmits must be unsignaled when the semaphore signal operation it defines is executed on the device (https://vulkan.lunarg.com/doc/view/1.4.321.1/windows/antora/spec/latest/chapters/cmdbuffers.html#VUID-vkQueueSubmit-pSignalSemaphores-00067) validation layer: vkQueueSubmit(): pSubmits[0].pSignalSemaphores[0] (VkSemaphore 0x170000000017) is being signaled by VkQueue 0x14bd768b460, but it may still be in use by VkSwapchainKHR 0x30000000003. Here are the most recently acquired image indices: 0, [1], 2, 0.(brackets mark the last use of VkSemaphore 0x170000000017 in a presentation operation) Swapchain image 1 was presented but was not re-acquired, so VkSemaphore 0x170000000017 may still be in use and cannot be safely reused with image index 0. Vulkan insight: One solution is to assign each image its own semaphore. Here are some common methods to ensure that a semaphore passed to vkQueuePresentKHR is not in use and can be safely reused: a) Use a separate semaphore per swapchain image. Index these semaphores using the index of the acquired image. b) Consider the VK_KHR_swapchain_maintenance1 extension. It allows using a VkFence with the presentation operation. The Vulkan spec states: Each binary semaphore element of the pSignalSemaphores member of any element of pSubmits must be unsignaled when the semaphore signal operation it defines is executed on the device (https://vulkan.lunarg.com/doc/view/1.4.321.1/windows/antora/spec/latest/chapters/cmdbuffers.html#VUID-vkQueueSubmit-pSignalSemaphores-00067) validation layer: vkQueueSubmit(): pSubmits[0].pSignalSemaphores[0] (VkSemaphore 0x140000000014) is being signaled by VkQueue 0x14bd768b460, but it may still be in use by VkSwapchainKHR 0x30000000003. Here are the most recently acquired image indices: 0, 1, 2, 0, [2], 1, 0. (brackets mark the last use of VkSemaphore 0x140000000014 in a presentation operation) Swapchain image 2 was presented but was not re-acquired, so VkSemaphore 0x140000000014 may still be in use and cannot be safely reused with image index 0. Vulkan insight: One solution is to assign each image its own semaphore. Here are some common methods to ensure that a semaphore passed to vkQueuePresentKHR is not in use and can be safely reused: a) Use a separate semaphore per swapchain image. Index these semaphores using the index of the acquired image. b) Consider the VK_KHR_swapchain_maintenance1 extension. It allows using a VkFence with the presentation operation. The Vulkan spec states: Each binary semaphore element of the pSignalSemaphores member of any element of pSubmits must be unsignaled when the semaphore signal operation it defines is executed on the device (https://vulkan.lunarg.com/doc/view/1.4.321.1/windows/antora/spec/latest/chapters/cmdbuffers.html#VUID-vkQueueSubmit-pSignalSemaphores-00067) validation layer: vkQueueSubmit(): pSubmits[0].pSignalSemaphores[0] (VkSemaphore 0x170000000017) is being signaled by VkQueue 0x14bd768b460, but it may still be in use by VkSwapchainKHR 0x30000000003. Here are the most recently acquired image indices: 2, 0, 2, 1, 0, [1], 2, 0. (brackets mark the last use of VkSemaphore 0x170000000017 in a presentation operation) Swapchain image 1 was presented but was not re-acquired, so VkSemaphore 0x170000000017 may still be in use and cannot be safely reused with image index 0. Vulkan insight: One solution is to assign each image its own semaphore. Here are some common methods to ensure that a semaphore passed to vkQueuePresentKHR is not in use and can be safely reused: a) Use a separate semaphore per swapchain image. Index these semaphores using the index of the acquired image. b) Consider the VK_KHR_swapchain_maintenance1 extension. It allows using a VkFence with the presentation operation. The Vulkan spec states: Each binary semaphore element of the pSignalSemaphores member of any element of pSubmits must be unsignaled when the semaphore signal operation it defines is executed on the device (https://vulkan.lunarg.com/doc/view/1.4.321.1/windows/antora/spec/latest/chapters/cmdbuffers.html#VUID-vkQueueSubmit-pSignalSemaphores-00067) validation layer: vkQueueSubmit(): pSubmits[0].pSignalSemaphores[0] (VkSemaphore 0x140000000014) is being signaled by VkQueue 0x14bd768b460, but it may still be in use by VkSwapchainKHR 0x30000000003. Here are the most recently acquired image indices: 1, 0, 1, 2, 0, [2], 1, 0. (brackets mark the last use of VkSemaphore 0x140000000014 in a presentation operation) Swapchain image 2 was presented but was not re-acquired, so VkSemaphore 0x140000000014 may still be in use and cannot be safely reused with image index 0. Vulkan insight: One solution is to assign each image its own semaphore. Here are some common methods to ensure that a semaphore passed to vkQueuePresentKHR is not in use and can be safely reused: a) Use a separate semaphore per swapchain image. Index these semaphores using the index of the acquired image. b) Consider the VK_KHR_swapchain_maintenance1 extension. It allows using a VkFence with the presentation operation. The Vulkan spec states: Each binary semaphore element of the pSignalSemaphores member of any element of pSubmits must be unsignaled when the semaphore signal operation it defines is executed on the device (https://vulkan.lunarg.com/doc/view/1.4.321.1/windows/antora/spec/latest/chapters/cmdbuffers.html#VUID-vkQueueSubmit-pSignalSemaphores-00067) validation layer: vkQueueSubmit(): pSubmits[0].pSignalSemaphores[0] (VkSemaphore 0x170000000017) is being signaled by VkQueue 0x14bd768b460, but it may still be in use by VkSwapchainKHR 0x30000000003. Here are the most recently acquired image indices: 2, 0, 2, 1, 0, [1], 2, 0. (brackets mark the last use of VkSemaphore 0x170000000017 in a presentation operation) Swapchain image 1 was presented but was not re-acquired, so VkSemaphore 0x170000000017 may still be in use and cannot be safely reused with image index 0. Vulkan insight: One solution is to assign each image its own semaphore. Here are some common methods to ensure that a semaphore passed to vkQueuePresentKHR is not in use and can be safely reused: a) Use a separate semaphore per swapchain image. Index these semaphores using the index of the acquired image. b) Consider the VK_KHR_swapchain_maintenance1 extension. It allows using a VkFence with the presentation operation. The Vulkan spec states: Each binary semaphore element of the pSignalSemaphores member of any element of pSubmits must be unsignaled when the semaphore signal operation it defines is executed on the device (https://vulkan.lunarg.com/doc/view/1.4.321.1/windows/antora/spec/latest/chapters/cmdbuffers.html#VUID-vkQueueSubmit-pSignalSemaphores-00067) validation layer: vkQueueSubmit(): pSubmits[0].pSignalSemaphores[0] (VkSemaphore 0x140000000014) is being signaled by VkQueue 0x14bd768b460, but it may still be in use by VkSwapchainKHR 0x30000000003. Here are the most recently acquired image indices: 1, 0, 1, 2, 0, [2], 1, 0. (brackets mark the last use of VkSemaphore 0x140000000014 in a presentation operation) Swapchain image 2 was presented but was not re-acquired, so VkSemaphore 0x140000000014 may still be in use and cannot be safely reused with image index 0. Vulkan insight: One solution is to assign each image its own semaphore. Here are some common methods to ensure that a semaphore passed to vkQueuePresentKHR is not in use and can be safely reused: a) Use a separate semaphore per swapchain image. Index these semaphores using the index of the acquired image. b) Consider the VK_KHR_swapchain_maintenance1 extension. It allows using a VkFence with the presentation operation. The Vulkan spec states: Each binary semaphore element of the pSignalSemaphores member of any element of pSubmits must be unsignaled when the semaphore signal operation it defines is executed on the device (https://vulkan.lunarg.com/doc/view/1.4.321.1/windows/antora/spec/latest/chapters/cmdbuffers.html#VUID-vkQueueSubmit-pSignalSemaphores-00067) validation layer: vkQueueSubmit(): pSubmits[0].pSignalSemaphores[0] (VkSemaphore 0x170000000017) is being signaled by VkQueue 0x14bd768b460, but it may still be in use by VkSwapchainKHR 0x30000000003. Here are the most recently acquired image indices: 2, 0, 2, 1, 0, [1], 0, 2. (brackets mark the last use of VkSemaphore 0x170000000017 in a presentation operation) Swapchain image 1 was presented but was not re-acquired, so VkSemaphore 0x170000000017 may still be in use and cannot be safely reused with image index 2. Vulkan insight: One solution is to assign each image its own semaphore. Here are some common methods to ensure that a semaphore passed to vkQueuePresentKHR is not in use and can be safely reused: a) Use a separate semaphore per swapchain image. Index these semaphores using the index of the acquired image. b) Consider the VK_KHR_swapchain_maintenance1 extension. It allows using a VkFence with the presentation operation. The Vulkan spec states: Each binary semaphore element of the pSignalSemaphores member of any element of pSubmits must be unsignaled when the semaphore signal operation it defines is executed on the device (https://vulkan.lunarg.com/doc/view/1.4.321.1/windows/antora/spec/latest/chapters/cmdbuffers.html#VUID-vkQueueSubmit-pSignalSemaphores-00067) validation layer: vkQueueSubmit(): pSubmits[0].pSignalSemaphores[0] (VkSemaphore 0x140000000014) is being signaled by VkQueue 0x14bd768b460, but it may still be in use by VkSwapchainKHR 0x30000000003. Here are the most recently acquired image indices: 1, 0, 1, 0, 2, [0], 1, 2. (brackets mark the last use of VkSemaphore 0x140000000014 in a presentation operation) Swapchain image 0 was presented but was not re-acquired, so VkSemaphore 0x140000000014 may still be in use and cannot be safely reused with image index 2. Vulkan insight: One solution is to assign each image its own semaphore. Here are some common methods to ensure that a semaphore passed to vkQueuePresentKHR is not in use and can be safely reused: a) Use a separate semaphore per swapchain image. Index these semaphores using the index of the acquired image. b) Consider the VK_KHR_swapchain_maintenance1 extension. It allows using a VkFence with the presentation operation. The Vulkan spec states: Each binary semaphore element of the pSignalSemaphores member of any element of pSubmits must be unsignaled when the semaphore signal operation it defines is executed on the device (https://vulkan.lunarg.com/doc/view/1.4.321.1/windows/antora/spec/latest/chapters/cmdbuffers.html#VUID-vkQueueSubmit-pSignalSemaphores-00067) validation layer: (Warning - This VUID has now been reported 10 times, which is the duplicated_message_limit value, this will be the last time reporting it). vkQueueSubmit(): pSubmits[0].pSignalSemaphores[0] (VkSemaphore 0x170000000017) is being signaled by VkQueue 0x14bd768b460, but it may still be in use by VkSwapchainKHR 0x30000000003. Here are the most recently acquired image indices: 0, 2, 0, 1, 2, [1], 0, 2. (brackets mark the last use of VkSemaphore 0x170000000017 in a presentation operation) Swapchain image 1 was presented but was not re-acquired, so VkSemaphore 0x170000000017 may still be in use and cannot be safely reused with image index 2. Vulkan insight: One solution is to assign each image its own semaphore. Here are some common methods to ensure that a semaphore passed to vkQueuePresentKHR is not in use and can be safely reused: a) Use a separate semaphore per swapchain image. Index these semaphores using the index of the acquired image. b) Consider the VK_KHR_swapchain_maintenance1 extension. It allows using a VkFence with the presentation operation. The Vulkan spec states: Each binary semaphore element of the pSignalSemaphores member of any element of pSubmits must be unsignaled when the semaphore signal operation it defines is executed on the device (https://vulkan.lunarg.com/doc/view/1.4.321.1/windows/antora/spec/latest/chapters/cmdbuffers.html#VUID-vkQueueSubmit-pSignalSemaphores-00067) 。
解决经历
我尝试过编写如下的输出信息进行古法调试,发现两个有趣的现象。一是imageIndex acquired在正常运行时,是0和1相互跳转,跟current frame数值是一样的,而一旦acquired的跟前一帧一样,整个程序就崩溃了。二是当我开启这一长段调试信息时,窗口的“存活时间”长了一点。
// std::cout << "--- Frame " << totalFramesRendered << " ---" << std::endl;
// std::cout << "currentFrame: " << currentFrame << std::endl;
// std::cout << "imageIndex acquired: " << imageIndex << std::endl;
// std::cout << "Using imageAvailableSemaphore: " << (void*)imageAvailableSemaphores[currentFrame] << std::endl;
// std::cout << "Using renderFinishedSemaphore: " << (void*)renderFinishedSemaphores[currentFrame] << std::endl;
// std::cout << "Using inFlightFence: " << (void*)inFlightFences[currentFrame] << std::endl;
// std::cout << "Checking inFlightFences[" << imageIndex << "]: " << (void*)inFlightFences[imageIndex] << std::endl;
事实上第二个现象更加说明这是个竞态条件问题,很有可能是CPU跑得太快了,迅速地完成了两帧的提交,立刻开始准备第3帧时,GPU还没来得及完成第1帧的显示,导致信号量冲突。加入调试信息输出以后,CPU被拖慢了一些,反而短暂避开了这个问题。
经提示我在 drawFrame() 的首行使用 vkDeviceWaitIdle(logicDevice) 进行调试,用以强制CPU等待直至GPU操作全部完成。结果是——程序没有崩溃,运行得非常好。这更加说明现在面临的时同步竞态问题。
根据验证层的提示,我的代码(和参考)只用了两个 renderFinishedSemaphores,但交换链有三个图像。在drawFrame()中,用的是currentFrame % 2循环信号量,但vkAcquireNextImageKHR 返回图像索引(0、1、2)。这意味着信号量可能在新图像的vkQueueSubmit中被重用,而仍绑定到先前的vkQueuePresentKHR,引发竞态(VUID-vkQueueSubmit-pSignalSemaphores-00067)。
以下是我的更改:在 createSyncObjects() 中,将 renderFinishedSemaphores 的大小设为交换链图像数,即uint32_t imageCount = static_cast<uint32_t>(swapChainImages.size());
renderFinishedSemaphores.resize(imageCount);// imageCount- MAX_FRAMES_IN_FLIGHT
for (size_t i = 0; i < imageCount; i++) {
vkCreateSemaphore(logicDevice, &semaphoreInfo, nullptr, &renderFinishedSemaphores[i]);
}
在 drawFrame() 中,
VkSemaphore signalSemaphores[] = {renderFinishedSemaphores[imageIndex]}; // imageIndex - currentFrame
结果程序跑通了,窗口没崩溃,甚至连警告都没了。此操作通过为每个交换链图像分配唯一的 renderFinishedSemaphores[imageIndex],确保在对应图像完全呈现并重新获取之前不重用任何信号量,从而消除了竞态条件。
个人认为,imageAvailableSemaphores 由 vkAcquireNextImageKHR 确认信号。因为 acquire 是 CPU 侧的顺序操作,用 currentFrame 本身是合理的,也确保当前帧的 acquire 不与上一帧冲突。 不过对 renderFinishedSemaphores 来说,教程可能是假设 swapchain images 数量约等于 MAX_FRAMES_IN_FLIGHT,重用这个变量的风险低。不过 Swapchain images 通常是 3 个(minImageCount +1),而 frames=2。所以 imageIndex(0,1,2 循环)比 currentFrame(0,1 循环)快,结果是同一个 renderFinishedSemaphores[0] 可能被重用于新 image,而它还被显示占用。参考代码没崩溃可能是因为时序更紧密?

Comments NOTHING