Версии библиотеки: asm.js и WebAssembly

Ammo.js — это порт физического движка Bullet Physics на язык JavaScript, полученный с использованием инструмента Emscripten. Исходный код Bullet, написанный на C++, транслируется в код, исполняемый в браузере. В зависимости от конфигурации компиляции Emscripten формирует две основные версии библиотеки:

  • сборку на основе asm.js
  • сборку на основе WebAssembly (WASM)

Обе версии предоставляют одинаковый API с точки зрения JavaScript, но существенно различаются по внутреннему устройству, производительности и механике загрузки.


Версия asm.js

Технологическая основа

asm.js — это строго ограниченное подмножество JavaScript, предназначенное для выполнения низкоуровневых вычислений с производительностью, близкой к нативной. Код asm.js генерируется Emscripten из C++-исходников.

Скомпилированная версия Ammo.js в формате asm.js представляет собой:

  • один крупный .js-файл
  • эмулированную линейную память
  • набор функций, имитирующих поведение C++-классов

Организация памяти

В asm.js используется большая типизированная область памяти:

var buffer = new ArrayBuffer(TOTAL_MEMORY);
var HEAPF32 = new Float32Array(buffer);
var HEAP32 = new Int32Array(buffer);

Память выделяется заранее и имеет фиксированный размер. Расширение памяти невозможно без полной перезагрузки модуля. Управление памятью осуществляется вручную через экспортируемые функции:

  • _malloc
  • _free

Это означает, что создание и удаление объектов физики требует контроля жизненного цикла, аналогично C++.

Производительность

Преимущества:

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

Недостатки:

  • больший размер итогового .js-файла
  • более медленная инициализация
  • более низкая производительность по сравнению с WASM
  • отсутствие нативной поддержки потоков

В сложных сценах с большим количеством тел, ограничений и столкновений asm.js начинает уступать по производительности WebAssembly-версии.


Версия WebAssembly (WASM)

Архитектурные особенности

WebAssembly — это бинарный формат для выполнения кода в браузере. В отличие от asm.js, WASM представляет собой компактный бинарный модуль (.wasm), загружаемый и компилируемый браузером напрямую.

Сборка Ammo.js в формате WASM состоит из:

  • JavaScript-обёртки (ammo.js)
  • бинарного файла (ammo.wasm)

JavaScript-файл выполняет роль загрузчика и интерфейса к WebAssembly-модулю.

Механизм загрузки

Загрузка происходит асинхронно:

Ammo().then(function(AmmoLib) {
    const Ammo = AmmoLib;
});

Модуль WebAssembly компилируется браузером в машинный код во время загрузки. Современные браузеры используют потоковую компиляцию (streaming compilation), что сокращает время старта.

Работа с памятью

WASM использует линейную память, аналогичную asm.js, но реализованную на уровне виртуальной машины браузера. Ключевые особенности:

  • более эффективная адресация
  • возможность динамического увеличения памяти (при соответствующей сборке)
  • меньшие накладные расходы на вызовы

Память также управляется вручную через экспортируемые функции _malloc и _free.

Производительность

Преимущества:

  • более компактный размер бинарного файла
  • более быстрая загрузка
  • более высокая производительность вычислений
  • лучшая оптимизация JIT-компилятора
  • поддержка многопоточности (при использовании SharedArrayBuffer и соответствующих флагов)

В задачах интенсивной физической симуляции WASM-версия показывает ощутимое преимущество, особенно при:

  • большом количестве динамических тел
  • сложных системах ограничений
  • расчётах soft-body
  • использовании непрерывной детекции столкновений (CCD)

Сравнение asm.js и WebAssembly

Характеристика asm.js WebAssembly
Формат JavaScript Бинарный
Размер Больше Меньше
Скорость Высокая Очень высокая
Время старта Дольше Быстрее
Поддержка потоков Нет Возможна
Совместимость Старые браузеры Современные браузеры

Основные различия касаются не API, а внутренней реализации и производительности. Код, написанный для Ammo.js, в большинстве случаев не требует изменений при переходе между версиями.


Особенности сборки через Emscripten

При компиляции C++-версии Bullet в asm.js или WASM используются различные флаги:

Для asm.js:

emcc -O3 -s WASM=0

Для WebAssembly:

emcc -O3 -s WASM=1

Дополнительные параметры позволяют:

  • включать поддержку потоков (-s USE_PTHREADS=1)
  • настраивать размер памяти (-s INITIAL_MEMORY)
  • разрешать рост памяти (-s ALLOW_MEMORY_GROWTH=1)

Выбор флагов напрямую влияет на поведение итоговой сборки Ammo.js.


Инициализация и различия в API

С точки зрения JavaScript-разработчика различия минимальны:

asm.js

var Ammo = Ammo();

WASM

Ammo().then(function(Ammo) {
    // работа с библиотекой
});

WASM-версия требует асинхронной инициализации из-за загрузки бинарного модуля. Это влияет на архитектуру приложения: создание физического мира должно происходить после полной загрузки библиотеки.


Практические рекомендации по выбору версии

asm.js оправдана в случаях:

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

WebAssembly предпочтительна при:

  • разработке современных 3D-приложений
  • интеграции с WebGL или WebGPU
  • использовании игровых движков (например, Three.js)
  • сложной физике в реальном времени

Влияние версии на интеграцию с рендерингом

При использовании Ammo.js совместно с графическими библиотеками различия проявляются в частоте обновления сцены. В связке с интенсивным рендерингом через WebGL WebAssembly снижает нагрузку на главный поток за счёт более быстрой обработки физического шага:

dynamicsWorld.stepSimulation(deltaTime, 10);

При высокой частоте кадров (60+ FPS) WASM позволяет уменьшить число субшагов и стабилизировать симуляцию.


Совместимость и эволюция стандарта

Современные браузеры полностью поддерживают WebAssembly. Поддержка asm.js сохраняется, но его развитие фактически прекращено в пользу WASM. Новые оптимизации в движках JavaScript ориентированы преимущественно на WebAssembly.

Переход на WASM стал естественным этапом эволюции портов C++-движков в веб-среду. Ammo.js в формате WebAssembly обеспечивает наиболее близкое соответствие производительности оригинальному движку Bullet на нативной платформе.

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