ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

移动端发热优化:纹理后处理带宽瘦身实战

移动端发热优化:纹理后处理带宽瘦身实战 项目封测前最后一次真机验证我一加8T跑了张刚做好的开放世界地图5分钟机身温度直接顶到47°C帧率从60线一路跌破30。群里炸锅的时候我第一反应是查DrawCall和顶点数——毕竟发烫问题里这俩是常客。可profiler拉出来一看渲染状态居然干净得很既没有爆批也没有炸多边形。真正把带宽吃到吐的是俩我一开始没在意的家伙纹理采样和一长串全屏后处理pass。这篇是发烫优化系列的第4篇专门把这两个搬运量最大的惯犯拎出来开刀。不管你是Unity还是UE、做手游还是做VR只要落地在移动端纹理和后处理就是你发热排查清单里必须排在前两名的怀疑对象。我尽量把原理讲明白、给出可复现的优化步骤也会把我在项目里踩过的坑一起倒出来。1. 发热的根源移动GPU最怕的不是算力而是带宽搬运1.1 TBDR架构下一次纹理采样到底搬了多少数据先放下工具把最底层的那笔账算清楚。移动端GPU几乎清一色是Tile-Based Deferred RenderingTBDR架构也就是把屏幕切成一个个小tile在芯片内部完成绝大多数计算后再写回显存。这个设计的本意是省电只有tile内部的数据交换发生在片上带宽压力小。但TBDR有个死穴——纹理采样和全屏读写绕不开DRAM。什么叫绕不开DRAMGPU内部有tile memory相当于一个顺手的工作台但纹理数据放在显存里显存在移动端通常是跟系统共享的DRAM相当于几米开外的仓库。每次shader要采样一张贴图GPU就得去仓库把对应的texel搬到片上缓存。搬多少、搬多频繁直接决定功耗。我一般这么跟队友类比CPU像厨师cache是操作台的调料架DRAM是后厨仓库。炒菜的时候每次都要跑到仓库扛一袋米回来跑一趟不觉得累但一帧里跑几百趟整个后厨的人都在忙着搬货电费自然哗哗涨。具体数字更直观。假设一张1024×1024的RGBA8888纹理单次全屏采样一遍数据量是多少1024×1024×4字节 4MB。如果场景里同时有几十张纹理每张都被各种pass采样多次60帧跑下来光纹理读取的带宽就是每秒几百MB甚至几GB。这些流量全部走DRAM而DRAM的每bit访问功耗比片上SRAM高两个数量级——发热就是这么来的。1.2 把带宽换算成温度一张无压缩的4K贴图有多费电我见过太多项目美术同学出于清晰度的执念把主场景贴图拉到4096×4096格式还是RGBA8888。算一下一张4K RGBA8888纹理是4096×4096×4 64MB。假设这个贴图在一个比较常见的视角下被完整采样一遍每帧搬运64MB60帧就是3.84GB/s的带宽。手机DDR4/DDR5的总带宽大概在20~40GB/s听起来没满问题是你不可能只有这一张贴图。地形、建筑、角色、特效加起来几十张再加上后处理很容易就把带宽吃到接近上限。而且GPU有个特性带宽一旦吃紧频率会主动往上抬去追赶渲染进度频率越高、电压越高、发热越猛降频来得就越快帧率像过山车一样往下掉。这就是为什么你会看到温度47°C、帧率25这种惨状。再对比一下压缩后的情况。同一张4K贴图用ASTC 8×8压缩数据量约为4MB是原来的1/16。被采样一遍的带宽从64MB降到4MB60帧就是240MB/s。**同样画质观感下发热成本差了一个数量级。**所以纹理问题从来不是清晰度问题而是搬运量问题。这也是为什么我每次做发热优化第一件事永远是先把所有纹理的压缩格式、mipmap和最大尺寸过一遍。2. 纹理瘦身方案从贴图导入设置开始的战场2.1 压缩格式怎么选ASTC、ETC2和平台上那些暗坑现在移动端主流的纹理压缩格式就两个方向ASTC和ETC2。先说结论新项目无脑ALL IN ASTC除非你还要兼容2016年之前的老古董安卓机。ASTC的优势在于块大小灵活从4×4到12×12都能选。4×4约8bpp画质接近RGBA88888×8约2bpp属于比较高压缩比的选择。控制在项目里一般是这么分的用途推荐格式理由UI大图、法线贴图、关键角色ASTC 4×4或6×6细节不能丢观感优先漫反射、地表、建筑ASTC 6×6平衡画质和带宽远景、噪声、遮罩图ASTC 8×8搬运量小质量损失人眼不易察觉老安卓兜底ETC2 RGB/RGBA兼容性兜底分辨率别拉太高ETC2在iOS和Android上都是硬性支持的但它的压缩率有限RGB约4bpp、RGBA约8bpp搬运量比ASTC 8×8高了4倍。所以ETC2只适合当低端兼容兜底不适合全项目统一用。这里有个容易踩的坑法线贴图不要直接压成ASTC 8×8法线方向上的精度丢失会导致光照出现肉眼可见的断层和闪光。做法是用法线贴图专用的压缩管线或者用法线重映射的方式先把法线范围重新映射到0~1再压缩。很多引擎提供了Normal Map的导入选项勾上就行但别为了省带宽把全部法线也压到8×8回来画质检查会让你改得想哭。2.2 mipmap、最大尺寸和通道打包一个都不能省压缩格式之外mipmap是另一个被严重低估的带宽杀手。很多人觉得mipmap只是防闪烁其实它真正救的是带宽和cache命中率。没有mipmap远处一个占屏幕不到几个像素的房子GPU依然从最高清晰度层级采样一个像素要反复跑仓库取数据效率极低。开了mipmap之后远处物体自动切到小尺寸层级读取量大幅下降。我在项目里立过一个硬性规范**所有3D纹理默认开启mipmapUI贴图不强制但超过512×512的UI图也建议开。**同时把各向异性过滤Anisotropic Filtering限制在4x或8x手机屏幕像素密度本来就高再往上基本看不出差别带宽却实打实多烧。最大尺寸限制也必须有。我见过一张角色贴图采样尺寸拉到8192纯属浪费。移动端惯例角色主贴图1024~2048大型地表拼接用512或1024的重复贴图远景物件256~512。**4K贴图在移动端不是资产是热源。**Unity导入设置里Max Size一栏按平台分别压下来UE里对应Texture Size Limit和Texture Streaming别让编辑器默认值替你决定。再说一个容易被忽略又特别有效的优化通道打包。把Metallic、Roughness、AO、Height这类标量属性塞进同一张RGBA纹理的四个通道让采样器只加载一张贴图就拿到全部PBR数据。这样等于把四张贴图的搬运量合并成一张带宽收益是75%。美术一开始会觉得麻烦等你把发热数据和包体数据摆出来基本都会配合。2.3 落地时可以抄的纹理规范清单项目里我会把下面这些直接写进规范文档新来的同学照着做就行所有3D贴图开启mipmap各向异性过滤不超过8x。漫反射/颜色贴图统一ASTC 6×6远景和遮罩ASTC 8×8UI和角色关键贴图ASTC 4×4。贴图最大尺寸按平台分级中端机1K封顶旗舰机2K封顶特殊情况单独审批。PBR标量属性统一走通道打包禁止一张图只存一个float。法线贴图单独压缩管线压缩质量调到High。定期用Asset Studio或TextureAnalyzer扫一遍工程凡是尺寸超过上限或格式还是RGBA8888的直接标红处理。这套规范看起来简单但落地之后我见过的实际效果是整个场景纹理带宽下降了大约70%镜头转动时卡顿感减轻机身温度肉眼可见地回落。纹理这块管住了后面才能安心处理后处理。3. 后处理才是真正的全屏级搬运黑洞3.1 一个Bloom为什么需要五六个pass每个pass烧多少带宽如果说纹理是单张资源的搬运大户后处理就是**全屏幕的搬运黑洞**。纹理还有压缩和尺寸可以压后处理根本不给你讨价还价的机会屏幕就那么大每个pass都在全屏读写。还是用数字说话。1080p的一帧RGBA8数据大约是8MB。一个全屏后处理pass读一次8MB、写一次8MB合计16MB带宽。单独看一个pass没什么但Post-processing从来不是一个pass——以最基础的Bloom为例常规流程需要亮度阈值提取全屏读取 全屏写入16MB降采样到1/4分辨率读取全屏8MB写入半分辨率2MB约10MB水平模糊小分辨率下读写约4MB垂直模糊约4MB再降一次采样做更柔和的扩散约2~3MB把Bloom结果合并回主画面读主画面8MB 读Bloom 2MB 写8MB约18MB整个Bloom跑完每帧约55MB带宽。60帧就是3.3GB/s这个数字比前面那张4K纹理压缩前的单次采样还夸张。而这还只是一个特效。如果后处理链上同时挂着Bloom、景深DOF、SSAO、色调映射每个都是全屏官方或半分辨率pass叠加起来每帧上百MB带宽太正常了。你想想这还只是单纯的搬运没算shader计算本身。移动端一旦后处理开满芯片想低功耗都难。3.2 高频后处理效果的开销排序哪些能留哪些必须砍在移动端做过性能优化的人心里得有张效果开销表。我按自己踩过的坑和实测数据排了个序效果实测开销移动端建议SSAO/HBAO极高6~10个pass随机采样默认关闭旗舰机或高端可开低档景深DOF高4~6个pass半分辨率还要多次混合中低端关闭重交互场景关掉体积光/泛光高依赖Ray Marchingpass数不可控除特定演出外不开Bloom中偏高5~6个pass保留但必须降采样到1/4甚至1/8TAA中2~3pass还要存历史缓冲视机型保留注意历史缓冲带宽运动模糊中1~2pass需要速度缓冲低端关闭色调映射/颜色分级低单pass全屏默认保留这是质感底线我自己总结的经验是**移动端默认后处理链条里只放色调映射和FXAA/TAABloom按档位开DOF和SSAO是重灾区能不碰就不碰。**很多开放世界手游其实后期氛围全靠美术调色和灯光跟SSAO较劲的性价比极低。你把它关了玩家未必说得出差别但手机温度一定会给你好脸色。4. 给后处理做降载手术分辨率、pass合并与动态开关4.1 降采样不是无脑砍半不同效果的分辨率策略后处理优化的第一原则也是性价比最高的一招能降分辨率就降分辨率。这里的降分辨率不是把整个屏幕渲染分辨率砍半而是把特定后处理pass放到更低的离屏分辨率上做。做法上分三种一是后处理输入降采样。以Bloom为例阈值提取完成后直接把亮度图降到1/4分辨率再做模糊最后合并时再upsample回来。因为Bloom本质是一种柔和的光晕人眼对它的分辨率敏感度很低1/4甚至1/8都够用。你可以算1/4分辨率的模糊pass带宽直接是原来的1/16。二是半分辨率后处理链。DOF、SSR这类效果把整条链子都放在半分辨率RT上做最后跟全屏画面对齐。半分辨率下的读写量只有全分辨率的1/4效果损失肉眼几乎不可见这才是移动端DOF的正确打开方式。三是关键pass保留全分辨率。像色调映射、颜色分级这种一帧就一个pass的没必要降成本本身可控降了反而容易出现画面文字、UI边缘的色阶断层。这个度要拿捏别为了优化把所有pass都扔半分辨率后期画面糊了还得返工。我在实际项目中给Bloom定的标准流程是亮度提取在1/2分辨率做模糊在1/4分辨率做两轮合并时用带一点偏移的upsample。实测观感和全分辨率Bloom差别很小带宽成本却低了大概60%。4.2 后处理队列重组与移动端友好选型降采样之外第二步是把后处理链子的结构重新排一遍。很多团队的后处理一多pass就一个接一个串行地blit这是典型的搬运浪费。可以合并的pass尽量合并比如亮度阈值提取和第一级降采样可以在同一个pass里完成——一边读原图一边按阈值筛选同时把结果写到低分辨率RT。一眼看过去Bloom链就从6个pass缩到了4个。框架选型上移动端优先考虑那些为TBDR友好的方案。UE里的Post Process Volume虽然好用但泛用性越强、pass越完整搬到手机上越吃力。Unity则建议直接用URP的Render Feature管理后处理它能让你精确控制每个pass的RT格式和分辨率。不要图省事把PC全套后处理卷轴直接搬到手机最后一定是用发热来还债。另外MSAA和后处理不能盲目叠加。后处理链每个pass都离不开采样MSAA一旦开高每个全屏pass读写的是多重采样缓冲带宽再乘以采样数。移动端能用TAAFXR就别硬上MSAA 4x除非你的RT管理和台积电真的同意。4.3 用温度感知做动态画质发热不是二值问题优化做到最后光靠静态配置还不够。手机型号、散热条件、环境温度差异巨大同一套设置不可能通吃所有设备。这时候可以做动态画质降级系统检测到设备温度偏高或帧率持续过低自动一步步把高开销的后处理关掉。我做过的一个简化逻辑长这样float currentTemp GetBatteryTemperature(); if (currentTemp 42.0f) { postProcessProfile.SetActive(Bloom, false); SetRenderScale(0.85f); QualitySettings.masterTextureLimit 1; } else if (currentTemp 39.5f) { SetRenderScale(0.9f); } else if (fpsAverage 40.0f) { SetRenderScale(0.95f); }温度阈值、降级步长这些参数要根据真机实测标定。注意降级最好是逐级渐进的一下子把所有特效全关玩家能明显感觉到画面变了分几步走感知会小很多。这个机制看似是牺牲画质其实是把连续可玩性放在第一位。我见过太多项目因为发热降帧被用户骂回炉装上动态降级之后差评明显少了。5. 一个真实案例复盘把中端机烫手的开放世界救回来5.1 三个profiler锁定真凶的过程理论讲完说个我手上的真实案例。前面开头提到那款开放世界锁定方向之后我用三条链路同时排查第一步Unity Profiler / Unreal Insights看CPU与GPU帧时间分布。发现CPU侧DrawCall和脚本耗时都不高GPU帧时间却占了近18ms明显瓶颈在GPU。第二步Xcode GPU Frame CaptureiOS或Snapdragon Profiler安卓看带宽和shader占的百分比。安卓机器上打开Snapdragon Profiler能直接看到GPU总线的实时带宽使用率和Frag Shader的占用曲线。当时实测总线带宽在13GB/s上下高得离谱而Fragment Shader ALU占用其实只有30%左右——这基本实锤了是搬运瓶颈而不是计算瓶颈。第三步截帧分析资源列表。用RenderDoc截一帧看所有绑定过的纹理资源。场面很壮观一张4K广场地砖、一堆RGBA8888未压缩贴图、一堆没有开mipmap的远景植被贴图后处理链上挂着SSAO、DOF和两重Bloom。所有问题全凑齐了。5.2 双管齐下的整改清单与前后数据对比按前面说的思路我带着图形组做了一轮整改核心动作如下优化项优化前优化后纹理压缩格式大量RGBA8888漫反射ASTC 6×6远景ASTC 8×8mipmap一半贴图没开全开各向异性过滤8x贴图最大尺寸4096为主角色2K场景1K远景512PBR标量属性独立贴图四通道打包后处理链SSAO DOF 两重Bloom 色调映射色调映射 单Bloom1/4分辨率 TAA动态降级无39.5°C/42°C两级降级跑完真机数据GPU帧时间从约18ms降到约9ms平均帧率从28fps恢复到55fps极限调度场景和稳定60fps常规场景机身温度满载10分钟从47°C降到39~41°C包体大小纹理资源总体积减小了约55%。这轮改动里没有任何让人眼前一亮的黑科技就是把纹理搬运量砍到合理范围、把后处理pass数压到移动端该有的程度。**数据说明带宽才是移动端渲染发热的根。**你对着shader调半天指令数可能不如把一张贴图压成ASTC、把SSAO关掉来得快。这套思路后来成了我们每个新项目的启动自查项先看纹理再看后处理最后才聊算法。我自己的习惯是每次发热抱怨进来先在profiler里把带宽曲线调出来只要它高企纹理和后处理里一定跑不掉一个。等你把这两位伺候好了手机凉下来剩下的优化都是加分项。后面如果时间允许我再写纹理串流和自适应画质的具体落地那块水比今天这两座雷池还深。
返回列表