最早看到这个概念是在《腾讯游戏开发精粹II》里,看了之后感觉非常实用,于是将其列为开放世界的一个必备组件。其运行逻辑简单总结就是:
主动监测当前游戏性能,如果发现指标异常,主动对游戏进行干预以提高帧率、内存等。
例如监测到帧率下降时,则进行减少一些特效、音效,对不重要的 AI 降频等操作,使帧率提升。这样使得游戏整体帧率保持平稳,且能最大限度保证玩家的游戏体验。
1、工作流程
与传统的在设置面板设置高中低配不同,负反馈性能调节系统贯穿游戏始终,且一直生效,使得游戏性能保持稳定。我们在玩很多 3A 大作的时候,也会发现这个系统的踪迹:当游戏性能吃紧时,即便我们没有调整画质,也会看到部分模型精度降低、部分 NPC 停止工作等现象。
负反馈调节系统的示意简图如下:
这里举一个例子,下面链接为游戏《漫威蜘蛛侠2》的实机演示,视频开头有一段玩家操纵蜘蛛侠飞跃城市的过程。在这个过程中,如果发生了掉帧的情况,则可以将远处的人物、汽车等停止动画甚至直接静止。远端的景物,可以切换成低模,以减少性能压力。
2、当前性能情况统计
获取当前内存的使用情况,可以参考下面的代码,更多 API 可以看 Untiy 的官方文档。
//返回托管内存的预留空间大小。
long heapSize = UnityEngine.Profiling.Profiler.GetMonoHeapSizeLong();
//活动对象和非收集对象的已分配托管内存。
long usedHeapSize = UnityEngine.Profiling.Profiler.GetMonoUsedSizeLong();
//收集 Unity 对象使用的本机内存。
long runtimeMemorySize = UnityEngine.Profiling.Profiler.GetRuntimeMemorySizeLong();
Profiling.Profiler - Unity 脚本手册 Scripting API - 泰课在线 - 国内专业的Unity在线学习平台|Unity3d培训|Unity教程|Unreal|虚幻|AR/VR通过脚本控制 Profiler。https://www.taikr.com/docs/UnityDocumentation_2019.1/ScriptReference/Profiling.Profiler.html 帧率可以自己算(例如使用 Time.deltaTime),但这个只能得到帧的总时间,不能区分 CPU 耗时和 GPU 耗时。可以使用 Unity 提供的新工具 FrameTimingManage 来监测帧耗时,示例代码如下:
#region FPS 统计
private int m_fpsCount;
public int FPS => m_fpsCount;
private FrameTiming[] mFrameTimingArray = new FrameTiming[5];
private ProfilerRecorder mainThreadTimeRecorder;
private double cpuFrameTime;
private double cpuMainThreadFrameTime;
private double cpuRenderThreadFrameTime;
private double gpuFrameTime;
private void UpdateFps()
{
int length = mFrameTimingArray.Length;
FrameTimingManager.GetLatestTimings((uint)length, mFrameTimingArray);
cpuFrameTime = 0;
cpuMainThreadFrameTime = 0;
gpuFrameTime = 0;
cpuRenderThreadFrameTime = 0;
foreach (var timing in mFrameTimingArray)
{
//是总 CPU 帧时间,是主线程某帧的开始到下一帧开始的时间间隔。
cpuFrameTime += timing.cpuFrameTime;
//主线程的工作时间,或者说是从帧开始到所有主线程任务完成的总耗时。
cpuMainThreadFrameTime += timing.cpuMainThreadFrameTime;
//渲染线程的工作时间,或者说是从渲染线程受到的第一个任务请求到 Present() 函数被调用的总时间间隔。
cpuRenderThreadFrameTime += timing.cpuRenderThreadFrameTime;
// GPU 的工作时间,或者说是从 GPU 收到任务到 GPU 发出完成信号的总时间间隔。
gpuFrameTime += timing.gpuFrameTime;
}
cpuFrameTime /= length;//这里算出来比 Time.deltaTime 少很多
cpuMainThreadFrameTime /= length;
cpuRenderThreadFrameTime /= length;
gpuFrameTime /= length;
m_fpsCount = (int)(1000 / cpuFrameTime);
}
#endregion
要注意的是 Frame Timing Manager 在 DevelopBuild 中是始终启用的。如果在 Release 中使用该功能,就必须激活它。勾选 Edit/Project Player/Other Settings/Rendering/Frame Timing Status 选项:
更多信息可参考以下链接:
用Unity Frame Timing Manager检测性能瓶颈 | 电子创新网 Imgtec 社区https://imgtec.eetrend.com/blog/2022/100563328.htmlFrameTimingManager - Unity 手册The FrameTimingManager is an API that captures detailed timing data about performance during individual frames in an application. You can use this data to assess those frames to understand why your application doesn’t meet performance targets.
https://docs.unity.cn/cn/2022.3/Manual/frame-timing-manager.html