Vulkan API:将rending工作移动到自己的线程中

时间:2018-02-05 01:56:30

标签: vulkan

我正在学习Khronos Vulkan API,而且我很难在多个线程中同步工作。

我试图将渲染工作分解为自己的线程。我的想法是有一个主线程,它将负责高级应用程序管理的东西,更重要的是,它将调用VkQueuePresentKHR()来呈现表面。但是,实际的VkCmd *渲染工作将在专用线程上完成。

我希望我的应用程序使用三重缓冲,因为对我来说,拥有平滑的60fps非常重要。在视频演示中,NVIDIA提到您希望每个帧缓冲区有1个VkCommandPool,并在每次使用后清除整个池;这比尝试清理单个命令缓冲区更有效。我更进了一步:我试图将渲染工作分解为6个队列:

  • 3个图形队列:每个帧都有自己的专用队列。
  • 1表示。
  • 1用于misc一次性图形命令。
  • 1用于专门的转移工作

我的GTX770显卡有16个通用队列+ 1个专用传输队列,所以这应该没问题。每个队列都有自己的VkCommandPool。 Queues,CommandPool和CommandBuffer对象在主线程中创建,然后在Render线程中使用。

enter image description here

我遇到了以下问题:

  • 在Render线程中调用VkAcquireNextImageKHR。如果线程获取3个图像,那么我不能再次调用该函数并依赖它来阻止直到下一个图像可用。相反,我得到了一个验证层错误,我已经抓住了所有可用的图像。而且,无论如何,我更愿意检测到当前没有可用的图像,以便我可以在线程中执行其他工作,例如对内存进行碎片整理。我现在如何知道当前所有图像都已获得,并且我应该打电话给vkAcquireNextImageKHR。
  • 如何确定vkQueuePresentKHR()是否完成呈现,以及我是否可以发送另一帧?我不知道如何判断我何时可以再次使用该帧缓冲。
  • 在vkQueueSubmit()返回后立即清除CommandPool是不安全的,对吗?要清除CommandPool,我是否需要监视为vkQueueSubmit()提供的围栅?通过阅读规范,我并不完全清楚。
  • 我知道从多个线程弄乱CommandPool是不安全的,但是可以从一个线程使用多个Queues / CommandPools吗?
  • 我可能做错了,但看起来在某些围栏上调用vkResetFence()之后,你就不能在其上调用vkGetFenceStatus(),否则你会得到验证层错误。它是否正确?

1 个答案:

答案 0 :(得分:4)

  1. vkAcquireNextImageKHR获取图片的所有权,vkQueuePresentKHR将其所有权发布回演示引擎。 PE需要拥有当前显示的图像,有时还需要拥有其他图像。获取将阻止,直到图像可用或超时到期。当您无法依赖Acquire返回时,您会收到验证错误,因为您没有提供足够的图像。规范说:

      

    n 为交换链中的图片总数, m VkSurfaceCapabilitiesKHR::minImageCount的值, a 为应用程序当前获取的可呈现图像的数量(即使用vkAcquireNextImageKHR获取但尚未显示vkQueuePresentKHR的图像)。如果在调用vkAcquireNextImageKHR a n - m vkAcquireNextImageKHR总能成功。如果 a >,则不应调用vkAcquireNextImageKHR n - m ,超时为UINT64_MAX;在这种情况下,vkAcquireNextImageKHR可能无限期阻止。

  2. 传入vkAcquireNextImageKHR的信号量/围栏将在PE完成从图像读取时发出信号。此时,您可以再次开始写入,并在准备好时将其显示出来。

  3. 只要命令池中的任何命令缓冲区仍在执行,您就无法清除它。当栅栏传递给vkQueueSubmit信号时,您知道它们何时完成执行。

  4. 是的,您可以在单个线程中使用任意类型的任意数量的对象。 Vulkan实现中的任何内容都不会绑定到它所使用的特定线程。从多个线程访问对象的限制类似于您对自己的对象所拥有的限制:某些对象在内部被序列化,因此并发访问是可以的,而其他对象则没有。

  5. 这似乎不对。您应该可以随时在任何有效的围栏上致电vkGetFenceStatus