【问题标题】:When is an MTLFence or MTLEvent required for synchronization between command encoders?命令编码器之间的同步何时需要 MTLFence 或 MTLEvent?
【发布时间】:2019-05-03 21:19:49
【问题描述】:

注意:在许多方面,这是How do you synchronize a Metal Performance Shader with an MTLBlitCommandEncoder? 的后续行动@

当顺序命令编码器之间需要显式同步以及由于 Metal 的架构而不需要同步时,我仍然有点困惑。

在上面链接的问题中,引用了 Apple 的文档:

内存屏障

命令编码器之间

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

我将此解释为暗示MTLRenderCommandEncoder 不需要与先前的MTLBlitCommandEncoder 显式同步,如果它们都在同一个命令缓冲区中并且一个接一个地出现。

然而,Apple 自己的示例代码似乎与此相矛盾。在Image Filter Graph with Heaps and Fences 中,表明需要MTLFence 来同步对首先在MTLBitCommandEncoder 中使用的纹理的访问,然后是两个连续的MTLComputeCommandEncoder 调用。 (一个用于水平模糊,另一个用于垂直模糊。)

See:
AAPLFilter.m (L:199)
AAPLRenderer.m (L:413)

这些命令编码器在同一个命令缓冲区中执行。为什么第一个MTLComputeCommandEncoder 需要显式等待blit 完成,为什么第二个计算编码器需要等待第一个计算编码器,如果如上所述,“在给定命令编码器中执行的所有资源写入都是在下一个命令编码器中可见。”?

伪示例代码:

- (void)drawInMTKView:(nonnull MTKView *)view {

  id <MTLCommandBuffer> commandBuffer = [_commandQueue commandBuffer];

  id<MTLTexture> masterTexture = self.masterTexture;
  id<MTLTexture> incomingTexture = [self dequeueRenderedTextureIfPresent];

  id<MTLBlitCommandEncoder> blitEncoder = commandBuffer.blitCommandEncoder;
  [blitEncoder copyFromTexture:incomingTexture ... toTexture:masterTexture];
  [blitEncoder endEncoding];

  id <MTLRenderCommandEncoder> renderEncoder = [commandBuffer renderCommandEncoderWithDescriptor];

  // Is synchronization with the blit encoder required here? 
  // 
  // The fragment shader is going to sample from masterTexture and will
  // expect that the blit command above will have been completed.

  [renderEncoder setFragmentTexture:masterTexture atIndex:0];

  [renderEncoder drawPrimitives:...];
  [commandBuffer commit];
}

在上面的伪代码中,渲染命令编码器是否必须显式等待 blit 命令编码器完成?在我的previous question 的回答中,我相信答案是“不”。但是查看 Apple 使用栅栏和事件的示例代码,我相信答案是“是”。

如果不需要同步,那么这个伪代码和苹果的示例代码有什么不同呢?

编辑#1:

感谢 Ken 在下面的回答,我很快在 Apple 的开发者论坛上找到了一个相关主题,其中涵盖了这个确切的问题。

苹果开发者论坛:MTLFence detailed behaviour?

正如 Ken 正确指出的那样,要理解的关键细节是跟踪纹理和未跟踪纹理之间的区别。

【问题讨论】:

    标签: macos metal metalkit


    【解决方案1】:

    只有 Metal 不会自动跟踪的资源才需要这种手动同步。从MTLHeaps 分配的资源不会自动跟踪。使用 MTLResourceHazardTrackingModeUntracked 选项显式创建的资源也不会被跟踪。

    来自您在Optimize Resource Allocation and Performance 下链接的带有堆和栅栏的图像过滤器图示例的概述:

    从设备分配资源时,Metal 会创建并跟踪其他状态,以确保资源内存在需要给定资源的任何命令缓冲区的整个生命周期内都得到分配、同步和可用。即使资源本身在命令缓冲区开始执行之前被销毁,它也会这样做。

    虽然 Metal 也对堆执行此过程,但它不会对堆内的资源执行此操作。相反,当应用从堆中创建对象并重用内存时,它必须执行显式的细粒度同步。

    如果资源未被跟踪,您问题中的伪代码只需要显式同步。

    【讨论】:

    • 啊,所以示例代码需要显式同步,因为纹理是从MTLHeap 分配的,因此被视为“未跟踪”。所以,我假设,使用[MTLDevice newTextureWithDescriptor:]newTextureWithDescriptor:iosurface 创建跟踪纹理并因此获得额外的状态以允许Metal 执行同步。使用[MTLTexture newTextureViewWithPixelFormat:textureType:levels:slices:] 创建的纹理视图是否被视为已跟踪或未跟踪?
    • 跟踪由非堆支持的纹理支持的纹理视图。
    • 那么这是否意味着纹理视图的跟踪模式与支持它的纹理上的跟踪模式无关? (即:纹理视图始终具有与其创建的纹理相同的跟踪模式。)
    • 我希望纹理视图没有像原始纹理这样的单独行为。它们仅引用具有不同解释的相同底层资源。
    猜你喜欢
    • 2016-06-27
    • 2016-09-25
    • 2012-01-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-07-10
    • 1970-01-01
    相关资源
    最近更新 更多