Dispose WebGL renderer when a game stops 🧹 (#4295)

## Problem

`ClientGameRunner.stop()` tore down the worker, network, and sound, but
left the `MapRenderer` (and its WebGL context), the WebGL canvas, the
input overlay, and the self-driving RAF loop in place.

When you exit a game via the **Exit button** or browser **back**, the
page navigates to `/`, so the browser reclaims everything — that path is
fine. But you can start a new game **without** a reload: matchmaking and
joining another lobby go through `handleJoinLobby`, which calls
`lobbyHandle.stop(true)` then `joinLobby()` on the same document. The
old WebGL context stayed alive (the never-cancelled RAF kept it
referenced, so it wasn't even GC'd), and each new game stacked another
context. After a few games, mobile browsers hit their WebGL context
limit — matching the repro in #4267.

## Fix

`stop()` now disposes the renderer:

- cancels the self-driving RAF loop and disconnects the frame-loop
resize observer
- disposes the `MapRenderer` (frees all GPU resources)
- removes the WebGL canvas and the input overlay from the DOM

`GPURenderer.dispose()` additionally calls
`WEBGL_lose_context.loseContext()` so the context is released promptly
instead of waiting on unreliable GC. The territory-patterns settings
listener is wired to the existing graphics `AbortController` so it no
longer outlives the disposed view.

The cleanup runs unconditionally in `stop()` (a superseded join can stop
before the game becomes active) and is idempotent against repeated
`stop()` calls.

Fixes #4267

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Evan
2026-06-17 09:05:12 -07:00
committed by GitHub
parent 64409cae4d
commit 305534cc65
2 changed files with 42 additions and 6 deletions
+4
View File
@@ -1226,5 +1226,9 @@ export class GPURenderer {
this.gl.deleteTexture(this.sceneTarget.tex);
this.lastUnits = new Map();
this.lastStructures = new Map();
// Deleting GL resources isn't enough — the context itself counts against
// the browser's WebGL context limit until it's GC'd, which is unreliable
// on mobile. Explicitly drop it so repeated game starts don't overflow.
this.gl.getExtension("WEBGL_lose_context")?.loseContext();
}
}