系统与设计
的意见

评估同步和Graphics-Compute-Graphics危害

挑战保持GPU忙于在缩减如果帧速率优化利用目标是打击。

受欢迎程度

在现代渲染环境,有很多情况下,使用计算工作负载在一个框架。计算是通用的(非固定函数)在GPU并行编程,常用的技术,要么是具有挑战性的,完全不可能的,或者仅仅是低效率的实现与标准图形管道(顶点/几何/镶嵌/光栅/片段)。一般来说,计算提供了近乎绝对的灵活性在实施技术。然而,这种一般性介绍其他挑战:有更少的假设GPU可以使关于同步呈现任务,尤其当我们试图优化和保持GPU饱和与工作。

没有太多的细节,让GPU占据至关重要;事实上,它是一个最重要的表现因素。几乎毫无意义,试图micro-optimize一个应用程序如果你有时间,GPU是什么都没做——或者是很少使用的,而我们的帧速率目标尚未实现。

另一方面,这种情况改变当我们的最大帧速率的目标是:如果我们已经呈现,我们需要呈现在我们规定的最小帧时间——换句话说显示我们需要尽可能多的框架——我们应该允许闲置的GPU,花费更少的力量和排放更少的热量。然而,这不是一个借口不正确同步;小work-spikes不会最佳吸收,如果同步是不正确的,会导致不必要的FPS波动。

在凡尔康同步,从概念上讲,我们需要表达不同的操作之间的依赖关系。凡尔康非常灵活和强大的。然而,这种灵活性是一把双刃剑;任务可以成为令人生畏的同步变得复杂和冗长,而且它不是一个简单的任务对最优路径是什么原因。工具用于同步壁垒、事件、信号量、和栅栏;每一个执行的顺序操作在不同的场景中。最常见的和轻量级一个障碍,它执行一个命令指定类型的命令之前和之后的GPU。

从本质上讲,通常使用一个障碍的方法是表达一个源和目标依赖。翻译成英语,这将表达的东西如以下:

“对所有图形的命令已经记录了这一点;确保至少他们的片段步骤执行;之前开始执行顶点一步;记录的图形命令后点”

作为一个例子,这可能是一个颜色的附件作为输入使用附件另一个传球(无视这种特殊情况下可能会更好的表达subpass依赖-这只是一个例子)。

当命令缓冲区包含这一障碍然后提交到一个队列,队列中的所有命令生效,(这是后来的提示)。如果我们需要这个效果在不同的队列,正确的原始信号。如果我们需要同步等待事件在CPU上,我们将使用栅栏或事件。如果我们想要的任意的CPU / GPU之间的同步,我们将使用事件。

在我们的特定的用例中,障碍我们将使用如下:

“所有计算命令,已经记录到目前为止;确保他们完成;在开始顶点之前步骤;图形命令后记录这一点”。我们将称之为计算→图形障碍。

“对所有图形的命令已经记录了这一点;确保他们的像素阶段执行完成;在开始执行;计算命令后记录这一点”。我们将称之为图形→计算障碍。

首先,选择的武器
在处理任何PowerVR平台上的性能调优时,你最好的朋友PVRTune。PVRTune绝对是我们的GPU分析应用程序,提供了一个丰富的信息。这包括所有任务执行实时GPU,主机的硬件计数器、负荷水平,处理,和许多更多。我们不能强调足够,PVRTune应该总是第一个和最后一个停止分析应用程序的设备上。它支持所有PowerVR平台,所以一定要下载PVRTune和其他免费的PowerVR工具

注意在图
对于本文,下面我们所有的示例中,为了简单起见,我们将忽略砖瓦匠任务(图中标记TN)。

砖瓦匠在这里指的是相同的处理阶段顶点的任务,这两个术语可以互换使用。

渲染器是指同样的处理阶段片段/像素任务和条件可以交替使用。

一般来说,我们在本文中是指可以外推到顶点的任务,但是顶点任务通常更容易处理,因为他们倾向于自然重叠mid-frame计算任务,通常是最困难的情况下讨论了这里与片段依赖阶段。

然而,如果你有计算→顶点(甚至顶点→计算)障碍,完全相同的解决方案可能发生类似情况与顶点的任务。

一个注意multi-buffering
在台式电脑,双缓冲(使用两个framebuffer图像在你的交换链)现在似乎是常态。

然而,实际上双缓冲不会让你从不同的帧在移动设备上重叠足够的工作。PowerVR是一个基于题目延迟渲染架构和造就伟大的可以使用并行处理多个帧的能力。没有太多的细节,基于详细规范,通常是不可能的framebuffer来呈现图像,目前正呈现在屏幕上。这意味着,在任何给定的时间点,GPU只能积极呈现“免费”的形象(back-buffer)。

因此强烈推荐使用三个framebuffer映像创建交换链,特别是在垂直同步的情况下(Vsync)启用(或强制,例如在Android平台上)。该技术通常被称为triple-buffering比双缓冲,可以允许更高的性能。

triple-buffering的缺点是,它引入了一个额外的延迟而双缓冲。这可以不需要的是一些对延迟敏感的场景(竞争在这个快节奏的FPS游戏里游戏桌面电脑),但很少是一个问题在移动设备上。

然而,这不是一篇关于multi-buffering,所以我们不会进入进一步的细节,但我只想说本文假设您使用的是三个framebuffer图像利用并行性。Triple-buffering是默认如果使用PowerVR SDK和潜在的其他解决方案。

简单的情况
微不足道的(但不常见的)或许计算和图形工作负载的“高度平行”:完全相互独立的,能够完全并行执行。这将会发生在每一帧如果我们有一些不相关的任务,如呈现两种不同的工作负载的两个不同的屏幕不相互作用。没有障碍(或其他同步原语)通常需要之间发生,所以GPU可以并行,将安排他们。

调用API中,看起来像这样:

DispatchCompute→→画

在PVRTune,计算和图形任务可能会看起来像这样:

从不同的帧(数字表示任务)

计算工作量:B0 B1 B2 B3…

图形工作量:A0 A1 A2 A3…


图1:异常琐碎的工作负载。没有不同的任务的同步原语防止重叠。这是微不足道的,很少见。

这可以提供实实在在的利益:通过更多的工作安排,GPU可以隐藏延迟和提供一个良好的性能优势和执行这些任务一个接一个。然而,这种“高度平行”不是一个特别感兴趣的,并不是所有常见的。然而,它强调了原则是有用的但并不总是显而易见的:只有同步一样需要。

简单的情况
这种情况很少见,我们可以说无趣的。更常见的是一个需要计算工作量发生之前一个图形工作负载。这可能是一些顶点处理,计算扑杀,几何生成,或任何其他工作需要在之后的框架。数据通常需要在顶点着色器。在这种情况下,我们需要插入一个vkCmdPipelineBarrier vkCmdDispatchCompute和vkCmdDrawXXX,为了实现这个之前的关系。电话看起来像这样:

DispatchCompute→屏障(来源:计算,目的地:图形/顶点)→→画

或者它可能是相反的,比如执行一些计算后处理。然而,这意味着写作与计算,直接进入framebuffer通常并不理想:片段管道framebuffer写作更优化,有很多益处,比如framebuffer压缩。

画→屏障(来源:图形/片段,目的地:计算)→DispatchCompute→礼物

人们可能会错误地认为这样的发生,事实上如果我们只应用了双缓冲或某种比同步错误,我们可能会结束。

如果我们应用了双缓冲代替triple-buffering,我们将需要等待Vsync为了继续呈现,因此大量的并行处理可能会丢失。

计算工作量:B0 B1 B2 B3 B4…

图形工作量:A0 A1 A2 A3 A4…


图2:障碍计算- >顶点(橙色虚线)正确预防/ BN重叠-其他因素防止/ BN + 1重叠

注意:这个图只是病态与假设以下事实:

  • 我们使用三重缓冲
  • Vsync不是启用或我们没有达到最大fps的平台

如果我们正确同步和马克斯Vsync fps(通常60 fps),这种情况下就完全有效:如果没有足够的工作要做,GPU可以少工作困难,甚至闲置,节省电力,任务通常会自然这样的序列化。这是非常有效的,值得在这些情况下。这里的转折点是,这不应该发生在一个应用程序,该应用程序运行低于最大FPS。

在这种情况下,我们希望,如果我们triple-buffer通常发生什么和不同步,重叠要好得多。GPU应该能够安排一个帧的像素操作(N)与下一帧的计算操作(N + 1),通常不存在任何障碍会阻止。如上所述,我们需要这种并行实现了最大的性能。

计算工作量:B0 B1 B2 B3…

图形工作量:A0 A1 A2 A3…


图3:计算前图形。障碍compute-Vertex(橙色)正确预防一万亿/ BN mashup但是允许BN + 1 / TN-AN重叠。

我们需要我们的对象准备好之前我们画他们。但在很多情况下,我们不需要前一帧的渲染呈现后续帧。因此,在这些情况下,允许B GPU是免费的N + 1/一个N重叠和包的东西很不错的性能提升。

更复杂,病理情况
不幸的是,像往常一样,事情并不那么简单。当你朝着先进的多程管道,我们呈现往往是比这更复杂。即使我们完全顺序操作正确的话,我们将非常通常最后一个操作,计算调度图形操作夹在中间。例如,我们可能会使用一个计算中间框架的一些优化的模糊值计算之前使用片段着色器来做进一步的后处理和UI屏幕的合成。

鸟瞰这画的一个简单的版本流程是这样的:

画→屏障(来源:图形/片段目的地:计算)→DispatchCompute→屏障(来源:计算,目的地:图形/顶点)→→画

这些案件可能相当有问题的,原因将很快变得明显。

单帧的任务,在这种情况下,像这样:


图4:图形→计算障碍的橙色,计算→图形用绿色屏障。

看着前面的例子,你应该希望,在这一点上,你的一个1应该夹在吗0和C0与B,重叠0

不幸的是,如果用上面的障碍做基本同步“正确”,这不是你会得到什么。你会得到这相反:

计算工作负载:B1 B2 B3 B4 B5

图形工作量:A1 C1 A2 C2 A3 C3 A4 C4 A5 C5…


图5:不幸,但共同之处。一无所有与mid-frame计算任务…屏障图形→计算(橙色)防止CN重叠BN + 1和屏障计算→图形(绿色)与BN阻止一个+ 1的重叠

所以,我们看到,早期碎片的任务一帧后几乎完全取代整个前一帧。

但是为什么呢?

看着上面的工作负载中,看着壁垒答案应该变得相当明显:

(橙色)障碍A和B之间可以用英语读:“没有计算任务;后安排N计划;可以开始执行;之前N执行完毕”。嗯…这看起来合理。

B和C之间的(绿色)屏障写道:“没有图形任务;被安排在BN计划;可以开始执行;B之前N执行完毕”。这一障碍也有意义,因为我们需要CNB后开始N完成。

但是我们也会像一个N + 1(这也是一个图形任务)尽快安排;最好是尽快CN就开始了。然而,B之间的屏障N/ CN不允许这个,导致一切级联:一个N + 1B后流离失所N但由于CN已经安排,它取代了吗N + 1进一步(不同像素任务不能相互重叠),导致一个完整的序列化的所有任务。

简而言之,计算/图形障碍不允许早期图形的任务下一个框架从执行计算任务的同时(绿色箭头)。

这是坏消息。南加州大学(统一着色集群,PowerVR gpu的心脏,所有数学和计算发生)在这种情况下是非常不够的风险,必须有至少一些在这些任务之间的通信开销。可以安排的工作,越少越好利用的机会。此外,计算和图形任务通常不同的化妆,一个是内存/纹理有限和其他ALU /数学有限,和很好的候选人被安排在了同一时段。另外,如果所有的任务都是串行,微小的差距也可能是一般介绍它们之间(管道泡沫)最后v-sync进一步将加重这一问题。所有这些因素可以增加可观的性能差异,我们已经看到很多变化在这个领域;虽然大小可能不同,数字的20%是不常见的。“低”的情况下,通过更好的重叠,潜在收益5%,我们已经看到高达30%性能收益在现实的应用程序中。

所以总结:我们希望早期从第二帧(图形N + 1)之前可以安排后期的图形坐标系N (CN),这样他们执行并行计算当前帧(BN)为了达到更好的GPU的利用率。

这是你所能遇到的想法在凡尔康在处理同步。在我们检查可能的解决方案后续文章



留下一个回复


(注意:这个名字会显示公开)

Baidu