8.3 KiB
OpenGL, GAL, and WebGL Strategy for KiCad WASM
ARCHIVED / HISTORICAL — the WebGL-GAL backend described here was implemented (see
kicad/common/gal/webgl/, ~22.5k lines). Live test docs:tests/gal-regression/README.md. Kept for design rationale.
Summary
This document analyzes KiCad's graphics architecture and evaluates strategies for WebGL rendering in the WASM build.
Current Implementation: Emscripten LEGACY_GL_EMULATION
We currently use Emscripten's legacy OpenGL emulation:
-sLEGACY_GL_EMULATION -sMAX_WEBGL_VERSION=2
How it works:
KiCad/wxWidgets C++ (glBegin, glVertex, glColor, etc.)
│
▼
gl_immediate_shim.js (our fix for color-per-vertex bug)
│
▼
Emscripten GLImmediate (translates legacy GL → shaders + VBOs)
│
▼
WebGL 2.0 (ES 3.0 compatible, shader-based)
Key components:
| Component | File | Purpose |
|---|---|---|
| wxGLCanvas | wxwidgets/src/wasm/glcanvas.cpp |
Creates WebGL contexts on dedicated HTML5 canvases |
| GL Shim | wasm/shims/gl_immediate_shim.js |
Fixes color-per-vertex bug in GLImmediate |
| GLU Tesselator | wasm/stubs/glu_wasm_impl.cpp |
Polygon triangulation using earcut (GLU not available in WebGL) |
| GLEW Stub | wasm/cmake/FindGLEW.cmake |
Stubs out GLEW (not needed with Emscripten) |
| kiglew.h | kicad/include/gal/opengl/kiglew.h |
Maps double→float, stubs unsupported functions |
GLU Status
KiCad uses GLU for polygon tesselation (gluTess* functions for zones, complex shapes).
We already solved this with /wasm/stubs/glu_wasm_impl.cpp which implements the GLU tesselator API using KiCad's earcut algorithm. This works with both LEGACY_GL_EMULATION and would work with pure ES3.
GLU is NOT a blocker.
Where KiCad Uses Legacy OpenGL
Two separate systems use legacy OpenGL:
1. OPENGL_GAL (2D Rendering)
Used for schematic and PCB editors.
Files:
kicad/common/gal/opengl/opengl_gal.cpp- Main implementationkicad/common/gal/opengl/gpu_manager.cpp- VBO managementkicad/common/gal/opengl/opengl_compositor.cpp- Framebuffer compositingkicad/common/gal/opengl/antialiasing.cpp- AA effects
Legacy GL usage in opengl_gal.cpp:
- Lines 1557-1605: Bitmap rendering with
glBegin(GL_QUADS) - Lines 2690-2755: Cursor drawing with
glBegin(GL_LINES) - Lines 566-629, 1558-1602, 2704-2751: Matrix stack operations (
glPushMatrix,glPopMatrix,glMatrixMode)
2. 3D Viewer (3D Rendering)
Separate from GAL, used for 3D board visualization.
Files using legacy GL:
kicad/3d-viewer/3d_rendering/opengl/render_3d_opengl.cppkicad/3d-viewer/3d_rendering/opengl/opengl_utils.cppkicad/3d-viewer/3d_rendering/opengl/layer_triangles.cppkicad/3d-viewer/3d_rendering/opengl/3d_spheres_gizmo.cppkicad/3d-viewer/3d_rendering/opengl/3d_model.cppkicad/3d-viewer/3d_model_viewer/eda_3d_model_viewer.cppkicad/3d-viewer/3d_canvas/eda_3d_canvas_pivot.cpp
If We Cover GAL, Is That Enough?
For 2D editing (schematic + PCB): YES
The entire 2D rendering pipeline goes through GAL. A working GAL backend enables full schematic and PCB editing.
For 3D viewer: NO
The 3D viewer is a separate rendering system. It could be disabled/stubbed initially and added later.
The GAL API
GAL (Graphics Abstraction Layer) is a clean 2D drawing interface defined in kicad/include/gal/graphics_abstraction_layer.h.
NO raw OpenGL is exposed. The API consists of ~40 virtual methods:
// Drawing primitives
virtual void DrawLine(const VECTOR2D& start, const VECTOR2D& end);
virtual void DrawSegment(const VECTOR2D& start, const VECTOR2D& end, double width);
virtual void DrawPolyline(const std::vector<VECTOR2D>& points);
virtual void DrawCircle(const VECTOR2D& center, double radius);
virtual void DrawArc(const VECTOR2D& center, double radius, const EDA_ANGLE& start, const EDA_ANGLE& angle);
virtual void DrawRectangle(const VECTOR2D& start, const VECTOR2D& end);
virtual void DrawPolygon(const SHAPE_POLY_SET& polySet);
virtual void DrawBitmap(const BITMAP_BASE& bitmap, double alpha);
virtual void DrawGlyph(const KIFONT::GLYPH& glyph);
// Attributes
virtual void SetFillColor(const COLOR4D& color);
virtual void SetStrokeColor(const COLOR4D& color);
virtual void SetLineWidth(float width);
virtual void SetIsFill(bool enabled);
virtual void SetIsStroke(bool enabled);
virtual void SetLayerDepth(double depth);
// Transforms (matrix stack)
virtual void Save();
virtual void Restore();
virtual void Transform(const MATRIX3x3D& matrix);
virtual void Rotate(double angle);
virtual void Translate(const VECTOR2D& translation);
virtual void Scale(const VECTOR2D& scale);
// Rendering control
virtual void BeginDrawing();
virtual void EndDrawing();
virtual void SetTarget(RENDER_TARGET target);
virtual void ClearTarget(RENDER_TARGET target);
virtual void ClearScreen();
// Grid and cursor
virtual void DrawGrid();
virtual void DrawCursor(const VECTOR2D& position);
Existing GAL implementations:
| Class | File | Purpose |
|---|---|---|
OPENGL_GAL |
kicad/include/gal/opengl/opengl_gal.h |
OpenGL rendering (uses legacy GL internally) |
CAIRO_GAL |
kicad/include/gal/cairo/cairo_gal.h |
Cairo software rendering |
CALLBACK_GAL |
kicad/include/callback_gal.h |
Hit testing, no actual rendering |
What Breaks with FULL_ES3 (No Legacy Emulation)
If we removed LEGACY_GL_EMULATION:
| Feature | Used In | Status in FULL_ES3 |
|---|---|---|
gluTess* (tesselation) |
Polygon rendering | Our stub works |
glBegin/glEnd |
Bitmap quads, cursor | Not available |
glVertex/glColor |
Immediate mode drawing | Not available |
glPushMatrix/glPopMatrix |
Transformations | Not available |
glMatrixMode |
Matrix switching | Not available |
GL_QUADS |
Bitmap rendering | Not available (only triangles) |
glEnableClientState |
Legacy vertex arrays | Not available |
Strategy Options
Option 1: Keep LEGACY_GL_EMULATION (Current)
Pros:
- Works now
- Minimal code changes to KiCad
- All immediate mode functions available
Cons:
- ~200KB binary overhead from GLImmediate
- Runtime overhead from emulation
- Color-per-vertex bug requires our shim
- Some edge cases may not work
Build flags:
-sLEGACY_GL_EMULATION -sMAX_WEBGL_VERSION=2
Option 2: Create WEBGL_GAL (New Backend)
Create a new GAL implementation that uses pure WebGL 2.0 (ES 3.0) without legacy emulation.
Pros:
- No emulation overhead
- Smaller binary
- Cleaner, more maintainable
- Better performance
Cons:
- Development effort (~2-3 weeks)
- Need to maintain separate backend
Estimated scope:
New class: WEBGL_GAL : public GAL
Files needed:
├── webgl_gal.h (~300 lines)
├── webgl_gal.cpp (~2500 lines)
├── webgl_shaders.cpp (~500 lines)
└── webgl_compositor.cpp (~500 lines)
Total: ~3500-4000 lines
Why it's feasible:
- GAL API is clean - no raw GL leaks through
- CAIRO_GAL proves it works (~2500 lines)
- All drawing is 2D with simple primitives
- KiCad's shaders already exist and work in WebGL
Option 3: Cairo-only (Software Rendering)
Use CAIRO_GAL exclusively, render to HTML5 2D canvas.
Pros:
- Already exists
- No WebGL needed
- Works everywhere
Cons:
- Slow (CPU-only)
- May struggle with complex boards
- No hardware acceleration
Recommendation
Short-term: Keep LEGACY_GL_EMULATION - it works and allows rapid development.
Medium-term: Create WEBGL_GAL - cleaner architecture, better performance, removes emulation hacks.
For 3D viewer: Disable initially, add later as separate effort.
Key Files Reference
| Purpose | File |
|---|---|
| Build flags | scripts/common/env.sh |
| wxGLCanvas WASM | wxwidgets/src/wasm/glcanvas.cpp |
| GL immediate shim | wasm/shims/gl_immediate_shim.js |
| GLU tesselator | wasm/stubs/glu_wasm_impl.cpp |
| KiCad GL compat | kicad/include/gal/opengl/kiglew.h |
| KiCad shaders | kicad/common/gal/shaders/kicad_*.glsl |
| GAL base class | kicad/include/gal/graphics_abstraction_layer.h |
| OPENGL_GAL | kicad/include/gal/opengl/opengl_gal.h |
| CAIRO_GAL | kicad/include/gal/cairo/cairo_gal.h |
| GL documentation | tests/GL_README.md |