使用Dynamic Resolution自适应帧率

解读

国内一线/二线游戏公司面试时,这道题往往出现在“性能优化”环节,面试官真正想验证的是:

  1. 你是否亲手落地过Dynamic Resolution(DR),而不是只看过文档;
  2. 能否把帧率、渲染耗时、分辨率缩放系数三者闭环,做到“玩家无感知、设备不吃力”;
  3. 是否理解Unity原生方案与自研方案的差异,以及如何在iOS/Android差异化硬件上稳定运行;
  4. GPU Bound vs CPU Bound的判定逻辑是否严谨,能否在毫秒级做出决策。

一句话:不是“开没开DR”,而是“怎么让它敢降、会升、不抖动”。

知识点

  1. Unity内置DR管线

    • 开启条件:Build Settings→Player Settings→ActiveDynamic Resolution+对应Render Pipeline Asset勾选Enable;
    • 缩放粒度:Hardware/Software模式,Software DR可在Tile-Based Deferred机型上避免带宽爆炸;
    • 回调接口:ScalableBufferManager.ResizeBuffers(scale, scale)主线程与渲染线程双端原子写入
    • URP/HDRP差异:URP在UniversalRenderPipelineAsset里用maxRenderScale/minRenderScale钳位,HDRP用DynamicResolutionHandler+ConVar动态采样。
  2. 自研DR框架(国内项目主流)

    • 采样窗口:连续4帧GPU Frame TimeRecorder.GPU)去掉最高最低取平均,防止帧率抖动误判;
    • 分级策略:把1080p→540p切成6档,每档对应0.95/0.85/0.75/0.65/0.55/0.5缩放系数,避免非等比压缩导致UI糊;
    • 升/降阈值:目标帧率50 ms(20 FPS)下,>52 ms降一档<42 ms升一档,带30帧冷却防止乒乓;
    • 黑帧保护:检测到Camera.onPreRenderonPostRender之间Screen.width/height突变>5 %,强制回弹1.0并锁定60帧,防止安卓Rom层Surface重启;
    • 兼容性:华为麒麟9000、骁龙8 Gen2的Tile Size为32×32,缩放系数必须对齐1/2ⁿ,否则出现Load/Store带宽翻倍
  3. 与热更新、资源管理的耦合

    • 热更场景下,Shader Variant可能缺失降采样分支,需在Scriptable Build Pipeline里把multi_compile _ DYNAMIC_RESOLUTION打全;
    • 图集策略:UGUI的CanvasScalerScale With Screen SizeReference Resolution必须和DR逻辑统一,否则字体边缘出现半像素偏移
    • 后处理:Bloom、DoF的Pyramid Down Sample阶段需读取RTHandleProperties.currentViewportSize,否则降分辨率后亮度异常
  4. 性能验证指标

    • GPU Time:Adreno GPU用adb shell gpuvis抓帧,GPU Utilization低于80 %即判定GPU Bound;
    • DDR带宽:骁龙Profiler里Memory Read Beat突增>1.2 GB/s说明降采样未对齐Tile;
    • 用户体验:腾讯PerfDogJank & BigJank连续3次>8 ms即视为玩家可感知卡顿。

答案

“我在上一个重度MMO项目中完整落地了自研Dynamic Resolution,目标是在骁龙7系与苹果A12以下机型稳30 FPS。核心思路分三步:

第一步,GPU耗时采样。用Unity.Profiling.Recorder采集GPU标记,每4帧做一次滑动平均,排除局部峰值;同时用SystemInfo.graphicsMemorySize内存压力修正,显存占用>80 %时阈值再收紧10 %。

第二步,分辨率分级与缩放。将屏幕长边按1.0/0.9/0.8/0.7/0.6/0.5六档映射到RenderScale短边等比缩放保证像素对齐;对安卓Tile-Based架构,强制把缩放系数向下对齐到1/2ⁿ,防止Store-Load带宽爆炸。所有RTHandle通过RTHandleSystem重新分配,确保UI Overlay不走缩放,避免字体模糊。

第三步,升降策略与防抖动。当连续3帧GPU耗时>目标帧率+2 ms时降一档,<目标帧率−8 ms且持续30帧后升一档;引入Hysteresis冷却计时器,防止乒乓切换。检测到安卓Rom层onSurfaceChanged导致分辨率突变时,强制回弹1.0并锁定60帧,待稳定后再重新评估。

最终效果:在红米Note 11(骁龙680)上,同屏60人团战,GPU帧时间从52 ms降到38 ms,DDR带宽下降28 %,PerfDog大卡顿次数从11次/分钟降至2次/分钟,用户留存提升1.7 %。上线三个月无一起因分辨率变化导致的画面异常投诉。”

拓展思考

  1. VRS(Variable Rate Shading)+DR混合:在骁龙8+ Gen1以上芯片,Tile-Based VRS可与DR叠加,** shading rate降到1×2的同时分辨率再降0.8,总体像素吞吐量节省45 %,但需解决VRS与Alpha Test冲突,需把植被Shader改成Alpha-to-Coverage**。

  2. 云游戏场景:当RTT>80 ms时,客户端DR策略需让位于码率自适应,此时分辨率不再由本地GPU决定,而由WebRTC QP值反馈,DR系数与Bitrate联动,防止“降了分辨率却推高码率”的负优化。

  3. XR分支:Quest2等VR设备有Eye-Tracked Foveated Rendering,中心凹区域分辨率1.0、边缘0.5,DR需与Foveation Level乘法叠加,但需保证Total Render Target Size不超过XRSettings.eyeTextureWidth/Height上限,否则**ATW(Async TimeWarp)**会强制回弹,导致瞬移眩晕。

  4. 未来趋势:Unity2023.2开始把DR系统迁移到Rendering GraphResize节点可插入Pass任意位置,自定义Upsample Filter(如FSR2、DLSS)作为PostProcessPass直接复用,原生支持Vulkan/Metal的Texture-Space Shading,届时DR策略将从“整屏缩放”细化到“Pass级”,对引擎组提出更高要求。