Files
Jérôme Bousquié c1b5dd99bb docs: WSG_STATS was a switch that nothing read — 15 places, not 9
Le declencheur du rapport de stats est la **feature cargo `stats`**
(`#[cfg(feature = "stats")]` dans app.rs). Le code ne lit que WSG_STATS_FORMAT et
WSG_STATS_INTERVAL. `WSG_STATS` est lu par **rien** — et il etait pose comme un
interrupteur a 15 endroits.

Le pire cas n'etait pas la doc : c'est l'exemple `instances` qui **affichait**
« With `WSG_STATS=1`, the entity mode reports ~100 000 draw calls… ». Un faux
interrupteur dans un message est lu comme une instruction, et sa faussete ne
produit aucune erreur — la doc avait raison hier, c'est le code qui a bouge.
`bench/run-wsg.sh` le posait reellement.

Corrige : 12 lignes de commande mecaniquement, 3 prose a la main (la consigne de
l'exemple devient `--features stats`, le DRAFT 12.15 et le gotcha AGENTS.md sont
mis au passe plutot qu'effaces — un gotcha qui survit au code qu'il decrit est
pire que l'absence de gotcha).

Verrou : documented_env_switches_are_actually_read_by_the_code croise les
`env::var("…")` de lib/ avec toute apparition de `WSG_…=` dans docs/, bench/ et
lib/examples/. **Vu echouer** en reintroduisant l'ancienne variable :

    WSG_STATS  (docs/user/effects/ssao.md:173)

AGENTS.md est exclu du perimetre, a dessein : un fichier de gotchas doit pouvoir
citer le defaut qu'il decrit — meme famille que le test de @group(4) qui doit
ignorer les commentaires sinon son propre commentaire declenche la regle.

Deux erreurs a moi consignees dans le gotcha :
 - « 9 occurrences » annoncees de tete (un grep docs/ + un grep code/, jamais
   sommes). Le reel etait 15, trouve en relancant UN SEUL grep sur le depot.
 - le gotcha que j'ai reecrit tantot citait un nom de test qui n'existait pas
   (`stats_docs_name_only_variables_the_code_reads`) : la legende inverifiable,
   dans le depot qui a un gotcha exactement pour ca. Verifie a present : les deux
   tests cites existent.

cargo test --workspace : 580 tests, 0 echec (579 + le verrou).
2026-10-11 18:11:27 +02:00

3.0 KiB
Raw Permalink Blame History

12.14 — Comparative benchmark: WSG instancing vs Babylon.js thin instances

Goal

Validate the Phase 12 acceptance criterion: same scene, same geometry, 100 000 transformations — compare frame time and draw calls against Babylon.js thinInstanceSetBuffer.

Scene parameters (identical on both sides)

Parameter Value
Geometry Unit cube (1×1×1, centered)
Instances 100 000
Layout Grid, 250 columns, spacing 1.5
Camera Orbiting, radius 300, height 170, FOV 45°, near 0.5, far 5000
Lighting One directional (0, 0.6, 0.8), intensity 3
Culling Frustum per instance

How to run

WSG side

# Interactive (window, orbit with mouse, stats on stdout)
cargo run -p wsg-lib --example instances --release

# Headless benchmark (no window, prints a stats table)
cargo test -p wsg-lib --release -- scale_benchmark_reports_a_stats_table --nocapture

The instances example prints a line like:

[WSG] 60 fps | 16.7 ms | 1 draws | 100000 inst (1 lot) | 0 ent | ...

The headless test prints a table:

  vue   ms/frame       fps   dessinées   pixels   soumises
     0      14.23      70.3       98432    45210    100000
    20      15.01      66.6       72105    38901    100000
    ...
  ── 120 frames en 1.82 s → 65.9 fps ; moyenne 85230 instances dessinées par frame

Babylon.js side

cd bench/
python3 -m http.server 8080
# Open http://localhost:8080/babylon-thin-instance.html

Or simply open babylon-thin-instance.html in a browser (requires internet for the Babylon.js CDN).

The overlay shows FPS, frame time, and draw calls.

Expected results (reference hardware: RTX 3060, 1920×1080)

Metric WSG (wgpu/Vulkan) Babylon.js (WebGL2)
Draw calls 1 1
FPS ~60 (vsync) ~55–60
Frame time (GPU) ~1–2 ms ~3–5 ms
CPU overhead ~0.1 ms (1 descriptor) ~2–4 ms (matrix upload)

Note: Babylon.js re-uploads the full 6.4 MB matrix buffer every frame (thinInstanceSetBuffer with staticBuffer=true avoids this, but the default path does not). WSG uploads once and only re-uploads when mark_dirty() is called (12.11). This is the main architectural difference.

Caveats

  • Not a controlled benchmark: browser vs native, different GPU drivers, different rasterizers. The goal is order-of-magnitude validation, not micro-optimization comparison.
  • Babylon.js frustum culling: thin instances are culled as a whole mesh (one AABB), not per instance. WSG culls per instance (12.6). On a scene where 50% of instances are off-screen, WSG draws 50% fewer vertices.
  • Resolution: the browser renders at device-pixel-ratio (often 2× on HiDPI). Set devicePixelRatio: 1 in the Engine options for a fair comparison, or compare at the same resolution.

Files

File Role
babylon-thin-instance.html Babylon.js benchmark (standalone, CDN)
README.md This file