Caso real de desarrollo de CONTRABAND

Lifecycle de WebGL: dispose no es liberar

Un producto educativo de Martinez AI Studios

Astillero, holograma de lock, mapa 3D, hangar e inventario abrían cada uno un contexto GPU. El canvas de vuelo pagaba la cuenta.

Chromium guarda un presupuesto pequeño de contextos WebGL por proceso (del orden de 8–16). Las UI de preview de CONTRABAND construían un WebGLRenderer al abrir y dispose() al cerrar. dispose() no libera el contexto GPU de inmediato. Tras unos ciclos de reopen el runtime registraba demasiados contextos activos, el canvas de vuelo recibía CONTEXT_LOST_WEBGL, el HUD DOM seguía y la vista 3D se iba a blanco.

Categoría
Rendering / lifecycle
Proyecto
CONTRABAND
Registrado
2026-08-19
Estado
Verificado · publicado

1. Síntoma

La vista de vuelo se iba a blanco o vacío tras usar astillero / preview de lock / mapa / hangar. Consola: “Too many active WebGL contexts” y luego THREE.WebGLRenderer: Context Lost en el canvas principal. Los chips de HUD podían seguir pintando.

2. Impacto en el jugador

La sesión parecía un crash del espacio: UI de cabina viva, estrellas idas. Recargar recuperaba; repetir Equip/tier en el astillero lo reproducía sin un error de “gameplay”.

3. Reproducción

Abrir y cerrar previews 3D (hangar inline del astillero, visor de nave, mapa galáctico, holograma de lock) varias veces, o refrescar el astillero en cada Equip. Mirar el aviso de context-lost en el canvas de vuelo. El módulo pool documenta esta secuencia en su comentario de cabecera.

4. Ownership del sistema

Renderer principal del mundo: play/three/scene.js (listener webglcontextlost). Previews: shipyard.js, preview de lock del HUD, galaxy-map-3d, hangar-ship, ui-ship-preview. Pool compartido: webgl-preview-pool.js (ids PREVIEW_GL). gpu-warning.js muestra info de GPU/renderer.

5. Supuesto erróneo

Llamar renderer.dispose() devuelve el contexto al presupuesto antes de la siguiente apertura. Crear un WebGLRenderer fresco por cada open de panel es “arquitectura limpia”. El canvas principal está aislado de los canvas de preview.

6. Causa raíz

El recuento de contextos es de todo el proceso. dispose() es asíncrono respecto al presupuesto de Chromium. Los ciclos de reopen apilaban contextos vivos hasta que mataban el más antiguo — a menudo el renderer de vuelo. renderShipyard() históricamente destruía y reconstruía el preview inline en Equip/tier/compra, un camino de mucho churn.

7. Arreglo mínimo

Adquirir una vez por id de preview, pausar RAF al cerrar, destruir solo si el contexto ya se perdió. No reemplazar con innerHTML una columna de hangar viva. El contrato es acquirePreviewRenderer / pausePreviewRenderer / destroyPreviewRenderer del pool.

8. Test de regresión

tests/webgl-preview-lifecycle.test.mjs cubre reuso vs recuento de creates y destroy por contexto perdido. El churn anterior del astillero también quedó cubierto para que Equip no abra contextos extra.

9. Validación de release

Log de desarrollo 19 ago 2026, v0.4.158 (pool de previews). Entradas anteriores registran el flash blanco al atracar (F abría un segundo WebGL de estación) y el teardown del hangar. El canvas principal sigue escuchando webglcontextlost.

10. Lección generalizada

Los contextos GPU son un presupuesto compartido, no un campo privado de un componente. El lifecycle es pausar vs destruir. Una IA que “limpia” con dispose() en cada unmount estilo React puede tumbar la vista del juego. Cuenta renderers vivos; testa bucles de reopen, no un solo open.

Extracto simplificado

Simplificado / pseudocódigo para enseñar. No es un volcado de archivos de producción.

La regla del pool, simplificada del comentario del módulo.

// Simplified teaching excerpt — not CONTRABAND production source.
// Chromium ~8–16 WebGL contexts per process.
function acquirePreviewRenderer(id) {
  const existing = slots.get(id);
  if (existing && !existing.contextLost) return existing; // reuse
  return createOnce(id);
}
function onPreviewClose(id) {
  pauseRaf(id);               // do not dispose every close
  // destroy only if getContext().isContextLost()
}

Evidencia pública

  • Comentario de cabecera de webgl-preview-pool.js y API acquire/pause/destroy.
  • Log de desarrollo de CONTRABAND, 19 ago 2026, v0.4.158; notas anteriores de canvas blanco / churn de astillero.
  • webgl-preview-lifecycle.test.mjs

Módulos relacionados del curso insignia

Notas relacionadas