Большая часть прошедшей недели для меня прошла под знаком 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 раз меньше, а мусора они практически не создают:
Assets.getText()
и парсинг 1000 анимаций, сохраненных в json, на моей машине заняли почти 16 секунд, а потребление памяти приложением составило около 1920 МБ;
Assets.getBytes()
и парсинг 1000 анимаций, но сохраненных в бинарных skel-файлах, заняла около 3,4 секунды, а потребление памяти составило около 1400 МБ;
sys.io.File.read()
и их парсинг (для той же тысячи итераций) показала интересные результаты, выбивающиеся из общей картины. Время загрузки составило 65-70 секунд! А потребление памяти приложением - около 1350 Мб. Таким образом, использование метода File.read()
для чтения файлов по отдельности отпадает из-за его крайне низкой скорости.
Assets.getBytes()
и его парсинг заняли около 2,5 секунд, а потребление памяти составило около 1420 МБ;
sys.io.File.read()
, заняло примерно столько же времени и памяти (результаты отличаются на 0,04 секунды и примерно 5 МБ в пользу Assets.getBytes()
).
Таким образом, вполне естественно будет перейти на использование бинарных файлов, и скорее всего я остановлюсь на использовании метода Assets.getBytes()
с загрузкой файлов по отдельности (второй тест), как наиболее оптимальном с точки зрения “время-память”.
Тогда следующей задачей будет автоматизация конвертации анимаций в бинарный формат, т.к. в проекте используется несколько сотен анимаций. Следует поблагодарить авторов Spine, т.к. они предусмотрели такую возможность и экспорт файлов возможен через командную строку. Посмотрим, что из этого выйдет.