--- 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 [--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( PRIVATE -Os # Optimize for size -fno-exceptions # No C++ exceptions (huge win) -fno-rtti # No RTTI -ffunction-sections -fdata-sections ) target_link_options( 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 ` — corre las 4 y produce reporte markdown. ## Tecnicas de reduccion (cuando se exceda) Documentar en `cpp/GAMEDEV.md`: 1. **No usar ``** — ~80KB de runtime. Usar `printf` o nada. 2. **No usar ``** — 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` 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.