大多数浏览器和
Developer App 均支持流媒体播放。
-
利用 MetalFX Upscaling 提升性能
了解 MetalFX,它是为 Metal App 提供平台优化图形效果的全新 API。借助 MetalFX Upscaling,您的 App 现在能够以较低分辨率对帧进行渲染,缩短渲染时间,而且不会牺牲渲染质量。我们还将向您介绍如何以及何时使用它的两种效果,分别是空间放大 (实质性性能提升) 和时态反锯齿及放大 (最优质渲染)。
资源
相关视频
Tech Talks
WWDC23
WWDC22
-
下载
Kelvin Chiu: 大家好 欢迎 我是 Kelvin Chiu 来自 Apple GPU Software 团队 今天 我来谈下如何 用 MetalFX Upscaling 功能 提高您的 Metal 应用性能 MetalFX 是一个新的 API 为 Metal 应用 提供平台优化的图形效果 它通过高性能放大 提高应用的性能 同时维持渲染质量 渲染高分辨率帧是非常 耗费 GPU 时间的 为了减少这一时间 通常会使用 低分辨率渲染的方法 然而 代价就是低渲染质量 通过 MetalFX 放大 您的应用可以在渲染帧时 使用低分辨率 减少渲染时间 同时无需为渲染质量做妥协 MetalFX 放大经过优化 可更好地 在 Apple 设备上运行 应用到游戏中也易如反掌 MetalFX 提供了两种放大效果 我稍后将详细解释 空间放大使用简单 也能带来极大的性能提高 时序抗锯齿和放大 可整合多帧信息 从而生成高质量输出
我稍后会跟大家说下 使用这些效果的最佳实践 在讲座的最后 我会为大家 演示实际效果
我们先从空间放大开始 MetalFX 空间放大 分析输入的空间信息 以生成全新的 放大的样本 整合空间放大很简单 只需抗锯齿色彩输入 即可生成空间放大色彩输出 在典型游戏渲染管线中 有多个渲染通道 包括抗锯齿渲染 和多种后期效果处理 将 MetalFX 空间放大 紧接着添加在游戏的色彩映射过程 完成之后 如果输入已完成色彩映射 且在视觉色彩空间中 运行效果最佳 我们来看看 MetalFX 空间放大的实际效果
这个象棋场景是 高质量参考渲染器 在 4K 分辨率下生成的 它使用路径跟踪 结合复杂的图形效果 如光线追踪反射和阴影 这是并排比较 左边是 540p 输入
右边通过 MetalFX 空间放大 以 1080p 输出
如果我放大这个皇后 左边 图像缺少细节 分辨率低 右边 空间放大输出下 倒影更清晰 边缘更精细
接下来 我和大家一起看下 如何实现 MetalFX 空间放大
在 Metal 中 您通常会创建 一个指令编码器 将指令编码到指令缓冲区 为效果生成输入 类似的 您也可以创建一个 MetalFX 效果对象 将指令编码到指令缓冲区 从而运行效果 最后 创建第三个指令缓冲区 编码指令 使用 MetalFX 输出 您应该只在 App 首次运行 或改变显示分辨率时 创建一个新的空间缩放器对象 因为它创建的成本很高
首先 创建和配置 一个 MTLFXSpatialScalerDescriptor 然后 通过调用 makeSpatialScaler() 方法 创建缩放器对象 在初始化代码中 先为描述符 填入输入和输出纹理的 宽度和高度 然后 为稍后要在缩放器对象上 设置的纹理 设置纹理格式 设置色彩处理模式 告诉 API 输入和输出 在什么色彩空间 您可以将模式设置为 视觉 线性 或 HDR 色彩空间 描述符填完后 创建缩放器对象
创建缩放器对象后 您可以随时 修改属性 然后调用 encode() 方法 开始放大过程 在您每帧的绘图代码中 在缩放效果编码到指令缓冲区前 确保缩放器对象上 输入和输出纹理都已设置正确 空间放大提供了 可提高性能的简单方法
如果您想要更高质量的渲染 MetalFX 时序抗锯齿和放大 就可以发挥作用了 时序抗锯齿和放大技术 使用了上一帧的数据 来生成高质量放大输出 这意味着上一帧的放大输出 将会成为当前帧放大的输入之一
为了更好地理解为什么 时序抗锯齿和放大 需要上一帧的数据 我先回顾下超级采样的概念
在超级采样中 每个像素会做多重采样计算 然后再整合到一个像素值中 我们整合到每个像素的样本越多 效果就越好 然而 在单一帧内计算 每个像素的多重样本 成本很高 但是 你可以通过时序采样 代替在同一帧内 对每个像素进行多重位置采样 时序采样这一概念 指的是在指定帧内 为所有像素 都按不同的采样位置渲染 这让您能以极低的成本 通过多重帧 获得超级采样的质量 通过从多重帧对应采样位置 累积采样 时序抗锯齿和放大 可以将样本合理地 整合为目标分辨率像素 带来高质量抗锯齿放大输出 然而 由于内容通常 在帧与帧之间变化 它会要求更多输入数据 来感知这些变化 除了上一帧的输出 时序抗锯齿和放大也需要 抖动色彩输入 以及场景中的运动数据 和深度数据 我会逐一解释其原因
首先是抖动色彩输入
这是无抖动渲染的红色三角形 亮白色的边缘代表三角形已渲染 每个小正方形代表一个像素 中间的灰点是像素采样处
这是通过微小抖动 渲染的同一个三角形 灰点表示指定像素的采样位置 抖动偏移对一组数量的帧来说是唯一的 从而可以充分收集到所需数量的样本 我稍后将会详细讲到 抖动序列的话题
接下来是来自场景的运动信息 来自场景的运动信息指的是 对象从上一帧移动了多少 从哪个方向移动 时序抗锯齿和放大 通过回溯运动信息 找到上一帧的对应位置 从而正确收集采样
另一个输入是来自场景的 深度信息 通过场景的深度数据 可以知道什么在前 什么在后 这对于优先处理 前景边缘抗锯齿非常重要 也可在上一帧收集采样时 提供相关线索 提示有哪些其它对象可能是新出现的 最后一个输入数据是 上一帧的输出
上一帧的输出包括 之前整合的所有采样 这些与当前帧的抖动色彩输入相融合 增加了每像素的采样数据 通过结合当前帧和上一帧的信息 生成的图像提供更多细节 MetalFX 持续跟踪放大输出 所以您只需传入当前渲染帧的 色彩 动作和深度即可 回到典型游戏的渲染管线 MetalFX 时序抗锯齿和放大应在 所有后期效果处理前运行 因为这些效果将会 干扰放大效果
我们再来看这个象棋渲染 这次用的是 MetalFX 时序抗锯齿和放大 左边是 1080p 输入 右边是 4K 放大输出 我们对两者并排比较
放大看下皇后 输入为低分辨率 带锯齿 而右边的时序放大输出 为高分辨率 边缘平滑 反射细节更精细
如空间缩放器一样 创建新的时序缩放器 成本高 只应在 App 首次启动 或改变显示分辨率时完成 首先 您要分配及填写 一个 MTLFXTemporalScalerDescriptor 然后调用 makeTemporalScaler() 方法 来创建缩放器对象
在初始化代码中 先从描述符开始 填入输入和输出纹理的 宽度和高度 然后为缩放器对象上 稍后作为输入纹理的 抖动色彩 深度 和动作纹理的设置格式
最后 为 MetalFX 存储放大输出的 输出纹理设置格式 描述符填写好后 创建缩放器对象 在缩放器对象中 设置动作缩放属性 这可以帮助您按 API 期望的方式 缩放 App 的动作数据 MetalFX 期望运动数据 是在渲染分辨率像素空间 提供从当前帧的位置 到上一帧的位置 作为示例 我用了 1080p 的渲染分辨率 假设有一个对象 从剪辑空间坐标 (-0.75, -0.75) 开始 移动到 剪辑空间坐标 (0.25, 0.25) 动作数据存储为 (1, 1)
设置运动矢量比例属性 为 (-960, 540) 这样 MetalFX 可以 正确分析您游戏的动作数据
您可以随时修改 缩放器对象的属性 调用 encode() 方法 来开启放大过程
在每帧绘图代码方面 首先设置 resetHistory 属性 当您的 App 加载第一帧 或场景切换时 将数值设置为 true 然后设置效果输入的纹理 随后是输出纹理 接下来 设置 reversedDepth 属性 表明深度值 是否采用反向 Z 映射
编码缩放器效果前 最后一个要设置的属性 是当前的抖动补偿
正确获取抖动补偿 对输出质量来说 是很重要的 我们来快速看下 如何设置抖动补偿
在示例中 左边是 抖动渲染的三角形
右边是像素的放大视图 样本位于 (0.625, 0.78) 坐标 橙色点的位置是像素中心 位于坐标 (0.5, 0.5)
在这个示例中 抖动补偿是 (-0.125, -0.28) 注意抖动补偿的范围 通常是 -0.5 至 0.5 要验证您提供的抖动补偿 是否正确 用不同的抖动补偿序列 在没有镜头和对象动作的情况下 渲染场景 左边是设置了 错误抖动补偿的示例 静态对象会偏移 细线变得模糊 右边是设置了 正确抖动补偿的输出 对象保持在正确位置 细线逐步得以解析 MetalFX 的时序抗锯齿和放大效果 提高了您 App 的性能 为您提供了可与原生目标分辨率渲染 相比拟的放大质量 当使用两种放大效果时 为了获得最佳质量和性能 我来为大家展示一些最佳实践
首先是空间放大 为了达到最佳空间放大质量 色彩输入应为抗锯齿且无噪声 因为噪声效果和锯齿图像 影响边缘判定 进而影响空间放大质量 要达到最佳性能 请使用视觉色彩处理模式 这意味着您的输入色彩 应为经过色彩映射 在 sRGB 色彩空间中 从 0 到 1 的数值 最后 要为更高的纹理细节 设置合适的负向 mip bias 为空间放大 推荐的 mip bias 计算方法为 渲染分辨率宽度 除以目标分辨率宽度 再取 log2
比如 将每个渲染分辨率尺寸 按 2x 缩放 结果是 -1 mip bias 而如果按 1.5x 比例缩放 结果是 -0.58 mip bias 注意较低的 mip 等级 可能会导致高频图案的 纹理闪烁 如果看到这种失真现象 应调整 特定纹理的 mip bias 接下来 我为大家介绍下 时序抗锯齿和放大的最佳实践 时序抗锯齿和放大 要获得最高质量 选择良好的抖动序列是很重要的 要找到一个抖动序列 可以为放大后目标分辨率中 所有的像素 都能提供良好的分布 通常 每个输出像素 8 个抖动样本 可生成高质量的 抗锯齿放大输出 在 2x 缩放的案例中建议使用 有 32 次抖动的 Halton (2,3) 序列 生成抖动色彩输入 这是 Halton (2,3) 序列中 前 32 个样本位置 每个输出像素大约生成 8 个样本 设置合适的负向 mip bias 对更高纹理细节 同样重要 时序抗锯齿和放大的 推荐 mip bias 计算方法为 渲染分辨率宽度 除以目标分辨率宽度 取 log2 再减去 1 比如 每个渲染分辨率尺寸 以 2x 比例缩放 结果是 -2 mip bias 而每个尺寸 以 1.5x 比例缩放 结果是 -1.58 mip bias 接下来 我给大家演示下 mip bias 如何在不同情况下 影响您的输出的示例 这是同一个场景下 MetalFX 时序抗锯齿和放大输出 分别使用 0 -1 和 -2 的 mip bias
-2 的 mip bias 可生成 锐化度最高 最清晰的输出 而 0 mip bias 生成 最柔和 最模糊的输出
这是使用时序放大效果的 电路板的三种渲染 从上到下 纹理采样时 应用的 mip bias 数值分别是 0 -1 -2 因为电路板的纹理 有高频图案 如微型跟踪电线 -2 的 mip bias 会导致闪烁 和波纹效果 然而 -1 的 mip bias 可极大减少这种效果 0 mip bias 可完全抵消 该效果 低 mip 水平 能带来更精细的细节 使用我们的 mip bias 建议 作为初始点 但为高频图案的纹理 选择 mip bias 时要谨慎 遵循这些方法就可以确保 通过 MetalFX 时序抗锯齿和放大 产生抗锯齿的高质量的放大输出
最后 我再说下使用 MetalFX 放大以获得 最佳性能的方法 要通过 MetalFX 放大 获得最佳性能 你应该注意避免 两个没有依赖关系的渲染器 或计算通道 绑定同样的资源产生读写 这样会 造成错误依赖关系 在 Metal 中应该尽量避免 错误的依赖关系 但这对 MetalFX 放大来说 尤为重要 我稍后会进一步阐述 在这个例子中 有两个帧 阴影和后处理通道完全无关 没有资源依赖 Metal 会用当前帧的 后处理通道 重叠下一帧的阴影通道
然而 如果后处理通道 在阴影通道 要读取 Metal 缓冲区时 还在写入同一缓冲区 Metal 会阻止这两个通道 在 GPU 上并发运行 以此避免读取同时写入 同一资源的潜在风险 帧与帧之间的错误依赖关系 对 MetalFX 的放大性能 会带来负面影响 如果帧与帧之间 没有错误依赖关系 下一帧的阴影通道可能会 与上一帧的 MetalFX 放大重叠 然而 由于帧与帧之间的 错误依赖关系 现在的性能下降了 还包括了 MetalFX 放大用于 完成处理的时间 理想情况上 您应该确保 在帧与帧之间 没有错误的依赖关系 允许不同帧之间的任务可以重叠 确保使用 MetalFX 放大时的 最佳性能 在这个示例中 您可以为后期处理和阴影通道 创建一个独立的缓冲区 来阻止错误的依赖关系 使得独立通道彼此可以 并行执行
避免错误依赖关系 是您在应用 MetalFX 放大时 需要留意的 在确定这两个效果之间 如何选择时 有几个因素需要考虑 对于不断增长的着色成本 和像素数量 可以考虑 时序抗锯齿和放大 按时序分期绘制像素 可以增加视觉保真 并提高性能 如果您还没有较好的 时序抗锯齿方案 并且可以渲染抖动色彩 位移和深度缓冲区 MetalFX 时序抗锯齿和放大 可为您提供 强大的平台优化方案 如果您没有所需的输入 或者已经有了一个调整好的 抗锯齿方案 可以考虑使用 MetalFX 空间放大 希望您现在对选择 哪种放大效果 有了较好的了解 接下来我为大家演示下 Metal 应用程序中的 实际运行效果 这是我们 “Modern Rendering with Metal” 代码中 “Bistro” 场景的并排比较 它提供多种实时渲染算法功能 如环境光遮蔽和体积雾 左边是 1080p 本地渲染 右边是使用 MetalFX 空间放大的 4K 输出 这个示例有自己的 时序抗锯齿解决方案 我们以此作为 MetalFX 空间放大的输入
放大仔细看看这部摩托车
左边的图像有点模糊 而右边 空间放大输出 图像锐化度更高 边缘更清晰 车把的直线抗锯齿效果很好
车身的曲线也比较平滑 我们来对比下性能 左边是 4K 的原生渲染 右边是 MetalFX 空间放大的 4K 输出
随着镜头的移动 左边的原生渲染 运行帧率有起伏 而右边的空间放大输出 则更为平滑
接下来是光线跟踪场景的 并排比较 有很多反射和阴影 左边是 1080p 的原生渲染 右边是 MetalFX 时序抗锯齿和放大的 4K 输出
放大仔细看看吊灯
左边的原生输出有锯齿 而右边的时序放大输出 边缘锐化度更高 细节更精细 阴影很清晰 不会模糊 吊灯上的细节都能看清楚
MetalFX 时序抗锯齿和放大下 性能提升很明显 左边是 4K 原生渲染 右边是 MetalFX 时序抗锯齿和放大的 4K 输出 随着镜头的移动 左边的原生渲染 运行帧率非常低 而右边的时序放大输出 则更平滑
顶尖的游戏开发者们 对 MetalFX 放大的实力 都很兴奋 今年晚些时候将会带来 “Grid: Legends” “Resident Evil: Village” 和 “No Man’s Sky” 接下来 我为大家演示下 使用这一框架的初期作品
在这个场景中 我们可以看到 使用 MetalFX 时序抗锯齿和放大 带来的叹为观止的视觉效果 和流畅的游戏体验
回顾下 MetalFX 是专注于放大的全新 API 空间放大应用简单 可以带来明显的性能提升 您也可以使用时序抗锯齿和放大 获得高质量的渲染 按照我之前跟您说的 最佳实践 可以确保您充分利用 MetalFX 放大的功能 感谢大家的观看
-
-
3:39 - Spatial upscaling (initialization)
// Spatial upscaling (initialization) let desc = MTLFXSpatialScalerDescriptor() desc.inputWidth = 1280 desc.inputHeight = 720 desc.outputWidth = 2560 desc.outputHeight = 1440 desc.colorTextureFormat = .bgra8Unorm_srgb desc.outputTextureFormat = .bgra8Unorm_srgb desc.colorProcessingMode = .perceptual spatialScaler = desc.makeSpatialScaler(device: mtlDevice)
-
9:16 - Spatial upscaling (per frame)
// Spatial upscaling (per frame) // Encode Metal commands to draw game frame here... // Begin setting per frame properties for effect spatialScaler.colorTexture = currentFrameColor spatialScaler.outputTexture = currentFrameUpscaledColor // Encode scaling effect into command buffer spatialScaler.encode(commandBuffer: cmdBuffer) // Encode Metal commands for particle/noise effects and game UI drawing for frame here...
-
9:16 - Temporal antialiasing and upscaling (initialization)
// Temporal antialiasing and upscaling (initialization) let desc = MTLFXTemporalScalerDescriptor() desc.inputWidth = 1280 desc.inputHeight = 720 desc.outputWidth = 2560 desc.outputHeight = 1440 desc.colorTextureFormat = .rgba16Float desc.depthTextureFormat = .depth32Float desc.motionTextureFormat = .rg16Float desc.outputTextureFormat = .rgba16Float temporalScaler = desc.makeTemporalScaler(device: mtlDevice) temporalScaler.motionVectorScale = CGPoint(x: 1280, y: 720)
-
10:35 - Temporal antialiasing and upscaling (per frame)
// Temporal antialiasing and upscaling (per frame) // Encode Metal commands to draw game frame here... // Setup per frame effect properties temporalScaler.resetHistory = firstFrameOrSceneCut temporalScaler.colorTexture = currentFrameColor temporalScaler.depthTexture = currentFrameDepth temporalScaler.motionTexture = currentFrameMotion temporalScaler.outputTexture = currentFrameUpscaledColor temporalScaler.reversedDepth = reversedDepth temporalScaler.jitterOffset = currentFrameJitterOffset // Encode scaling effect into commandBuffer temporalScaler.encode(commandBuffer: cmdBuffer) // Encode Metal commands for post processing/game UI drawing for frame here...
-
-
正在查找特定内容?在上方输入一个主题,就能直接跳转到相应的精彩内容。
提交你查询的内容时出现错误。请检查互联网连接,然后再试一次。