Кеширование ресурсов и повторное использование

В графических приложениях на базе PixiJS значительная часть производительности зависит от того, как загружаются, хранятся и повторно используются ресурсы: текстуры, атласы, шрифты, видео и другие данные. Неправильная работа с ними приводит к избыточному потреблению памяти, дублированию объектов в GPU и падению FPS.

В PixiJS реализована встроенная система кеширования, позволяющая:

  • исключить повторную загрузку одних и тех же файлов;
  • переиспользовать текстуры и базовые данные;
  • управлять памятью видеокарты;
  • ускорять создание спрайтов и других графических объектов.

Архитектура хранения ресурсов

Основой кеширования являются три ключевые структуры:

  • Assets — современный API загрузки ресурсов (начиная с PixiJS v7);
  • TextureCache — кеш текстур;
  • BaseTextureCache — кеш базовых текстур (обёртка над GPU-ресурсом).

Assets — централизованная система загрузки

Модуль Assets управляет загрузкой, хранением и повторным использованием ресурсов.

import { Assets } from 'pixi.js';

await Assets.load('images/player.png');

const texture = Assets.get('images/player.png');

При повторном вызове Assets.load() с тем же URL повторная загрузка не происходит — используется кешированная версия.

Принцип работы кеша

  1. При загрузке создаётся BaseTexture.
  2. На его основе создаётся Texture.
  3. Объекты сохраняются во внутренних словарях PixiJS.
  4. Повторный запрос возвращает уже существующий экземпляр.

Это предотвращает повторное создание GPU-ресурсов.


Texture и BaseTexture

BaseTexture

BaseTexture представляет физический ресурс в памяти видеокарты. Если один и тот же файл используется несколькими спрайтами, в памяти хранится только одна BaseTexture.

Texture

Texture — это область внутри BaseTexture. Например, в случае атласа:

  • BaseTexture — весь файл атласа,
  • Texture — отдельный фрагмент внутри него.

Повторное использование текстур:

import { Texture, Sprite } from 'pixi.js';

const texture = Texture.from('images/enemy.png');

const enemy1 = new Sprite(texture);
const enemy2 = new Sprite(texture);

Оба спрайта используют один и тот же GPU-ресурс.


Автоматическое кеширование через Texture.from

Метод Texture.from() автоматически использует глобальный кеш:

const texture1 = Texture.from('images/tree.png');
const texture2 = Texture.from('images/tree.png');

console.log(texture1 === texture2); // true

Повторное создание текстуры не создаёт новый объект, если он уже существует в кеше.


Кеширование атласов текстур

Для игровых приложений рекомендуется использовать текстурные атласы. Они уменьшают количество draw call и ускоряют рендеринг.

Пример загрузки атласа:

await Assets.load('atlas/game.json');

const heroTexture = Texture.from('hero_idle.png');

После загрузки JSON-файла атласа:

  • создаётся одна BaseTexture;
  • создаются множественные Texture;
  • все они автоматически регистрируются в кеше.

Управление памятью

Удаление ресурсов из кеша

Для освобождения памяти используется метод:

await Assets.unload('images/player.png');

Удаляются:

  • текстуры;
  • связанные BaseTexture;
  • данные из кеша.

Важно учитывать, что если текстура используется активными объектами сцены, удаление приведёт к ошибкам рендеринга.


Явное уничтожение текстур

texture.destroy(true);

Параметр true означает удаление связанного BaseTexture. Это необходимо, если больше ни один объект его не использует.


Повторное использование графических объектов

Кеширование ресурсов тесно связано с повторным использованием объектов сцены.

Object Pooling

В игровых сценах (пули, частицы, враги) создание и удаление спрайтов каждую секунду приводит к:

  • нагрузке на сборщик мусора;
  • фрагментации памяти;
  • падению производительности.

Используется пул объектов:

const pool = [];

function getBullet(texture) {
    let bullet = pool.pop();

    if (!bullet) {
        bullet = new Sprite(texture);
    }

    bullet.visible = true;
    return bullet;
}

function releaseBullet(bullet) {
    bullet.visible = false;
    pool.push(bullet);
}

Объекты не уничтожаются — они переиспользуются.


Кеширование графики через cacheAsBitmap

PixiJS позволяет кешировать отображение сложного контейнера в виде текстуры.

container.cacheAsBitmap = true;

При этом:

  • контейнер рендерится в текстуру;
  • последующие кадры используют готовое изображение;
  • уменьшается нагрузка на GPU.

Подходит для:

  • статических UI-элементов;
  • сложных векторных объектов;
  • редко изменяемых контейнеров.

После изменения содержимого необходимо сбросить кеш:

container.cacheAsBitmap = false;
container.cacheAsBitmap = true;

RenderTexture как механизм кеширования

RenderTexture позволяет вручную сохранять результат рендеринга в текстуру.

import { RenderTexture } from 'pixi.js';

const renderTexture = RenderTexture.create({
    width: 800,
    height: 600
});

app.renderer.render(container, { renderTexture });

Это позволяет:

  • создавать мини-карты;
  • реализовывать эффекты постобработки;
  • кешировать сложные сцены;
  • реализовывать слои с редким обновлением.

Кеширование шрифтов и текстовых объектов

Текст в PixiJS может быть дорогим по производительности, особенно при частом обновлении.

BitmapText

Использование Bitmap-шрифтов существенно снижает нагрузку:

import { BitmapText } from 'pixi.js';

const score = new BitmapText('100', {
    fontName: 'ArcadeFont'
});

Глифы заранее загружены и используются как текстуры.


Управление жизненным циклом ресурсов

Рациональное кеширование требует:

  • загрузки ресурсов перед сценой;
  • выгрузки при смене уровня;
  • исключения повторных загрузок.

Типичная структура:

async function loadLevel() {
    await Assets.load([
        'images/background.png',
        'atlas/level1.json'
    ]);
}

async function unloadLevel() {
    await Assets.unload([
        'images/background.png',
        'atlas/level1.json'
    ]);
}

Такой подход предотвращает утечки памяти.


Стратегии оптимизации

1. Минимизировать количество уникальных текстур

Каждая уникальная текстура — это потенциальный новый draw call.

2. Использовать атласы

Один атлас вместо десятков отдельных PNG-файлов.

3. Избегать частого destroy/create

Предпочитать скрытие и переиспользование.

4. Контролировать размеры текстур

Слишком большие изображения увеличивают потребление видеопамяти.

5. Следить за GPU-памятью

Особенно важно в мобильных браузерах.


Проблемы при неправильном кешировании

  1. Дублирование текстур при разных путях к одному файлу.
  2. Утечки памяти при забытом destroy.
  3. Повторная загрузка ресурсов при переходах между сценами.
  4. Использование cacheAsBitmap для часто изменяемых объектов.

Практический пример архитектуры сцены

  1. Глобальный загрузчик ресурсов.
  2. Переиспользование текстур через Texture.from.
  3. Пул объектов для динамических элементов.
  4. Удаление ресурсов при выгрузке уровня.
  5. Использование атласов вместо отдельных файлов.

Такой подход обеспечивает:

  • стабильный FPS;
  • минимальное количество draw call;
  • контролируемое использование памяти;
  • предсказуемый жизненный цикл ресурсов.

Грамотное кеширование и повторное использование — фундамент производительных приложений на PixiJS.