哪些阻止了 Vulkan 功能?

Which are blocking Vulkan functions?

在 Vulkan 中,建议将 API 调用分成单独的线程以获得更好的吞吐量。我不确定哪一类调用在计算上是昂贵的,会导致线程阻塞,因此应该异步使用。

在我看来,这些是可能需要很长时间才能执行的 calls/family-of-calls。

但是,我对它们的思考越多,就越觉得大多数电话都相当便宜。我会解释我的理性,这可能是有缺陷的。

vkAcquireImageKHR()

如果您选择超时,这可能会阻塞。但是,充分优化的应用程序很可能会以 0 超时调用此函数,如果图像尚不可用,则只做其他工作。所以,这个功能可以即时实现。如果应用足够智能,无需等待。

vkQueueSubmit()

此函数采用栅栏,当 GPU 完成执行命令缓冲区时将发出信号。因此,它实际上并没有在 GPU 执行工作时等待。我假设这个函数是启动命令缓冲区数据到 GPU 的物理移动的函数,但我假设它告诉硬件从某个内存位置读取,然后函数 returns 作为尽快。因此,当命令缓冲区发送到 GPU 时,它不会等待。

vkQueuePresentKHR()

向 GPU 发出信号,将一些图像发送到 window/monitor。不用等太久吧?

memcpy 到映射内存

这可能很慢。

vkCmd* 调用

这一类电话是我最不确定的。当我读到线程和 Vulkan 时,通常是这些调用被放到线程上。但是,这些电话到底在做什么?他们是否正在构建一些由一些整数和指针组成的操作码缓冲区,以发送到 GPU?如果是这样,那应该是非常快的。实际工作是执行那些操作码描述的操作。

定义“块”。 traditional definition of "block"ing 是等待一些内部同步,因此花费的时间比操作严格需要的时间长。做 memcpy 并不是做任何同步;它只是复制数据。

所以你似乎并不担心“阻塞”;您只是在谈论哪些操作 昂贵

vkQueueSubmit 不阻塞。但这并不意味着它不昂贵。它不是“告诉[ing]硬件从某个内存位置读取”只要看看它的界面。它不需要一个命令缓冲区;它需要任意数量的,它们被分成批次,每个批次在执行前等待信号量,执行后发出信号量,整个操作发出栅栏信号。

你不能合理地期望这样的事情的实现只是复制一些指针。

这甚至没有涉及不同类型的命令缓冲区的问题。提交 SIMULTANEOUS_USE 命令缓冲区可能需要创建其缓冲数据的临时副本,以便不同的批次可以包含相同的命令缓冲区。

现在显然,vkQueueSubmit 将在它提交的任何工作实际执行之前 return 完成。但是不要错误地认为将工作交给 GPU 是免费的。 Vulkan 规范在注释中花时间直接告诉您不要更频繁地调用该函数:

Submission can be a high overhead operation, and applications should attempt to batch work together into as few calls to vkQueueSubmit as possible.

在提交生成正在呈现的图像的 CB 的同一线程上呈现的原因并不是因为这些操作中的任何一个都必然很慢。这是为了简单的实用主义;这三个操作(获取、提交、呈现)必须按顺序发生。确保这一点的最简单和最简单的方法是在同一个线程上执行它们。

在获得交换链图像之前,您不能提交渲染到交换链图像的作品。因此,要么你在同一个线程上做,要么你必须有一些线程间通信管道来告诉等待构建主 CB 的线程获取的图像是什么。这两个过程不能重叠。

与acquire不同,present是队列操作。 vkQueueSubmitvkQueuePresent 都要求对其 VkQueue 参数的访问必须“外部同步”。这当然意味着您不能同时在同一个 VkQueue 上从不同的线程调用它们。因此,如果您尝试并行执行这些操作,则需要一个互斥体或其他东西来同步 CPU 对 VkQueue.

的访问

而如果您在同一个线程上执行它们,则没有必要。

此外,为了呈现图像,您必须提供呈现将等待的信号量。该信号量将由为图像生成数据的批处理发出信号。 Vulkan 要求信号量 signal/wait 对 有序 ;您不能执行等待信号量的队列操作,直到发出信号量信号的操作已提交。因此,您要么按顺序在同一个线程上执行,要么使用一些线程间通信管道来告知正在等待呈现图像的线程已经发出了呈现给它的提交操作。

那么将这些操作拆分到不同的线程上有什么好处呢?它们 必须 按顺序发生,因此您最好按现有最简单的方法按顺序进行:在同一个线程上。

虽然时间线信号量现在允许您在提交递增信号量计数器的工作之前调用当前函数,但您仍然不能在单独的线程(没有同步)上调用它们,因为它们影响同一个队列。所以你也可以在同一个线程上发布它们(尽管不一定是获取、提交、呈现的顺序)。

最终,不清楚这个练习的意义是什么。是的,单个 vkCmd* 调用会非常快。所以呢?在真实场景中,您将每帧调用这些函数 次。将它们平均分布在 4 个内核上可为您节省约 4 倍的性能。