feat(webgl): Add WebGL GAL test infrastructure (Phase 1)

Add complete test infrastructure for WebGL GAL visual regression testing:

- scripts/test-gal-regression.sh: Master script that builds both backends,
  runs tests, and performs two-level comparison (native vs baseline,
  webgl vs native)
- scripts/build-gal-webgl-test.sh: WASM build using Makefile with em++
- tests/gal-regression/wasm/: WebGL test harness (stub WEBGL_GAL)
- tests/e2e/gal-webgl.spec.ts: Playwright test for screenshot capture

Fix Homebrew Emscripten environment in scripts/common/env.sh:
- Set EMSDK_PYTHON for Python 3.10+ (em++ reads this, not $PYTHON)
- Add bundled LLVM to PATH (Emscripten needs its clang with WASM backend)

Verified: Native vs Baseline passes (28/28), WebGL generates blank
screenshots as expected (WEBGL_GAL implementation is Phase 2).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Viktor Vaczi 2026-01-07 16:11:31 +01:00
commit a4f444fea8
10 changed files with 1401 additions and 0 deletions

View file

@ -0,0 +1,181 @@
# WebGL GAL Port - Master Plan
## Goal
Port KiCad's GAL (Graphics Abstraction Layer) from OpenGL to WebGL to enable full KiCad functionality in the browser. Use the existing 28-scenario test suite to verify visual parity between native OpenGL and WebGL implementations.
## Current State
- **GAL Test Harness**: 28 scenarios covering 100% of GAL API (~70 methods)
- **Native Test**: C++ binary using real OPENGL_GAL, outputs PNGs to `tests/gal-regression/baseline/`
- **KiCad WASM**: Already runs in browser but crashes on OpenGL compatibility issues
- **OpenGL GAL**: ~9,500 lines of C++/GLSL using mix of legacy GL and modern VBOs
## Key Decisions
- **Approach**: Copy and modify existing OpenGL GAL code
- **Scope**: Full feature parity (all 28 scenarios)
- **Location**: Develop in `tests/gal-regression/` first, move to KiCad later
## Architecture
**Two-backend test architecture:**
```
┌─────────────────────────────────────────────────────────────┐
│ SAME 28 SCENARIO FILES │
│ (scenarios/*.cpp - pure GAL API calls) │
└─────────────────────────────────────────────────────────────┘
┌───────────────┴───────────────┐
▼ ▼
┌─────────────────────────────┐ ┌─────────────────────────┐
│ NATIVE TEST HARNESS │ │ WEBGL TEST HARNESS │
│ (gal_native_test.cpp) │ │ (gal_webgl_test.cpp) │
│ │ │ │
│ Uses: OPENGL_GAL │ │ Uses: WEBGL_GAL │
│ Runs: macOS native │ │ Runs: Browser/WASM │
└─────────────────────────────┘ └─────────────────────────┘
│ │
▼ ▼
┌─────────────────────────────┐ ┌─────────────────────────┐
│ output/native/gal-*.png │ │ output/webgl/gal-*.png │
└─────────────────────────────┘ └─────────────────────────┘
```
## Master Test Script: `scripts/test-gal-regression.sh`
**This is the only script we run.** Single command to build, test, and compare everything:
```bash
#!/bin/bash
# Single script to build, run, and compare both backends
# 1. BUILD BOTH
scripts/build-gal-native-test.sh
scripts/build-gal-webgl-test.sh
# 2. RUN BOTH TESTS
./tests/gal-regression/native/build/gal_native_test --output tests/gal-regression/output/native/
npx playwright test gal-webgl.spec.ts # outputs to tests/gal-regression/output/webgl/
# 3. COMPARE (two-level)
compare_screenshots output/native/ baseline/ # Catch native regressions
compare_screenshots output/webgl/ output/native/ # Verify WebGL matches native
# 4. REPORT
# Exit 0 if all match, exit 1 if any differ
```
**Two-level comparison:**
1. **native vs baseline** → Catches if native code regressed
2. **webgl vs native** → Verifies WebGL implementation matches
**Output structure:**
```
tests/gal-regression/
├── baseline/ # Committed reference screenshots
├── output/
│ ├── native/ # Fresh native run
│ └── webgl/ # WebGL run via Playwright
```
## Phases
### Phase 1: Master Test Script & WebGL Harness Infrastructure
Create the unified test script and WASM-based test harness.
**Deliverables:**
- [x] `scripts/test-gal-regression.sh` - Master build/test/compare script
- [x] `tests/gal-regression/wasm/` directory structure
- [x] `tests/gal-regression/wasm/Makefile` - Emscripten build (use Makefile, not CMake)
- [x] `tests/gal-regression/wasm/gal_webgl_test.cpp` - WASM entry point
- [x] `tests/gal-regression/wasm/gal_webgl_test.html` - Test page with canvas
- [x] `scripts/build-gal-webgl-test.sh` - WASM build script (uses Makefile)
- [x] `tests/e2e/gal-webgl.spec.ts` - Playwright spec for screenshots
**Build Approach:**
- Use a Makefile with direct `em++` calls (like `tests/apps/Makefile.wasm`)
- Avoid `emcmake cmake` which requires Python 3.10+ (system has 3.9.6)
- Follow the same pattern as `build-wasm-test.sh`
**Verification:** Master script builds both, runs native successfully, WebGL loads empty page.
### Phase 2: WEBGL_GAL Implementation
Copy and modify OpenGL GAL to create pure WebGL implementation.
**Approach:** Start with `kicad/common/gal/opengl/opengl_gal.cpp`, then:
1. Replace legacy immediate mode (`glBegin/glEnd`) with VBO-based rendering
2. Replace GL matrix stack with glm matrices (already used internally)
3. Adapt shaders for WebGL 2.0 / GLSL ES 3.0
4. Handle WebGL-specific limitations
**Key Files:**
- [ ] `tests/gal-regression/wasm/webgl_gal.h` - Class declaration
- [ ] `tests/gal-regression/wasm/webgl_gal.cpp` - Main implementation
- [ ] `tests/gal-regression/wasm/webgl_shaders.cpp` - Shader sources
- [ ] Adapt vertex_manager, gpu_manager as needed
**Verification:** Scenario 0 (basic-lines) renders, master script compares successfully.
### Phase 3: Complete API Coverage
Implement all GAL methods to pass all 28 scenarios.
**Method Groups (incremental):**
1. Basic drawing: DrawLine, DrawSegment, DrawCircle, DrawArc
2. Shapes: DrawRectangle, DrawPolygon, DrawPolyline
3. Advanced: DrawBezier, DrawBezierArc, DrawArcSegment, DrawSegmentChain
4. State: Colors, transforms, depth testing, render targets
5. Groups: BeginGroup, EndGroup, DrawGroup, ChangeGroupColor/Depth
6. Text: DrawGlyph, DrawGlyphs, BitmapText
7. Special: DrawGrid, DrawCursor, DrawBitmap
**Verification:** Run master script after each group - all implemented scenarios match.
### Phase 4: Integration with KiCad
Move WEBGL_GAL into KiCad source and enable for browser builds.
**Deliverables:**
- [ ] Move `webgl_gal.*` to `kicad/common/gal/webgl/`
- [ ] CMake integration for Emscripten builds
- [ ] Runtime GAL selection based on platform
- [ ] KiCad WASM builds successfully with WEBGL_GAL
**Verification:** KiCad loads in browser, opens PCB, renders correctly.
## File Structure
```
tests/gal-regression/
├── baseline/ # Committed reference (from native)
├── output/
│ ├── native/ # Fresh native run
│ └── webgl/ # WebGL run via Playwright
├── native/ # Native C++ harness (existing)
│ ├── gal_native_test.cpp
│ └── ...
├── wasm/ # WASM harness (new)
│ ├── Makefile # Use Makefile, not CMake (avoids Python issues)
│ ├── gal_webgl_test.cpp
│ ├── gal_webgl_test.html
│ ├── webgl_gal.h
│ ├── webgl_gal.cpp
│ └── webgl_shaders.cpp
└── scenarios/ # Shared scenarios (existing)
scripts/
├── build-gal-native-test.sh # existing
├── build-gal-webgl-test.sh # new
└── test-gal-regression.sh # new - MASTER SCRIPT
```
## Critical Files to Modify/Create
**New files:**
- `scripts/test-gal-regression.sh` - Master test script
- `scripts/build-gal-webgl-test.sh` - WASM build
- `tests/gal-regression/wasm/*` - All WASM harness files
- `tests/e2e/gal-webgl.spec.ts` - Playwright test
**Reference files (copy from):**
- `kicad/common/gal/opengl/opengl_gal.cpp` (3098 lines)
- `kicad/common/gal/opengl/opengl_gal.h` (614 lines)
- `kicad/common/gal/opengl/shader.cpp` (298 lines)
- `kicad/common/gal/opengl/vertex_manager.cpp` (318 lines)
- `kicad/common/gal/opengl/gpu_manager.cpp` (340 lines)