Metal - 必需品 - 将您的金属代码移植到Apple Si
创建可以在Apple 芯片和基于Intel的mac计算机上运行的metal应用程序的版本。
概述
如何将metal应用程序移植到具有Apple 芯片的mac上,取决于您的项目是否支持apple 系列GPU.
如果您的项目支持ios 或 tvos,则它还支持apple 系列GPU。如果您的代码仅适用于macos,则可能会发现apple系列GPU的行为与基于intel的macos中的GPU有所不同的情况,本文介绍了如何移植您的应用程序,使其能够在GPU系列中使用。
在移植您的应用程序之前,请在Rosetta翻译下对其进行测试。当您运行通过Rosetta转换与macos 10.15 或更早版本连接的应用程序时,metal 通过针对常见编程错误的软件变通办法来支持向后兼容性。这些变通办法将某些GPU性能转成了与基于intel的macs 更加一致的行为。为了加快修复应用程序的速度,本文介绍了您可能会常见的移植问题。对于某些问题,metal 不会自动应用解决方法,因为性能损失太大。如果您希望您的应用在翻译后运行,请首先解决这些问题。
准备好移植应用程序时,请在xcode中打开metal诊断工具以搜索代码中的问题。解决这些问题后,请按照构建通用macos binary 中的说明 使用xcode 12 重新编辑您的应用程序。然后,解决本文所述的移植问题列表,以确保您的代码解决了这些问题。一旦您的应用正确运行,就可以分析其性能并调整功能渲染策略,以利用apple 系列GPU的优势。
识别并纠正潜在的移植问题
xcode可以帮助验证您的应用是否正确使用了metal,尽管xcode找不到所有问题,但在移植应用程序之前解决常见问题可以使移植过程更容易,一旦开始移植应用程序,请使用xcode验证代码更改并了解您的代码如何在apple系列GPU上运行。
-
使用api 验证来查找常见的metal 框架编译错误。启用api验证后,如果您错误的调用metal api,xcode将在调试器中停止。有关更多信息,请参见及早诊断金属编程问题。
-
使用着色器验证可重新编译着色器,并添加其他调试功能。例如,如果您尝试访问缓冲区外部的内存,则着色器验证将在调试器中停止,并出现越界错误。有关更多信息,请参见早期诊断金属编程问题
-
使用metal debugger 捕获GPU跟踪以分析一组相关的metal调用,例如用于渲染单个动画帧的所有命令。metal debugger 从 metal api 调用中收集数据,分析生成的GPU跟踪,并提供修复错误或提高新能的建议。例如,现在游戏引擎需要数十次传递和数千个GPU命令才能渲染复杂的场景。metal debugger 可以找到通行证之间的依赖关系,并揭示错误生成或存储数据的位置。并非metal debugger 的所有建议都是您必须解决的问题。您可以决定是否对每个建议采取行动。有关更多信息,请参阅帧捕获调试工具。
测试您的应用程序使用的GPU功能
metal 将GPU功能收集到GPU系列中,因此您可以使用单个查询测试一组功能。当功能变化并且功能不足以成为特定系列的一部分时,metal 提供其他查询以获取有关GPU的详细信息。您可能需要测试多个GPU系列以及特定的metal设备对象属性,以了解GPU的功能。使用数据可以在运行时确定您的应用使用哪种策略来利用GPU。
以前,apple GPU和Mac GPU 属于不同家族,每个GPU仅支持一个家族,因此,除非您设计了跨平台应用程序,否则您仅检查单个家族的成员。带有apple 芯片的mac 中的GPU是这两个GPU家族的成员,并且支持mac家族和apple家族功能集。现在,要同时支持apple 芯片和基于图快递渲染应用特定的优化。当设备对象具有确定某个功能是否可用的特定方法或属性时,请使用该方法或属性,而不要测试GPU系列。
始终是使用可用性方法和属性来确定功能,而不依赖GPU名称或其他应编码信息。GPU名称可能会在将来的硬件上更改,因此name设备对象的属性是功能集的不可靠指标。同样,为特定GPU硬编码的值而不是使用设备查询的值可能会导致您的应用崩溃或阻止您的应用利用新功能。
有关更多信息,请参阅检测GPU功能和金属软件版本
有关特定查询的列表,请参见MTLDevice
在渲染过程中设置加载和存储操作
渲染过程中的每个渲染目标都有一个加载和存储操作,分别指示您期望纹理内容位于渲染过程的开始和结束时的内容,为macos 11 编译应用程序时,必须为渲染目标选择正确的加载和存储操作。
在基与intel的Mac 上,错误地设置这些操作通常不会产生任何效果,因为这些系统中的GPU直接渲染到内存,并且没有专的阶段在内存和GPU之间复制纹理数据,您可能在应用程序中错误地配置了这些操作。
苹果家族的GPU使用GPU内部的图块内存在渲染过程中临时保存纹理内容。加载操作确定GPU是否将纹理的现有内容复制到切片内存中,而存储操作确定是否将切片内容存储会内存中,如果您将其中一项设置为无关紧要,则apple系列GPU会跳过这些内存操作以提高新性能。如果您无意间执行了此操作,则会在渲染的内容中看到视觉暇疵。
如果您的应用程序已针对macos 10.15 或更早版本进行了连接,并且已在Rosetta 转换下运行,并且您将加载操作设置为MTLLoadAction.dontcare,则metal 会强制GPU将内容加载到切片内存中,从而使性能与基于intel的macs 更加一致。
有关加载和存储操作的更多信息,请参见设置加载和存储操作
使顶点着色器位置不变
许多渲染技术需要多次渲染遍历,其中较早的遍历生成的数据被以后的渲染遍历消耗。每遍都使用不同的顶点着色器来执行其特定的计算。此类技术通常依赖于不同的顶点着色器以相同的方式计算顶点位置。例如,早期通过可能会将深度值写入深度纹理,而后期通过可能会针对存储在深度纹理中的数据测试其计算的位置。如果两个着色器以不同的方式计算位置数据,则这些测试可能会失败。
如果您的应用程序使用了这种渲染技术,请将该invariant 属性添加到该渲染技术中使用的每个顶点着色器的位置输出中,如以下代码所示
typedef struct {
float4 position [[position,invariant]];
float4 color;
} RasterizerData;
然后,使用preserve-invariance 设置的标志编译着色器。如果改为在运行时编译着色器,请在创建MTLLibrary对象时指定preserverInvariance选项。metal 更保守地编译这些着色器,以确保GPU以相同的方式计算标记有invariant属性的位置。
当 metal 编译顶点着色器时,编译器会优化着色器的性能,而不是严格要求不同着色器之间的一致性和精度。编译器可能会发出不同的指令序列或将浮点运算合并在一起,这可能会稍微改变GPU计算每个结果的方式,此编译器行为取决于每个着色器中的完整源代码,apple系列GPU的编译器会积极进行优化,因此将位置输出标记为不可变的,否则您可能会在其它GPU上通常看不到的地方看到视觉伪像.
如果您的应用程序时针对macos10.15 或更早版本进行连接的,并且在Rosetta转换下运行,则metal会以出现invariant关键字的方式编译顶点位置,从而使性能交易行为与基于intel的macs更加一致。