我如何与Metal沟通,以避免GPU和CPU之间的数据冲突
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了我如何与Metal沟通,以避免GPU和CPU之间的数据冲突相关的知识,希望对你有一定的参考价值。
因此,当涉及到ios的图形时,共享内存模型调节在图形应用程序中访问内存的方式缓冲是一个重要的概念。
我们的想法是,您缓存正在更新每一帧的数据,这种方式是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确保不会写入大量数据?对齐时我需要做些什么吗?他们是否锁定对某些大小的内存或某些内容的访问?
为了使这个问题更加复杂,想象一下我在一个缓冲区内存储了120帧移动的1000个不同对象的路径。有几种方法可以通过在缓冲区内以不同方式布局数据来实现此目的。例如,我可以这样做
----- ----- -----
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何时/何时决定在共享数据时等待。
我如何与Metal沟通,确保在GPU尝试读取时,CPU确保不会写入大量数据?对齐时我需要做些什么吗?他们是否锁定对某些大小的内存或某些内容的访问?
您无需将任何此类信息传达给Metal API。 Metal为程序员增加了负担,以确保您不会修改GPU尚未消耗的数据。
样本中的典型方法(例如XCode中的默认iOS Metal Game项目,或描述here的示例)是创建信号量并定义MaxBuffersInFlight
。在填写命令缓冲区并在帧命令缓冲区完成处理程序中发出信号之前,等待信号量。
凭借只有MaxBuffersInFlight
将在飞行中的知识,你可以自由地管理任何双/三/四/任何缓冲方案将避免写入数据。
您没有在图形时间轴调试器中看到任何并发(正如您在注释中提到的那样)可能是一个单独的错误。也许你的渲染循环代码中存在问题。或者也许你的CPU工作是如此微不足道,以至于它总是在GPU上等待(反之亦然)。
以上是关于我如何与Metal沟通,以避免GPU和CPU之间的数据冲突的主要内容,如果未能解决你的问题,请参考以下文章