如何与Metal交流,避免在GPU和CPU之间发生数据冲突

时间:2018-12-27 19:48:10

标签: ios memory graphics gpu metal

因此,对于iOS的图形来说,共享内存模型可调节图形应用程序中如何访问内存,缓冲是一个重要的概念。

WWDC Diagram

这个想法是,您缓存的数据将更新每一帧,以使CPU始终写入与GPU读取不同的缓冲区。然后,您等待帧完成其渲染,然后再开始写入CPU缓冲区的不同部分。

在谈论完全更新每一帧的数据时,如何实现这一点非常清楚。我的问题是如何针对历史数据执行此操作。想象一下,当某个物体在场景中移动时,我想为它存储一条路径的顶点。

然后,我将拥有一种循环缓冲区,以跟踪大小恒定的最后120帧数据,然后我可以让CPU每帧将数据写入循环缓冲区的不同部分。

----- ----- ----- ----- ----- ----- -----
 n-3   n-2   n-1    n    n+1   n+2   n-4
----- ----- ----- ----- ----- ----- -----
                               ^CPU Write

在上面的示例中,对于给定的帧,其中n表示渲染的最近部分,CPU将写入标记为n + 2的缓冲区中的斑点,GPU将在两次绘制调用中渲染n-3 -> n和n-4。从技术上讲,这可以避免CPU和GPU同时混乱同一块数据的情况,对此我感到担心。

我的问题本质上是。我如何与Metal交流,以确保在GPU尝试读取数据时,CPU不会写入大块数据?我需要做些对齐工作吗?他们是否锁定对某些大小的内存的访问权限?

为了使这个问题更复杂,假设我在一个缓冲区中存储了1000个不同对象的120帧运动轨迹。有几种方法可以通过在缓冲区内不同地布局数据来完成此操作。例如,我可以像这样

-----  -----  -----
 p1      p2    p3
-----  -----  -----

每个p块代表该粒子的120帧历史,然后我可以应用与上面相同的概念,它最多具有两个绘制调用,以避免当前写入绘制数据。

或者我可以这样布置

----- ----- ----- ----- ----- ----- -----
 n-3   n-2   n-1    n    n+1   n+2   n-4
----- ----- ----- ----- ----- ----- -----
                               ^CPU Write

n个块中每个块的数据并排。

要使事情变得更加复杂,我可以完全避免多次绘制调用,并使用索引缓冲区将其打开以进行alpha混合(对三角形绘制顺序进行排序)。索引缓冲区确实可以确保CPU和GPU在技术上不需要彼此等待。 但是他们知道吗?

当我有索引缓冲区时,我什至可以实现此优化吗?还是使内存访问变得不可预测?

我意识到这是一篇冗长的文章!主要问题以粗体显示。本质上,我只是想知道GPU和CPU在共享数据时如何/何时决定等待。

1 个答案:

答案 0 :(得分:2)

  

我如何与Metal沟通,以确保在GPU尝试读取数据时不会由CPU写入大量数据?我需要做些对齐工作吗?他们是否锁定对某些大小的内存的访问权限?

您不需要将任何此类信息传达给Metal API。 Metal给程序员造成了负担,以确保您不修改GPU尚未消耗的数据。

示例(例如XCode中默认的iOS Metal Game项目或here描述的示例)中的典型方法是创建信号量并定义MaxBuffersInFlight。等待信号量,然后填充命令缓冲区,并在帧命令缓冲区完成处理程序中发出信号。

了解到只有MaxBuffersInFlight可以使用时,由您来决定采用哪种双倍/三倍/四倍/任何缓冲方案来避免写入数据。

您在图形时间轴调试器中看不到任何并发性(如您在评论中所提到的)可能是一个单独的错误。也许您在渲染循环代码中遇到了问题。或者,也许您的CPU工作非常琐碎,以至于总是在GPU上等待(反之亦然)。