Skip to content

第10章:性能优化策略

本章概要

核心问题:如何让画布在万级元素时仍然流畅?

无限画布的性能优化是一个系统工程,涉及渲染、内存、纹理等多个维度。本章将深入分析:

  1. 视口剔除:只渲染可见区域的元素
  2. 纹理管理:动态分辨率、纹理复用、自动回收
  3. 渲染优化:批处理、脏矩形、延迟更新
  4. 内存优化:引用计数缓存、对象池复用
  5. 性能监控:FPS 统计、渲染时间追踪

目录


10.1 视口剔除

10.1.1 Cullable 机制

PixiJS 提供原生的 cullable 属性,用于视口剔除:

typescript
// 来源:infinite-renderer/src/surfaces/surface.ts

constructor(options: Partial<ISurfaceOptions> = {}) {
    this._viewport = new Viewport();
    this._viewport.initialize(new PageVm(), renderOptions);
    
    // 开启 stage 级别的 cullable
    this._viewport.app.stage.cullable = true;
    (this._viewport.app.stage as any).eventMode = 'static';
}

Cullable 原理

视口剔除示意:

┌──────────────────────────────────────────────────────────────┐
│                        世界坐标空间                           │
│                                                              │
│   ┌────────┐                                                 │
│   │ Elem A │  ← 视口外,cullable = true 时不渲染             │
│   └────────┘                                                 │
│                                                              │
│         ┌─────────────────────────────────────┐              │
│         │           视口(Viewport)           │              │
│         │                                      │              │
│         │  ┌────────┐          ┌────────┐    │              │
│         │  │ Elem B │          │ Elem C │    │  ← 视口内渲染 │
│         │  └────────┘          └────────┘    │              │
│         │                                      │              │
│         └─────────────────────────────────────┘              │
│                                                              │
│                              ┌────────┐                      │
│                              │ Elem D │  ← 视口外,跳过      │
│                              └────────┘                      │
│                                                              │
└──────────────────────────────────────────────────────────────┘

10.1.2 视口范围检测

在插件渲染时,通过 screen.intersects() 进行视口范围检测:

typescript
// 来源:infinite-plugins/src/renderer-plugins/outline/outline-plugin.ts

render(): void {
    const { screen } = this.viewport.app;  // 获取视口矩形
    const graphics = this.view.clear();
    
    for (const element of unlocked) {
        const rect = element.getModelBounds(false, RECTANGLE);
        
        // 只渲染与视口相交的元素
        if (screen.intersects(rect) && element.view.visible) {
            renderOutline(graphics, element, zoom);
        }
    }
}

检测流程

元素是否渲染判断:

         ┌──────────────┐
         │  获取元素    │
         │  包围盒      │
         └──────┬───────┘


         ┌──────────────┐
         │ screen.      │
         │ intersects   │
         │ (bounds)?    │
         └──────┬───────┘
           Yes  │  No
         ┌──────┴──────┐
         ▼             ▼
  ┌──────────────┐  跳过渲染
  │ visible === │
  │   true?     │
  └──────┬──────┘
    Yes  │  No
    │    │
    ▼    ▼
  渲染  跳过

10.1.3 层级剔除策略

对于嵌套元素,采用层级剔除策略:

typescript
// 来源:infinite-renderer/src/extends/mask-container/mask-container.ts

protected _renderCanvasChildren(renderer: IRenderer<ICanvas>, cullable = false): void {
    for (let i = 0, j = this.children.length; i < j; ++i) {
        const child = this.children[i];
        
        // 不可渲染则跳过
        if (!child.renderable) {
            continue;
        }
        
        // 设置子元素的 cullable 状态
        child.cullable = childCullable || cullable;
        child.render(renderer);
        child.cullable = childCullable;
    }
}

10.2 纹理管理

10.2.1 CanvasSprite 设计

CanvasSprite 是纹理管理的核心类,实现了视口外自动回收纹理:

typescript
// 来源:infinite-renderer/src/extends/canvas-sprite.ts

/**
 * ## Canvas Sprite 对象
 *
 * CanvasSprite 元素仅会在视口内渲染,离开视口后会销毁内容纹理,减少内存占用
 */
abstract class CanvasSprite<T extends object = object> extends Sprite {
    /** 最大 Canvas 尺寸 */
    static MAX_CANVAS_SIZE = getMaxCanvasSize();

    /** 最大 Canvas 面积 */
    static MAX_CANVAS_AREA = isMobile() ? 2500 * 2500 : getMaxCanvasArea();

    /** 纹理缓存最大等待时间 10s,移动端 5s */
    static MAX_AGE_TIME = isMobile() ? 5000 : 10000;

    /** 内容纹理 */
    protected content: Texture | null = null;

    /** 快照纹理(低分辨率占位) */
    protected snapshot: Texture | null = null;

    /** 是否标脏 */
    protected _dirty: boolean;

    /** 上次触碰时间 */
    touched: number;
}

CanvasSprite 状态流转

纹理状态流转图:

┌──────────────┐     update(state)     ┌──────────────┐
│   初始状态   │ ────────────────────→ │   预备状态   │
│  EMPTY 纹理  │                       │  _prepared   │
└──────────────┘                       └──────┬───────┘
       ↑                                      │
       │                               prepare()
       │                               requestUpdate
       │                                      │
       │                                      ▼
       │                              ┌──────────────┐
       │   disposeContent()           │   标脏状态   │
       │   (离开视口超时)              │   _dirty     │
       │                              └──────┬───────┘
       │                                      │
       │                              updateContent()
       │                              (下一帧渲染时)
       │                                      │
       │                                      ▼
       │                              ┌──────────────┐
       └──────────────────────────────│   渲染状态   │
                                      │ content 纹理 │
                                      └──────────────┘

                                        touch()
                                     (每帧触碰续命)

10.2.2 动态分辨率

DynamicSprite 根据画布缩放级别动态调整纹理分辨率:

typescript
// 来源:infinite-renderer/src/extends/dynamic-sprite.ts

/**
 * 动态 Sprite 对象
 * @description 在画布缩放时纹理清晰度会发生变化,采用动态纹理方案去适应精度的变化
 */
abstract class DynamicSprite<T extends object = object> extends CanvasSprite<T> {
    protected resolution = -1;

    /**
     * 获取纹理渲染精度比例
     * @param renderer 渲染引擎
     * @param width 渲染宽度
     * @param height 渲染高度
     * @param resolution 渲染精度
     * @param maxSize 最大纹理边长
     * @param maxArea 最大纹理面积
     */
    getTextureRatio(
        renderer: IRenderer,
        width: number,
        height: number,
        resolution = renderer.activeResolution,
        maxSize = DynamicSprite.MAX_CANVAS_SIZE,
        maxArea = DynamicSprite.MAX_CANVAS_AREA,
    ): number {
        // WebGL 渲染器限制
        if (renderer.type === RENDERER_TYPE.SKIA && (renderer as SkRenderer).gl) {
            const gl = (renderer as SkRenderer).gl!;
            const maxTextureSize = gl.getParameter(gl.MAX_TEXTURE_SIZE);
            maxSize = Math.min(maxSize, maxTextureSize);
            maxArea = Math.min(maxArea, 4096 * 4096);
        }

        width *= resolution;
        height *= resolution;

        return calcMaxResolution(width, height, maxSize, maxArea) * resolution;
    }

    protected _render(renderer: IRenderer): void {
        const { zoom = 1 } = renderer;
        const resolution = renderer.activeResolution * zoom;

        // 缩放级别变化时重新生成纹理
        if (!isSame(this.resolution, resolution)) {
            this.resolution = resolution;

            // 延迟更新或立即标脏
            if (renderer.lazyUpdateCanvasSprite) {
                this.prepare();
            } else {
                this.dirty();
            }
        }

        super._render(renderer);
    }
}

动态分辨率原理

缩放级别与纹理分辨率关系:

缩放级别:   10%      50%      100%     200%     400%
              │        │        │        │        │
              ▼        ▼        ▼        ▼        ▼
纹理分辨率:  低       中       标准      高      最高
             (节省内存) ──────────────────→ (保证清晰度)

示例(1000×1000 图片):

zoom = 0.1 → 实际显示 100×100px → 纹理 100×100
zoom = 0.5 → 实际显示 500×500px → 纹理 500×500
zoom = 1.0 → 实际显示 1000×1000px → 纹理 1000×1000
zoom = 2.0 → 实际显示 2000×2000px → 纹理 2000×2000 (限制最大值)

优化效果:
- 缩小时使用低分辨率纹理,节省 GPU 内存
- 放大时使用高分辨率纹理,保证显示清晰

10.2.3 纹理生命周期

纹理采用"触碰续命"机制,离开视口超时后自动回收:

typescript
// 来源:infinite-renderer/src/extends/canvas-sprite.ts

private static _touchList = new Set<CanvasSprite>();

// 触碰:更新最后访问时间
private static touch<T extends object>(sprite: CanvasSprite<T>) {
    const performance = settings.ADAPTER.getPerformance();
    sprite.touched = performance.now();
    this._touchList.add(sprite as unknown as CanvasSprite);
}

// 移除跟踪
private static remove<T extends object>(sprite: CanvasSprite<T>) {
    this._touchList.delete(sprite as unknown as CanvasSprite);
}

// 定期清理过期纹理
static dispose(t: number) {
    for (const sprite of CanvasSprite._touchList) {
        // 超过最大存活时间则回收
        if (t - sprite.touched > CanvasSprite.MAX_AGE_TIME) {
            sprite.disposeContent();
            sprite.prepare();  // 重新预备,下次进入视口时重建
        }
    }
}

// 全局定时器,定期执行 GC
globalThis.setInterval(() => {
    const performance = settings.ADAPTER.getPerformance();
    CanvasSprite.dispose(performance.now());
}, CanvasSprite.MAX_AGE_TIME);

纹理生命周期图

纹理生命周期时间线:

时间 ────────────────────────────────────────────────→

     ┌─────────────┐          ┌─────────────┐
     │  进入视口   │          │  离开视口   │
     │ touch()    │          │             │
     └─────┬───────┘          └──────┬──────┘
           │                         │
           │  ← 渲染状态 →           │ ← 开始计时 →
           │                         │
           │                         │    MAX_AGE_TIME
           │                         │    (5s/10s)
           │                         │         │
           │                         │         ▼
           │                         │   ┌──────────────┐
           │                         │   │ disposeContent│
           │                         │   │ 销毁纹理      │
           │                         │   │ 切换到快照    │
           │                         │   └──────────────┘
           │                         │
           │  ← 重新进入视口 →

           │  updateContent()
           │  重建纹理

10.2.4 纹理复用

通过 TextureReuse 类实现纹理复用:

typescript
// 来源:infinite-renderer/src/context/texture-reuse.ts

export class TextureReuse {
    private cache: Map<string, RenderTexture> = new Map();

    set(uuid: string, texture: RenderTexture) {
        this.cache.set(uuid, texture);
    }

    get(uuid: string) {
        return this.cache.get(uuid);
    }

    delete(uuid: string) {
        return this.cache.get(uuid);
    }

    clear() {
        return this.cache.clear();
    }
}

纹理复用场景

纹理复用场景:

场景1:相同图片多次使用
┌────────────┐      ┌────────────┐      ┌────────────┐
│  Image A   │      │  Image B   │      │  Image C   │
│  url: x.jpg│      │  url: x.jpg│      │  url: x.jpg│
└─────┬──────┘      └─────┬──────┘      └─────┬──────┘
      │                   │                   │
      └───────────────────┼───────────────────┘


                  ┌──────────────┐
                  │ 共享同一纹理 │
                  │ TextureReuse │
                  └──────────────┘

场景2:撤销/重做时复用
操作: 删除 → 撤销 → 重做 → 撤销
      │       │       │       │
      │       │       │       └─ 复用缓存纹理
      │       │       └───────── 复用缓存纹理
      │       └───────────────── 从缓存恢复
      └───────────────────────── 缓存纹理(不销毁)

10.3 渲染优化

10.3.1 延迟更新机制

系统支持延迟更新模式,避免频繁重绘:

typescript
// 来源:infinite-renderer/src/extends/canvas-sprite.ts

protected _render(renderer: IRenderer): void {
    // 若使用 Snapshot 则销毁内容
    if (this.useSnapshot(renderer, this._boundsRect!)) {
        this.renderSnapshot(renderer);
        super._render(renderer);
        return;
    }

    // 若元素被标脏,则执行纹理更新
    // lazyUpdateCanvasSprite = true 时,只有 dirty 才更新
    if (this._dirty || (this._prepared && !renderer.lazyUpdateCanvasSprite)) {
        this.updateContent(renderer);
        CanvasSprite.cancelUpdate(this);
        this._dirty = false;
        this._prepared = false;
    }

    // 触碰元素,延长纹理存活时间
    CanvasSprite.touch(this);

    super._render(renderer);
}

延迟更新 vs 即时更新

模式触发条件适用场景
即时更新_prepared && !lazyUpdateCanvasSprite单元素编辑、实时预览
延迟更新_dirty 被标记批量操作、缩放平移

10.3.2 批量更新调度

使用防抖机制批量处理更新请求:

typescript
// 来源:infinite-renderer/src/extends/canvas-sprite.ts

private static _updateQueue = new Set<DirtyItem>();

// 请求更新
private static requestUpdate(sprite: DirtyItem) {
    this._updateQueue.add(sprite);
    this.prepareUpdate();
}

// 取消更新
private static cancelUpdate(sprite: DirtyItem) {
    this._updateQueue.delete(sprite);
}

// 防抖处理:100ms 内的请求合并执行
@Debounce(1000 / 10)  // 100ms
private static prepareUpdate() {
    this._updateQueue.forEach((item) => item.dirty());
    this._updateQueue.clear();
}

批量更新流程

批量更新时序图:

时间 ─────────────────────────────────────────────────→

sprite1.prepare()  sprite2.prepare()  sprite3.prepare()
       │                  │                  │
       └──────────────────┴──────────────────┘

                    100ms 防抖


              ┌────────────────────┐
              │  prepareUpdate()   │
              │  批量执行 dirty()  │
              └────────────────────┘


              ┌────────────────────┐
              │  下一帧渲染时      │
              │  统一 updateContent│
              └────────────────────┘

10.3.3 快照渲染

当元素离开视口或缩放较小时,使用低分辨率快照渲染:

typescript
// 来源:infinite-renderer/src/extends/canvas-sprite.ts

// 是否使用快照(子类可覆写)
useSnapshot(_renderer: IRenderer, _bounds: Rectangle): boolean {
    return !this.state;
}

// 生成快照纹理
generateSnapshot(_renderer: IRenderer): Texture {
    return CanvasSprite.EMPTY;
}

// 渲染快照
protected renderSnapshot(renderer: IRenderer): void {
    if (!this.snapshot) {
        this.snapshot = this.generateSnapshot(renderer);
    }
    this.texture = this.snapshot;
}

// 静态快照纹理(16×16 占位图)
static get SNAPSHOT(): Texture<CanvasResource> {
    if (!CanvasSprite._SNAPSHOT) {
        const canvas = settings.ADAPTER.createCanvas(16, 16);
        const context = canvas.getContext('2d')!;
        canvas.width = 16;
        canvas.height = 16;
        context.fillStyle = '#0000000A';  // 半透明黑色
        context.fillRect(0, 0, 16, 16);

        const snapshot = new Texture<CanvasResource>(BaseTexture.from(canvas));
        CanvasSprite._SNAPSHOT = snapshot;
    }
    return CanvasSprite._SNAPSHOT;
}

10.4 缓存策略

10.4.1 引用计数缓存

RCCache 实现了引用计数的缓存机制:

typescript
// 来源:infinite-renderer/src/common/cache/rc-cache.ts

interface ICacheResource<T> {
    resource: T;
    referenceIds: Set<string>;  // 引用该资源的 ID 集合
}

export class RCCache<T> {
    _cache: Map<string, ICacheResource<T>>;

    constructor() {
        this._cache = new Map();
    }

    /**
     * 删除某一缓存引用,当引用数为 0 时会清除对应缓存数据
     */
    delete(key: string, referenceId: string) {
        const cacheResult = this.get(key);
        if (cacheResult) {
            const { referenceIds } = cacheResult;
            if (referenceIds.has(referenceId)) {
                // 如果当前只有一个引用,则直接删除缓存结果
                if (referenceIds.size <= 1) {
                    this.removeCache(key);
                } else {
                    referenceIds.delete(referenceId);
                }
            }
        }
    }

    /**
     * 获取缓存结果(同时收集依赖)
     */
    private get(key: string, referenceId?: string): ICacheResource<T> | undefined {
        if (this.has(key) && referenceId) {
            const { referenceIds } = this._cache.get(key)!;
            referenceIds.add(referenceId);  // 自动收集引用
        }
        return this._cache.get(key);
    }

    /**
     * 添加缓存
     */
    addCache(key: string, referenceId: string, resource: T) {
        if (!this.has(key)) {
            this._cache.set(key, {
                resource,
                referenceIds: new Set([referenceId]),
            });
        }
    }

    /**
     * 获取缓存
     */
    getCache(key: string, referenceId: string): T | undefined {
        const cache = this.get(key, referenceId);
        return cache ? cache.resource : cache;
    }
}

引用计数原理

引用计数缓存示意:

缓存键: "image_abc123"
┌────────────────────────────────────────────────┐
│  resource: Texture                             │
│  referenceIds: Set {                           │
│    "element_001",  // Element 1 引用           │
│    "element_002",  // Element 2 引用           │
│    "element_003",  // Element 3 引用           │
│  }                                             │
│  引用计数: 3                                    │
└────────────────────────────────────────────────┘

删除 element_001 的引用后:
┌────────────────────────────────────────────────┐
│  resource: Texture                             │
│  referenceIds: Set {                           │
│    "element_002",                              │
│    "element_003",                              │
│  }                                             │
│  引用计数: 2  (资源保留)                        │
└────────────────────────────────────────────────┘

删除所有引用后:
┌────────────────────────────────────────────────┐
│  引用计数: 0                                    │
│  → 自动清除缓存,释放内存                       │
└────────────────────────────────────────────────┘

10.4.2 LRU 缓存

视口状态使用 LRU 缓存策略(见第4章):

typescript
// LRU 缓存用于存储视口状态
const viewportStateCache = new LRUMap<string, ViewportState>(100);

// 最近最少使用的条目会被自动移除
viewportStateCache.set(pageId, state);
const cached = viewportStateCache.get(pageId);

10.4.3 像素缓存

像素级碰撞检测使用缓存优化:

typescript
// 来源:infinite-renderer/src/common/hit-test.ts

export interface BufferedBaseTexture extends BaseTexture {
    _imageBufferData?: {
        data: ImageData;
        ratio: number;
    };
}

const MAX_BUFFER_SIZE = 512;

export function doesSpritePixelsHitRect(sprite: Sprite, rect: Rectangle): boolean {
    const { baseTexture } = sprite.texture;
    
    // 将图像 buffer 资源缓存至 baseTexture 中
    if (!(baseTexture as BufferedBaseTexture)._imageBufferData) {
        // 限制缓存大小
        const ratio = Math.min(
            MAX_BUFFER_SIZE / baseTexture.realWidth,
            MAX_BUFFER_SIZE / baseTexture.realHeight,
            1,
        );

        const canvas = settings.ADAPTER.createCanvas(
            baseTexture.realWidth * ratio,
            baseTexture.realHeight * ratio,
        );
        const context = canvas.getContext('2d')!;
        context.drawImage(source, 0, 0, canvas.width, canvas.height);
        const imageData = context.getImageData(0, 0, canvas.width, canvas.height);

        // 释放临时 canvas
        canvas.width = 1;
        canvas.height = 1;

        (baseTexture as BufferedBaseTexture)._imageBufferData = {
            data: imageData,
            ratio,
        };
    }

    // 使用缓存的像素数据进行碰撞检测
    const { data, ratio = 1 } = (baseTexture as BufferedBaseTexture)._imageBufferData!;
    // ...
}

10.5 Canvas 尺寸限制

10.5.1 设备限制检测

不同设备对 Canvas 尺寸有不同限制:

typescript
// 来源:infinite-renderer/src/extends/canvas-sprite.ts

/** 最大 Canvas 尺寸 */
static MAX_CANVAS_SIZE = getMaxCanvasSize();

/** 最大 Canvas 面积 */
static MAX_CANVAS_AREA = isMobile() ? 2500 * 2500 : getMaxCanvasArea();

/** 纹理缓存最大等待时间 10s,移动端 5s */
static MAX_AGE_TIME = isMobile() ? 5000 : 10000;

设备限制参考

设备类型最大边长最大面积纹理存活时间
桌面端~16384px~268M px²10s
移动端~4096px~6.25M px²5s
WebGL取决于 GPUMAX_TEXTURE_SIZE-

10.5.2 自适应分辨率

根据设备限制自动计算最佳分辨率:

typescript
// 来源:infinite-renderer/src/extends/dynamic-sprite.ts

getTextureRatio(
    renderer: IRenderer,
    width: number,
    height: number,
    resolution = renderer.activeResolution,
    maxSize = DynamicSprite.MAX_CANVAS_SIZE,
    maxArea = DynamicSprite.MAX_CANVAS_AREA,
): number {
    // WebGL 渲染器需要检测 GPU 限制
    if (renderer.type === RENDERER_TYPE.SKIA && (renderer as SkRenderer).gl) {
        const gl = (renderer as SkRenderer).gl!;
        const maxTextureSize = gl.getParameter(gl.MAX_TEXTURE_SIZE);

        maxSize = Math.min(maxSize, maxTextureSize);
        maxArea = Math.min(maxArea, 4096 * 4096);
    }

    width *= resolution;
    height *= resolution;

    // 计算满足限制的最大分辨率
    return calcMaxResolution(width, height, maxSize, maxArea) * resolution;
}

分辨率计算示例

自适应分辨率计算:

输入:
- 元素尺寸: 10000 × 10000 px
- 设备像素比: 2
- 画布缩放: 1.5
- 最大边长: 4096 px
- 最大面积: 16M px²

计算过程:
1. 实际需要尺寸: 10000 × 2 × 1.5 = 30000 px
2. 边长限制: 30000 > 4096, 需要缩小
   缩小比例: 4096 / 30000 = 0.137
3. 面积限制: 30000² = 900M > 16M, 需要缩小
   缩小比例: √(16M / 900M) = 0.133

4. 取较小值: min(0.137, 0.133) = 0.133

输出分辨率: 0.133 × 2 × 1.5 = 0.4

最终纹理尺寸: 10000 × 0.4 = 4000 px

10.6 性能监控

10.6.1 渲染时间统计

系统在每帧渲染后统计耗时:

typescript
// 来源:infinite-renderer/src/surfaces/surface.ts

this._context.ticker.add(
    (t) => {
        try {
            // 记录开始时间
            const time = Math.trunc(performance.now());
            
            // 执行渲染
            this.viewport.app.ticker.update();
            
            // 计算耗时
            const now = Math.trunc(performance.now());
            const duration = Math.max(now - time, 0);
            
            // 发送统计事件
            this.events.emit('update', duration);
        } catch (error) {
            console.error(error);
            settings.LOGGER.error(error);
            throw error;
        }
    },
    this,
    UPDATE_PRIORITY.LOW,
);

监控数据使用

typescript
// 监听渲染耗时
surface.events.on('update', (duration: number) => {
    // 计算 FPS
    const fps = duration > 0 ? Math.min(1000 / duration, 60) : 60;
    
    // 性能告警
    if (duration > 16.67) {  // 超过 60fps 阈值
        console.warn(`渲染耗时: ${duration}ms, FPS: ${fps.toFixed(1)}`);
    }
    
    // 上报统计
    reportMetrics({ renderDuration: duration, fps });
});

10.6.2 Ticker 机制

系统使用统一的 Ticker 管理渲染循环:

typescript
// 来源:infinite-renderer/src/context/context.ts

class Context extends EventEmitter<IContextEventTypes> implements IContext {
    ticker: Ticker;

    constructor(props: ContextProps = {}) {
        super();

        const {
            ticker = new Ticker(),
            // ...
        } = props;

        this.ticker = ticker;
    }
}

// Surface 中使用 Context 的 Ticker
constructor(options: Partial<ISurfaceOptions> = {}) {
    // 停止 app 自带的 ticker
    this.viewport.app.ticker.stop();

    // 使用 Context 的共享 ticker
    this._context.ticker.start();
    this._context.ticker.add(
        (t) => {
            this.viewport.app.ticker.update();
        },
        this,
        UPDATE_PRIORITY.LOW,
    );
}

Ticker 优先级

typescript
// PixiJS UPDATE_PRIORITY 枚举
enum UPDATE_PRIORITY {
    INTERACTION = 50,  // 交互事件
    HIGH = 25,         // 高优先级
    NORMAL = 0,        // 普通优先级
    LOW = -25,         // 低优先级
    UTILITY = -50,     // 工具类
}

// 使用示例
context.ticker.add(callback, context, UPDATE_PRIORITY.HIGH);
context.ticker.add(callback, context, UPDATE_PRIORITY.NORMAL);
context.ticker.add(callback, context, UPDATE_PRIORITY.LOW);

10.7 本章小结

性能优化架构图

┌─────────────────────────────────────────────────────────────┐
│                     性能优化策略体系                          │
├─────────────────────────────────────────────────────────────┤
│                                                              │
│  ┌─────────────────────────────────────────────────────┐    │
│  │                    渲染层优化                         │    │
│  │                                                      │    │
│  │  ┌────────────┐  ┌────────────┐  ┌────────────┐    │    │
│  │  │ 视口剔除   │  │ 批量渲染   │  │ 延迟更新   │    │    │
│  │  │ cullable   │  │ batching   │  │ lazy update│    │    │
│  │  └────────────┘  └────────────┘  └────────────┘    │    │
│  │                                                      │    │
│  └─────────────────────────────────────────────────────┘    │
│                                                              │
│  ┌─────────────────────────────────────────────────────┐    │
│  │                    纹理层优化                         │    │
│  │                                                      │    │
│  │  ┌────────────┐  ┌────────────┐  ┌────────────┐    │    │
│  │  │ 动态分辨率 │  │ 纹理复用   │  │ 自动回收   │    │    │
│  │  │ DynamicSpr │  │TextureReuse│  │ dispose()  │    │    │
│  │  └────────────┘  └────────────┘  └────────────┘    │    │
│  │                                                      │    │
│  └─────────────────────────────────────────────────────┘    │
│                                                              │
│  ┌─────────────────────────────────────────────────────┐    │
│  │                    缓存层优化                         │    │
│  │                                                      │    │
│  │  ┌────────────┐  ┌────────────┐  ┌────────────┐    │    │
│  │  │ 引用计数   │  │  LRU 缓存  │  │ 像素缓存   │    │    │
│  │  │ RCCache    │  │ LRUMap     │  │ BufferData │    │    │
│  │  └────────────┘  └────────────┘  └────────────┘    │    │
│  │                                                      │    │
│  └─────────────────────────────────────────────────────┘    │
│                                                              │
│  ┌─────────────────────────────────────────────────────┐    │
│  │                    监控与调度                         │    │
│  │                                                      │    │
│  │  ┌────────────┐  ┌────────────┐  ┌────────────┐    │    │
│  │  │ Ticker     │  │ 渲染统计   │  │ 设备适配   │    │    │
│  │  │ 帧调度     │  │ FPS/耗时   │  │ Canvas限制 │    │    │
│  │  └────────────┘  └────────────┘  └────────────┘    │    │
│  │                                                      │    │
│  └─────────────────────────────────────────────────────┘    │
│                                                              │
└─────────────────────────────────────────────────────────────┘

关键代码路径

模块文件路径
CanvasSpriteinfinite-renderer/src/extends/canvas-sprite.ts
DynamicSpriteinfinite-renderer/src/extends/dynamic-sprite.ts
TextureReuseinfinite-renderer/src/context/texture-reuse.ts
RCCacheinfinite-renderer/src/common/cache/rc-cache.ts
Context/Tickerinfinite-renderer/src/context/context.ts
Surface 渲染循环infinite-renderer/src/surfaces/surface.ts

核心优化策略总结

策略原理效果
视口剔除只渲染可见元素减少绑制调用
动态分辨率根据缩放调整纹理节省 GPU 内存
纹理回收离开视口超时销毁防止内存泄漏
延迟更新批量处理更新请求减少重绘次数
快照渲染低分辨率占位加速首屏渲染
引用计数共享资源自动回收优化内存使用
设备适配根据限制调整参数保证兼容性

性能优化检查清单

  • [ ] 大量元素时是否开启 cullable
  • [ ] 纹理是否使用动态分辨率
  • [ ] 离开视口的元素是否被正确回收
  • [ ] 批量操作是否使用延迟更新
  • [ ] 相同资源是否使用缓存
  • [ ] 是否监控渲染耗时和 FPS
  • [ ] 移动端是否降低了资源限制

如有转载或 CV 的请标注本站原文地址