6

我试图更好地理解使用Metal Performance Shaders和时的同步要求MTLBlitCommandEncoder

我有一个MTLCommandBuffer设置如下:

  • 用于MTLBlitCommandEncoder将纹理 A 的区域复制到纹理 B。纹理 A 大于纹理 B。我正在从纹理 A 中提取“图块”并将其复制到纹理 B。

  • 使用MPSImageBilinearScale金属性能着色器,纹理 B 作为源纹理,第三个纹理纹理 C 作为目标。此金属性能着色器将缩放并可能将纹理 B 的内容转换为纹理 C。

在金属性能着色器开始尝试缩放纹理 B 之前,如何确保 blit 编码器完全完成将数据从纹理 A 复制到纹理 B?我是否甚至需要担心这一点,或者命令缓冲区的串行特性是否已经为我解决了这个问题?

Metal 有栅栏的概念,MTLFence用于同步对资源的访问,但我不认为有金属性能着色器在栅栏上等待。(而waitForFence:编码器上存在。)

如果我不能使用栅栏并且我确实需要同步,推荐的做法是将 blit 编码器排入队列,然后waitUntilCompleted在将着色器排入队列并再次调用之前调用命令缓冲区waitUntilCompleted?前任:

id<MTLCommandBuffer> commandBuffer;

// Enqueue blit encoder to copy Texture A -> Texture B
id<MTLBlitCommandEncoder> blitEncoder = [commandBuffer blitCommandEncoder];
[blitEncoder copyFromTexture:...];
[blitEncoder endEncoding];

// Wait for blit encoder to complete.
[commandBuffer commit];
[commandBuffer waitUntilCompleted];

// Scale Texture B -> Texture C
MPSImageBilinearScale *imageScaleShader = [[MPSImageBilinearScale alloc] initWithDevice:...];  
[imageScaleShader encodeToCommandBuffer:commandBuffer...];

// Wait for scaling shader to complete.
[commandBuffer commit];
[commandBuffer waitUntilCompleted];

我认为我需要将中间副本复制到纹理 B 的原因是因为MPSImageBilinearScale它似乎会缩放其整个源纹理。对clipOffset输出很有用,但不适用于实际的缩放或变换。所以需要将贴图从纹理 A 中提取到与贴图本身大小相同的纹理 B 中。然后缩放和变换将“有意义”。忽略这个脚注,因为我忘记了一些基本的数学原理,并且已经弄清楚如何使缩放变换的平移属性与 clipRect 一起使用。

4

2 回答 2

5

Metal 会为您解决这个问题。驱动程序和 GPU 在命令缓冲区中执行命令,就像以串行方式一样。(“好像”允许并行运行或乱序运行以提高效率,但前提是结果与串行运行时相同。)

当 CPU 和 GPU 都在处理相同的对象时,会出现同步问题。还可以在屏幕上呈现纹理。(您不应该渲染到屏幕上呈现的纹理。)

Metal Programming Guide的一部分处理了着色器对资源的读写访问,这并不完全相同,但应该让您放心:

内存屏障

命令编码器之间

在给定命令编码器中执行的所有资源写入在下一个命令编码器中可见。对于渲染和计算命令编码器都是如此。

在渲染命令编码器内

对于缓冲区,原子写入对跨多个线程的后续原子读取是可见的。

对于纹理,该textureBarrier方法确保在给定绘制调用中执行的写入对下一次绘制调用中的后续读取可见。

在计算命令编码器中

在给定内核函数中执行的所有资源写入在下一个内核函数中可见。

于 2018-08-24T18:44:01.367 回答
0

MPS 位于 Metal 之上(大部分情况下)。它不会取代它(大部分)。您可能会假设它正在使用您正在使用的常用命令编码器。

有几个领域需要 MTLFences,尤其是在与渲染编码器和 MTLHeaps 互操作时。如果可用,请使用 MPSImages 和缓冲区类型的同步方法,而不是自己滚动。

于 2020-06-25T14:57:57.067 回答