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>
This commit is contained in:
parent
19e850d098
commit
071eef5454
14 changed files with 2496 additions and 6 deletions
73
wasm/gl1/README.md
Normal file
73
wasm/gl1/README.md
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
# wasm/gl1 — GL 1.x fixed-function pipeline on WebGL2
|
||||
|
||||
Emulation layer that lets KiCad's 3D viewer renderer (`RENDER_3D_OPENGL`,
|
||||
pure GL 1.x fixed-function, compiled **unmodified**) render in the browser.
|
||||
Supersedes the no-op link stubs of `wasm/stubs/gl_ffp_stub.c` (git history
|
||||
has the old file): same public surface, real implementations.
|
||||
|
||||
TDD harness: `tests/3d-regression/` — 47 native-golden scenarios; parity via
|
||||
`npm run 3d:check:parity` (see that README).
|
||||
|
||||
## How the symbols resolve
|
||||
|
||||
In the WASM build, `gl*` are plain C functions split between two providers:
|
||||
|
||||
- **Emscripten's WebGL library** (`-sMAX_WEBGL_VERSION=2`) owns every
|
||||
modern/WebGL2 name (`glClear`, `glDrawArrays`, `glTexImage2D`, buffers,
|
||||
shaders, stencil...).
|
||||
- **This shim** owns the FFP-only names WebGL2 lacks (`glBegin`, display
|
||||
lists, matrix stack, `glLight*`/`glMaterial*`, client-array pointers,
|
||||
`glTexEnv*`, `glAlphaFunc`, GLU quadrics) — see `src/gl1_entry_ffp.cpp`.
|
||||
|
||||
The emulator must also *observe* a few Emscripten-owned calls (FFP `glEnable`
|
||||
caps, draws over client arrays, matrix readback, state recorded inside
|
||||
display lists). Those are intercepted with **wasm-ld `--wrap`**: every name in
|
||||
`wrapped_symbols.txt` gets a `-Wl,--wrap=<sym>` flag at both link sites, the
|
||||
interceptors live in `src/gl1_entry_wrapped.cpp`, and `__real_*` forwards to
|
||||
the WebGL library. No kicad/wxwidgets sources are touched.
|
||||
|
||||
Link sites (both read `sources.txt` + `wrapped_symbols.txt`):
|
||||
|
||||
- `tests/3d-regression/wasm/Makefile` (the TDD harness)
|
||||
- `scripts/kicad/build-kicad-target.sh` (`GL3D_LINK_FLAGS`, production
|
||||
`kicad_editor`)
|
||||
|
||||
## Draw routing
|
||||
|
||||
A `glDrawArrays`/`glDrawElements` call is FFP traffic iff `GL_VERTEX_ARRAY`
|
||||
client state is enabled: only GL1 code calls `glEnableClientState`, while the
|
||||
raytracer blit (`eda_3d_canvas_wasm.cpp`) and the 2D WebGL GAL drive their own
|
||||
GLSL programs and never touch client state — their draws pass through
|
||||
untouched.
|
||||
|
||||
## Invariants that are easy to break (learned from the native goldens)
|
||||
|
||||
- **Display lists snapshot client arrays at record time.** The renderer sets
|
||||
`gl*Pointer`, records `glDrawArrays` inside `glNewList`, then `delete[]`s
|
||||
the arrays right after `glEndList` (`layer_triangles.cpp`) — a recorder
|
||||
that stores pointers reads freed memory. Copy eagerly at record.
|
||||
- **Lights live in eye space, captured at `glLightfv` time.** `init_lights()`
|
||||
runs under an identity modelview → the directional lights are anchored to
|
||||
the camera. Re-deriving light directions at draw time breaks every lit
|
||||
scenario.
|
||||
- **Do not "fix" mismatched normal counts.** `generate_middle_triangles`
|
||||
rejecting countersink walls (normals ≠ vertices) is upstream KiCad behavior
|
||||
the goldens document; tolerating it would *diverge* from native.
|
||||
- **Lighting is per-vertex (Gouraud)** to match fixed-function output;
|
||||
per-fragment lighting visibly mismatches speculars on coarse meshes.
|
||||
|
||||
## Layout
|
||||
|
||||
```
|
||||
sources.txt / wrapped_symbols.txt single source of truth for both link sites
|
||||
include/gl1_shim.h internal API + the state mirror
|
||||
src/gl1_entry_ffp.cpp the 52 FFP-only public entry points
|
||||
src/gl1_entry_wrapped.cpp __wrap_* interceptors (mechanism-aware TU)
|
||||
src/gl1_state.cpp state singleton, capability routing
|
||||
src/gl1_matrix.cpp MODELVIEW/PROJECTION stacks (+readback)
|
||||
src/gl1_immediate.cpp glBegin/glEnd + primitive conversion
|
||||
src/gl1_dlist.cpp display-list recorder/replayer
|
||||
src/gl1_draw.cpp draw execution (stream VBO, attrib setup)
|
||||
src/gl1_shaders.cpp the FFP uber-program (ES 3.00) + uniforms
|
||||
src/gl1_glu.cpp GLU quadrics (SGI tessellation) + gluPerspective
|
||||
```
|
||||
Loading…
Reference in a new issue