因此,对于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不会写入大块数据?我需要做些对齐工作吗?他们是否锁定对某些大小的内存的访问权限?
为了使这个问题更复杂,假设我在一个缓冲区中存储了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在共享数据时如何/何时决定等待。
答案 0 :(得分:2)
我如何与Metal沟通,以确保在GPU尝试读取数据时不会由CPU写入大量数据?我需要做些对齐工作吗?他们是否锁定对某些大小的内存的访问权限?
您不需要将任何此类信息传达给Metal API。 Metal给程序员造成了负担,以确保您不修改GPU尚未消耗的数据。
示例(例如XCode中默认的iOS Metal Game项目或here描述的示例)中的典型方法是创建信号量并定义MaxBuffersInFlight
。等待信号量,然后填充命令缓冲区,并在帧命令缓冲区完成处理程序中发出信号。
了解到只有MaxBuffersInFlight
可以使用时,由您来决定采用哪种双倍/三倍/四倍/任何缓冲方案来避免写入数据。
您在图形时间轴调试器中看不到任何并发性(如您在评论中所提到的)可能是一个单独的错误。也许您在渲染循环代码中遇到了问题。或者,也许您的CPU工作非常琐碎,以至于总是在GPU上等待(反之亦然)。