Files
fn_registry/dev/issues/0072d-gamedev-wasm-build-size-budget.md
T

182 lines
5.7 KiB
Markdown

---
id: "0072d"
title: "gamedev — WASM build pipeline + size budget"
status: pendiente
type: feature
domain:
- gamedev
scope: multi-app
priority: alta
depends:
- "0072a"
- "0072b"
blocks: []
related: []
created: 2026-05-10
updated: 2026-05-17
tags:
- gamedev
- wasm
- infra
---
## Objetivo
Pipeline reproducible de build WASM con presupuestos de tamaño estrictos y herramientas de auditoria. WASM es la plataforma prioritaria por la integracion crypto (sub-issues 0072e, 0072f), y el peso del bundle es el factor de retencion mas critico (cada 100KB extra = ~1% de drop-off en navegador).
## Presupuestos
| Artefacto | Limite duro | Limite blando |
|---|---|---|
| `*.wasm` raw | 5 MB | 4 MB |
| `*.wasm.gz` (gzip -9) | 2 MB | 1.5 MB |
| `*.wasm.br` (brotli -11) | 1.5 MB | 1.2 MB |
| `*.js` (loader) gzip | 50 KB | 30 KB |
| `*.html` (shell) | 10 KB | 5 KB |
| Bundle assets `.pak` | 5 MB | 3 MB |
Total objetivo: **< 4 MB descargados** (wasm.br + js.gz + assets.gz) para "first paintable scene".
## Pipeline
`bash/functions/pipelines/build_wasm_cpp_pipelines.sh`:
```bash
#!/usr/bin/env bash
# build_wasm <app_name> [--release|--debug] [--no-budget-check]
set -euo pipefail
APP="$1"; shift
MODE="${1:-release}"
SRC_DIR="cpp/apps/$APP"
BUILD_DIR="build/wasm/$APP"
# 1. emsdk
[ -d emsdk ] || git clone https://github.com/emscripten-core/emsdk
(cd emsdk && ./emsdk install latest && ./emsdk activate latest)
source emsdk/emsdk_env.sh
# 2. cmake
emcmake cmake -B "$BUILD_DIR" -S "$SRC_DIR" \
-DCMAKE_BUILD_TYPE=$([ "$MODE" = "release" ] && echo MinSizeRel || echo Debug)
cmake --build "$BUILD_DIR" -j
# 3. compress
WASM="$BUILD_DIR/$APP.wasm"
gzip -9 -k "$WASM"
brotli -q 11 -k "$WASM"
# 4. report
echo "── Sizes ──"
ls -lah "$BUILD_DIR/$APP".{wasm,wasm.gz,wasm.br,js,html}
# 5. budget check
if [ "${1:-}" != "--no-budget-check" ]; then
bash bash/functions/gamedev/wasm_size_budget_check.sh "$BUILD_DIR" "$APP"
fi
```
## Funcion de auditoria
`bash/functions/gamedev/wasm_size_budget_check.sh`:
```bash
#!/usr/bin/env bash
# Falla con exit 1 si excede el budget duro
WASM_GZ="$BUILD_DIR/$APP.wasm.gz"
SIZE=$(stat -c%s "$WASM_GZ")
LIMIT=$((2 * 1024 * 1024))
if [ "$SIZE" -gt "$LIMIT" ]; then
echo "$WASM_GZ = $SIZE bytes > $LIMIT (2 MB)"
exit 1
fi
echo "$WASM_GZ within budget"
```
## Banderas de compilacion (MinSizeRel)
```cmake
if(EMSCRIPTEN)
target_compile_options(<target> PRIVATE
-Os # Optimize for size
-fno-exceptions # No C++ exceptions (huge win)
-fno-rtti # No RTTI
-ffunction-sections
-fdata-sections
)
target_link_options(<target> PRIVATE
-Os
-sASSERTIONS=0 # No runtime assertions in release
-sFILESYSTEM=0 # No emscripten filesystem (use fetch)
-sMINIMAL_RUNTIME=2 # Slim JS runtime
-sENVIRONMENT=web # Browser only, no node
-sUSE_WEBGL2=1
-sFULL_ES3=1
-sALLOW_MEMORY_GROWTH=1
-sINITIAL_MEMORY=33554432
-sSTACK_SIZE=1048576
--closure=1 # Closure compiler on JS glue
-Wl,--gc-sections
)
endif()
```
`MINIMAL_RUNTIME=2` ahorra ~50KB de JS glue. `FILESYSTEM=0` ahorra otros ~30KB. `--closure=1` reduce JS otro ~30%.
## Herramientas de auditoria
Cuando el budget se rompe, herramientas para encontrar el culpable:
1. **`twiggy`** (Rust tool, MIT) — analiza wasm y lista funciones por tamaño:
```bash
twiggy top -n 50 build/wasm/engine_smoke.wasm
```
2. **`wasm-objdump -h`** — secciones del binario.
3. **`emcc --emit-symbol-map`** — mapa de simbolos para correlacionar con codigo C++.
4. **Bloaty** (Google, ASL2) — analisis size attribution.
Funcion: `bash/functions/gamedev/wasm_size_audit.sh <wasm_path>` — corre las 4 y produce reporte markdown.
## Tecnicas de reduccion (cuando se exceda)
Documentar en `cpp/GAMEDEV.md`:
1. **No usar `<iostream>`** — ~80KB de runtime. Usar `printf` o nada.
2. **No usar `<filesystem>`** — pesado. SDL_RWops o emscripten fetch.
3. **`std::function` → punteros a funcion** cuando se pueda. ~5KB por instancia.
4. **`std::regex` prohibido** — ~100KB. Usar parsers manuales o `re2` mini.
5. **STL containers** — usar pero con `-fno-exceptions`. `std::vector` OK, `std::map` evitar (preferir `std::vector<pair>` ordenado o `flat_hash_map` mini).
6. **Templates** — instanciar pocas veces. Cada nueva instanciacion es codigo nuevo.
7. **Inlining** — `-Os` ya lo balancea. No forzar `__forceinline`.
8. **Vendor libs** — auditar antes de añadir. Cada lib pasa por size review en su PR.
## Streaming + caching
Para juegos mas grandes en el futuro:
- `--preload-file` empotra assets en `.data` adyacente al wasm. Cargado de golpe.
- `fetch()` async para assets opcionales (niveles avanzados, musica).
- Service Worker para cache offline (en sub-issue futuro, no urgente).
## Integracion con CI
GitHub Actions / Gitea Actions workflow `gamedev-wasm-budget.yml`:
- Trigger: PR que toca `cpp/apps/engine_*` o `cpp/functions/gfx/sg_*`
- Build WASM
- Comprueba budgets
- Comenta tamaños en el PR
- Falla si rompe limite duro
## Criterio de exito
- [x] Pipeline `build_wasm_cpp_pipelines.sh` reproducible en Linux + Windows WSL.
- [x] `engine_smoke` (de 0072a) pasa budget check.
- [x] `wasm_size_audit.sh` produce reporte legible.
- [x] CI bloquea PRs que rompen el budget.
- [x] Documentacion `cpp/GAMEDEV.md` actualizada con reglas de tamaño.
## No-objetivos
- WebGPU (sokol_gfx WebGPU backend es alpha) — diferido.
- WASM SIMD — opcional, evaluar despues de medir hot paths.
- Multi-threading WASM (SharedArrayBuffer + COOP/COEP headers) — overkill, lo dejamos para juegos que lo pidan.