pcbjam/tests/3d-regression/wasm/3d_webgl_test.html

43 lines
1.8 KiB
HTML
Raw Normal View History

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>3D Renderer WebGL Test</title>
<style>
body { margin: 0; background: #202020; }
#canvas { display: block; width: 800px; height: 600px; }
</style>
</head>
<body>
<canvas id="canvas" width="800" height="600"></canvas>
<script>
let ready = false;
let wasmModule = null;
window._threeDTestReady = () => { ready = true; };
window.threeDTest = {
isReady: () => ready,
runScenario: (i) => wasmModule.ccall('runScenario', 'number', ['number'], [i]),
getTotalScenarios: () => wasmModule.ccall('getTotalScenarios', 'number', [], []),
getScenarioName: (i) => wasmModule.ccall('getScenarioName', 'string', ['number'], [i]),
getCanvasWidth: () => wasmModule.ccall('getCanvasWidth', 'number', [], []),
getCanvasHeight: () => wasmModule.ccall('getCanvasHeight', 'number', [], []),
fix(3d): blank render + lost position after 3D viewer close/reopen (gl1 context guard) Closing the viewer destroys its wxGLCanvas's WebGL context; reopening mints a new one. The gl1 shim cached GL names (FFP program, stream/scratch VBOs) in never-reset statics behind `if (!handle)` guards — in the new context every draw died with INVALID_OPERATION and the viewer showed only the clear color ("Reload time 0.031 s" is benign: warm model caches make the rebuild fast). contextSync() (gl1_state.cpp) now detects the context change in programSync() — the one choke point every shim draw crosses, and a path the 2D GAL never reaches (a first attempt checking in the glBindTexture wrap saw the GAL's context and thrash-rebuilt the program 23x per run) — and drops the cached names for lazy rebuild in the new context. Context identity is a monotonic id stamped on Emscripten's per-context record: the numeric EMSCRIPTEN_WEBGL_CONTEXT_HANDLE is recycled, so a destroy-then-create can return the same number and a handle comparison detects nothing. The lost-position half is a wxwidgets wasm fix (pointer bump: GetFromWindow reports display 0; saved geometry used to carry display=(unsigned)-1, which LoadWindowState treats as "display not found" and re-centres the frame). TDD (red observed before each fix, green after): - tests/kicad/3d-viewer-reopen.spec.ts (new, own worker like the deadlock spec): load board, open viewer, render-gate, drag by the titlebar, close via the x, reopen; asserts the board re-renders (was: 1 distinct colour for 90 s) and the window position is restored (was: re-centred to 0,0 after closing at 40,90). Green run logs exactly one [gl1] context-change line. - 3d-regression harness: recreateContext() destroys the context AND swaps in a fresh canvas element (a browser canvas keeps its context for life, so same-element recreation hands back the live old context and hides the bug); the new 3d-webgl spec test renders redraw-mini-board-navigator before and after recreation and requires pixel-identical output. Parity: 47/47, zero drift. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-24 15:55:31 +02:00
recreateContext: () => wasmModule.ccall('recreateContext', 'number', [], []),
fix(3d): blank viewer after raytracing round-trip — owner-context FFP routing + VAO isolation (gl1) Switching OpenGL → raytracing → OpenGL could leave the viewer showing only the background gradient, with mid-session "[gl1] WebGL context changed" thrash and INVALID_OPERATION storms on BOTH WebGL contexts. Traced mechanism: the shim's FFP draw routing keyed on one process-global client-array flag; an interrupted fixed-function window (MODEL_3D::BeginDrawMulti loops, or the switch-back reload re-recording display lists with the GL context lock released across JSPI suspensions) left it set, after which the raytracer blit's and the 2D GAL's glDrawArrays were routed through the FFP pipeline — and one misrouted draw permanently repointed the VICTIM's own VAO attributes at shim buffers (the blit's attribute 0 collides with ATTR_POSITION), so both stayed broken/blank even after the flag cleared. Shim fixes (wasm/gl1): - Owner-context routing gate: __wrap_glDrawArrays/Elements route into the FFP pipeline only under the shim's owner context (adopted at the first FFP client-state mutation or programSync in a context); foreign-context draws always pass through — the 2D GAL can never be misrouted and the context guard can never thrash. - VAO isolation (ScopedDefaultVAO): draw executors do their attribute setup on VAO 0 and restore the caller's binding — a misrouted draw can no longer corrupt the caller. - contextSync() resets the whole client-array mirror on a context change (enables/pointers/VBO names all described the dead context). kicad pointer bump (d6e3dc1a87a): blit preamble disables the four client arrays (same-context firewall) + DoRePaint hidden-parent early return now clears m_is_currently_painting like its six siblings (a standalone sufficient cause of a permanently blank viewer). TDD (each observed red before its fix, green after; harness = authoritative): - T1 VAO corruption, T2 foreign-context routing + guard thrash, T3 stale client-state surviving context recreation — tests/e2e/3d-webgl.spec.ts over new harness choreography (appQuad/ffpMakeStale/createSecondContext/ quadDrawFresh). Parity stays 47/47 with zero drift. - tests/kicad/3d-viewer-engine-toggle.spec.ts (new, CI-skipped like the deadlock spec): real round-trip happy-path gate — board re-renders, zero [gl1] lines, zero INVALID_OPERATION. (The raytraced image itself never displays on the wasm build — the pre-existing inert-toggle KNOWN ISSUE in 3d-viewer-deadlock.spec.ts, out of scope here; the engine switch and the poisoning reload path run regardless.) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-25 11:24:26 +02:00
appQuadInit: () => wasmModule.ccall('appQuadInit', 'number', [], []),
appQuadDraw: (clear) => wasmModule.ccall('appQuadDraw', 'number', ['number'], [clear]),
ffpMakeStale: () => wasmModule.ccall('ffpMakeStale', null, [], []),
ffpClearStale: () => wasmModule.ccall('ffpClearStale', null, [], []),
createSecondContext: () => wasmModule.ccall('createSecondContext', 'number', [], []),
useContext: (n) => wasmModule.ccall('useContext', 'number', ['number'], [n]),
quadDrawFresh: () => wasmModule.ccall('quadDrawFresh', 'number', [], []),
};
</script>
<script src="3d_webgl_test.js"></script>
<script>
create3DTest({ canvas: document.getElementById('canvas') })
.then((mod) => { wasmModule = mod; })
.catch((err) => { console.error('[3d-webgl] module init failed:', err); });
</script>
</body>
</html>