Commit graph pcbjam/wasm/gl1
Author SHA1 Message Date
Istvan Matejcsok
daaff1f973 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:25:34 +02:00
Istvan Matejcsok
3722891d48 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 16:18:22 +02:00
Istvan Matejcsok
27992f4f18 feat(3d): gl1 shim M4+M6+M7 — GLU quadrics, production link, stub retired; 47/47 parity
- gl1_glu: gluCylinder/gluDisk/gluSphere per the SGI GLU reference
  tessellation (sin/cos phase, strip emission order, pole fans — Apple's
  GLU derives from the same source, so vertices match the goldens);
  emission goes through the public glBegin/glVertex entry points so
  quadrics record into display lists
- glLineWidth>1 one-time warning (browsers clamp to 1; affected coverage
  stays under the parity floor — zero floors.json overrides needed)
- production link site: build-kicad-target.sh now compiles wasm/gl1 from
  sources.txt and applies wrapped_symbols.txt --wrap flags (same manifests
  as the test harness); kicad_editor's RENDER_3D_OPENGL links against the
  real emulation layer instead of no-ops
- wasm/stubs/gl_ffp_stub.c deleted (role fully superseded; history in git)
- docs: 3d-regression README port-status section (floor-blind small
  geometry + line-width caveats), harness comments de-red-stated

Parity: 47/47 under the 0.02 floor, zero overrides. Native golden path
byte-identical to bb0e238 (whose gate ran green) — scenarios/, native/,
baseline/, manifest, floors, spec all untouched by the port.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-03 15:45:58 +02:00
Istvan Matejcsok
3fe4f6fb00 feat(3d): gl1 shim M3+M5 — display-list recorder, client arrays, VBO routing; 44/47 under parity floor
- display lists: literal command replay through the shim state machine
  (state leaks + between-list material changes get GL semantics for free);
  recorded glDrawArrays EAGERLY snapshots enabled client-memory arrays at
  compile time (the renderer frees them right after glEndList) and records
  buffer+offset for VBO-backed pointers
- client-array draws: shared AttribSource path packs client-memory arrays
  into one scratch-VBO upload and binds user VBOs with original offsets
  (GL_BYTE normals / GL_UNSIGNED_BYTE colors normalized per GL1)
- glDrawElements over user VBO/IBO passes indices through untouched
  (ELEMENT_ARRAY_BUFFER never touched by the shim)
- state mutators (enable/bindTexture/blendFunc/lineWidth/alphaFunc) shared
  between the __wrap interceptors and list replay

Parity: 44/47 under the 0.02 floor (was 20). Verified visually: mini-board
Tier-3 composite (mask translucency + stencil-punched TH holes), stencil
hole subtraction, alpha-tested seg-end discs, COMBINE const-alpha model
transparency, and post-machining reproducing the upstream countersink bug
(walls absent, matching the native golden). Remaining 3: camera-* trio
needing the M4 GLU quadrics (+5 small-geometry false-greens they share).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-03 15:45:58 +02:00
Istvan Matejcsok
071eef5454 feat(3d): gl1 shim M0-M2 — FFP-on-WebGL2 substrate + lighting; 12 scenarios truly green
wasm/gl1: the GL1.x fixed-function emulation layer replacing the
gl_ffp_stub.c no-ops in the 3d-regression harness link. This batch:

- symbol split: 52 FFP-only entry points implemented; 10 Emscripten-owned
  names intercepted via wasm-ld --wrap (sources.txt/wrapped_symbols.txt are
  the shared manifests for both link sites; production hookup lands in M7)
- matrix stacks (MODELVIEW/PROJECTION, glGetFloatv readback), immediate
  mode with all 8 GL1 primitive conversions, GL1-default state mirror
- full GL 1.5 Gouraud lighting uber-shader (eye-space light capture at
  glLightfv time, color-material, two-side, COMBINE evaluator + alpha test
  wired but inert until M3/M5)
- draw routing: FFP traffic identified by GL_VERTEX_ARRAY client state;
  blit/2D-GAL draws pass through untouched
- display lists: correct glGenLists/glIsList existing-empty semantics;
  recorder itself is M3 (recorded commands drop with a one-time warning)

Parity: 20/47 under the 0.02 floor, of which 12 verified genuinely
rendering (bg-gradient x2, bounding-box, half-open-cylinder, segment x2,
material x3, light x3 — eyeballed against baselines); the other 8 are
small-geometry scenarios whose missing GLU/display-list content sits under
the floor (become real in M3/M4).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-03 15:45:58 +02:00