pcbjam/docs/features/archive/webgl/0003-webgl-gal-implementation-plan.md
2026-06-05 12:16:13 +02:00

9.6 KiB

WebGL GAL Port - Implementation Complete

ARCHIVED / HISTORICAL — the WebGL-GAL backend described here was implemented (see kicad/common/gal/webgl/). Live test docs: tests/gal-regression/README.md. Kept for design rationale.

Status: ALL PHASES COMPLETE

Branch: webgl (21+ commits)

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.

Final State

  • WebGL GAL: Fully integrated into kicad/common/gal/webgl/ (~27,800 lines)
  • GAL Test Suite: 28 scenarios passing, visual parity with native OpenGL
  • KiCad WASM: Builds and runs successfully with WebGL GAL
  • 3D Viewer: Disabled with stubs for WASM builds (KICAD_BUILD_3D_VIEWER_WASM=OFF)

Key Decisions

  • Approach: Copy and modify existing OpenGL GAL code
  • Scope: Full feature parity (all 28 scenarios)
  • Location: Developed in tests/gal-regression/wasm/ first, moved to KiCad for integration

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:

#!/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 - ALL COMPLETE

Phase 1: Native Test Harness

Created unified test infrastructure with 28 scenarios covering 100% of GAL API.

Commits: 490f531051fb87

Deliverables:

  • scripts/test-gal-regression.sh - Master build/test/compare script
  • scripts/build-gal-native-test.sh - Native build script
  • tests/gal-regression/native/ - Native C++ test harness using OPENGL_GAL
  • tests/gal-regression/scenarios/ - 28 shared test scenarios
  • tests/gal-regression/baseline/ - Native OpenGL reference screenshots

Phase 2: WebGL GAL Implementation

Full port of OPENGL_GAL to WebGL 2.0 / OpenGL ES 3.0.

Commits: a4f444f74faa7e

Key Changes:

  • Replaced legacy glBegin/glEnd with VBO-based rendering
  • Replaced GL matrix stack with glm matrices
  • Converted GLSL shaders to ES 3.0 (attributein, varyingout, etc.)
  • Added VAO support (required for WebGL 2.0)
  • Replaced GLU tesselator with earcut.hpp

Deliverables:

  • tests/gal-regression/wasm/webgl/ - Initial WebGL GAL implementation
  • scripts/build-gal-webgl-test.sh - WASM build script
  • tests/e2e/gal-webgl.spec.ts - Playwright spec for screenshots

Phase 3: Complete API Coverage

All GAL methods implemented with visual parity.

Method Groups (all complete):

  • Basic drawing: DrawLine, DrawSegment, DrawCircle, DrawArc
  • Shapes: DrawRectangle, DrawPolygon, DrawPolyline
  • Advanced: DrawBezier, DrawBezierArc, DrawArcSegment, DrawSegmentChain
  • State: Colors, transforms, depth testing, render targets
  • Groups: BeginGroup, EndGroup, DrawGroup, ChangeGroupColor/Depth
  • Text: DrawGlyph, DrawGlyphs, BitmapText
  • Special: DrawGrid, DrawCursor, DrawBitmap

Phase 4: KiCad Integration

WebGL GAL moved to KiCad source tree with CMake integration.

Commit: 37d973f (KiCad submodule: 1b5bb125d2)

Deliverables:

  • Move webgl_gal.* to kicad/common/gal/webgl/
  • CMake integration for Emscripten builds
  • 3D viewer disabled with stubs (KICAD_BUILD_3D_VIEWER_WASM=OFF)
  • KiCad WASM builds and e2e tests pass

Technical Lessons Learned

1. GLSL ES 3.0 Shader Conversion

Desktop OpenGL shaders needed conversion for WebGL 2.0:

attribute  in
varying  out (vertex shader) / in (fragment shader)
texture2D()  texture()
gl_FragColor  explicit out variable

Automated in generate_shaders.py.

2. VAO Required for WebGL 2.0

WebGL 2.0 requires Vertex Array Objects (VAOs):

glGenVertexArrays(1, &m_vao);
glBindVertexArray(m_vao);

3. Legacy GL Elimination

All legacy OpenGL calls replaced:

  • glBegin/glEnd → VBO-based rendering
  • glPushMatrix/glPopMatrix → glm matrix stack
  • GL_QUADSGL_TRIANGLES (quads not supported in WebGL)
  • glEnableClientState → modern vertex attributes

4. GLU Tesselator Replacement

GLU not available in WebGL. Implemented using earcut.hpp:

  • File: kicad/common/gal/webgl/glu_tess_impl.cpp
  • Provides gluNewTess, gluTessBeginPolygon, etc.

5. Coordinate System & Retina Scaling

  • White background required for screenshot comparison (alpha compositing)
  • Retina 2x scaling requires proper devicePixelRatio handling
  • World-to-screen mapping: SetWorldUnitLength(1.0 / DPI)

6. Alpha Blending

Proper blend functions required for correct transparency:

glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA);

7. 3D Viewer Stubbing Strategy

When disabling 3D viewer (KICAD_BUILD_3D_VIEWER_WASM=OFF):

  • Guard includes/code with #ifndef __EMSCRIPTEN__
  • Stub classes need wxWidgets event table macros (BEGIN_EVENT_TABLE)
  • Complex classes (PANEL_PREVIEW_3D_MODEL) require all event handler stubs

File Structure (Final)

kicad/common/gal/webgl/           # WebGL GAL in KiCad source tree
├── webgl_gal.cpp                 # Main implementation (3184 lines)
├── webgl_gal.h                   # Class declaration (618 lines)
├── webgl_compositor.cpp          # FBO compositing
├── webgl_antialiasing.cpp        # SMAA implementation
├── gpu_manager.cpp               # VBO/VAO management
├── vertex_manager.cpp            # Vertex accumulation
├── shader.cpp                    # GLSL compilation
├── glu_tess_impl.cpp             # GLU tesselator (earcut)
├── earcut.hpp                    # Polygon triangulation
└── ... (20+ files total)

tests/gal-regression/
├── baseline/                     # Native OpenGL reference (28 PNGs)
├── baseline-webgl/               # WebGL reference (29 PNGs)
├── native/                       # Native test harness
├── wasm/                         # WebGL test harness (uses KiCad GAL)
└── scenarios/                    # Shared 28 test scenarios

wasm/stubs/
├── 3d_canvas_stub.cpp            # ~500 lines of 3D stubs
├── 3d_viewer_stub.cpp            # EDA_3D_VIEWER_FRAME stub
└── 3d_scenegraph_stub.cpp        # VRML export stubs

scripts/
├── build-gal-native-test.sh      # Native build script
├── build-gal-webgl-test.sh       # WASM build script
└── test-gal-regression.sh        # Master test script

Verification Commands

# Build KiCad WASM
docker/build.sh

# Run GAL regression tests
scripts/test-gal-regression.sh

# Run KiCad e2e tests
cd tests && npm run test:kicad