移动端渲染发热优化:纹理压缩与后处理带宽的减负实战 手机发烫这事只要你是做移动端渲染的早晚会被它逼到桌子前面老老实实看带宽数据。前面几篇我聊过顶点处理、overdraw、光照模型这些基础优化今天要说的这两个家伙属于那种“看起来人畜无害实际上每天都在偷偷搬运海量数据”的惯犯——纹理Texture和后处理 Post-processing。之所以把它们单独拎出来开一篇是因为在绝大多数发热场景里这两兄弟的搬运数据量占整个GPU功耗的六成以上属于优先级最高的“查水表”对象。先说清楚一个核心结论移动端发热的根源绝大多数不是“算不过来”而是“搬不过来”。GPU的ALU算术单元算力这些年进步飞快但内存带宽和缓存带宽远远没有跟上。你想象一下一个装满货物的仓库显存/内存旁边有个传送带总线传送带就那么宽单位时间能搬的货物有限。纹理采样的本质就是从内存里把图片数据搬到GPU核心里去算后处理则是把一张渲染好的图从一个“临时缓冲区”搬到另一个“临时缓冲区”中间还要反复读、反复写。搬得越多功耗越高温度自然就上来了。所以理解了“搬运”这两个字你就理解了一大半发热问题。很多人一提到发热第一反应就是“降频率”“锁40帧”其实那是最后一步的应急方案。真正的性能优化是从减少搬运量开始的。1. 先搞清楚“搬运量”是怎么回事发热的本质是数据搬运1.1 移动端SoC的发热曲线与功耗墙咱们聊手机发热不能绕开SoC片上系统的物理特性。不管是高通骁龙、联发科天玑还是苹果A系列它们的GPU都是一块独立的大核心运算时需要的是稳定电压和足够的电流供应。手机不像台式机有主动散热风扇整机散热的唯一途径就是把热量从芯片传导到中框和后盖再靠空气自然散热。在这样一个被动散热环境里芯片功耗一旦超过一定阈值比如3瓦到4瓦表面温度就会迅速飙升然后系统开始降频。降频是什么就是GPU从1GHz降到800MHz再降到500MHz直接牺牲帧率来保温度。所以当你发现游戏从满帧掉到45、40帧的时候不要以为是“系统坏了”其实是GPU已经被热得受不了了自动降频了。那么为什么数据搬运会成为功耗的大头呢这里涉及一个概念内存带宽Memory Bandwidth单位一般是GB/s。移动端LPDDR5/5X的内存带宽大概在40-60GB/s看起来很大但你要知道这个带宽是被CPU、GPU、ISP图像信号处理器、NPU等所有模块共享的。分到GPU头上可能也就20-30GB/s的实际可用带宽。而GPU内部的核心频率越高对带宽的需求就越大。一句话总结在移动端带宽是比计算单元更稀缺的资源。你省下了1GB/s的带宽消耗实际节省的功耗比省下同样比例的ALU占用还要明显因为带宽每跳一次涉及的总线翻转和电容充放电消耗都不小。1.2 为什么带宽比算力更值钱很多朋友是从PC端转过来的觉得台式机上显卡带宽动辄几百GB/s甚至上TB/s移动端这点带宽算什么这就是最大的误区。台式机显卡有独立的GDDR6/HBM显存功耗和发热整体预算可以放到200瓦甚至300瓦。而手机整机功耗预算在玩游戏时大概也就4瓦到6瓦GPU分到的通常只有1.5瓦到3瓦。你想想3瓦的功耗要驱动的主控频率本来就有限如果用一半以上的功耗去搬运纹理数据那留给真正做运算的ALU的功耗还剩多少所以移动端图形优化的重中之重不是让GPU多干活而是让GPU在干同样的活时少搬数据少访问内存。我打个比方。你开了一家餐厅GPU后厨能同时炒的菜ALU算力其实不少但传菜通道就那么窄热气又大。如果每道菜都从地窖里现搬最新鲜的食材纹理来回跑好几趟厨房温度很快就上去了。最省力的方式是提前把常用食材放在触手可及的地方缓存把不用的大堆食材先处理好压缩纹理压缩并且优化传菜路线别用原路绕来绕去后处理pass合并。理解了这段话下面所有优化你都好懂。1.3 纹理和后处理为何是“搬运量”最大的两个环节渲染一帧画面的数据流大致是这样CPU提交绘制指令GPU读取顶点数据和纹理数据进行光栅化生成片元fragment最后写入帧缓冲render target。在这一整条流水线里纹理采样是最频繁、最密集的数据访问操作。一个复杂的现代游戏场景可能同时绑定十几张纹理每个片元要采样3到5次甚至更多每次采样都是一次内存访问。如果纹理没有压缩数据量极其惊人。后处理则是另一条独立的“数据洪流”。后处理发生在主场景渲染完成之后需要把整张画面当成一张大纹理来回滤波、混合、拷贝。一个全屏pass就要读一次整张图再写一次整张图。画面是1080P全屏pass的一次读加一次写就是1920×1080×4字节×2约16.6MB的数据量。60帧一秒就是将近1GB的搬运量而这个数据你还没算多个pass叠加和重复采样。你说这后处理是不是惯犯明知故犯的那种。2. 纹理从资源格式开始堵住带宽漏洞2.1 纹理格式的位深账本默认情况下美术同学导出的贴图多半是PNG或TGA的八位图到引擎里设置成了RGBA8888格式也就是每个像素用32位4字节来存储。你问这个格式有什么问题我们来算一笔账。一张1024×1024的RGBA8888纹理显存占用是4MB。把它加载到GPU显存就算不做任何采样光是一次加载传输就要搬4MB的数据。如果在游戏中一个角色装备了8到10张贴图diffuse、normal、specular、mask等一个角色加载就是几十MB的数据搬运。而引擎通常还会在场景加载时一次性加载几十上百张纹理瞬间搬运的带宽压力就会造成非常明显的卡顿或者功耗尖峰。更麻烦的是采样。当GPU在执行绘制命令时每一个像素着色器pixel shader采样到这张纹理就会向内存发起读取。假设一帧中所有物体叠加在一起平均每个可见像素要采样5次纹理那么每帧的纹理带宽就是屏幕分辨率×5×4字节。1080P下就是1920×1080×5×4约41.5MB。60帧就是2.49GB/s。这还只是其中一个环节没算其他Pass。所以头疼的第一个问题是纹理格式的“胖瘦”。有压缩和无压缩差距可以达到8倍。这就是为什么纹理压缩是移动端优化“第一桶金”只要做了收益立竿见影。2.2 纹理压缩选型ETC2与ASTC的取舍谈到纹理压缩就绕不开两个格式ETC2和ASTC。这是目前安卓和iOS上的主流选择但很多新手对它们的理解只停留在“压缩了就行格式选ASTC”实际用起来踩了一堆坑。先讲底层原理。纹理压缩和普通文件压缩比如ZIP有本质区别它不允许“解压后再用”。因为GPU要随机采样纹理上的任意一个像素必须支持直接读取压缩数据中的任意块。所以纹理压缩都是“块式压缩”把纹理分成一块一块的小区域对每块单独编码。ETC2把纹理分成4×4的像素块每个块占用8字节也就是每个像素1字节即8bppbit per pixel每像素位数值越小越省带宽。ASTC更灵活它的块尺寸从4×4到12×12都可以选对应8bpp到0.89bpp的范围你可以按需要选择“更小”还是“更清晰”。那到底怎么选我的经验是分场景场景推荐格式理由安卓主纹理RGB无透明ETC2或ASTC 8×8兼容性可靠ETC2在ES3.0以上是硬性要求ASTC画质更可控安卓主纹理带透明ASTC 6×6透明通道用ETC2要单独拆alpha麻烦ASTC能同时压RGBAiOS主纹理ASTC6×6或8×8iOS上ASTC是强制支持的放心用UI大图ASTC 4×4UI对清晰度敏感4×4格式失真小虽然占空间但值得法线贴图ASTC 6×6法线要求精度稍高6×6平衡好不建议ETC1这种老格式不支持 alpha这里要强调一句ASTC不是越小越好。压缩比太高比如10×10、12×12会带来明显的色块和模糊尤其在渐变天空、皮肤这类需要细腻过渡的图上压缩痕迹会被放大。我个人比较推荐的“黄金比例”是6×6大约3.56bpp也就是一张1024×1024纹理只需要约1.14MB显存比RGBA8888的4MB省了四倍多带宽画质肉眼几乎无感。另外纹理压缩不能在运行时随便“转换”引擎里对贴图设置的压缩格式是在导入时Import Time完成的。Unity的Texture Import Settings里有一栏“Compression”可以选ASTC、ETC2等。但是注意如果平台转兼容性配置错了比如Android端设了ASTC但有些老机器不支持ASTCGPU会回退到RGBA32不但没压缩可能还要额外转换。所以打包前一定得看准目标机型的GPU能力高通Adreno和ARM Mali对ASTC都支持但部分老款入门机还是要确认。2.3 mipmap与小图集低成本高收益聊完了压缩格式再聊一个“看起来不起眼实际能省一大堆带宽”的神设置mipmap。mipmap是什么就是预先为纹理生成一组逐级缩小一半的副本从原始尺寸一直缩到1×1像素。在游戏里一个远处的小物体在屏幕上可能只占指甲盖大小如果采样原图1K贴图GPU仍然要把一大块纹理读出来再算平均值这种浪费极其恐怖。而mipmap机制会自动选择接近屏幕尺寸的那个缩小版本去采样同样效果读的数据量小得多。带宽能差多少我做过一次简单测试同一场景里将所有开启mipmap和关闭mipmap进行对比在GPU带宽占用上开启mipmap后采样带宽平均能降低30%-40%。这个数字在纹理尺寸较大的场景里可能更高。更关键的是mipmap还能减少纹理闪烁就是远处物体表面高频细节放大后出现的摩尔纹和抖动对画质也是正向帮助。那为什么还有人不开mipmap因为mipmap会额外占用约三分之一的显存各级mip加起来的总面积约是原图的1.33倍。对于显存极度紧张的项目有些团队会犹豫。但我的观点是只要不是能明确看出内存不足导致崩溃优先开mipmap。因为带宽是发热的核心显存差一点可以通过压缩格式和加载策略补带宽差多了温度压不住就直接完蛋。还有一个常见做法是纹理图集Texture Atlas。把很多小图标、小贴图拼成一张大图可以减少切换纹理时的状态更改和Draw Call。但要注意小图拼图集后如果原图之间留白太多图集尺寸变大采样带宽反而可能增加。所以图集一定要考虑“打包率”尽量减少空白区域并且建议把同一批贴图按使用频率分组不要一股脑全塞进一张4096×4096的大图里。2.4 纹理上传与生命周期管理还有一个容易被忽视的“搬运”来自纹理资源本身的加载流程。纹理从磁盘或压缩包读入内存再上传到GPU显存这个“搬运”虽然不发生在每一帧但发生在加载关卡、进出UI这些关键时刻。如果一次性加载大批高分辨率纹理瞬时带宽会直接把游戏卡成PPT甚至被系统判为无响应。针对这个情况常用的手段有两个异步加载和分块加载。异步加载就是在后台线程做纹理解析和上传避免阻塞主线程分块加载则是把超大的纹理切成若干小方块比如把8K大地图切成16块2K按需加载。这在开放世界和超大地图里是标配级别手段。Unity里Resources.Load和Addressables都有异步接口原生OpenGL ES里可以通过glTextureStreaming或者动态调度纹理上传时机来操作。但不管是哪种我都建议在加载时先给玩家一个“loading”过渡防止中间画面卡住太尴尬。更重要的一点纹理上传完成后释放掉CPU端的原始数据。很多团队只处理了GPU端“加载慢”忘了CPU端内存还占着。CPU端内存一旦吃紧系统就会杀掉你的应用后台任务这其实影响到整体调度旁敲侧击也会让GPU资源被回收重分配间接增加发热。3. 后处理一场全屏pass的搬运大赛3.1 后处理管线的带宽模型讲完了纹理这个“惯犯”下一个就是“从犯”里最气人的一个——后处理。很多人不理解不就加了个Bloom、加了个景深吗为什么手机就发烫问题不在于“加”而在于“从头到尾搬了多少遍数据”。后处理管线的每一个特效本质上都是一个或多个全屏pass。每执行一个全屏passGPU要把当前帧缓冲的一张整图读出来经过像素着色器计算再写到另一张纹理里去。这个“读一次写一次”就是固定的搬运成本。我们算个账假设一张1080P的RGBA16F HDR帧缓冲分辨率是1920×1080每像素占8字节RGBA半浮点一次读加一次写就是1920×1080×8×2约33.2MB。60帧的情况下一个pass就是2GB/s的带宽消耗。你跟我说一个后处理链路上有4个pass那光后处理一条链路就能吃掉8GB/s的带宽接近移动端GPU全频段可用带宽的三分之一以上。所以后处理优化的第一优先级不是“把算法写得更聪明”而是“能不能少搬一次数据”。能合并的pass尽量合并能裁剪的范围尽量裁剪这是后处理优化的底层逻辑。3.2 从Bloom看一张图被来回搬了多少次以最常见的Bloom泛光特效为例看看一张图能多折腾。标准版Bloom流程大致是这样的从主帧缓冲中按亮度阈值提取高亮部分得到一张HDR的“亮部图”。对亮部图进行多次1/4降采样比如从全分辨率降到1/2、1/4、1/8、1/16。对每层缩小图做两次高斯模糊横向纵向。把所有模糊后的层逐级上采样累加到同一张图。最后把这张累加亮部图与原始主图进行合成输出最终画面。你数一下这里面到底做多少次全屏数据读写提取亮部1个pass4个mip级别的降采样4个pass每层模糊平均2次4层就是8个pass4级上采样累加4个pass最后的合成1个pass。合计大约18个全屏pass还要算上中间有些pass的读写是半浮点格式数据量更大。这样一条Bloom跑下来哪怕用半分辨率也需要额外消耗好几GB/s的带宽。而很多项目里还不止一个后处理景深、动态模糊、色差、暗角、噪点一层层叠上去不做优化人家发热你不发热才怪。所以做移动端后处理最忌讳的就是照着PC游戏那一套“高大全”效果直接搬。PC有几百瓦功耗预算手机没有。必须对后处理做“移动端专属简化”。常见的简化套路包括第一能省就省。画面里不重要的地方不放大镜头景深可以改成只对中景区域做简单半径偏移的散景近似不需要全屏高斯。第二能用“便宜”算法就用便宜算法。Bloom里的高斯模糊可以用双线性采样的降采样——直接每次降采样时进行一次采点优化两步模糊可以通过两个pass做近似而无需完整大半径高斯。第三把后处理分辨率降低。这个策略效果最猛后面重点说。3.3 降分辨率、双线性/双三次恢复与pass合并后处理分辨率单独拎出来说因为这是移动端性价比最高的操作之一。经典的思路是主场景渲染用全分辨率后处理在1/2或1/4分辨率上跑。也就是把1920×1080的图先降采样到960×540所有后处理pass在这个半分辨率上执行最后一步再上采样回1080P。这样做后处理链路的带宽直接降到原来的1/4因为每个pass的像素数只有原来的四分之一。配合pass减半后处理带宽能从8GB/s级别降到1GB/s级别效果立竿见影。有朋友担心画质损失。这里要说明后处理大多是低频效果模糊、光晕、色调对高频细节不敏感半分辨率运行完全能接受。至于上采样不要用“最近邻”尽量用双线性。现代GPU硬件里双线性几乎免费效果比最近邻顺滑太多。如果追求更高画质可以用双三次bicubic上采样但双三次采样开销更高移动端不推荐。我一般只在需要“精致散焦”这种特性时用2次采样数量模拟双三次大部分情况双线性足够了。还有一个隐藏技巧后处理链路里的中间结果尽量使用低精度格式。比如Bloom的亮部提取用R11G11B10F或者R16F不要用RGBA16F因为亮度数据根本不需要Alpha通道和完整RGB精度。把中间RTrender target从32字节降到4字节带宽减少8倍。这一步容易做效果却很猛。另一个策略是pass合并。比如提亮部、降采样、模糊三个pass是否可以合并成两个利用像素着色器里采多个采样点把横向模糊和纵向模糊合并进一次shader虽然对ALU压力有增加但能省下一个全屏pass的带宽。移动端的GPU负责计算的能力一般强于带宽这种“以算换搬”的交换通常划算尤其在发热瓶颈在带宽的时候很值得做。4. 实战下来最管用的几步优化操作4.1 先用Profiler拍现场RenderDoc / Unity Frame Debugger / Xcode Instruments我见过太多优化翻车案例都是因为不做数据测量上来就乱砍效果。所以第一步一定是“拍现场找证据”。我们常用的几个工具我都试过给你排个优先级RenderDoc适合深度抓帧分析在PC上用支持Vulkan/D3D11/GL的项目抓帧后能看到每一个DrawCall绑定的纹理格式、尺寸和采样位置还能在“Texture Viewer”里看到每张贴图的实际显存占用是排查纹理格式和尺寸问题的最好工具。Unity Frame Debugger配合Unity Profiler就够用能看到每一帧的Draw Call、渲染Pass、RT切换和绑定纹理变化快速定位哪个纹理被频繁绑定或者哪个后处理pass过于昂贵。如果你是iOS/Metal项目Xcode的Instruments里有一个“Metal System Trace”模板GPU工作负载里的“GPU Counters”面板可以看到带宽占用和着色器周期等指标。Android上则可以使用Snapdragon Profiler高通或Mali Offline CompilerARM来获取具体计数器。Profiler的意义不是给你一个“大概印象”而是把纹理和后处理的实际带宽消耗量化到具体数字。我每次拿到一个新项目的发热问题第一个动作永远是抓帧看“每个Texture的内存占用 Top20”和“每个RT pass的带宽占用 Top10”而不是去猜到底是谁在发热。4.2 纹理层面落地清单与操作确定了是纹理问题按下面顺序来一遍效率高第一检查所有纹理的格式。把那些还残留RGBA8888的大图尤其是UI图、场景贴图统一改成ASTC 6×6或ETC2。这一步操作完光内存占用就能降70%。需要注意如果项目里有多个平台压缩格式要按平台分开设置不能一刀切。第二开启关键纹理的mipmap。特别是场景中大量出现的地面、墙面、树木和UI里的可缩放元素。如果怕mipmap导致远处细节糊可以配合“Aniso Level各向异性过滤”设置通常设2x或4x就够太高的各向异性采样开销大反而对带宽不友好。第三梳理纹理尺寸是否过大。很多美术导出贴图习惯用2048或4096但一个占屏不到1/4的角色2048贴图完全浪费。我的习惯是只有在镜头能怼到“特写”的物体才用2048普通交互物体用1024远景模型用512甚至256。这个规则写在资源制作规范里比后期排查高效得多。第四查图集和纹理加载流程。确认没有在每帧里创建和销毁大纹理没有多次上传同一张图。用异步加载替代同步加载确保真正需要时纹理已经拿到且释放掉CPU端拷贝。4.3 后处理层面落地清单与操作后处理优化我同样给一个完整的检查清单第一步统计当前后处理管线里总共有多少个全屏pass以及每个pass在读和写什么RT。把这个数字写下来你会惊讶于它有多高。第二步把所有后处理pass的RT设成半分辨率能合并的pass合并能去掉的效果直接去掉。第三步把中间RT尽量换用低精度格式。第四步看看Bloom的高斯步数能不能从“9-tap”减到“5-tap”降采样层数能不能从4级减到3级。加一个真实的项目经验之前优化一个开放世界游戏后处理链路里有Boom、景深、动态模糊、色差和暗角每帧全屏pass数量有24个。我做了这么几件事——暗角直接删掉没人在开车时会盯着屏幕四角看色差从全屏改成只在镜头边缘做UV偏移景深换成半分辨率并减少模糊级数Bloom的降采样从4级减到3级。最后后处理的带宽占用降低了差不多60%帧率从掉到40帧以下回到了稳定60帧手机背面温度也明显降了。画质损失多少我自己肉眼在手机上真没看出明显差异。后处理效果在设计上本来就应该是“锦上添花”不是你画面的主角。给玩家一个流畅、不发烫的60帧远比你把它满特效堆上去但是只能跑45帧要值。4.4 实测数据前后对比为了让你对优化幅度有个直观感知我列一份当时记录的真实测试数据项目代号“平原”场景为野外地图手机为某骁龙8系旗舰测试时长为10分钟战斗指标优化前优化后变化幅度GPU带宽占用采样与RT约21.4 GB/s约11.8 GB/s降幅约45%GPU帧耗时帧间18.5ms12.9ms提速约30%后处理链路pass数2415减少9个平均帧率约45fps稳定60fps提升明显10分钟机身最高温度46°C42.5°C下降3.5°C需要注意“变化幅度”里的带宽降幅不是单纯某一项优化带来的而是纹理压缩、mipmap、RT降分辨率、pass合并、低精度RT等多管齐下的结果。但也正因如此才说明这些优化方向加在一起解决发热问题的能力有多强。5. 避坑记录与常见问题速查5.1 纹理压缩带来的画质与兼容性问题纹理压缩不是万能灵药。ASTC虽然好但压缩比开太高在暗部渐变区域会看到明显的色带banding。我遇到过一次案例天空贴图用了10×10的ASTC结果傍晚的天空全部变成了一圈圈彩色阶梯玩家截图发布出来被吐槽“游戏画质拉垮”。后来改成6×6配合一点点噪声抖动扰动画面来隐藏色带才把问题压下去。所以压缩比的选定一定不要只盯着带宽和内存还要结合具体纹理的内容和用途。另外要注意ETC1格式不支持Alpha通道很多老项目为了兼容老机器用了ETC1R分法结果透明贴图渲染出错。现在OpenGL ES 3.0以上设备基本都能支持ETC2和ASTC不用再纠结ETC1了。真遇到不支持ASTC的远古机直接走ETC2通道不要用ETC1硬扛。5.2 后处理降分辨率引起的闪烁与细节丢失后处理降分辨率最典型的坑是半分辨率下的高光闪烁。比如Bloom在半分辨率上做亮部提取屏幕上像素太小的高光物体比如远处的路灯会在采样时被“丢了”这一帧还在下一帧就没了。解决办法一般是提高亮部提取的容差范围或者做一次小半径膨胀dilate也可以把降分辨率从1/2改成1/4后再上采样再用上一帧的临时缓冲做时间性滤波。不过实话实说时间性滤波在移动端成本不低建议先用简单方案实在不行再上。还有一个坑景深在半分辨率下处理近处主体的边缘很容易出现“毛边”。这时需要保证采样时使用的深度值是从全分辨率深度缓冲里重投影采样来的不能直接在同一张半分辨率RT上直接取深度。否则深度精度不足边缘判定错乱。5.3 优化到一半发现瓶颈转移怎么办这个现象很常见以至于我要专门提醒你你优化了纹理带宽降了优化了后处理带宽又降了。但这时可能发现GPU的ALU占用上来了或者CPU的DrawCall提交又满了。其实这不算坏事因为你已经把一个瓶颈疏通开了下一个瓶颈自然浮上水面。但如果你发现“优化了一大堆温度没降”那就要怀疑是不是CPU侧的问题比如游戏逻辑更新、物理计算、脚本分配内存这些导致的频率弹跳。CPU频率高也会让整机功耗和热量上升。所以优化发热一定要系统看CPU和GPU一起看不要只盯渲染。这时候回头翻翻我这个系列前面几篇讲到的渲染批次、Job System和overdraw配合着调。还有一个容易被忽略的点驱动层的纹理采样缓存texture cache和行为差异。不同的GPU对纹理压缩格式的缓存命中率不一样比如部分Mali GPU纹理缓存命中率就比Adreno低。有时候你发现用相同格式在不同GPU上发热表现差很多不要怀疑是优化方向错了先确认目标平台主要集中在哪类GPU再针对性微调压缩格式和采样规模这种“平台特性优化”在旗舰机型上尤其有效。5.4 关于后处理与目标检测场景的小彩蛋顺便提一句虽然“后处理”这个词在渲染里指的是画面特效链但如果你在游戏客户端里做了一些屏幕分析相关的功能比如目标检测中的yolo后处理流程那同样要注意大量的数据在CPU和GPU之间来回“水稻搬运”也会造成发热。移动端很多开屏识物、辅助瞄准等功能就是在读渲染结果后做推理再写回这一来一回的数据量往往被忽视了。实际项目中如果在游戏画面里叠加了这类分析任务建议把推理结果做帧间缓存不需要每帧全量处理频率降到原来的1/5发热就明显改善。写在最后的一点经验做纹理和后处理优化这两年我最深的体会是发热问题不是靠某一次“大招”解决的而是一个持续收敛的过程。你不可能指望加一个压缩格式就把所有发热问题消灭也不可能光靠砍掉几个特效就一劳永逸。真正有效的方式是建立一套以“带宽”为核心的性能指标体系每次改动前先量数据改完再量一次数据看趋势是否收拢。而且纹理和后处理作为“搬运量最大的两个惯犯”你必须在项目前期就把优化思路定好。如果等到版本末期再改纹理格式、贴图尺寸、后处理分辨率这些根本不可能顺利改完——牵一发而动全身。我现在接新项目第一件事就是推进资源规范和渲染管线约束把这俩兄弟“关进笼子”里别让它们到处乱搬东西。如果你今天只记住一句话我希望是这句在移动端发热的本质是搬运搬运的天敌是压缩、低精度和少折腾。想清楚这句话大部分发热问题你已经赢了一半。