外观
Lesson 053|Overdraw 与 Fill Rate:DrawCall 不高为什么仍然会卡?
Tags: #Creator2.x #Stage04 #Overdraw #FillRate #GPUDifficulty: ⭐⭐⭐⭐☆
真实问题:一个全屏弹窗只有 6 个 DrawCall,低端机为什么掉到 20 FPS
弹窗层依次绘制:
text
游戏背景
→ 全屏模糊截图
→ 黑色半透明 Mask
→ 半透明 Dialog 阴影
→ 粒子亮点
→ UI 内容屏幕中央像素可能被写五六次。DrawCall 很少,但每次覆盖面积巨大,Fragment Shader 和混合带宽成为瓶颈。
🎯 本课目标
本课回答一个常见性能问题:
DrawCall 已经很低,为什么画面在低端设备上仍然卡?
答案经常在像素阶段:Overdraw、Fill Rate、纹理带宽和 Fragment Shader。
二、Overdraw 是什么
同一个屏幕像素被多次绘制,就是 Overdraw:
text
背景绘制一次
透明面板再绘制
按钮再绘制
文字再绘制
粒子再绘制最终可能一个像素被处理 5 次甚至更多。
Overdraw 关注的不是“最终有多少屏幕像素”,而是为了得到这些像素,前面发生了多少次片元工作。
假设屏幕中央依次绘制背景、模糊图、黑色遮罩、面板阴影、面板和粒子:
text
最终像素:1 个结果
候选片元与混合:可能 6 次如果最后的不透明面板完全覆盖下层,前面的部分工作对最终画面没有贡献,但 GPU 可能已经执行完了。
三、Fill Rate 是什么
Fill Rate 可以粗略理解为 GPU 每秒处理和写入像素的能力。它受到:
- 屏幕分辨率。
- 覆盖面积。
- Overdraw 层数。
- Fragment Shader 复杂度。
- 纹理采样和带宽。
影响。
高分辨率设备更容易暴露全屏透明和后处理成本。
分辨率增长是面积级的:
text
1280×720 ≈ 92 万像素
1920×1080 ≈ 207 万像素
2560×1440 ≈ 369 万像素同样六层全屏效果,从 720p 到 1440p,基础覆盖像素约放大四倍。DrawCall 可以完全不变,但每个 Pass 的 Fragment、纹理读取和目标写入都会随面积增长。
四、为什么 2D UI 也会有严重 Overdraw
UI 常见结构:
text
全屏背景
→ 半透明遮罩
→ 面板背景
→ 九宫格边框
→ 图标
→ 文字
→ 阴影和特效每一层都可能覆盖相同区域。视觉上只是一个面板,GPU 看到的是多次片段处理。
五、优化方向
- 删除完全被覆盖的底层装饰。
- 缩小透明节点的实际尺寸。
- 避免大面积透明阴影和模糊。
- 将静态层合成一张图片。
- 降低粒子和特效覆盖区域。
- 简化 Fragment Shader 和采样次数。
- 在目标设备上测量,不用编辑器主观判断。
六、DrawCall 与 Overdraw 的关系
text
DrawCall:提交多少次
Overdraw:像素被处理多少次可以出现:
text
DrawCall 少 + Overdraw 高 → GPU 仍然卡
DrawCall 多 + Overdraw 低 → CPU 提交可能卡优化方向必须先判断瓶颈属于 CPU 还是 GPU。
七、案例:全屏透明遮罩
一个半透明遮罩覆盖整屏:
text
遮罩只占一个 Sprite
但覆盖所有像素它可能不增加很多 DrawCall,却增加整屏 Fragment 混合。若同时叠加模糊和粒子,GPU 成本会快速增长。
把遮罩 opacity 从 150 降到 50,只改变混合结果,不一定减少覆盖:
text
四边形仍覆盖全屏
→ 片元仍生成
→ Shader 仍运行
→ Blend 仍执行真正减少像素工作通常需要缩小几何覆盖、减少层数、降低 RenderTarget 分辨率、简化 Shader,或避免提交不可见对象。
八、测量方法
建议记录:
text
目标设备和分辨率
GPU Frame Time
Overdraw 可视化结果
透明层数量
纹理采样次数
修改前后帧时间只在编辑器里看 FPS 不足以证明优化有效。
九、常见误区
误区一:DrawCall 少就没有 GPU 问题
像素计算和带宽可能是主要瓶颈。
误区二:透明图片面积不重要
面积决定可能产生的 Fragment 数量。
误区三:把所有透明效果都删掉
应先定位贡献最大的层,再平衡视觉和性能。
误区四:Overdraw 颜色图就是最终性能
它是诊断工具,还要结合真实设备和时间数据。
十、深入推导:Overdraw 与 Fill Rate 的数量级
屏幕分辨率 1920×1080,约 207 万像素。若平均每像素绘制 4 次:
text
约 207 万 × 4
= 约 829 万片元覆盖若每个片元还要多次纹理采样与混合,工作继续增加。实际 GPU 有裁剪、缓存、Early-Z 等机制,公式只用于数量级理解。
Fill Rate 可理解为单位时间能处理/写入的像素量,受:
- 分辨率;
- 覆盖次数;
- Shader 复杂度;
- 纹理采样;
- Blend;
- RenderTarget 格式;
- GPU 架构和带宽。
十一、Creator 2.4.x 实验:缩分辨率验证像素瓶颈
场景
创建 8 个全屏半透明 Sprite,颜色不同,可逐层启用。保持同 Texture/材质,尽量不让 DrawCall 成为主要变量。
OverdrawProbe.ts
ts
const { ccclass, property } = cc._decorator;
@ccclass
export default class OverdrawProbe extends cc.Component {
@property([cc.Node])
layers: cc.Node[] = [];
showCount(count: number) {
for (let i = 0; i < this.layers.length; i++) {
this.layers[i].active = i < count;
}
}
setScale(value: number) {
for (const layer of this.layers) {
layer.scale = value;
}
}
}对照步骤
- 只显示 1 层,记录帧时间。
- 显示 4 层、8 层,保持 DrawCall 记录。
- 将所有层 scale 从 1 降为 0.5,观察覆盖面积。
- 在不同预览分辨率或目标设备分辨率测试。
- 用平台 GPU 工具的 Overdraw/Frame Capture(若可用)验证热点。
如果降低分辨率或覆盖面积显著改善,而 DrawCall 变化很小,像素阶段是强证据。
十二、透明大图的“空白”为什么也有成本
一个 1024×1024 粒子贴图只有中间 100×100 发光,其余 Alpha 为 0。四边形仍覆盖完整 1024 区域,透明片元通常仍要采样和执行 Shader。
优化方向:
- 裁掉纹理透明留白;
- 缩小粒子 Quad;
- 降低同时活跃数量;
- 避免多层全屏半透明;
- 使用较小 RenderTexture;
- 对遮挡完全不透明区域利用合适裁剪/深度策略。
Alpha 为 0 通常是采样纹理之后才知道。GPU 可能已经完成 UV 插值和纹理读取,才得到“完全透明”。即使 Shader 使用 discard,也不表示前面的工作全部消失,其效果还依赖平台与 Shader。
更稳定的优化是收紧粒子或 UI Quad 的几何范围,让透明留白从一开始就不生成片元。
十三、真实项目故障:新机流畅、旧机 UI 发热掉帧
现象
DrawCall 只有 12,CPU Frame 也稳定。旧设备 GPU 占用高且发热,几分钟后降频。
根因
首页使用三层全屏动态渐变、实时模糊、透明视频和粒子。高分辨率屏幕把像素量放大,持续 GPU 满载导致热降频。
修复
- 静态背景合成;
- 模糊降采样且按需刷新;
- 视频与粒子在不可见时暂停;
- 低端档关闭部分层;
- 限制目标渲染分辨率;
- 做 10~20 分钟热稳定测试,而非只测启动一分钟。
十四、DrawCall 与 Overdraw 的取舍
把多个层离线合成一张图:
text
DrawCall 下降
Overdraw 下降
纹理内存可能上升
动态性下降
包体可能上升把大 Atlas 拆小:
text
纹理生命周期更合理
透明留白可能减少
DrawCall 可能上升优化是多指标决策,不能只守一个数字。
可以用二维表选择优先方向:
| 现象 | CPU Submit | GPU 像素 | 优先验证 |
|---|---|---|---|
| 很多小图、状态分散 | 高风险 | 可能较低 | 合批、Texture、Material |
| 少量全屏多采样 Pass | 可能较低 | 高风险 | 分辨率、采样、RenderTarget |
| 大量大粒子 | 可能中等 | 高风险 | 活跃数、面积、重叠 |
| 大量动态 Label | 高风险 | 视面积而定 | 排版、字形、刷新频率 |
同一页面可能同时有两类瓶颈。先解决一个后,另一个才会成为新的最大成本。
十五、故障诊断
DrawCall 低、CPU 低、GPU 高
优先测试分辨率、覆盖面积、Shader、纹理采样和 RenderTexture。
降低 opacity 没改善
Alpha 更低不代表少画片元,覆盖与混合仍发生。应减少层、面积或 Shader 工作。
隐藏看似无关的大背景后改善
检查背景是否有透明区域、复杂材质、超屏尺寸或多 Camera 重复绘制。
十六、练习、推导答案与版本边界
Creator Profiler 不一定提供完整 GPU 分解;应结合 Chrome/平台 GPU 工具、真机与分辨率对照。不同 GPU 的 Fill Rate 和热行为差异很大。
- DrawCall 低为何不代表 Fill Rate 低? 一个 DrawCall 可以覆盖全屏并执行复杂多采样 Shader。
- opacity 0 是否保证无 Fragment 成本? 不保证。若几何仍提交,Shader 通常仍需运行后才得到 Alpha。
- 为什么降半宽高可能大幅改善? 像素数约变为四分之一,全屏 Pass 的采样/写入随之大降。
- 如何证明是像素瓶颈? 在逻辑与批次基本不变时降低分辨率/覆盖面积/Shader 复杂度,观察 GPU 帧时间显著变化。
能力验收
你应该能估算分辨率与平均覆盖层数形成的片元数量级,解释透明留白为什么仍可能执行 Shader,并通过分辨率、覆盖面积和 Shader 复杂度三个控制变量证明像素瓶颈。
本课总结
text
Overdraw 衡量像素重复处理。
Fill Rate 受面积、层数、Shader 和带宽影响。
DrawCall 和 Overdraw 是不同维度。
低 DrawCall 不代表低 GPU 成本。十一、下一课预告
text
Lesson054|阶段复习:一次 UI 修改如何经过 Renderer 到达 GPU?