外观
Lesson 051|Dynamic Atlas 如何降低 DrawCall?
Tags: #Creator2.x #Stage04 #DynamicAtlas #TextureAtlas #DrawCallDifficulty: ⭐⭐⭐⭐☆
真实问题:50 张零散小图开启动态图集后,DrawCall 为什么没降
商城从服务器配置中加载 50 张本地小图,每张都是独立 Texture。团队开启 Dynamic Atlas,预期全部合批,结果指标几乎不变。
可能原因包括:
- 图片尺寸/格式不符合动态图集条件;
- 已经属于静态 Atlas 或不允许打入;
- 材质、Mask 和顺序仍不兼容;
- 图集页满而产生多页;
- 资源在测量前还未进入预期路径;
- 真正瓶颈不是 Texture 切换。
Dynamic Atlas 只尝试统一 Texture,不会消除其他状态边界。
🎯 本课目标
本课理解 Dynamic Atlas 解决的问题、适用条件和代价:
运行时把多个小纹理整理到共享纹理中,减少纹理切换和拆批机会。
二、静态图集和动态图集
text
静态图集:编辑器或构建阶段提前打包
动态图集:运行时根据资源加入共享 Atlas静态图集可预测、稳定;动态图集更灵活,但运行时需要额外管理和复制像素。
三、动态图集的高层流程
text
SpriteFrame 第一次使用
↓
检查是否适合加入 Dynamic Atlas
↓
分配 Atlas 区域
↓
复制或上传纹理像素
↓
生成新的纹理区域引用
↓
后续对象共享 Atlas Texture加入后,SpriteFrame 的 UV 可能指向动态图集中的新区域。
假设 A、B 原来各自占一张 64×64 Texture:
text
A UV 0~1 → Texture A
B UV 0~1 → Texture B动态图集分配一张更大的 Atlas 后:
text
A 像素复制到 Atlas 左上区域
B 像素复制到 Atlas 另一块区域
A、B 的采样 UV 改为各自在 Atlas 中的子矩形显示内容没变,但两者现在绑定同一张 Atlas Texture,于是“纹理不同”这个批次边界可能消失。
四、它为什么可能降低 DrawCall
text
原来:A、B、C 使用三张 Texture
→ 多次纹理绑定
加入动态图集后:
A、B、C 共享 Atlas Texture
→ 更容易合批但最终仍受 Material、Render State 和排序影响。
五、动态图集的代价
- 首次加入有 CPU 和纹理复制成本。
- Atlas 有容量限制。
- 纹理尺寸和显存可能增加。
- 资源生命周期被共享纹理绑定。
- 某些纹理格式、尺寸或组件不适合加入。
- 运行时结果可能受加载顺序影响。
因此 Dynamic Atlas 是运行时策略,不是无条件打开就一定更快。
收益和成本发生在不同时间:
text
首次出现
→ 资格判断、区域分配、像素复制、UV 更新
稳定绘制
→ 共享 Texture,可能减少切换和 DrawCall
资源退出
→ Frame 与图集页的生命周期、碎片和回收问题如果页面只停留很短时间,首次合图成本可能还没来得及被稳定帧收益摊薄;如果页面长时间滚动,大量小图的持续合批收益可能更有价值。
六、适合和不适合的资源
更适合:
- 大量小尺寸 UI 图标。
- 生命周期相近的 SpriteFrame。
- 材质相同的普通 2D 图片。
需要谨慎:
- 超大纹理。
- 特殊压缩格式。
- 需要独立释放的资源。
- 高频变化或特殊 Shader 对象。
- 本身已经在静态图集中的资源。
具体限制以 Creator 2.x 版本实现为准。
七、案例:动态列表图标
商城列表中图标来自多个小资源:
text
初始加载:图标分散在多张纹理
滚动使用:运行时逐渐加入 Dynamic Atlas
后续绘制:更多图标共享 Atlas需要测量:
- 首次滚动是否出现尖峰。
- Atlas 显存是否超预算。
- 场景切换后是否正确释放。
- DrawCall 是否真的下降。
还要区分“资源加载顺序”和“最终资源集合”。动态页可能按首次使用顺序分配空间,不同用户路径造成不同图集页布局与碎片。测试时应覆盖冷启动、常见滚动路径和页面反复进入,而不是只测一次已经预热的编辑器预览。
八、常见误区
误区一:动态图集没有运行时成本
首次加入需要分配、复制和更新纹理。
误区二:所有 Sprite 都能进入动态图集
尺寸、格式、组件和引擎限制都会影响结果。
误区三:加入动态图集后一定只有一个 DrawCall
材质、状态和顺序仍可能拆批。
误区四:动态图集越大越好
过大的 Atlas 会增加显存和生命周期耦合。
九、深入推导:动态合图的运行时过程
高层模型:
text
零散 SpriteFrame 首次参与渲染
→ 判断尺寸、格式与配置是否允许
→ 在动态图集页寻找空间
→ 复制像素到 Atlas Texture
→ 为 Frame 建立图集区域/UV
→ 后续 Sprite 采样 Atlas
→ 相邻兼容对象获得合批机会这会带来运行时复制、图集页内存和管理成本。它是以运行时工作换取后续提交优化。
进入动态图集后,Sprite 看到的内容没有变,但采样位置变了:
text
原来 UV:指向独立 Texture 的 0~1 区域
加入图集后:指向 Atlas Texture 中分配到的小矩形运行时通常不需要每帧重新复制图片。主要复制和区域建立发生在进入图集时,后续绘制复用新的 Atlas 区域;但图集页容量、Frame 生命周期、重置和平台实现仍由具体 Creator 版本决定。
这也解释了它为什么只解决纹理绑定问题:UV 可以改到同一 Atlas,Material、Blend、Mask、Camera 和透明顺序却不会因此自动相同。
十、Creator 2.4.x 实验:开关前后做控制变量
通过 Creator 编辑器的项目设置配置 Dynamic Atlas。不要直接修改引擎缓存、导入产物或私有图集数据。
准备
- 导入 30 张尺寸较小的独立 PNG,不做静态 Atlas。
- 创建 30 个相邻 Sprite,确保默认材质和无 Mask。
- 创建另一组相同 Sprite,但交替插入 Label/Mask,作为对照。
- 在 Project Settings 中确认 Dynamic Atlas 设置。
DynamicAtlasProbe.ts
ts
const { ccclass, property } = cc._decorator;
@ccclass
export default class DynamicAtlasProbe extends cc.Component {
@property(cc.Node)
compatibleGroup: cc.Node = null;
@property(cc.Node)
interruptedGroup: cc.Node = null;
showCompatible() {
this.compatibleGroup.active = true;
this.interruptedGroup.active = false;
}
showInterrupted() {
this.compatibleGroup.active = false;
this.interruptedGroup.active = true;
}
}实验步骤
- 关闭 Dynamic Atlas,重启预览并记录两组 DrawCall。
- 开启后重启预览,等待资源首次显示,再记录。
- 比较第一次出现帧与稳定帧,观察运行时合图是否有首帧成本。
- 增大图片到不适合合图的尺寸,再比较。
- 保持动态图集开启,但插入不同材质/Mask,验证其他边界仍存在。
修改项目设置后应按 Creator 工作流重新预览/构建,不能假设运行中热切换完全生效。
十一、静态 Atlas、Dynamic Atlas 与独立纹理怎么选
静态 Atlas
适合内容稳定、同场景使用、可在构建前规划的 UI。优点是结果可控、无运行时复制;代价是更新粒度和生命周期绑定。
Dynamic Atlas
适合许多小而零散、运行时才组合的 Sprite。优点是减少手工规划;代价是首帧复制、页管理、条件限制和可观测性。
保持独立
大图、频繁更新的纹理、特殊采样、RenderTexture 或生命周期完全不同的内容,可能不适合合图。
十二、真实项目故障:打开背包首帧卡,之后很流畅
现象
开启 Dynamic Atlas 后稳定 DrawCall 显著下降,但首次打开背包卡顿增加。
根因
text
大量小图同帧首次显示
→ 同帧发生纹理解码/上传
→ 动态图集分配与像素复制
→ RenderData/UV 更新
→ 首帧尖峰优化
- 分批创建可见 Item;
- 在合适加载阶段预热必要图标;
- 常用稳定资源改为静态 Atlas;
- 限制动态图集候选和页面尺寸;
- 避免为了稳定帧牺牲不可接受的首帧;
- 同时记录首次与后续帧。
十三、图集页为什么也会浪费
若小图尺寸差异大或生命周期杂乱:
text
Atlas Page 产生碎片
→ 一些 Frame 不再使用
→ Page 因其他 Frame 仍活跃不能释放
→ 运行时纹理占用高于预期Dynamic Atlas 不是资源释放器。它甚至可能把不同业务资源绑定到同一运行时页,需要结合版本行为理解回收策略。
十四、故障诊断
开启后 DrawCall 不变
先验证候选是否进入图集,再检查材质、状态、顺序和 Camera。不要仅凭开关状态断言生效。
图片边缘异常
检查 padding、Filter、纹理格式、动态图集复制条件和缩放。
内存上升
统计原 Texture、Atlas Page、CPU 副本和页面生命周期。减少 DrawCall 可能用更多图集内存换来。
十五、练习、推导答案与版本边界
Dynamic Atlas 的资格、页面尺寸、调试方式和平台行为高度依赖 Creator 2.4.x 小版本。配置以当前编辑器为准,业务不访问私有 manager 数据结构。
- Dynamic Atlas 降低 DrawCall 的必要但不充分条件是什么? 多个 Sprite 最终共享 Atlas Texture;材质、状态、顺序等也要兼容。
- 为什么首帧可能更慢? 首次需要分配图集页、复制像素并更新 UV/渲染数据。
- 大背景为何通常不适合? 占据巨大页空间、复制成本高,且本身往往没有与大量小图合批的收益。
- 如何选择静态与动态 Atlas? 根据资源稳定性、同屏关系、生命周期、首帧预算、更新发布粒度和实测指标。
能力验收
你应该能画出零散纹理进入 Dynamic Atlas 的运行时过程,解释它为什么只解决纹理兼容而不解决其他批次边界,并能同时比较首次合图成本、稳定帧收益、图集页内存和资源生命周期。
本课总结
text
Dynamic Atlas 通过共享纹理提高合批机会。
它不是免费优化,首次加入和显存管理都有成本。
最终收益必须用 DrawCall、帧时间和内存实测。十一、下一课预告
text
Lesson052|Mask、Graphics、Spine、Particle 的特殊渲染成本