- Update build script with bakefile_gen Docker instructions and PCRE fix - Add wxWidgets submodule with OpenGL/WebGL support for WASM - Document comprehensive WASM test plan and wxGLCanvas implementation - Document legacy GL emulation strategy for KiCad (423+ legacy GL calls) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
8.8 KiB
05 - wxGLCanvas WASM Implementation
This document outlines the plan to implement wxGLCanvas (OpenGL/WebGL support) for the wxWidgets WASM port, enabling KiCad's OpenGL-based rendering in the browser.
Current Status: Completed ✅
wxGLCanvas implementation is complete. The GL library builds successfully:
libwx_wasmunivu_gl-3.2-emscripten.a
Build Command
./scripts/build-wxuniversal-wasm.sh # Incremental build
./scripts/build-wxuniversal-wasm.sh --clean # Clean build
Implementation Summary
- Created
wxwidgets/include/wx/wasm/glcanvas.h - Created
wxwidgets/src/wasm/glcanvas.cpp - Updated
wxwidgets/configure.infor WASM OpenGL support - Updated
wxwidgets/build/bakefiles/files.bkl - Added empty stubs for legacy wxGLAPI functions in
glcmn.cpp(KiCad doesn't use these)
Phase 1: Fix wxWidgets Build (PCRE2 Issue) ✅ Completed
Problem
fatal error: 'pcre2.h' file not found
Root Cause
Build race condition during parallel make. With -j parallel builds, regex.cpp can compile before PCRE generates its headers.
Solution
Modify scripts/build-wxuniversal-wasm.sh to build PCRE first:
# After configure section, add:
echo ""
echo "=== Building PCRE first (dependency) ==="
emmake make -C 3rdparty/pcre
# Then existing make:
echo ""
echo "=== Building ==="
emmake make -j$(nproc 2>/dev/null || sysctl -n hw.ncpu)
Phase 2: Implement wxGLCanvas for WASM ✅ Completed
Estimated Effort: 18-27 hours (Actual: ~8 hours)
| Component | Files | Effort |
|---|---|---|
| Header file | include/wx/wasm/glcanvas.h |
2-3 hours |
| Implementation | src/wasm/glcanvas.cpp |
8-12 hours |
| Configure updates | configure.in |
1-2 hours |
| Build system | build/files.bkl |
1 hour |
| Testing & debug | - | 6-10 hours |
Implementation Approach: Direct WebGL via Emscripten
Use Emscripten's HTML5 API (emscripten/html5.h) for WebGL context management.
1. Create include/wx/wasm/glcanvas.h (~100-150 lines)
#ifndef _WX_WASM_GLCANVAS_H_
#define _WX_WASM_GLCANVAS_H_
#include "wx/glcanvas.h"
#include <emscripten/html5.h>
class WXDLLIMPEXP_GL wxGLContext : public wxGLContextBase
{
public:
wxGLContext(wxGLCanvas *win, const wxGLContext *other = NULL,
const wxGLContextAttrs *ctxAttrs = NULL);
virtual ~wxGLContext();
virtual bool SetCurrent(const wxGLCanvas& win) const wxOVERRIDE;
private:
EMSCRIPTEN_WEBGL_CONTEXT_HANDLE m_context;
wxDECLARE_CLASS(wxGLContext);
};
class WXDLLIMPEXP_GL wxGLCanvas : public wxGLCanvasBase
{
public:
wxGLCanvas(wxWindow *parent,
const wxGLAttributes& dispAttrs,
wxWindowID id = wxID_ANY,
const wxPoint& pos = wxDefaultPosition,
const wxSize& size = wxDefaultSize,
long style = 0,
const wxString& name = wxGLCanvasName,
const wxPalette& palette = wxNullPalette);
virtual ~wxGLCanvas();
virtual bool SwapBuffers() wxOVERRIDE;
// Get the canvas element ID for Emscripten
const char* GetCanvasId() const { return m_canvasId.c_str(); }
private:
bool CreateWindow(wxWindow *parent, const wxGLAttributes& dispAttrs,
wxWindowID id, const wxPoint& pos, const wxSize& size,
long style, const wxString& name);
std::string m_canvasId;
wxDECLARE_CLASS(wxGLCanvas);
};
#endif // _WX_WASM_GLCANVAS_H_
2. Create src/wasm/glcanvas.cpp (~500-700 lines)
Key implementation tasks:
- Parse wxGL_* attributes → WebGL context attributes
- Create WebGL context using
emscripten_webgl_create_context() - Implement
SetCurrent()usingemscripten_webgl_make_context_current() - Handle canvas resize events
SwapBuffers()- typically no-op for WebGL (auto-swaps)
3. Update configure.in (line ~3890)
elif test "$wxUSE_WASM" = 1; then
dnl WASM uses WebGL through Emscripten
OPENGL_LIBS=""
wxUSE_OPENGL="yes"
4. Update build/files.bkl
Add glcanvas.cpp to WASM sources list.
Reference Files
src/unix/glegl.cpp- EGL implementation (922 lines)src/gtk/glcanvas.cpp- GTK implementation (303 lines)src/common/glcmn.cpp- Base implementation shared by all ports
Phase 3: Legacy OpenGL Emulation for KiCad
The Problem
KiCad uses legacy OpenGL immediate mode which is NOT supported in WebGL. KiCad does NOT use wxGLAPI (wxWidgets' wrapper), but calls raw OpenGL directly:
| Function Category | Count | Examples |
|---|---|---|
| glBegin/glEnd | 70 | Immediate mode drawing |
| glVertex2f/3f/2d/3d | 112 | Vertex specification |
| glMatrixMode/glPushMatrix/glPopMatrix | 88 | Matrix stack operations |
| glEnableClientState/glDisableClientState | 43 | Legacy vertex arrays |
| glColor3f/4f/3d/4d | 34 | Per-vertex colors |
| glTranslatef/glRotatef/glScalef | 26 | Matrix transforms |
| glTexCoord2f/2d | 19 | Texture coordinates |
| glVertexPointer/glColorPointer | 18 | Legacy vertex arrays |
| glNormal3f/3d | 12 | Normal vectors |
| TOTAL | 423+ | Must be emulated |
Most Affected KiCad Files
3D Viewer (heaviest usage):
opengl_utils.cpp- 88+ calls (bounding boxes, debug geometry)render_3d_opengl.cpp- 50+ calls (core 3D board rendering)3d_spheres_gizmo.cpp- 48+ calls (interactive gizmos)layer_triangles.cpp- 34 calls (layer mesh rendering)
2D PCB Viewer:
opengl_gal.cpp- 39+ calls (bitmap text, cursor)antialiasing.cpp- 37+ calls (fullscreen AA post-processing)opengl_compositor.cpp- 19+ calls (screen compositing)
Solution: Emscripten Legacy GL Emulation
Link flag: -sLEGACY_GL_EMULATION
This built-in Emscripten feature emulates legacy OpenGL (immediate mode, matrix stack, etc.) on top of WebGL. It was used to port the Sauerbraten 3D game (BananaBread).
Optional performance flags:
-sGL_UNSAFE_OPTS- Skip redundant GL work-sGL_FFP_ONLY- Disable programmable pipeline detection
Alternatives (if needed later):
Phase 4: Test Graphics
Goal
Create tests that exercise all GL functions KiCad uses to verify emulation works.
GL Functions to Test (Based on KiCad Usage)
Immediate Mode Drawing
glBegin(GL_TRIANGLES);
glBegin(GL_QUADS);
glBegin(GL_LINE_STRIP);
glBegin(GL_LINE_LOOP);
glBegin(GL_LINES);
glEnd();
Vertex Specification
glVertex2f(x, y);
glVertex3f(x, y, z);
glVertex2d(x, y);
glVertex3d(x, y, z);
Color Specification
glColor3f(r, g, b);
glColor4f(r, g, b, a);
glColor3ub(r, g, b);
glColor4ub(r, g, b, a);
Matrix Operations
glMatrixMode(GL_PROJECTION);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glPushMatrix();
glPopMatrix();
glTranslatef(x, y, z);
glRotatef(angle, x, y, z);
glScalef(x, y, z);
glOrtho(...);
gluPerspective(...);
Texture Coordinates
glTexCoord2f(s, t);
glTexCoord2d(s, t);
Normal Vectors
glNormal3f(nx, ny, nz);
glNormal3d(nx, ny, nz);
Legacy Vertex Arrays
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_COLOR_ARRAY);
glDisableClientState(...);
glVertexPointer(...);
glColorPointer(...);
glDrawArrays(...);
State Management
glEnable(GL_BLEND);
glEnable(GL_DEPTH_TEST);
glEnable(GL_TEXTURE_2D);
glDisable(...);
glBlendFunc(...);
Test File: tests/wasm-app/gl_test.cpp
Create test application that:
- Creates wxGLCanvas with WebGL context
- Tests each category of GL functions
- Renders a known pattern (e.g., colored shapes)
- Logs results to console
Playwright Tests: tests/e2e/gl.spec.ts
Automated tests:
- WebGL context creation succeeds
- Each GL function category executes without errors
- Visual verification of rendered output (screenshot comparison)
Files to Create/Modify
| File | Action |
|---|---|
scripts/build-wxuniversal-wasm.sh |
Modify - fix PCRE build order |
wxwidgets/include/wx/wasm/glcanvas.h |
Create - wxGLCanvas header |
wxwidgets/src/wasm/glcanvas.cpp |
Create - wxGLCanvas implementation |
wxwidgets/configure.in |
Modify - add WASM OpenGL support |
wxwidgets/build/files.bkl |
Modify - add source file |
tests/wasm-app/gl_test.cpp |
Create - test application |
tests/e2e/gl.spec.ts |
Create - Playwright tests |
Why This Matters for KiCad
KiCad uses a Graphics Abstraction Layer (GAL) with two backends:
- Cairo backend: Uses wxDC (2D) - works with existing wxUniversal WASM
- OpenGL backend: Uses wxGLCanvas - requires this implementation
KiCad's OpenGL GAL provides:
- Hardware-accelerated rendering
- Better performance for complex PCBs
- 3D viewer support
Without wxGLCanvas, KiCad would be limited to Cairo rendering only.