Add wxGLCanvas WASM implementation and documentation
- 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>
This commit is contained in:
parent
4069de6a0b
commit
9e1d971797
4 changed files with 700 additions and 4 deletions
347
docs/04-COMPREHENSIVE-WASM-TEST.md
Normal file
347
docs/04-COMPREHENSIVE-WASM-TEST.md
Normal file
|
|
@ -0,0 +1,347 @@
|
||||||
|
# Phase 4: Comprehensive wxWidgets WASM Test Application
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
Create a multi-widget showcase test application for the wxWidgets WASM port that thoroughly exercises wxUniversal controls and event handling. This validates the WASM port before integrating KiCad components.
|
||||||
|
|
||||||
|
## Goal
|
||||||
|
|
||||||
|
1. Display various wxUniversal controls in a tabbed interface
|
||||||
|
2. Support full interaction: mouse clicks, keyboard input, mouse drag/draw, hover effects
|
||||||
|
3. Show an on-screen event log panel for visual verification
|
||||||
|
4. Have corresponding Playwright tests to verify all interactions work
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Application Architecture
|
||||||
|
|
||||||
|
```
|
||||||
|
+-------------------------------------------------------+
|
||||||
|
| Menu: File | Edit | Help |
|
||||||
|
+-------------------------------------------------------+
|
||||||
|
| [Controls Tab] [Text Tab] [Drawing Tab] [Lists Tab] |
|
||||||
|
+-------------------------------------------------------+
|
||||||
|
| |
|
||||||
|
| Tab Content Area |
|
||||||
|
| (varies by tab) |
|
||||||
|
| |
|
||||||
|
+-------------------------------------------------------+
|
||||||
|
| Event Log Panel (wxListBox) |
|
||||||
|
| - "Button 'Test' clicked" |
|
||||||
|
| - "Checkbox toggled: checked" |
|
||||||
|
| - "Mouse moved to (123, 456)" |
|
||||||
|
+-------------------------------------------------------+
|
||||||
|
| Status Bar: Ready |
|
||||||
|
+-------------------------------------------------------+
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Files to Modify
|
||||||
|
|
||||||
|
| File | Action | Description |
|
||||||
|
|------|--------|-------------|
|
||||||
|
| `tests/wasm-app/minimal_test.cpp` | Replace | Comprehensive widget showcase |
|
||||||
|
| `tests/e2e/wxwidgets.spec.ts` | Update | Add interaction tests |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Test Application Structure
|
||||||
|
|
||||||
|
### Control IDs
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
enum {
|
||||||
|
ID_BTN_TEST = wxID_HIGHEST + 1,
|
||||||
|
ID_BTN_TOGGLE,
|
||||||
|
ID_CHK_FEATURE,
|
||||||
|
ID_RADIO_OPTIONS,
|
||||||
|
ID_SLIDER,
|
||||||
|
ID_GAUGE,
|
||||||
|
ID_TEXT_SINGLE,
|
||||||
|
ID_TEXT_MULTI,
|
||||||
|
ID_TEXT_PASSWORD,
|
||||||
|
ID_COMBO,
|
||||||
|
ID_LISTBOX,
|
||||||
|
ID_CHOICE,
|
||||||
|
ID_BTN_CLEAR,
|
||||||
|
ID_EVENT_LOG,
|
||||||
|
ID_DRAWING_PANEL
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
### Main Classes
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
class TestApp : public wxApp; // Application entry point
|
||||||
|
class TestFrame : public wxFrame; // Main frame with notebook and event log
|
||||||
|
class ControlsPage : public wxPanel; // Tab 1: Buttons, checkboxes, sliders
|
||||||
|
class TextPage : public wxPanel; // Tab 2: Text controls
|
||||||
|
class DrawingPage : public wxPanel; // Tab 3: Mouse drawing canvas
|
||||||
|
class ListsPage : public wxPanel; // Tab 4: Lists and dropdowns
|
||||||
|
class DrawingPanel : public wxPanel; // Custom drawing canvas
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Tab Contents
|
||||||
|
|
||||||
|
### Tab 1: Controls
|
||||||
|
|
||||||
|
| Control | ID | Purpose |
|
||||||
|
|---------|-----|---------|
|
||||||
|
| wxButton ("Click Me") | `ID_BTN_TEST` | Basic click event |
|
||||||
|
| wxToggleButton ("Toggle") | `ID_BTN_TOGGLE` | Two-state button |
|
||||||
|
| wxCheckBox ("Enable feature") | `ID_CHK_FEATURE` | Boolean toggle |
|
||||||
|
| wxRadioBox ("Options": A/B/C) | `ID_RADIO_OPTIONS` | Exclusive selection |
|
||||||
|
| wxSlider (0-100) | `ID_SLIDER` | Range selection |
|
||||||
|
| wxGauge | `ID_GAUGE` | Shows slider value |
|
||||||
|
|
||||||
|
### Tab 2: Text Input
|
||||||
|
|
||||||
|
| Control | ID | Purpose |
|
||||||
|
|---------|-----|---------|
|
||||||
|
| wxTextCtrl (single-line) | `ID_TEXT_SINGLE` | Basic text input |
|
||||||
|
| wxTextCtrl (multi-line) | `ID_TEXT_MULTI` | Multi-line text |
|
||||||
|
| wxTextCtrl (password) | `ID_TEXT_PASSWORD` | Masked input |
|
||||||
|
| wxComboBox | `ID_COMBO` | Dropdown with text |
|
||||||
|
|
||||||
|
### Tab 3: Drawing Canvas
|
||||||
|
|
||||||
|
| Control | ID | Purpose |
|
||||||
|
|---------|-----|---------|
|
||||||
|
| DrawingPanel | `ID_DRAWING_PANEL` | Custom painting |
|
||||||
|
| wxButton ("Clear") | `ID_BTN_CLEAR` | Reset canvas |
|
||||||
|
|
||||||
|
Mouse interactions:
|
||||||
|
- `wxEVT_LEFT_DOWN` - Start drawing
|
||||||
|
- `wxEVT_MOTION` - Draw line segments
|
||||||
|
- `wxEVT_LEFT_UP` - End drawing
|
||||||
|
|
||||||
|
### Tab 4: Lists
|
||||||
|
|
||||||
|
| Control | ID | Purpose |
|
||||||
|
|---------|-----|---------|
|
||||||
|
| wxListBox | `ID_LISTBOX` | Multi-item selection |
|
||||||
|
| wxChoice | `ID_CHOICE` | Dropdown selection |
|
||||||
|
| Add/Remove buttons | - | Modify list contents |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Event Log Panel
|
||||||
|
|
||||||
|
- `wxListBox` at bottom of frame
|
||||||
|
- ID: `ID_EVENT_LOG`
|
||||||
|
- Max 100 entries (older entries removed)
|
||||||
|
- Auto-scrolls to bottom
|
||||||
|
- Format: `"[HH:MM:SS] Event description"`
|
||||||
|
|
||||||
|
Events also logged to browser console for Playwright verification.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Event Types to Test
|
||||||
|
|
||||||
|
| Event | Control | Test Method |
|
||||||
|
|-------|---------|-------------|
|
||||||
|
| `wxEVT_BUTTON` | Button | Click canvas coordinates |
|
||||||
|
| `wxEVT_TOGGLEBUTTON` | ToggleButton | Click and verify state |
|
||||||
|
| `wxEVT_CHECKBOX` | CheckBox | Click and verify checked |
|
||||||
|
| `wxEVT_RADIOBOX` | RadioBox | Click option and verify |
|
||||||
|
| `wxEVT_SLIDER` | Slider | Drag and verify value |
|
||||||
|
| `wxEVT_TEXT` | TextCtrl | Type and verify content |
|
||||||
|
| `wxEVT_TEXT_ENTER` | TextCtrl | Press Enter |
|
||||||
|
| `wxEVT_LISTBOX` | ListBox | Click item and verify |
|
||||||
|
| `wxEVT_CHOICE` | Choice | Click and select |
|
||||||
|
| `wxEVT_LEFT_DOWN` | Panel | Mouse down |
|
||||||
|
| `wxEVT_MOTION` | Panel | Mouse move |
|
||||||
|
| `wxEVT_LEFT_UP` | Panel | Mouse up |
|
||||||
|
| `wxEVT_PAINT` | Panel | Automatic on invalidate |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Playwright Test Structure
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
test.describe('wxWidgets WASM Comprehensive', () => {
|
||||||
|
// Existing loading tests...
|
||||||
|
|
||||||
|
test.describe('Controls Tab', () => {
|
||||||
|
test('button click logs event');
|
||||||
|
test('checkbox toggle works');
|
||||||
|
test('slider interaction');
|
||||||
|
test('radio button selection');
|
||||||
|
});
|
||||||
|
|
||||||
|
test.describe('Text Input Tab', () => {
|
||||||
|
test('text input accepts keyboard');
|
||||||
|
test('multiline text input');
|
||||||
|
test('password field masks input');
|
||||||
|
});
|
||||||
|
|
||||||
|
test.describe('Drawing Tab', () => {
|
||||||
|
test('mouse drag draws on canvas');
|
||||||
|
test('clear button resets canvas');
|
||||||
|
});
|
||||||
|
|
||||||
|
test.describe('Lists Tab', () => {
|
||||||
|
test('listbox selection');
|
||||||
|
test('choice dropdown');
|
||||||
|
});
|
||||||
|
|
||||||
|
test.describe('Event Log', () => {
|
||||||
|
test('events are logged visually');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### Test Helpers
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// Wait for app to be ready
|
||||||
|
async function waitForApp(page: Page) {
|
||||||
|
await page.waitForSelector('canvas', { state: 'visible', timeout: 30000 });
|
||||||
|
await page.waitForTimeout(500); // Let UI settle
|
||||||
|
}
|
||||||
|
|
||||||
|
// Simulate click at canvas coordinates
|
||||||
|
async function clickAt(page: Page, x: number, y: number) {
|
||||||
|
const canvas = page.locator('canvas');
|
||||||
|
await canvas.click({ position: { x, y } });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check console for event log entries
|
||||||
|
async function getConsoleEvents(page: Page): Promise<string[]> {
|
||||||
|
// Capture console.log messages with [EVENT] prefix
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Testing Challenges & Solutions
|
||||||
|
|
||||||
|
### Challenge 1: Canvas Coordinate Mapping
|
||||||
|
|
||||||
|
wxUniversal renders all widgets to a single HTML5 canvas. Tests need to know widget positions.
|
||||||
|
|
||||||
|
**Solution**: Use consistent layout with predictable positions. Log control bounds to console on startup.
|
||||||
|
|
||||||
|
### Challenge 2: Verifying Event Log Content
|
||||||
|
|
||||||
|
The event log is inside the WASM app, not directly accessible from JavaScript.
|
||||||
|
|
||||||
|
**Solution**: Log events to browser console with `[EVENT]` prefix. Playwright captures via `page.on('console')`.
|
||||||
|
|
||||||
|
### Challenge 3: Keyboard Input
|
||||||
|
|
||||||
|
Need to route keyboard events to the correct wxWidgets control.
|
||||||
|
|
||||||
|
**Solution**: Focus canvas first, then use `page.keyboard.type()`. wxWidgets handles focus internally.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## wxUniversal Controls Reference
|
||||||
|
|
||||||
|
Available controls in wxUniversal (verified in `wxwidgets/include/wx/univ/`):
|
||||||
|
|
||||||
|
**Buttons:**
|
||||||
|
- wxButton, wxBitmapButton, wxToggleButton
|
||||||
|
|
||||||
|
**Input Controls:**
|
||||||
|
- wxCheckBox, wxRadioButton, wxRadioBox
|
||||||
|
- wxTextCtrl (single/multi-line)
|
||||||
|
- wxComboBox, wxChoice
|
||||||
|
|
||||||
|
**Lists:**
|
||||||
|
- wxListBox, wxCheckListBox
|
||||||
|
|
||||||
|
**Range Controls:**
|
||||||
|
- wxSlider, wxGauge, wxSpinButton
|
||||||
|
|
||||||
|
**Containers:**
|
||||||
|
- wxNotebook (tabbed container)
|
||||||
|
- wxPanel, wxScrolledWindow
|
||||||
|
|
||||||
|
**Static Controls:**
|
||||||
|
- wxStaticText, wxStaticBox, wxStaticLine
|
||||||
|
|
||||||
|
**Frame Elements:**
|
||||||
|
- wxMenu, wxMenuBar, wxStatusBar
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Implementation Steps
|
||||||
|
|
||||||
|
### Step 1: Update minimal_test.cpp
|
||||||
|
|
||||||
|
Replace current minimal test with comprehensive widget showcase.
|
||||||
|
|
||||||
|
Key components:
|
||||||
|
1. `TestFrame` constructor creates notebook, tabs, and event log
|
||||||
|
2. Each tab page creates its controls with appropriate sizers
|
||||||
|
3. Event handlers call `LogEvent()` to record actions
|
||||||
|
4. `LogEvent()` adds to wxListBox AND prints to console
|
||||||
|
|
||||||
|
### Step 2: Update Playwright Tests
|
||||||
|
|
||||||
|
Extend `tests/e2e/wxwidgets.spec.ts`:
|
||||||
|
1. Keep existing loading tests
|
||||||
|
2. Add test groups for each tab
|
||||||
|
3. Add console event capture
|
||||||
|
4. Add canvas interaction helpers
|
||||||
|
|
||||||
|
### Step 3: Build and Test
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Rebuild test app
|
||||||
|
./scripts/build-wasm-test.sh
|
||||||
|
|
||||||
|
# Run tests
|
||||||
|
cd tests && npm test
|
||||||
|
|
||||||
|
# Visual debugging
|
||||||
|
npm run test:ui
|
||||||
|
# or
|
||||||
|
cd wasm-app && npx serve .
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Success Criteria
|
||||||
|
|
||||||
|
### Visual Verification
|
||||||
|
- [ ] App displays with menu bar
|
||||||
|
- [ ] Four tabs visible and switchable
|
||||||
|
- [ ] Controls visible on each tab
|
||||||
|
- [ ] Event log panel at bottom
|
||||||
|
|
||||||
|
### Interaction Tests
|
||||||
|
- [ ] Button click logs "Button clicked"
|
||||||
|
- [ ] Checkbox toggle logs state change
|
||||||
|
- [ ] Slider drag updates gauge and logs value
|
||||||
|
- [ ] Text input shows typed text and logs changes
|
||||||
|
- [ ] Drawing canvas responds to mouse drag
|
||||||
|
- [ ] ListBox/Choice selection works
|
||||||
|
|
||||||
|
### Playwright Tests
|
||||||
|
- [ ] All existing loading tests pass
|
||||||
|
- [ ] Button interaction test passes
|
||||||
|
- [ ] Checkbox interaction test passes
|
||||||
|
- [ ] Text input test passes
|
||||||
|
- [ ] Drawing interaction test passes
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Dependencies
|
||||||
|
|
||||||
|
Before implementation:
|
||||||
|
- wxWidgets WASM build complete (`./scripts/build-wxuniversal-wasm.sh`)
|
||||||
|
- Existing Playwright tests passing (`cd tests && npm test`)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## References
|
||||||
|
|
||||||
|
- wxWidgets Samples: `wxwidgets/samples/widgets/` - Multi-widget showcase pattern
|
||||||
|
- wxWidgets Samples: `wxwidgets/samples/drawing/` - Custom painting pattern
|
||||||
|
- wxUniversal Headers: `wxwidgets/include/wx/univ/` - Available controls
|
||||||
324
docs/05-WXGLCANVAS-WASM-IMPLEMENTATION.md
Normal file
324
docs/05-WXGLCANVAS-WASM-IMPLEMENTATION.md
Normal file
|
|
@ -0,0 +1,324 @@
|
||||||
|
# 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
|
||||||
|
```bash
|
||||||
|
./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.in` for 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:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 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)
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
#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()` using `emscripten_webgl_make_context_current()`
|
||||||
|
- Handle canvas resize events
|
||||||
|
- `SwapBuffers()` - typically no-op for WebGL (auto-swaps)
|
||||||
|
|
||||||
|
#### 3. Update `configure.in` (line ~3890)
|
||||||
|
|
||||||
|
```autoconf
|
||||||
|
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):**
|
||||||
|
- [gl4es](https://github.com/ptitSeb/gl4es) - OpenGL 2.1 → GLES 2.0 translation
|
||||||
|
- [Regal](https://github.com/emscripten-ports/regal) - Used by D3Wasm (Doom 3 port)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 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
|
||||||
|
```cpp
|
||||||
|
glBegin(GL_TRIANGLES);
|
||||||
|
glBegin(GL_QUADS);
|
||||||
|
glBegin(GL_LINE_STRIP);
|
||||||
|
glBegin(GL_LINE_LOOP);
|
||||||
|
glBegin(GL_LINES);
|
||||||
|
glEnd();
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Vertex Specification
|
||||||
|
```cpp
|
||||||
|
glVertex2f(x, y);
|
||||||
|
glVertex3f(x, y, z);
|
||||||
|
glVertex2d(x, y);
|
||||||
|
glVertex3d(x, y, z);
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Color Specification
|
||||||
|
```cpp
|
||||||
|
glColor3f(r, g, b);
|
||||||
|
glColor4f(r, g, b, a);
|
||||||
|
glColor3ub(r, g, b);
|
||||||
|
glColor4ub(r, g, b, a);
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Matrix Operations
|
||||||
|
```cpp
|
||||||
|
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
|
||||||
|
```cpp
|
||||||
|
glTexCoord2f(s, t);
|
||||||
|
glTexCoord2d(s, t);
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Normal Vectors
|
||||||
|
```cpp
|
||||||
|
glNormal3f(nx, ny, nz);
|
||||||
|
glNormal3d(nx, ny, nz);
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Legacy Vertex Arrays
|
||||||
|
```cpp
|
||||||
|
glEnableClientState(GL_VERTEX_ARRAY);
|
||||||
|
glEnableClientState(GL_COLOR_ARRAY);
|
||||||
|
glDisableClientState(...);
|
||||||
|
glVertexPointer(...);
|
||||||
|
glColorPointer(...);
|
||||||
|
glDrawArrays(...);
|
||||||
|
```
|
||||||
|
|
||||||
|
#### State Management
|
||||||
|
```cpp
|
||||||
|
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:
|
||||||
|
1. Creates wxGLCanvas with WebGL context
|
||||||
|
2. Tests each category of GL functions
|
||||||
|
3. Renders a known pattern (e.g., colored shapes)
|
||||||
|
4. 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.
|
||||||
|
|
@ -1,6 +1,19 @@
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
# Build wxWidgets with wxUniversal for WebAssembly
|
# Build wxWidgets with wxUniversal for WebAssembly
|
||||||
# This builds the GUI-enabled wxWidgets needed for KiCad
|
# This builds the GUI-enabled wxWidgets needed for KiCad
|
||||||
|
#
|
||||||
|
# Prerequisites:
|
||||||
|
# - Emscripten SDK installed and activated
|
||||||
|
# - autoconf (for regenerating configure from configure.in)
|
||||||
|
#
|
||||||
|
# To regenerate Makefile.in from bakefiles (after modifying files.bkl):
|
||||||
|
# cd wxwidgets/build/bakefiles
|
||||||
|
# docker run --rm -v "$(pwd)/../..":"$(pwd)/../.." -w "$(pwd)" \
|
||||||
|
# ghcr.io/vslavik/bakefile:0.2 bakefile_gen
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# ./build-wxuniversal-wasm.sh # Build (incremental)
|
||||||
|
# ./build-wxuniversal-wasm.sh --clean # Clean build
|
||||||
|
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
|
|
@ -15,12 +28,18 @@ echo "Build dir: $BUILD_DIR"
|
||||||
echo "wxWidgets source: $WX_SOURCE"
|
echo "wxWidgets source: $WX_SOURCE"
|
||||||
|
|
||||||
# Verify we're in the right place
|
# Verify we're in the right place
|
||||||
if [ ! -f "$WX_SOURCE/configure" ]; then
|
if [ ! -f "$WX_SOURCE/configure.in" ]; then
|
||||||
echo "ERROR: wxWidgets source not found at $WX_SOURCE"
|
echo "ERROR: wxWidgets source not found at $WX_SOURCE"
|
||||||
echo "Make sure the wxwidgets submodule is initialized"
|
echo "Make sure the wxwidgets submodule is initialized"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Regenerate configure if configure.in is newer
|
||||||
|
if [ "$WX_SOURCE/configure.in" -nt "$WX_SOURCE/configure" ]; then
|
||||||
|
echo "configure.in is newer than configure, regenerating..."
|
||||||
|
(cd "$WX_SOURCE" && autoconf)
|
||||||
|
fi
|
||||||
|
|
||||||
# Clean if requested
|
# Clean if requested
|
||||||
if [ "$1" = "--clean" ]; then
|
if [ "$1" = "--clean" ]; then
|
||||||
echo "Cleaning build directory..."
|
echo "Cleaning build directory..."
|
||||||
|
|
@ -64,9 +83,15 @@ emconfigure "$WX_SOURCE/configure" \
|
||||||
--with-cxx=17 \
|
--with-cxx=17 \
|
||||||
--enable-utf8
|
--enable-utf8
|
||||||
|
|
||||||
# Build
|
# Build PCRE first to avoid race condition with parallel builds
|
||||||
|
# PCRE headers (pcre2.h) must be generated before regex.cpp compiles
|
||||||
echo ""
|
echo ""
|
||||||
echo "=== Building ==="
|
echo "=== Building PCRE first (dependency) ==="
|
||||||
|
emmake make -C 3rdparty/pcre
|
||||||
|
|
||||||
|
# Build wxWidgets
|
||||||
|
echo ""
|
||||||
|
echo "=== Building wxWidgets ==="
|
||||||
emmake make -j$(nproc 2>/dev/null || sysctl -n hw.ncpu)
|
emmake make -j$(nproc 2>/dev/null || sysctl -n hw.ncpu)
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
|
|
|
||||||
|
|
@ -1 +1 @@
|
||||||
Subproject commit fd8c4884833eff35ce790bfd6f085c1556227456
|
Subproject commit f9229d7b06260dfdf699932fac84d3f0c7620c0f
|
||||||
Loading…
Reference in a new issue