[Performance] Document web-build (WASM) profiling workflow — DevTools + emcc, sibling to #345 #346

Open
opened 2026-07-10 22:50:26 +00:00 by john · 1 comment
Owner

Motivation

#345 stands up the native profiling rig (Callgrind + perf). Those tools are native-ELF/CPU-counter based and cannot profile WASM running in a browser — different execution substrate behind the browser sandbox. The web target needs its own, separate workflow. This is a lighter, docs-focused sibling to #345.

Key principle: minimize the web-specific surface

For C++/algorithmic hot-path work (#342/#343/#344 and similar), profile the native build as a proxy — the C++ source is identical across targets; only the LLVM codegen backend differs. Algorithmic cost, allocation counts, and comparison-cascade depth transfer. Do NOT re-confirm those on web.

Reserve browser profiling for genuinely web-specific costs that native can't predict:

  • WASM↔JS boundary crossings (no native analog)
  • The WebGL / GL ES 2 render backend (SDL2Renderer.cpp) — a different codebase from SFML
  • Single-threaded execution (native threading collapses to serial)
  • ALLOW_MEMORY_GROWTH realloc stalls / JS heap interaction
  • Browser frame pacing (rAF, main-thread contention)

Deliverables (documentation)

  1. Chrome DevTools Performance panel workflow against make wasm-debug / make playground-debug (these already build with -g4 -gsource-map).
  2. emcc --profiling-funcs — emit named WASM function symbols so the flame chart shows UIFrame::setProperty instead of wasm-function[2317] (web equivalent of "not stripped"). Determine which build variant should carry it.
  3. Note the optional "C/C++ DevTools Support (DWARF)" browser extension for C++ source-line mapping.
  4. Caveat: browser timer coarsening. performance.now() is clamped (~100µs, Spectre mitigation) and fine-grained timers are gated behind COOP/COEP headers. The in-engine ProfilerOverlay (which uses emscripten_get_now()) is good for "is this frame janky" but not microbenchmark precision on web — use DevTools sampling for precise numbers.

Portable through-line

The in-engine ProfilingMetrics/ProfilerOverlay (src/Profiler.*, GameEngine.h) compiles to every backend and is the one instrument identical on desktop and web — document it as the cross-target HUD, with the timer-coarsening caveat above.

Related: #345 (native rig), #331, #342, #343, #344.

## Motivation #345 stands up the **native** profiling rig (Callgrind + perf). Those tools are native-ELF/CPU-counter based and **cannot** profile WASM running in a browser — different execution substrate behind the browser sandbox. The web target needs its own, separate workflow. This is a lighter, docs-focused sibling to #345. ## Key principle: minimize the web-specific surface For C++/algorithmic hot-path work (#342/#343/#344 and similar), **profile the native build as a proxy** — the C++ source is identical across targets; only the LLVM codegen backend differs. Algorithmic cost, allocation counts, and comparison-cascade depth transfer. Do NOT re-confirm those on web. Reserve browser profiling for genuinely **web-specific** costs that native can't predict: - WASM↔JS boundary crossings (no native analog) - The WebGL / GL ES 2 render backend (`SDL2Renderer.cpp`) — a different codebase from SFML - Single-threaded execution (native threading collapses to serial) - `ALLOW_MEMORY_GROWTH` realloc stalls / JS heap interaction - Browser frame pacing (rAF, main-thread contention) ## Deliverables (documentation) 1. **Chrome DevTools Performance panel workflow** against `make wasm-debug` / `make playground-debug` (these already build with `-g4 -gsource-map`). 2. **emcc `--profiling-funcs`** — emit named WASM function symbols so the flame chart shows `UIFrame::setProperty` instead of `wasm-function[2317]` (web equivalent of "not stripped"). Determine which build variant should carry it. 3. Note the optional **"C/C++ DevTools Support (DWARF)"** browser extension for C++ source-line mapping. 4. **Caveat: browser timer coarsening.** `performance.now()` is clamped (~100µs, Spectre mitigation) and fine-grained timers are gated behind COOP/COEP headers. The in-engine `ProfilerOverlay` (which uses `emscripten_get_now()`) is good for "is this frame janky" but not microbenchmark precision on web — use DevTools sampling for precise numbers. ## Portable through-line The in-engine `ProfilingMetrics`/`ProfilerOverlay` (`src/Profiler.*`, `GameEngine.h`) compiles to every backend and is the one instrument identical on desktop and web — document it as the cross-target HUD, with the timer-coarsening caveat above. Related: #345 (native rig), #331, #342, #343, #344.
Author
Owner

Refinement (2026-07-11): now depends on #341; scoped as the browser-deployment leg

Added dependency on #341. This issue's "portable through-line" is the in-engine ProfilerOverlay/ProfilingMetrics HUD as the one instrument identical across desktop and web. But #341 reports that get_metrics() draw_calls/ui_elements/render counters read 0 in windowed builds — so the HUD can't yet be documented as the authoritative cross-target instrument. Fix #341 first, then the docs point at something true.

Scope (destination). This bucket is the browser-deployment leg of the agent environment (#55, plus #239 headless-browser testing). It's the loosest of the three enabling buckets: desktop Jupyter (#54) renders via screenshot-to-cell on the native build, so WASM instrumentation only becomes load-bearing if the notebook / agent env runs in-browser (JupyterLite-style). Scope the docs effort accordingly.

Cross-ref #347 (SFML vs SDL2/WebGL renderer parity) — the other genuinely web-specific unknown that native profiling can't proxy.

## Refinement (2026-07-11): now depends on #341; scoped as the browser-deployment leg **Added dependency on #341.** This issue's "portable through-line" is the in-engine `ProfilerOverlay`/`ProfilingMetrics` HUD as the one instrument identical across desktop and web. But #341 reports that `get_metrics()` draw_calls/ui_elements/render counters **read 0 in windowed builds** — so the HUD can't yet be documented as the authoritative cross-target instrument. Fix #341 first, then the docs point at something true. **Scope (destination).** This bucket is the **browser-deployment leg** of the agent environment (#55, plus #239 headless-browser testing). It's the loosest of the three enabling buckets: desktop Jupyter (#54) renders via screenshot-to-cell on the *native* build, so WASM instrumentation only becomes load-bearing if the notebook / agent env runs in-browser (JupyterLite-style). Scope the docs effort accordingly. Cross-ref #347 (SFML vs SDL2/WebGL renderer parity) — the other genuinely web-specific unknown that native profiling can't proxy.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Reference
john/McRogueFace#346
No description provided.