Rust компілюється у WebAssembly з близькою до нативної швидкістю та без
пауз на збирання сміття — ідеально для фізичних симуляцій, які мають
утримувати 60 fps під високим навантаженням на CPU.
wasm-bindgen робить взаємодію з JavaScript тривіальною:
експортуйте функції Rust і напряму спільно використовуйте пам'ять
типізованих масивів без копіювання.
1Налаштовуємо проєкт Rust / wasm-pack
# 1. Встановлюємо Rust (якщо його немає) curl --proto '=https' --tlsv1.2
-sSf https://sh.rustup.rs | sh # 2. Додаємо ціль WASM rustup target
add wasm32-unknown-unknown # 3. Встановлюємо wasm-pack cargo install
wasm-pack # 4. Створюємо новий бібліотечний крейт cargo new --lib particle-sim
cd particle-sim
# Cargo.toml — додаємо ці залежності та тип крейта [package] name =
"particle-sim" version = "0.1.0" edition = "2021" [lib] crate-type =
["cdylib"] [dependencies] wasm-bindgen = "0.2" [profile.release]
opt-level = 3 lto = true
import init, { Particles } from './pkg/particle_sim.js'; const wasm =
await init('./pkg/particle_sim_bg.wasm'); const sim = new Particles();
const N = sim.len(); // Отримуємо представлення лінійної пам'яті WASM без копіювання //
Вони вказують напряму на купу WASM — без копії! const memory =
wasm.memory; function getPxView() { return new
Float32Array(memory.buffer, sim.px_ptr(), N); } function getPyView() {
return new Float32Array(memory.buffer, sim.py_ptr(), N); } // Просуваємо
симуляцію на 60 fps function update() { sim.step(1 / 60); }
Доступ без копіювання:memory.buffer — це
ArrayBuffer лінійної пам'яті WASM. Створення представлення
типізованого масиву з вказівника уникає будь-якої серіалізації — JS-бік
читає байти, які Rust-бік записав напряму.
5Рендеринг без копіювання у Three.js
import * as THREE from
'https://cdn.jsdelivr.net/npm/three@0.160/build/three.module.js'; //
Будуємо геометрію з поточного представлення буфера const geo = new
THREE.BufferGeometry(); // чергуємо x,y у 3-компонентному масиві
позицій const pos3 = new Float32Array(N * 3); let posAttr = new
THREE.BufferAttribute(pos3, 3);
posAttr.setUsage(THREE.DynamicDrawUsage); geo.setAttribute('position',
posAttr); const mat = new THREE.PointsMaterial({ size: 0.002, color:
0x22c55e }); const points = new THREE.Points(geo, mat);
scene.add(points); renderer.setAnimationLoop(() => { sim.step(1 / 60);
// Після step() оновлюємо представлення типізованого масиву (пам'ять WASM могла
зрости) const px = new Float32Array(memory.buffer, sim.px_ptr(), N);
const py = new Float32Array(memory.buffer, sim.py_ptr(), N); // Копіюємо
у чергований буфер позицій for (let i = 0; i < N; i++) {
pos3[i * 3] = px[i]; pos3[i * 3 + 1] = py[i]; pos3[i * 3 + 2] = 0; }
posAttr.needsUpdate = true; renderer.render(scene, camera); });
Копіювання з (px, py) у чергований pos3 неминуче без зміни розкладки
даних Rust зі SoA на AoS. Для максимальної продуктивності
переструктуруйте структуру Rust так, щоб зберігати xyz
чергованими напряму.
Часті запитання
Чого я навчуся в цьому уроці?
Скомпілюйте симуляцію фізики частинок, написану на Rust, у WebAssembly, викликайте її з JavaScript і рендерте 100 тис. частинок на 60 fps у браузері.
Які теми розглядаються в цьому уроці?
Цей урок охоплює такі теми: Налаштовуємо проєкт Rust/wasm-pack, Пишемо симуляцію частинок на Rust, Експортуємо буфери типізованих масивів у JS, Завантажуємо та викликаємо з JavaScript, Рендеринг без копіювання у Three.js.
Які інструменти й технології використовуються?
Цей урок використовує: Rust, WebAssembly, wasm-bindgen, Three.js.