性能 — MRTK2

入门

使性能合理化的最简单方法是通过帧速率或应用程序每秒呈现的图像次数。 必须满足目标帧速率,如目标平台所述(例如 Windows Mixed RealityOculus 等)。 例如,在 HoloLens,目标帧速率为 60 FPS。 低帧速率应用程序可能会导致不良用户体验,如降级的全息影像稳定性、外部环境跟踪、手部跟踪等。 为了帮助开发人员跟踪和实现高质量的帧速率,混合现实工具包提供了各种工具和脚本。

可视化探查器

若要在开发生命周期内持续跟踪性能,强烈建议在运行 & 调试应用程序时始终显示帧速率视觉对象。 混合现实 工具包提供 Visual Profiler 诊断工具,该工具提供有关应用程序视图中当前 FPS 和内存使用情况的实时信息。 可视化探查器可以通过 MRTK 配置文件检查器下的诊断系统设置进行配置。

此外,在设备上运行时,使用可视化探查其跟踪帧速率尤为重要,这不同于在 Unity 编辑器或仿真器中运行。 在具有发布配置版本的设备上运行时,将描述最准确的性能结果。

注意

如果针对Windows Mixed Reality进行生成,请使用 MASTER 配置生成进行部署。

可视探查器接口

优化窗口

MRTK 优化窗口提供的信息和自动化工具可帮助混合现实开发人员设置其环境,以获得最佳性能结果,并识别其场景中的潜在瓶颈 & 资产。 Unity 中的某些关键配置可以帮助为混合现实项目提供实质上大幅优化的结果。

通常,这些设置涉及非常适合混合现实的渲染配置。 与传统 3D 图形开发相比,混合现实应用程序的独特之处在于它使用两个屏幕(即两只眼睛)来渲染整个场景。

可以通过利用 MRTK 优化窗口在 Unity 项目中自动配置下面引用的建议设置。

MRTK 优化窗口设置

Unity 探查器

Unity 探查器是一款非常有用的工具,可用于逐帧调查应用程序性能的详细信息。

CPU 上所用的时间

示例 Unity Profiler Graph

为了保持合适的帧速率(通常为每秒 60 帧),应用程序需要实现最大帧时间,即 16.6 毫秒的 CPU 时间。 为了帮助确定 MRTK 功能的成本,Microsoft 混合现实 工具包包含一个标记,用于每帧 () 代码路径。 这些标记使用以下格式来帮助了解正在使用的特定功能:

[MRTK] className.methodName

注意

方法名称后可能有其他数据。 这用于标识有条件执行的、可能成本高昂的功能,对应用程序代码进行少量更改时可能会避免使用这些功能。

示例 Unity Profiler 层次结构

本示例已扩展层次结构,以显示 WindowsMixedRealityArticulatedHand class 类的 UpdateHandData 方法在分析帧期间消耗了 0.44 毫秒的 CPU 时间。 此数据可用于帮助确定性能问题是与应用程序代码相关还是与系统中其他位置相关。

强烈建议开发人员以类似方式检测应用程序代码。 应用程序代码检测的主要关注区域位于事件处理程序中,因为这些方法在引发事件时会向 MRTK 更新循环收费。 MRTK 更新循环中的帧时间过长可能表示事件处理程序方法中的代码成本高昂。

单通道实例化渲染

Unity 中 XR 的默认渲染配置为多通道。 此设置指示 Unity 执行整个渲染管线两次,每只眼睛执行一次。 这可以通过选择单通道实例化呈现来进行优化。 此配置利用渲染目标数组来为每只眼睛执行一个单独的绘制调用,以实例化到相应的渲染目标中。 此外,该模式允许在单个渲染管线执行中完成所有呈现。 因此,选择单通道实例化呈现作为混合现实应用程序的呈现路径可以 节省 CPU & GPU 上的大量时间 ,并且是推荐的呈现配置。

但是,为了向每只眼睛发出针对每个网格的单次绘制调用,所有着色器都必须支持 GPU 实例化。 实例化允许 GPU 跨两只眼睛多路绘制调用。 Unity 内置着色器和 MRTK 标准着色器默认情况下包含着色器代码中必要的实例化说明。 但是,如果为 Unity 编写自定义着色器,可能需要更新这些着色器以支持单通道实例化渲染。

自定义着色器的示例代码

struct appdata
{
    float4 vertex : POSITION;
    float2 uv : TEXCOORD0;

    UNITY_VERTEX_INPUT_INSTANCE_ID //Insert
};

struct v2f
{
    float2 uv : TEXCOORD0;
    float4 vertex : SV_POSITION;

    UNITY_VERTEX_OUTPUT_STEREO //Insert
};

v2f vert (appdata v)
{
    v2f o;

    UNITY_SETUP_INSTANCE_ID(v); //Insert
    UNITY_INITIALIZE_OUTPUT(v2f, o); //Insert
    UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o); //Insert

    o.vertex = UnityObjectToClipPos(v.vertex);

    o.uv = v.uv;

    return o;
}

质量设置

Unity 提供了预设来控制每个平台终结点的渲染质量。 这些预设控制可以启用的图形功能,例如阴影、抗锯齿、全局照明等。 建议降低这些设置,并优化渲染期间执行的计算数量。

步骤 1:更新混合现实 Unity 项目以使用低质量级别设置
转到“编辑”>“项目设置”,然后选择“质量”类别 > 为“UWP 平台”选择“低质量”

步骤 2:对于每个 Unity 场景文件,禁用实时全局照明
“窗口”>“渲染”>“照明设置”>取消选中“实时全局照明”

深度缓冲区共享 (HoloLens)

如果是针对 Windows Mixed Reality 平台开发的,特别是 HoloLens,则启用“XR 设置”下的“深度缓冲区共享”有助于实现全息影像稳定性。 不过,处理深度缓冲区可能会导致性能降低,特别是在使用 24 位深度格式时。 因此,强烈建议将深度缓冲区配置为 16 位精度。

如果由于低位格式发生了 z 冲突,请确认所有摄像头的远剪裁平面均设置为应用程序的最小可能值。 默认情况下,Unity 将远剪裁平面设置为 1000 米。 在 HoloLens 中,50 米的远剪切平面对于大多数应用场景来说已经足够了。

注意

如果使用 16 位深度格式,模具缓冲区所需效果将不起作用,因为 Unity 不会在此设置中创建模具缓冲区。 相反,选择 24 位深度格式通常会创建 8 位模具缓冲区(如果适用于终结点图形平台)。

如果使用需要模具缓冲区的掩码组件,请考虑改用 RectMask2D,它不需要使用模具缓冲区,因此可与 16 位深度格式一起使用。

注意

若要快速确定场景中不在视觉上写入深度缓冲区的对象,可以使用 MRTK 配置文件的“编辑器设置”下的“渲染深度缓冲区”实用工具

优化网格数据

优化网格数据设置尝试删除应用程序中未使用的顶点属性。 该设置通过运行生成中每个网格上每个材料中的各个着色器通道来实现此目标。 这对游戏数据大小和运行时性能有好处,但可能会极大地阻碍生成时间。

建议在开发过程中禁用此设置,并在创建“Master”版本期间重新启用此设置。 此设置可在“编辑”>“项目设置”>“播放器”>“其他设置”>“优化网格数据”下找到。

常规建议

对于混合现实开发人员来说,性能可能是一个不明确且不断变化的挑战,而使性能合理化的知识范围非常广泛。 不过,对于了解如何提高应用程序性能,有一些常规建议。

将应用程序执行简化为在 CPU 或 GPU 上运行的多个部分是很有用的,从而确定应用是否受到这两个组件的限制。 可能同时存在跨处理单元的瓶颈,以及必须仔细研究的一些独特场景。 但是,对于入门,最好掌握应用程序执行时间最多的方面。

GPU 受限

由于混合现实应用程序的大多数平台都在使用立体渲染,因此 GPU 受限很常见,这是呈现“双宽度”屏幕的性质导致的。 此外,移动混合现实平台(如 HoloLens 或 Oculus Quest)将受到移动类 CPU & GPU 处理能力的限制。

专注于 GPU 时,应用程序通常需要完成每个帧的两个重要阶段。

  1. 执行顶点着色器
  2. 执行像素着色器(也称为片段着色器)

如果不深入探讨计算机图形 & 渲染管道的复杂领域,每个着色器阶段都是在 GPU 上运行以生成以下内容的程序。

  1. 顶点着色器将网格顶点转换为屏幕空间中的坐标(即每个顶点的代码执行)
  2. 像素着色器计算要为给定像素和网格片段绘制的颜色(即每个像素的代码执行)

在性能优化方面,专注于优化像素着色器中的操作通常会更有成效。 应用程序可能只需要绘制一个只有 8 个顶点的立方体。 但是,立方体占用的屏幕空间可能有数百万像素。 因此,减少诸如 10 个的操作的着色器代码可以节省更多工作(如果是在像素着色器上减少,而不是在顶点着色器上减少)。

这是利用 MRTK 标准着色器 的主要原因之一,因为此着色器通常比 Unity 标准着色器执行的每像素 & 顶点少得多的指令,同时获得可比的美学结果。

CPU 优化 GPU 优化
应用模拟逻辑 呈现操作
简化物理特性 减少照明计算
简化动画 减少可绘制对象的多边形计数 & 个
管理垃圾回收 减少透明对象数目
缓存引用 避免后处理/全屏效果

绘制调用实例化

Unity 中最常见的一个降低性能的错误就是在运行时复制材料。 如果 Gameobject 共享相同的材料和/或相同的网格,则可以通过诸如静态批处理动态批处理GPU 实例化之类的技术将其优化成单个绘制调用。 但是,如果开发人员在运行时修改渲染器材料的属性,Unity 将创建分配材料的克隆副本。

例如,如果一个场景中有一个 100 个立方体,则开发人员可能需要在运行时为每个立方体分配唯一的颜色。 C# 中的 renderer.material.color 访问权限将使 Unity 在内存中为此特定渲染器/GameObject 创建一个新材料。 这 100 个立方体都有其自己的材料,因此它们不能合并为一个绘制调用,而是会成为从 CPU 到 GPU 的 100 个绘制调用请求。

为了克服这一障碍,并仍为每个立方体分配唯一的颜色,开发人员应利用 MaterialPropertyBlock

private PropertyBlock m_PropertyBlock ;
private Renderer myRenderer;

private void Start()
{
     myRenderer = GetComponent<Renderer>();
     m_PropertyBlock = new MaterialPropertyBlock();
}

private void ChangeColor()
{
    // Creates a copy of the material once for this renderer
    myRenderer.material.color = Color.red;

    // vs.

    // Retains instancing capability for renderer
    m_PropertyBlock.SetColor("_Color", Color.red);
    myRenderer.SetPropertyBlock(m_PropertyBlock);
}

Unity 性能工具

Unity 提供编辑器中内置的极佳性能工具。

如果估算一个着色器和另一个着色器之间的粗略性能权衡,则编译每个着色器并查看每个着色器阶段的操作数量会很有用。 为此,可以选择着色器资产,并单击“编译和显示代码”按钮。 这将编译所有着色器变体,并打开 Visual Studio 以显示结果。 注意:生成的统计信息结果可能因使用给定着色器的材料上启用的功能而异。 Unity 只会编译当前项目中直接使用的着色器变体。

Unity 标准着色器统计信息示例

Unity 标准着色器统计信息 1

MRTK 标准着色器统计信息示例

MRTK 标准着色器统计信息 2

请参阅

Unity

Windows Mixed Reality

Oculus

网格优化