Dlaczego wątek główny się blokuje
JavaScript w wątku głównym jest jednowątkowy. Gdy krok fizyki zajmuje 12 ms, a pętla renderowania potrzebuje kolejnych 8 ms, na klatkę wychodzi 20 ms — to ledwie 50 FPS przy wyświetlaczu 60 Hz. A przy złożonej symulacji z 2000 ciałami sztywnymi to jeszcze optymistyczne założenie.
Rozwiązaniem jest rozdzielenie obowiązków: wątek renderujący wywołuje
requestAnimationFrame i rysuje; wątek fizyki uruchamia
Cannon-es i zapisuje pozycje z powrotem. Komunikują się asynchronicznie.
Architektura: potok dwuwątkowy
Co klatkę wątek główny wysyła do workera wiadomość step.
Worker przesuwa symulację o krok, pakuje pozycje i kwaterniony ciał
do wspólnego Float32Array i przekazuje go z powrotem.
Wątek renderujący odczytuje tablicę i aktualizuje macierze siatek
Three.js.
Konfiguracja Workera
Przekazywalne ArrayBuffer (zero kopiowania)
Domyślnie postMessage kopiuje dane — dla
dużych buforów jest to kosztowne. Oznaczenie leżącego u podstaw
ArrayBuffer jako transferable
(przekazywalnego) każe przeglądarce przenieść własność na drugi
wątek w czasie stałym. Oryginalny bufor staje się pusty
(odłączony) po przekazaniu:
Alternatywa: SharedArrayBuffer + Atomics
Jeśli wolisz odpytywanie zamiast przesyłania wiadomości (niższe
opóźnienie przy aktualizacjach o wysokiej częstotliwości), przydziel
wspólny bufor, który oba wątki mogą jednocześnie odczytywać i
zapisywać. Wymaga nagłówków Cross-Origin-Isolation:
Kiedy NIE używać Workerów
Pętla renderowania musi pozostać w wątku głównym.
requestAnimationFrame, rysowanie na canvasie i
renderer.render() Three.js są niedostępne w
Workerach. Odciążenie fizyki jest bezpieczne; odciążenie
renderowania wymaga OffscreenCanvas (ograniczone
wsparcie przeglądarek i trudniejsze użycie z Three.js — na razie
niezalecane).
Inne sytuacje, w których workery nie pomagają:
- Mała liczba ciał (<200): narzut postMessage może przewyższyć zyski z obliczeń.
- Ściśle powiązane fizyka i renderowanie: jeśli twój shader odczytuje wynik fizyki dla każdego piksela (cząstki GPU), koszt przekazywania danych przewyższa korzyść.
- Debugowanie: Workery utrudniają pracę z Chrome DevTools — najpierw prototypuj fizykę w wątku głównym.
Wsparcie przeglądarek: Web Workery są dostępne
we wszystkich nowoczesnych przeglądarkach. SharedArrayBuffer
wymaga nagłówków odpowiedzi
Cross-Origin-Opener-Policy: same-origin i
Cross-Origin-Embedder-Policy: require-corp na
twoim serwerze — inaczej funkcja jest wyłączona ze względów
bezpieczeństwa od czasu podatności Spectre.
Powiązane wpisy
Devlog #15 opisuje uzupełniające techniki wydajnościowe, które działają razem z Workerami — adaptacyjne presety jakości, leniwe ładowanie Three.js oraz throttling zależny od poziomu baterii.