fx-quality
本文回答:
fx.setQuality / getQuality+getMetrics()+ 事件render / empty / overflow / compact。
一、三档画质
fx.setQuality('lite'); // 中低端机
fx.setQuality('standard'); // 默认
fx.setQuality('cinematic'); // 演示机 / 大屏
fx.getQuality(); // 当前档
| 档 | shader 宏 QUALITY |
含义 |
|---|---|---|
lite |
1 |
简化迭代、降 octave、丢 bloom 抗锯齿 |
standard |
2 |
默认平衡 |
cinematic |
3 |
完整迭代、平滑边缘、最大噪声层数 |
所有 12 点 / 5 线 / Shadertoy 都在 shader 编译期读 #if QUALITY >= 2 这类宏分支(宏名是 QUALITY,不是 FX_QUALITY)→ 预设作者可根据档位写分支。setQuality 对非法值直接 throw(lite|standard|cinematic 之外报错)。
二、切换耗时
首次切档:用到的 program 重编译一次(< 30 ms);
切回旧档:从 programCache 读缓存(≈ 0 ms)。
console.time('lite');
fx.setQuality('lite');
console.timeEnd('lite'); // 8~30 ms 首次
console.time('lite-again');
fx.setQuality('lite'); // 无变化,直接返回
console.timeEnd('lite-again'); // < 1 ms
三、渲染分桶 bucket
fx.addPoint('halo', { cad: [x, y], size: 50, bucket: 'topmost' });
| bucket | z-order |
|---|---|
below |
底图 下方;适合地下管线烟雾 |
above(默认) |
底图 + CAD 实体之上 |
topmost |
所有图层之上;告警高亮 / 教学引导 |
默认只有 above 一个渲染层;第一次用 below / topmost 自动创建子层。批量数组里每个元素的 bucket 独立生效。
四、批量 API
| API | 用途 | 性能 |
|---|---|---|
addPoint(preset, opts) |
单点 / 偶发 | 只写 CPU 影子缓冲,下一帧 draw 时合并成一次 bufferSubData |
addPoints(preset, list) |
一次加多点 | 内部循环 addPoint,按 bucket 分组——与同步循环 addPoint 性能相当,胜在写法简洁 |
replaceAllPoints(preset, list) |
周期整数据替换 | 清空该预设默认 above 桶后 addPoints;忽略各项 bucket、不处理 triggerOnce/strikeEvery,并使该预设旧句柄(含 bindToEntity)失效 |
setPointData(handle, patch) |
改单点色 / 大小 / 位置 | 增量写 |
// 1 万火焰
const handles = fx.addPoints('fire', list);
// 周期刷新
fx.replaceAllPoints('fire', newList);
// 单点改色
fx.setPointData(handles[0], { color: [1, 0, 0, 1] });
五、事件 fx.on
| 事件 | payload | 何时 fire |
|---|---|---|
render |
(activeCount) |
每一帧且 ≥ 1 个实例存活 |
empty |
() |
active 从 N 降到 0 时一次 |
overflow |
({ kind, preset, capacity }) |
GPU 容量自动扩容时 |
compact |
({ preset, result }) |
闲置槽位整理完成 |
fx.on('render', active => hud.active = active);
fx.on('empty', () => console.log('全部清空'));
fx.on('overflow', ({ kind, preset, capacity }) =>
console.warn(`扩容 ${kind}/${preset} → ${capacity}`));
fx.on('compact', ({ preset, result }) =>
console.log(`compacted ${preset}, moved=${result?.remap?.size ?? 0}`));
render不在 active=0 的帧 fire → 适合 FPS 采样empty是边沿事件 → 适合 "清场后" 逻辑overflow/compact都是良性提示
六、getMetrics() 实时指标
const arr = fx.getMetrics();
// [
// { bucket: 'above', active: 5234, points: [...], lines: [...], shadertoys: [...] },
// { bucket: 'topmost', active: 12, ... }
// ]
| 字段 | 说明 |
|---|---|
bucket |
'below' / 'above' / 'topmost' |
active |
该桶活跃 FX 实例总数 |
points |
{ preset, live, capacity }[] |
lines |
{ preset, live }[] |
shadertoys |
{ programKey, live, capacity }[] |
调用成本 O(unique presets);随便 setInterval 250ms 也无压力。
七、事件仪表盘工具
function attachDashboard(fx) {
const s = { fps: 0, frameMs: 0, peakMs: 0, renderFrames: 0, emptyFires: 0, overflowFires: 0, compactFires: 0 };
let last = 0; const win = [];
const onRender = () => {
s.renderFrames++;
const now = performance.now();
if (last) {
const dt = now - last;
win.push(dt); if (win.length > 60) win.shift();
s.frameMs = win.reduce((a, b) => a + b, 0) / win.length;
s.peakMs = Math.max(s.peakMs, dt);
s.fps = 1000 / s.frameMs;
}
last = now;
};
fx.on('render', onRender);
fx.on('empty', () => { s.emptyFires++; last = 0; win.length = 0; });
fx.on('overflow', () => s.overflowFires++);
fx.on('compact', () => s.compactFires++);
return s;
}
const stats = attachDashboard(fx);
setInterval(() => console.log(stats), 1000);
八、调优套餐
卡(< 30 FPS)
fx.setQuality('lite')→ 常见 +20% FPS- 查
getMetrics哪个 preset live 最大 → 瓶颈 - 同区域重叠多个 cinematic Shadertoy → 换预设
- Symbol 图标:
fx.symbolImage.addPreset时fps: 24~30
海量点
addPoints与同步循环addPoint都是每帧一次合并上传,差别不大;瓶颈通常在实例数与 shader 档位- 周期刷新走
replaceAllPoints(仅限above桶、且该预设没有bindToEntity绑定时) - 海量同形点 →
fx.symbolImage性能与点数解耦 - 上万点还要 60 FPS → 聚合 (cluster) + symbolImage
偶发卡顿
overflow频繁 → 反复 add/remove 改为setPointData({ visible: false })重用compact触发 → 自动整理,handle 自动 remap,无需手动处理- 切 quality 首次 30ms 编译 → 缓存后 0ms
九、压测参考(中端集成显卡)
| 预设 / 数量 | 1k | 5k | 10k | 20k | 50k |
|---|---|---|---|---|---|
| pulseRing | 60 | 60 | 58 | 50 | 35 |
| fire | 60 | 60 | 55 | 45 | 28 |
| halo | 60 | 60 | 60 | 55 | 40 |
| radarSweep | 60 | 60 | 55 | 42 | 25 |
| meteor | 60 | 60 | 55 | 40 | 22 |
| lightning | 60 | 60 | 50 | 35 | 18 |
数字仅供参考;实际看 GPU / 分辨率 / size 屏占比。
十、常见坑
- setQuality 每帧切:没必要;程序生命周期里通常只切 1~2 次。
- cinematic 开全:中低端机撑不住;给次要特效 lite。
- bucket topmost 滥用:所有 FX 都 topmost → z-order 混乱,UI 遮挡。
- forgot to off fx events:组件卸载时必须
fx.off。 - metrics 拿来做 UI 高频刷新:250ms 够了;别 16ms。
相关
- FX 总览 → fx-overview
- 预设 → fx-presets
- 大量点 → large-markers