Spine и память

Большая часть прошедшей недели для меня прошла под знаком Spine.

Во-первых, с целью покопаться в устройстве Heaps решил портировать для него проигрыватель анимаций Spine. Сделать это оказалось довольно просто, за основу сначала была взята библиотека openfl-spine (сейчас я переключился на spine-hx), в которой есть простой проигрыватель, работающий либо через drawTriangles() либо через Tilemap, а также класс h2d.Graphics, из устройства которого стало понятно, как в Heaps рисовать треугольники. Однако сам класс h2d.Graphics не совсем подходил для этой цели, т.к. каждый раз, когда мы очищаем его геометрию методом clear() (очищая список команд для отрисовки линий, прямоугольников, заливок), происходит удаление и создание новых буферов, то есть данный класс оптимизирован для отрисовки статической геометрии. В моей реализации буфер пересоздается только тогда, когда в него не помещается вся геометрия анимации (то есть очень редко).

Для хранения геометрии в Heaps используется класс h3d.prim.Primitive и его наследники, данные о положении, uv-координатах и цветах вершин хранятся в свойстве buffer:Buffer, а индексы вершин - в свойстве indexes:Indexes. Основными методами примитивов являются alloc(), в котором осуществляется загрузка данных примитива на GPU, и render(), ответственный за отрисовку примитива (делает он это не напрямую, а вызывая методы самого главного класса - Engine, который в свою очередь вызывает методы “класса-драйвера” - платформо-зависимой реализации графических методов).

Сейчас в комплекте c Heaps идут реализации под OpenGL/WebGL, DirectX и Stage3D.

В одной из следующих заметок постараюсь рассмотреть реализацию GlDriver’а.

Вторая часть связана с моей основной работой - появилась необходимость оптимизации потребления памяти. И одним из источников повышенного потребления памяти и генерации мусора является парсинг Spine-анимаций, экспортированных в json. Следовательно, нужно использовать какой-то другой способ хранения и чтения анимаций - и Spine его предоставляет - бинарный формат. Однако я нигде не нашел реализации загрузчика бинарных файлов для Haxe. Поэтому пользуясь документацией и реализацией для C#, пришлось писать свою версию, заняло это почти 3 дня, но сравнительные тесты показали, что время потрачено не зря - сами бинарные файлы занимают меньше места на диске (в 2-3 раза), время загрузки примерно в 10 раз меньше, а мусора они практически не создают:

  1. Загрузка с помощью метода Assets.getText() и парсинг 1000 анимаций, сохраненных в json, на моей машине заняли почти 16 секунд, а потребление памяти приложением составило около 1920 МБ;
  2. Загрузка с помощью метода Assets.getBytes() и парсинг 1000 анимаций, но сохраненных в бинарных skel-файлах, заняла около 3,4 секунды, а потребление памяти составило около 1400 МБ;
  3. Загрузка с помощью потокового чтения бинарных данных с диска с помощью метода sys.io.File.read() и их парсинг (для той же тысячи итераций) показала интересные результаты, выбивающиеся из общей картины. Время загрузки составило 65-70 секунд! А потребление памяти приложением - около 1350 Мб. Таким образом, использование метода File.read() для чтения файлов по отдельности отпадает из-за его крайне низкой скорости.
  4. Далее я решил пойти немного дальше и упаковать 1000 анимаций в один бинарный файл, так чтобы анимации размещались в нем одна за другой, и уже из него читать непрерывные данные. Загрузка такого большого файла с помощью Assets.getBytes() и его парсинг заняли около 2,5 секунд, а потребление памяти составило около 1420 МБ;
  5. Загрузка и чтение того же файла, но методом sys.io.File.read(), заняло примерно столько же времени и памяти (результаты отличаются на 0,04 секунды и примерно 5 МБ в пользу Assets.getBytes()).

Таким образом, вполне естественно будет перейти на использование бинарных файлов, и скорее всего я остановлюсь на использовании метода Assets.getBytes() с загрузкой файлов по отдельности (второй тест), как наиболее оптимальном с точки зрения “время-память”.

Тогда следующей задачей будет автоматизация конвертации анимаций в бинарный формат, т.к. в проекте используется несколько сотен анимаций. Следует поблагодарить авторов Spine, т.к. они предусмотрели такую возможность и экспорт файлов возможен через командную строку. Посмотрим, что из этого выйдет.