Vulkan:vkQueuePresentKHR 做了啥?

Posted

技术标签:

【中文标题】Vulkan:vkQueuePresentKHR 做了啥?【英文标题】:Vulkan: What did vkQueuePresentKHR do?Vulkan:vkQueuePresentKHR 做了什么? 【发布时间】:2019-10-30 16:37:40 【问题描述】:
void drawFrame() 

    vkWaitForFences(device, 1, &inFlightFences[currentFrame], VK_TRUE, std::numeric_limits<uint64_t>::max());

    ...

    VkSemaphore signalSemaphores[] = renderFinishedSemaphores[currentFrame];
    submitInfo.signalSemaphoreCount = 1;
    submitInfo.pSignalSemaphores = signalSemaphores;

    vkQueueSubmit(graphicsQueue, 1, &submitInfo, inFlightFences[currentFrame])

    ...

    VkSwapchainKHR swapChains[] = swapChain;

    VkPresentInfoKHR presentInfo = ;
    presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
    presentInfo.waitSemaphoreCount = 1;
    presentInfo.pWaitSemaphores = signalSemaphores;
    presentInfo.swapchainCount = 1;
    presentInfo.pSwapchains = swapChains;
    presentInfo.pImageIndices = &imageIndex;

    vkQueuePresentKHR(presentQueue, &presentInfo); // <- vkQueuePresentKHR
    ...

    currentFrame = (currentFrame + 1) % MAX_FRAMES_IN_FLIGHT;

第一帧vkQueueSubmit提交后调用vkQueuePresentKHR进行presont,第二帧调用vkWaitForFences等待上一帧的vkQueueSubmit结束再调用vkQueueSubmit提交,但此时第一帧的vkQueuePresentKHR 可能在渲染中,它占用了第一帧的Framebuffer。那么第二帧的vkQueueSubmit会和第一帧的vkQueuePresentKHR冲突吗?

我认为可能有两种情况:

1:第一帧和第二帧不占用同一个Framebuffer,当第一帧vkQueueSubmit结束时,数据已经复制到了新的内存中。 vkQueuePresentKHR 将数据呈现在新内存中,因此将第二帧渲染到 Framebuffer 不会影响第一帧的内容。

2:第一帧和第二帧占用同一个Framebuffer,当第一帧的vkQueuePresentKHR和第二帧的vkQueueSubmit同时使用Framebuffer时,会出现画面撕裂(我猜的)。

我认为第二种可能性会更高。

我想将结果渲染到我自己的记忆中,而不是在屏幕上显示。我想弄清楚 vkQueuePresentKHR 是做什么的。

这里是渲染到自己内存的例子:https://github.com/SaschaWillems/Vulkan/blob/master/examples/renderheadless/renderheadless.cpp

在本例中,渲染的 Framebuffer 数据被传输到新的内存中。

在我设计的程序中,有一个辅助线程负责等待Fence完成。一旦传输完成,就会使用此内存。类似于下面的例子:

主线程:

void drawFrame() 

    vkWaitForFences(device, 1, &inFlightFences[currentFrame], VK_TRUE, std::numeric_limits<uint64_t>::max());

    ...

    VkSemaphore signalSemaphores[] = renderFinishedSemaphores[currentFrame];
    submitInfo.signalSemaphoreCount = 1;
    submitInfo.pSignalSemaphores = signalSemaphores;

    // VkQueueSubmit(graphicsQueue, 1, &submitInfo, inFlightFences[currentFrame]) cannot be executed before the execution of 
    // vkQueueSubmit(graphicsQueue, 1, &transferSubmitInfo, transferCompleteFences[currentFrame]) in the previous frame. Otherwise,
    // they will occupy the same FrameBuffer. I think it is not possible.
    transferCompleteMutexs[currentFrame].lock();                                                // <- lock transferCompleteMutexs

    vkQueueSubmit(graphicsQueue, 1, &submitInfo, inFlightFences[currentFrame])

    ...

    VkSubmitInfo transferSubmitInfo = ;
    transferSubmitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
    transferSubmitInfo.commandBufferCount = 1;
    transferSubmitInfo.pCommandBuffers = &commandBuffer;
    transferSubmitInfo.waitSemaphoreCount = 1;
    transferSubmitInfo.pWaitSemaphores = signalSemaphores;
    vkQueueSubmit(graphicsQueue, 1, &transferSubmitInfo, transferCompleteFences[currentFrame]) // Signal <- transferCompleteFences
    ...

次要线程:

while(true) 
    // Waiting for transfer to complete
    vkWaitForFences(logicalDevice_->vkLogicalDevice(),
                    1,
                    &transferCompleteFences[currentFrame],                         // Wait <- transferCompleteFences
                    VK_TRUE,
                    std::numeric_limits<uint64_t>::max());

    // using transfer completed data 
    ...

    // Secondary thread knows that the current frame is transmitted. The next Primary thread can be submitted.
    transferCompleteMutexs[currentFrame].unlock();                                // <- unlock transferCompleteMutexs

上面的代码使用transferCompleteMutexs来保护FrameBuffer,我认为这是必要的。但显然 vkQueuePresentKHR 并没有这样做。我是否有更好的方法来模拟 vkQueuePresentKHR 以将数据呈现到我自己的内存中,或者我现在这样做是否正确?

【问题讨论】:

"那么第二帧的 vkQueueSubmit 会和第一帧的 vkQueuePresentKHR 冲突吗?" 这完全取决于你提交的内容。既然你不说,这个问题就没法回答了。 【参考方案1】:

第二帧的vkQueueSubmit不能与第一帧的vkQueuePresent“冲突”。

在第二帧中,您还可以执行vkAcquireNextImage,它会给出一个信号量。如果信号量已发出信号,则表示引擎不再使用该图像(无论它是否与前一帧的图像相同)。

vkQueueSubmit 可能与之前的vkQueueSubmit 以及使用的各种资源发生冲突。

vkQueuePresent 所做的是在返回之前提交您的信号量等待执行。在信号量发出信号之前,它不会访问图像。它将从 Vulkan 队列中异步呈现 Image 中的图片。其他任何内容都未指定。图像可以被复制、即时复制或直接使用,或任何其他符合上述 API 合同的内容。

【讨论】:

谢谢,我明白了,那么vulkan可以产生OpenGL垂直同步画面撕裂吗? VkPresentModeKHR

以上是关于Vulkan:vkQueuePresentKHR 做了啥?的主要内容,如果未能解决你的问题,请参考以下文章

[学习Vulkan之一] 初识Vulkan

Vulkan Tutorial 02 编写Vulkan应用程序框架原型

Vulkan Tutorial 02 编写Vulkan应用程序框架原型

初识Vulkan

如果设备不支持vulkan,就用swiftshader,否则就加载系统的vulkan的正确姿势(让程序能够智能的在vulkan-1.dll和libvk_swiftshader.dll之间切换)

初识Vulkan