MiniStats
MiniStats 是 PlayCanvas 应用的轻量性能面板,可在与场景交互时观察帧间隔、绘制调用、CPU/GPU 耗时和 GPU 资源内存估算。不需要面板、只通过代码采集时,请使用 AppStats。
本页介绍引擎 2.23 及之后版本的 MiniStats 界面。
启用 MiniStats
编辑器用户可在 Launch 按钮菜单中启用:
独立应用在初始化应用后创建面板:
import { MiniStats } from 'playcanvas';
const miniStats = new MiniStats(app);
通过 script 标签加载引擎时,改用 new pc.MiniStats(app)。启用 GPU 计数器时,MiniStats 会启用 GPU 分析,但仍需满足设备支持要求。
显示尺寸
点击分区标题之外的位置,可循环切换三种默认视图;也可以聚焦面板后按 Enter 或空格。
| 视图 | 内容 |
|---|---|
| Compact | 核心计数器与平均值。 |
| Medium | 可折叠的 Engine、User、CPU、GPU 和 VRAM 分区,显示平均值,不显示图表和峰值列。 |
| Large | 相同分区,并在文本后显示历史图表,同时显示平均值和峰值。 |
分区按 Engine、User、CPU、GPU、VRAM 排列。列表高于屏幕时,可用滚轮或触摸拖动。所有内容都在固定列标题下滚动,包括 Draw calls 和 Frame。
大视图中分区展开和折叠的效果如下:
| 展开 | 折叠 |
|---|---|
![]() | ![]() |
折叠分区
中、大视图中,点击分区标题可隐藏或显示计数器。分区默认展开。折叠后标题仍显示,CPU、GPU、VRAM 也保留总值。Engine 和 User 的计数器单位可能不同,因此没有总值。
| 分区 | 内容 |
|---|---|
| Engine | 在 options.stats 中配置的内置计数器,默认包括 Draw calls 和 Frame。Update、FPS、图元数及高斯泼溅数量等也归此区。 |
| User | 配置路径全部以 user. 开头、从 app.stats.user 读取的计数器。示例中只有 Wave 属于此区。 |
| CPU、GPU、VRAM | 总值及可用的耗时或内存细分。 |
没有配置计数器时,Engine 和 User 不显示。折叠不会停止采样或历史记录,状态在尺寸切换后保留。但紧凑模式没有分区标题,直接显示计数器,忽略折叠状态。
以下可读写布尔属性控制同样的状态:
miniStats.engineCollapsed = true;
miniStats.userCollapsed = true;
miniStats.cpuCollapsed = true;
miniStats.gpuCollapsed = true;
miniStats.vramCollapsed = true;
// Expand GPU details again.
miniStats.gpuCollapsed = false;
五个属性默认均为 false,并与标题点击同步。紧凑模式下也可提前设置。折叠分区与 miniStats.enabled = false 不同,不会停止测量。
平均值、峰值与历史
数字默认约每半秒刷新。Avg (0.5s) 是上次刷新后收集的帧样本算术平均值,Peak 是同一区间内最大样本,不是历史最大值。紧凑模式虽然不显示列标题,也使用相同平均窗口。
每次刷新后开始新窗口,并非连续滑动平均。textRefreshRate 以毫秒控制窗口,修改后标题也会变化。窗口在帧边界结束,因此可能比配置区间略长。
大视图每个应用帧都采集历史图表,与文本刷新独立,因此不同帧率下图表覆盖的时间长度不同。GPU 使用最新异步结果,等待新结果时可能重复采样旧值。
基础统计
默认配置包含:
| 指标 | 含义 |
|---|---|
| Draw calls | 每帧提交的绘制命令,包括阴影、后期处理等附加通道,并非对象数量。 |
| Frame | 应用 tick 之间的毫秒间隔,包括浏览器调度和引擎外等待,不只是引擎执行时间。稳定 60 FPS 约为 16.67 毫秒,30 FPS 约为 33.33 毫秒。 |
| CPU | 跨引擎帧更新与渲染事件测得的 CPU 经过时间,不是 CPU 利用率百分比。 |
| GPU | 设备支持时的整帧 GPU 经过时间,单位为毫秒;不是 GPU 利用率,也不是所有通道耗时之和。 |
| VRAM | 被跟踪纹理和 GPU 缓冲区的内存估算。面板标为 MB,但每单位使用 1,048,576 字节,即 MiB。 |
CPU 与 GPU 工作可以重叠,两者耗时相加不等于 Frame。MiniStats 总 CPU 计时边界也不同于 AppStats.cpuUpdateTime 和 cpuRenderTime,两者之和不一定等于面板的 CPU 值。
详细计时模式
在中、大视图展开 CPU、GPU 和 VRAM 后,会显示总值和可用子项。
CPU 子阶段耗时
| 行 | 含义 |
|---|---|
| Render | 准备和提交渲染的 CPU 时间,包括 prerender/postrender 监听器、层级同步和批处理,不包含 GPU 执行。 |
| Script update | 组件系统更新阶段,包括脚本 update、物理和其他订阅系统,不仅是用户脚本。 |
| Script post-update | 组件系统后更新阶段,包括脚本 postUpdate。 |
| Animation | AnimComponentSystem 使用的专用动画更新阶段,旧版动画组件在系统更新阶段运行。 |
| Physics | 最近一次物理步,包括同步和接触处理,通常已包含在 Script update 中。 |
| Splat sort | Worker 报告的高斯泼溅排序时间,独立于主线程 CPU 时间。 |
不要将全部行直接相加:阶段可能重叠或包含其他阶段,Worker 数据也来自另一线程。Animation、Physics 和 Splat sort 在出现正值后才显示。
GPU 通道耗时
WebGPU 支持时间戳查询时,详细视图显示各渲染和计算通道的耗时。同名通道合并为一行,常见项包括 Forward、Downsample、Upsample 和 Compose,取决于场景与渲染配置。
WebGL 2 中,PlayCanvas 提供整帧耗时而非逐通道细分。这是 PlayCanvas WebGL 分析实现的限制,并非计时查询只能测量整帧。
GPU 结果异步到达,可能来自数帧之前。各通道执行区间可能重叠,因此总和不一定等于整帧 GPU 耗时。
显存细分
| 行 | 跟踪资源 |
|---|---|
| Textures | GPU 纹理。 |
| Geometry | 顶点和索引缓冲区。 |
| Buffers | 统一和存储缓冲区,仅 WebGPU 显示此行。 |
这些是图形设备资源估算,设备可能由多个应用共享。不包含 JavaScript 堆内存、未跟踪的驱动开销或物理 GPU 总容量。各类缓冲区的字节数可通过 AppStats 内存 getter 获取。
GPU 计时要求
| 后端 | 要求 |
|---|---|
| WebGL 2 | EXT_disjoint_timer_query_webgl2 扩展。 |
| WebGPU | timestamp-query 适配器特性,可用时引擎自动请求。 |
启用面板不会让设备获得本来不支持的能力。不可用的 GPU 测量可能在 MiniStats 中显示为零,不代表渲染没有 GPU 开销。没有有效测量时,app.stats.gpuFrameTime 返回 undefined。
默认 CPU 计时、绘制调用和内存估算支持所有构建,包括发布和压缩构建。部分附加计数器需要调试或 profiler 构建,参见支持列表。
定制面板
建议先取得默认选项,修改后再构造 MiniStats。传入不完整选项对象不会自动与默认值合并。
import { MiniStats } from 'playcanvas';
const options = MiniStats.getDefaultOptions();
options.startSizeIndex = 2; // Large view
options.textRefreshRate = 500; // Averaging window in milliseconds
options.sizes[1].width = 190; // Medium panel width in CSS pixels
options.sizes[2].width = 260; // Large panel width in CSS pixels
options.cpu.watermark = 1000 / 60;
options.gpu.watermark = 1000 / 60;
const miniStats = new MiniStats(app, options);
每种尺寸都独立配置宽度、行高、间距、图表显示、详细模式和峰值。watermark 设置图表参考预算和纵轴比例;上面两个值标记 60 FPS 预算,不会限制实际执行时间。
附加计数器
options.stats 中各项解析相对于 app.stats 的数字属性路径。尽可能使用公开的 AppStats getter。同一项配置多个路径时会相加。decimalPlaces 控制显示小数位,multiplier 缩放样本值,unitsName 指定单位标签。
例如,在创建面板前向选项添加:
options.stats.push({
name: 'Update',
stats: ['cpuUpdateTime'],
decimalPlaces: 1,
unitsName: 'ms',
watermark: 1000 / 60
});
// Primitive counts are available only in debug and profiler builds.
if (app.stats.primitiveCount !== undefined) {
options.stats.push({
name: 'Primitives',
stats: ['primitiveCount'],
decimalPlaces: 1,
multiplier: 1 / 1000,
unitsName: 'k',
watermark: 500
});
}
设置 miniStats.enabled = false 可隐藏面板并停止计数器采样,调用 miniStats.destroy() 可释放面板。GPU 分析是设备级设置,隐藏或销毁面板不会关闭;若没有其他工具需要,可在分析器存在时设置 app.graphicsDevice.gpuProfiler.enabled = false。
MiniStats 将面板合并到一个网格中,并复用文本图集。只有启用图表的视图收集历史记录。虽然开销经过控制,渲染和 GPU 查询仍有成本,前后对比时应保持检测配置一致。
全部选项和生命周期方法见 MiniStats API 参考。
用户计数器
从引擎 2.23 起,app.stats.user 提供 Map<string, number>,保存应用定义的队列长度、手动 CPU 耗时等计数器。getter 始终返回同一个 Map,可通过 set、get、delete 和 clear 管理条目,所有引擎构建都支持。
创建 MiniStats 前,将图表添加到选项。user.wave 读取名为 wave 的 Map 条目;计数器名称应避免句点,因为句点用于分隔路径。下例显示一个在 0 到 20 之间振荡的值:
import { MiniStats } from 'playcanvas';
app.stats.user.set('wave', 10);
const options = MiniStats.getDefaultOptions();
options.startSizeIndex = 2; // Start with graph history visible
options.stats.push({
name: 'Wave',
stats: ['user.wave'],
decimalPlaces: 1,
watermark: 20
});
const miniStats = new MiniStats(app, options);
let time = 0;
app.on('update', (dt) => {
time += dt;
app.stats.user.set('wave', 10 + 10 * Math.sin(time));
});
引擎不会重置用户计数器。逐帧总量应先初始化,并在 frameupdate 中于累加前重置。测量同步代码时,可使用两次 performance.now() 的差值,并配置 unitsName: 'ms'。单位由应用定义,MiniStats 不会推断,也不会为新增 Map 条目自动添加图表。
示例
资源分配示例会反复创建和释放实体、材质、顶点缓冲区及纹理。观察它们对计数器的影响,点击分区标题展开或折叠,再点击其他位置比较三种视图。

