Restore on webgl context loss (#3968)

## Description:

When WebGL context is lost, restore context and all elements.

In GameView, handle potentially transient undefined states during
context loss gracefully.

Test with chrome://gpucrash from another tab, then return to the game
tab to see it being restored
(This fake gpucrash only works once sometimes. Because the second time
the browser might reject the tab it thinks caused the gpu crash, access
to hardware acceleration. And after even more tries even disables it
browser-wide. A browser restart resets it in that case.)

## Please complete the following:

- [x] I have added screenshots for all UI updates
- [x] I process any text displayed to the user through translateText()
and I've added it to the en.json file
- [x] I have added relevant tests to the test directory
- [x] I confirm I have thoroughly tested these changes and take full
responsibility for any bugs introduced

## Please put your Discord username so you can be contacted if a bug or
regression is found:

tryout33
This commit is contained in:
VariableVince
2026-05-21 21:27:45 +02:00
committed by GitHub
parent 41ef675e98
commit 7acf1aebc2
6 changed files with 216 additions and 107 deletions
+29 -2
View File
@@ -68,7 +68,7 @@ import { createCanvas } from "./Utils";
import { WebGLFrameBuilder } from "./WebGLFrameBuilder";
import { createRenderer, GameRenderer } from "./hud/GameRenderer";
import { GameView as WebGLGameView } from "./render/gl";
import { ALL_UNIT_TYPES } from "./render/types";
import { ALL_UNIT_TYPES, UnitState } from "./render/types";
import { SoundManager } from "./sound/SoundManager";
export interface LobbyConfig {
@@ -372,7 +372,34 @@ function mountWebGLFrameLoop(
};
requestAnimationFrame(driveFrame);
return { builder: new WebGLFrameBuilder(view) };
const builder = new WebGLFrameBuilder(view);
// When context is lost and restored, WebGL loses all textures and geometry.
// Force a full re-upload of the simulation state.
view.on("contextrestored", () => {
builder.clearCaches();
// Full upload of terrain, territory & trail state
const mapSize = mapWidth * mapHeight;
const allRefs = new Array(mapSize);
const allTerrain = new Uint8Array(mapSize);
for (let i = 0; i < mapSize; i++) {
allRefs[i] = i;
allTerrain[i] = gameView.terrainByte(i);
}
view.applyTerrainDelta(allRefs, allTerrain);
const frameData = gameView.frameData();
view.uploadTileAndTrailState(frameData.tileState, frameData.trailState);
// Structures and railroads normally skip GPU upload unless marked dirty, now force
view.updateStructures(frameData.units as Map<number, UnitState>);
view.uploadRailroadState(frameData.railroadState);
builder.update(gameView);
});
return { builder };
}
async function createClientGame(