mirror of
https://github.com/got-feedBack/feedBack.git
synced 2026-08-11 11:19:24 +00:00
Switching the active window / alt-tabbing away (most often on Windows) can trigger a GPU context reset. The 3D highway's WebGL renderer had no webglcontextlost handler, so a lost context was left to escalate into a render-process crash -- matching the intermittent "randomly crashes when I change windows" desktop reports. The renderer now binds webglcontextlost/webglcontextrestored on its own WebGL canvas (ren.domElement): the loss is preventDefault()'d so the browser keeps the context restorable, draw() bails while the context is down so no GL work runs on a dead context, and on restore the viewport is re-applied and rendering resumes (Three re-uploads scene resources on the next frame). Listeners are removed in teardown. Root cause is a strong hypothesis -- the crash is intermittent and unreproducible -- but the fix is low-risk and additive and closes a real gap: there was no context-loss handling anywhere in the renderer. plugins/highway_3d 3.31.2 -> 3.31.3. Tests: tests/js/highway_3d_context_loss.test.js (source-contract, like the other highway_* tests). The sibling keys_highway_3d / drum_highway_3d renderers share the same gap -- follow-up in their repos. Claude-Session: https://claude.ai/code/session_01UR2Cr7GEu3yMY7SrfxH6c1 Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
46 lines
2.4 KiB
JavaScript
46 lines
2.4 KiB
JavaScript
// Contract test: 3D Highway WebGL context-loss recovery.
|
|
//
|
|
// Switching the active window / alt-tabbing (especially on Windows) can trigger
|
|
// a GPU context reset. Without a handler the lost WebGL context escalates into a
|
|
// render-process crash. The renderer owns its own WebGL canvas + heavy Three.js
|
|
// lifecycle (too much to construct in a vm sandbox), so — like the other
|
|
// highway_* source-contract tests here — this pins the wiring at the source
|
|
// level: the loss must be preventDefault()'d (so the browser restores it), draw
|
|
// must bail while lost, and the listeners must be torn down.
|
|
|
|
const { test } = require('node:test');
|
|
const assert = require('node:assert/strict');
|
|
const fs = require('node:fs');
|
|
const path = require('node:path');
|
|
|
|
const SCREEN_JS = path.join(__dirname, '..', '..', 'plugins', 'highway_3d', 'screen.js');
|
|
const src = fs.readFileSync(SCREEN_JS, 'utf8');
|
|
|
|
test('binds webglcontextlost + webglcontextrestored on the renderer canvas', () => {
|
|
assert.match(src, /ren\.domElement\.addEventListener\(\s*['"]webglcontextlost['"]/,
|
|
'must listen for webglcontextlost on ren.domElement (the WebGL canvas)');
|
|
assert.match(src, /ren\.domElement\.addEventListener\(\s*['"]webglcontextrestored['"]/,
|
|
'must listen for webglcontextrestored on ren.domElement');
|
|
});
|
|
|
|
test('the context-lost handler preventDefaults and pauses drawing', () => {
|
|
// Without preventDefault() the browser will not attempt to restore the
|
|
// context and the loss can escalate to a renderer crash.
|
|
const m = src.match(/_onCtxLost\s*=\s*\(e\)\s*=>\s*\{[\s\S]*?\};/);
|
|
assert.ok(m, '_onCtxLost handler must exist');
|
|
assert.match(m[0], /preventDefault\(\)/, 'context-lost handler must call preventDefault()');
|
|
assert.match(m[0], /_ctxLost\s*=\s*true/, 'context-lost handler must set _ctxLost = true');
|
|
});
|
|
|
|
test('draw() early-returns while the context is lost', () => {
|
|
assert.match(src, /draw\(bundle\)\s*\{[\s\S]*?if\s*\(_ctxLost\)\s*return;/,
|
|
'draw() must bail while _ctxLost is set so no GL work runs on a dead context');
|
|
});
|
|
|
|
test('teardown removes the context-loss listeners', () => {
|
|
assert.match(src, /removeEventListener\(\s*['"]webglcontextlost['"]/,
|
|
'teardown must remove the webglcontextlost listener');
|
|
assert.match(src, /removeEventListener\(\s*['"]webglcontextrestored['"]/,
|
|
'teardown must remove the webglcontextrestored listener');
|
|
});
|