了解混合现实的性能
本文介绍混合现实应用性能的重要性。 如果应用程序不以最佳帧速率运行,则用户体验可能会受到很大影响。 全息影像会显得不稳定,环境的头部跟踪会不准确,从而导致用户体验不佳。 必须将性能视为混合现实开发的第一级特性,而不是锦上添花的任务。
下面列出了每个目标平台的高性能帧速率值。
平台 | 目标帧速率 |
---|---|
HoloLens | 60 FPS |
Windows Mixed Reality Ultra 电脑 | 90 FPS |
Windows Mixed Reality 电脑 | 60 FPS |
以下框架概述了达到目标帧速率的最佳做法。 有关在 Unity 环境中测量和提高帧速率的提示,建议阅读 Unity 性能建议文章
了解性能瓶颈
如果应用的帧速率表现不佳,第一步是分析并了解应用程序中的哪个位置存在计算密集型负载。 有两个主要处理器负责渲染场景:CPU 和 GPU,每个处理器处理混合现实应用的不同方面。 可能出现瓶颈的三个关键位置是:
- 应用线程 - CPU - 负责应用逻辑,包括处理输入、动画、物理学和其他应用逻辑。
- 渲染线程 - CPU 到 GPU - 负责将绘制调用提交到 GPU。 当应用要渲染某个对象(例如立方体或模型)时,此线程会向 GPU 发送请求以执行操作。
- GPU - 最常见的任务是处理应用程序的图形管道,以将 3D 数据(模型、纹理等)转换为像素。 它最终会生成一个要提交到设备屏幕的 2D 图像。
一般情况下,HoloLens 应用程序受 GPU 的限制,但并非总是如此。 使用以下工具和方法来了解特定应用的瓶颈所在。
如何分析应用程序
可使用多种工具来了解混合现实应用程序中的性能概况和潜在瓶颈。
以下常用工具可帮助你收集应用程序的深度分析信息:
- Intel Graphics Performance Analyzers
- Visual Studio Graphics Debuggers
- Unity Profiler
- Unity Frame Debugger
- Unreal Insights
- PIX
- Unreal 中的 GPU 分析
如何在任何环境中进行分析
确定应用程序是否受 GPU 或 CPU 限制的方法之一是降低渲染目标输出的分辨率。 减少要计算的像素数可以降低 GPU 负载。 设备将以较小的纹理渲染,然后上采样以显示最终图像。
降低渲染分辨率后,如果:
- 应用程序帧速率提高,则表示可能受 GPU 限制
- 应用程序帧速率无变化,则表示可能受 CPU 限制
注意
Unity 提供了在运行时通过 XRSettings.renderViewportScale 属性轻松修改应用程序渲染目标分辨率的功能。 设备上呈现的最终图像采用固定分辨率。 平台将对较低分辨率的输出进行采样,以生成可在显示器上渲染的较高分辨率图像。
UnityEngine.XR.XRSettings.renderScale = 0.7f;
如何改进应用程序
CPU 性能建议
一般情况下,CPU 上的混合现实应用程序中的大多数工作都涉及到“模拟”场景和处理应用程序逻辑。 优化目标包括以下方面:
- 动画
- 物理
- 内存分配
- 复杂算法(例如逆向运动、路径查找)
GPU 性能建议
了解带宽与填充率
在 GPU 上渲染帧时,应用程序受内存带宽或填充率的限制。
- 内存带宽是 GPU 可以从内存中执行读取和写入的速率
- 若要确定带宽限制,请降低纹理质量并检查帧速率是否提高。
- 若要确定填充率限制,请降低显示分辨率并查看帧速率是否提高。
注意
如果使用的是 Unity,请查看特定于 Unity 的 GPU 性能建议。 - 在 Unity 中,使用 XRSettings.renderViewportScale 属性
内存带宽通常涉及以下任一方面的优化:
- 较低纹理分辨率
- 使用更少的纹理(法线、镜面反射等)
填充率注重减少需要为最终渲染像素计算的操作数量,包括:
- 要渲染/处理的对象数量
- 每个着色器的操作数量
- 实现最终结果的 GPU 阶段数(几何着色器、后处理效果等)
- 要渲染的像素数(显示分辨率)
减少多边形计数
多边形计数较大会导致 GPU 执行更多操作,因此减少场景中的多边形数量会减少渲染时间。 还有其他因素会导致几何体着色开销很高,但多边形计数是确定渲染场景所需工作量的最简单指标。
限制过度绘制
如果渲染多个对象,但未在屏幕上显示这些对象(因为被某个对象遮挡),则会发生较高的过度绘制。 设想一下,你正在看着一堵墙,它遮挡了对象。 将处理所有几何体以进行渲染,但只需要渲染不透明的墙,这会导致不必要的操作。
着色器
着色器是在 GPU 上运行的小程序,在渲染中执行两个重要步骤:
- 确定应绘制哪些顶点以及它们在屏幕空间中的位置(顶点着色器)
- 顶点着色器按每个网格的每个顶点执行。
- 确定每个像素的颜色(像素着色器)
- 像素着色器按像素执行,按几何体渲染为目标渲染纹理。
通常,着色器会执行大量的转换和照明计算。 尽管复杂的照明模型、阴影和其他操作可以生成出色的效果,但它们也是有代价的。 减少着色器中计算的操作数量可以大大减少 GPU 处理每帧所需的工作量。
着色器编程建议
- 尽可能使用双线性筛选
- 重新排列表达式以使用 MAD 内部函数同时执行乘法和加法
- 尽可能在 CPU 上预先计算并作为常量传递给材料
- 优先使用从像素着色器到顶点着色器的移动操作
- 一般情况下,顶点数远小于像素数(720p 为 921,600 像素,1080p 为 2,073,600 像素,以此类推)
删除 GPU 阶段
后处理效果的开销可能很高,并会提高应用程序的填充率,包括 MSAA 之类的抗锯齿技术。 在 HoloLens 上,我们建议避免使用这些技术和其他着色器阶段,例如几何体、外壳和计算着色器。
内存建议
过多的内存分配和解除分配操作会导致性能不一致、帧冻结和其他有害行为。 在 Unity 中进行开发时,了解内存注意事项特别重要,因为内存管理由垃圾回收器进行控制。
对象池
对象池是一种热门技术,可以降低对象连续分配和解除分配所造成的开销。 此技术是通过以下方式实现的:分配一个由相同对象构成的较大池并重复使用此池中非活动的可用实例,而不是在各个时间内不断生成和销毁对象。 对象池非常适合应用中生存期可变的可重用组件。