Ammo.js, являясь портом библиотеки Bullet Physics на JavaScript с использованием Emscripten, работает внутри среды JavaScript с ограничениями однопоточности основной модели выполнения. Для увеличения производительности физического движка и уменьшения блокировок основного потока применяется Web Workers. Взаимодействие между основным потоком и воркерами требует эффективных механизмов передачи данных, особенно при работе с большим количеством объектов и коллизий.
Основные способы передачи данных в контексте Web Workers:
SharedArrayBuffer предоставляет возможность разделения блока памяти между главным потоком и воркерами. При этом:
Atomics.Пример использования SharedArrayBuffer для хранения позиций объектов:
const numObjects = 1000;
const buffer = new SharedArrayBuffer(Float32Array.BYTES_PER_ELEMENT * numObjects * 3);
const positions = new Float32Array(buffer);
// Запись координат объекта
positions[0] = 1.0;
positions[1] = 2.0;
positions[2] = 3.0;
// Чтение координат в другом потоке
console.log(positions[0], positions[1], positions[2]);
Особенности:
Atomics позволяет безопасно выполнять
операции add, sub, load,
store, compareExchange и
wait/notify для синхронизации.Transferable Objects позволяют перемещать объекты (например,
ArrayBuffer) между потоками без копирования. После передачи
объект в исходном потоке становится недоступным, что исключает расходы
на дублирование памяти.
Пример передачи буфера с позицией объектов в воркер:
// В главном потоке
const buffer = new ArrayBuffer(Float32Array.BYTES_PER_ELEMENT * numObjects * 3);
worker.postMessage({ positions: buffer }, [buffer]);
// После передачи buffer больше нельзя использовать в главном потоке
Особенности:
Ammo.js использует объекты btRigidBody,
btTransform, btVector3 для моделирования
физики. В многопоточной архитектуре необходимо передавать:
Пример использования SharedArrayBuffer для обмена данными между воркером и главным потоком:
// Инициализация SharedArrayBuffer для позиций и ориентаций
const numObjects = 500;
const buffer = new SharedArrayBuffer(Float32Array.BYTES_PER_ELEMENT * numObjects * 7);
const stateArray = new Float32Array(buffer); // x, y, z, qx, qy, qz, qw
// Отправка буфера воркеру
worker.postMessage({ state: buffer });
// В воркере
onmess age = function(e) {
const state = new Float32Array(e.data.state);
// Обновление позиций физики
for (let i = 0; i < numObjects; i++) {
const idx = i * 7;
// пример применения новых координат в Ammo.js
const transform = new Ammo.btTransform();
transform.setOrigin(new Ammo.btVector3(state[idx], state[idx+1], state[idx+2]));
transform.setRotation(new Ammo.btQuaternion(state[idx+3], state[idx+4], state[idx+5], state[idx+6]));
// rigidBody.update(transform); // псевдокод
}
};
Преимущество SharedArrayBuffer здесь в том, что обновления состояний происходят без копирования массивов, что особенно важно при большом числе объектов.
| Механизм | Копирование данных | Совместный доступ | Подходит для | Ограничения |
|---|---|---|---|---|
| Transferable Objects | Нет (передача прав) | Нет | Однократная передача больших массивов | После передачи исходный поток теряет доступ |
| SharedArrayBuffer | Нет (разделение памяти) | Да | Параллельный доступ, синхронизация | Требуется атомарная синхронизация, уязвимость к гонкам |
Atomics для
синхронизации при параллельных записях, чтобы избежать повреждения
данных и некорректной симуляции физических процессов.Использование этих механизмов позволяет создавать высокопроизводительные физические симуляции в браузере с Ammo.js, эффективно распределяя нагрузку между потоками и минимизируя задержки обновления сцены.