性能优化
PixiJS v8 应用性能优化:定位瓶颈后按需应用销毁…
当 PixiJS v8 应用出现 FPS 下降、卡顿、draw call 过多或 GPU 内存泄漏时,先做性能剖析再针对性优化。
🧪 尚未被验证(还没有小呱复用过的记录,不代表不好)
GDI C 49/100
进化评分(GDI)
综合排序依据 —— 四个维度的加权结果,让你看出「为什么这条排在前面」
内在质量 85/100 ×0.35
使用度 0/100 ×0.30
口碑 50/100 ×0.20
新鲜度 60/100 ×0.15
适用场景
PixiJS v8 应用性能优化:定位瓶通用
解决思路
- 先用浏览器 DevTools Performance 与 GPU profiling 定位瓶颈,再应用对应的销毁、池化、批处理、缓存或剔除模式,不要盲目优化。
- 销毁对象时按需选择:仅销毁 sprite 用 sprite.destroy();连纹理一起销毁用 sprite.destroy({ children: true, texture: true, textureSource: true });彻底不再使用某资源时调用 Assets.unload('character.png') 从缓存移除并卸载 GPU 资源;自定义渲染中自己创建的 geometry 和 renderTarget 要调用 geometry.destroy() 和 renderTarget.destroy()。
- 销毁并重建 Application 时必须用 app.destroy({ releaseGlobalResources: true }),否则旧 app 的池化批次和纹理会泄漏到新 app,导致闪烁和画面损坏。
- 通过 Application init 选项调节纹理 GC:gcActive: true、gcMaxUnusedTime(空闲多久后清理,默认 60000ms)、gcFrequency(检查间隔,默认 30000ms);注意 textureGC.* 属性自 8.15.0 起已废弃。需要立即释放 GPU 内存时手动调用 texture.source.unload()。
- 用 PrepareSystem 预上传避免首帧卡顿:import 'pixi.js/prepare',在 app.stop() 后 await app.renderer.prepare.upload(app.stage) 再 app.start();upload() 可接受 Container(上传子树中所有纹理、文本、图形)或单个资源。
- 对复杂静态内容用 cacheAsTexture(true) 将子树渲染为单张纹理以减少 draw call,可传 { resolution: 2, antialias: true };修改子节点后调用 updateCacheTexture() 刷新;销毁前必须先 cacheAsTexture(false)。
- 查询 GPU 最大纹理尺寸以判断能否缓存:WebGPU 用 renderer.gpu.device.limits.maxTextureDimension2D,WebGL 用 renderer.gl.getParameter(WebGL2RenderingContext.MAX_TEXTURE_SIZE)。
- 用对象池复用对象而非销毁重建:从池中 pop 出对象后重置 texture、position、rotation、scale、alpha、tint、blendMode、visible 等属性;释放时设 visible = false 并 push 回池。
- 优化批处理:把相同类型对象(Sprite 与 Sprite、Graphics 与 Graphics)连续排列,避免 sprite/graphic 交替;同样按 blendMode 分组排列,减少批中断。
- 用 spritesheet 图集代替大量独立纹理:await Assets.load('game-atlas.json') 后通过 sheet.textures['hero.png'] 取帧,所有帧共享一张 GPU 纹理以启用批处理;半分辨率图集用 @0.5x 文件名后缀让 PixiJS 自动缩放。
- 频繁变化的文本用 BitmapText(如分数、计时器、计数器),它从预生成字形图集渲染,更新只重定位四边形,不重渲染 canvas 也不上传 GPU;若必须用 canvas Text,则仅在值真正变化时才更新。
- 可通过 text.resolution = 1 独立降低文本分辨率以减少高 DPI 下的 GPU 内存。
- 复杂 Graphics 用 app.renderer.generateTexture(complex) 渲染成纹理后用 Sprite 显示;约 100 点以下的小 Graphics 可像 Sprite 一样批处理。
- 启用剔除:在 Application.init 前 extensions.add(CullerPlugin),对可能离屏的对象设 sprite.cullable = true;可设 sprite.cullArea = new Rectangle(0, 0, 800, 600) 避免每帧计算边界;对静态 UI 设 uiRoot.cullableChildren = false 跳过整棵子树;也可不用插件手动调用 Culler.shared.cull(app.stage, app.renderer.screen)。
- 在移动端优先用 resolution: 1、antialias: false、backgroundAlpha: 1(不透明背景更快),因为 resolution: 2 会使像素数翻四倍。
- 低层优化:用 RenderTexture.create({ width: 1024, height: 1024, antialias: true, transient: true }) 创建单 pass 抗锯齿渲染纹理(transient 仅 WebGPU,仅用于单 pass 渲染且不会以 clear: false 重入或被滤镜包裹的纹理);用 buffer.update(changedBytes, offsetBytes) 只上传大缓冲区中变化的字节范围;WebGPU 上静态自定义绘制序列可录制 render bundle 后每帧重放。
- 批量销毁纹理时分帧错开:用 ticker 回调每帧销毁约 5 个纹理,避免单帧冻结。
- 滤镜与遮罩优化:已知边界时设 container.filterArea = new Rectangle(x, y, w, h) 避免每帧测量;用 container.filters = null 释放滤镜内存;全屏模糊设 repeatEdgePixels: true 保持屏幕尺寸;遮罩成本从低到高为轴对齐 Rectangle(scissor rect)< Graphics(stencil buffer)< Sprite/alpha(滤镜管线),优先用矩形遮罩;无交互子节点的容器设 interactiveChildren = false;大容器设 hitArea 跳过递归命中测试。
- 安全销毁顺序:先从父节点 removeChild 再 destroy;若必须帧中销毁,用 app.ticker.addOnce(() => { parent.removeChild(sprite); sprite.destroy(); }) 延迟执行。
- 避免在场景图中交替排列对象类型(sprite/graphic/sprite/graphic 为 4 次 draw call,分组后为 2 次),blendMode 同理。
- 避免每帧无条件更新 Text 或 HTMLText(每次更新都重渲染 canvas 并上传 GPU)。
- 避免使用超过 4096px 的纹理,部分移动 GPU 会失败。
- 避免在未做性能剖析的情况下启用高 resolution 或 antialias。
适用边界 · 注意事项
- 当应用是 CPU 瓶颈时不要启用剔除,因为逐对象边界检查会增加 CPU 开销。
- 不要反复切换 cacheAsTexture 开关,持续重新缓存会抵消收益;也不要缓存稀疏容器(收益可忽略)或大于 4096x4096 的容器。
- transient: true 仅用于单 pass 渲染且不会以 clear: false 重入或被滤镜包裹的纹理,不要用于其他场景。