Unity Profiler
目录+
Unity 性能优化相关。
下面梳理几个容易混淆的等待类 Profiler 标记,它们大多出现在帧的末端或渲染同步处,但成因不同——弄清成因才能判断瓶颈到底在 CPU、GPU 还是被 VSync 限制。瓶颈判定的整体思路和对应的优化手段见 性能优化大纲性能优化大纲diagram (1).png 前提:先测量,再优化 没有数据支撑的优化都是猜。动手前先用 Profiler 定位瓶颈,CPU/GPU Profiler、Memory Profiler、Frame Debugger 分别覆盖不同维度。 下面列出的所有手段,都是针对已定位的具体问题的备选方案,而不是一份要逐条执行的清单。 判定性能瓶颈 一帧之内发生了什么 一帧由 CPU 和 GPU 分工完成。要判断瓶颈在哪一端,得先知道这一帧里两边各自在做什么、按什么顺序做。 image.png CPU 侧 CPU 侧在主线程上按固定顺序推进: - 物理 >> 脚本逻辑 >> 动画 >> UI 重建 >> 渲染数据准备 渲染准备就是 Profiler 里的 Camera.Render,它做三件事: 剔除**(视锥剔除、遮挡剔除,筛掉看不见的物体); 排序**(不透明物体从前往后、半透明从后往前); 合批** 最后为每个要画的物体生成渲染命令。 Unity 默认开启多线程渲染,主线程生成渲染指令,渲染线程逐条取出指令,调用对应的图形 API(DX/GL),由驱动将命令写入 Comma。
WaitForTargetFPS
WaitForTargetFPS 是主线程在当前帧的所有逻辑处理(Update、Physics、Animation)和渲染提交完成后,为对齐设定的目标帧率(Application.targetFrameRate)而执行的一种主动节流(Throttling)/ 休眠。
Unity 每帧都会计算时间差:
T_wait = T_target - T_actual
T_target:目标帧间隔(如 60 FPS ≈ 16.66 ms)。T_actual:当前帧实际消耗的 CPU 时间。- 若
T_wait > 0,Unity 就执行 WaitForTargetFPS。
换句话说,它是**"为了不跑太快而故意浪费的时间"**。在 Profiler 里看到它,说明 CPU 性能过剩,能毫无压力地跑满设定帧率。
Gfx.WaitForPresent
这是最常见的一种:CPU 主线程或渲染线程在一帧最后请求 GPU 展示画面时发生的等待。CPU 提交完命令缓冲区(CommandBuffer)后,调用底层图形 API(如 d3dPresent、eglSwapBuffers、vkQueuePresentKHR),此时可能出现两种情况:
VSync 等待(正常):开启了垂直同步时,显示器还没到刷新时机(60 Hz 屏幕每 16.6 ms 刷新一次),CPU 跑得太快,只能在这里死等显示器的 VSync 信号。只要帧率稳定满帧,这个等待就是健康的(类似 WaitForTargetFPS)。
GPU 拥塞等待(GPU Bound,需警惕):关闭了 VSync,或开了 VSync 但没满帧。此时 CPU 已提交画面,但 GPU 还没画完(Back Buffer 被 GPU 锁住),CPU 被驱动层阻塞,必须等 GPU 释放 Back Buffer 才能提交当前帧。这是典型的 GPU 瓶颈。
一句话:WaitForPresent = "我(CPU)画完了,GPU 你好了没?屏幕你好了没?"
WaitForLastPresent
移动端 / Frame Pacing 特有——常见于 Android(尤其开启了 Swappy / Google Frame Pacing)或某些 Vulkan/Metal 实现,通常发生在一帧的开始阶段或提交渲染前。
它关乎流水线深度。为减少输入延迟(Input Lag),现代引擎会限制 CPU 能"超前" GPU 多少帧,通常配合双缓冲或三缓冲。设想 CPU 极快、GPU 极慢:
- 不加限制:CPU 连续把第 100、101、102 帧的命令塞进驱动队列。玩家在第 103 帧操作,却要等前 3 帧渲染完才能看到反应,手感极"肉"(延迟高)。
- 机制:
WaitForLastPresent是一道栅栏(Fence),引擎强制 CPU 等待,直到 GPU 彻底完成上一帧(或上上帧)的显示。 - 目的:让渲染队列不堆积太长,保持"CPU 只比 GPU 快一帧",从而降低延迟。
判定:Profiler 中 WaitForLastPresent 很高,说明 GPU 处理上一帧太慢,CPU 被迫等 GPU 腾位置——依然是 GPU 瓶颈。
一句话:WaitForLastPresent = "队列满了,我(CPU)得等 GPU 把上一帧消化完,才能开始搞新的一帧。"
Gfx.WaitForPresentOnGfxThread
这是 CPU 在等 GPU 的信号,意味着游戏 GPU Bound 或受 VSync 限制。 主线程跑得太快,只能停下来等渲染线程,而渲染线程正卡在 Gfx.PresentFrame 等 GPU 完成上一帧渲染或等 VSync 信号。
底层来看,Unity 采用 CPU Main → CPU Render → GPU 的多级流水线。当主线程发起 Gfx.WaitForPresentOnGfxThread,说明它已完成当前帧的所有逻辑更新和 DrawCall 提交、准备进入下一帧,但此时:
- GPU 负载过高:GPU 还在处理上一帧繁重的 Fragment Shader、后处理或高分辨率渲染,Command Buffer 积压。
- Render Thread 反压:渲染线程为防 Command Buffer 溢出(Unity 默认允许缓冲 2–3 帧)被迫阻塞等 GPU。
- 传导效应:渲染线程的阻塞最终传回主线程,让主线程在 WaitForPresent 处挂起。
如果开启了垂直同步(VSync Count = 1/2),即使 GPU 很快,渲染线程也会强制休眠等显示器的 VBlank 信号,此时该标记同样很高,但属于良性等待。