fad4006f60
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
182 lines
5.7 KiB
Markdown
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.
|