Чому головний потік блокується
JavaScript у головному потоці однопотоковий. Якщо крок фізики займає 12 мс, а циклу рендеру потрібно ще 8 мс, на кадр іде 20 мс — це заледве 50 FPS на дисплеї з частотою 60 Гц. А на складній симуляції з 2000 твердих тіл це ще оптимістична оцінка.
Рішення — розділити обов'язки: потік рендеру викликає
requestAnimationFrame і малює кадр; потік фізики
виконує Cannon-es і записує позиції назад. Вони обмінюються даними
асинхронно.
Архітектура: двопотоковий конвеєр
Кожен кадр головний потік надсилає воркеру повідомлення
step. Воркер просуває симуляцію вперед, пакує позиції
та кватерніони тіл у спільний Float32Array і передає
його назад. Потік рендеру читає масив і оновлює матриці мешів
Three.js.
Налаштування воркера
Переданні ArrayBuffer (без копіювання)
За замовчуванням postMessage копіює дані —
це дорого для великих буферів. Позначення нижньолежачого
ArrayBuffer як transferable (переданого) каже
браузеру перенести право власності на інший потік за константний
час. Початковий буфер стає порожнім (від'єднаним) після передачі:
Альтернатива: SharedArrayBuffer + Atomics
Якщо вам більше подобається опитування замість обміну
повідомленнями (нижча затримка для оновлень з високою частотою),
виділіть спільний буфер, який обидва потоки можуть одночасно
читати й записувати. Потребує заголовків
Cross-Origin-Isolation:
Коли НЕ варто використовувати воркери
Цикл рендеру має залишатися в головному потоці.
requestAnimationFrame, малювання на canvas і
renderer.render() у Three.js недоступні у воркерах.
Виносити фізику безпечно; винесення рендеру вимагає
OffscreenCanvas (обмежена підтримка браузерами і
складніше використання з Three.js — наразі не рекомендуємо).
Інші випадки, коли воркери не допомагають:
- Мала кількість тіл (<200): накладні витрати postMessage можуть перевищити виграш від обчислень.
- Тісно пов'язані фізика й рендер: якщо ваш шейдер читає результат фізики для кожного пікселя (GPU-частинки), вартість передачі даних переважує вигоду.
- Налагодження: воркери ускладнюють роботу з Chrome DevTools — спочатку прототипуйте фізику в головному потоці.
Підтримка браузерами: Web Workers доступні в усіх
сучасних браузерах. SharedArrayBuffer потребує заголовків
відповіді Cross-Origin-Opener-Policy: same-origin і
Cross-Origin-Embedder-Policy: require-corp на вашому
сервері — інакше функція вимкнена з міркувань безпеки після
вразливості Spectre.
Пов'язані публікації
Devlog #15 розповідає про суміжні прийоми оптимізації продуктивності, що працюють у парі з воркерами — адаптивні пресети якості, ліниве завантаження Three.js та контроль навантаження залежно від заряду батареї.