146 lines
4.1 KiB
Markdown
146 lines
4.1 KiB
Markdown
|
|
# 06 - wxGLCanvas WASM Canvas Element Integration
|
||
|
|
|
||
|
|
This document describes how to fix wxGLCanvas to render visibly in WASM by creating its own canvas element.
|
||
|
|
|
||
|
|
## Problem
|
||
|
|
|
||
|
|
wxGLCanvas currently hardcodes `m_canvasTarget = "#canvas"` which creates WebGL context on the main wxUniversal 2D canvas, causing a conflict. The GL rendering doesn't appear because it's overwritten by 2D UI rendering.
|
||
|
|
|
||
|
|
## Key Discovery
|
||
|
|
|
||
|
|
The WASM port already supports **multiple canvas elements per window**:
|
||
|
|
- Main window uses `#canvas` for 2D rendering
|
||
|
|
- Child windows can each get their own canvas element via `createWindow(id, needsCanvas=true)`
|
||
|
|
- Windows are positioned absolutely with z-index layering
|
||
|
|
- JavaScript (wx.js) manages canvas creation, positioning, and context stacking
|
||
|
|
|
||
|
|
## How KiCad Uses wxGLCanvas
|
||
|
|
|
||
|
|
```
|
||
|
|
wxFrame (EDA_DRAW_FRAME)
|
||
|
|
└── EDA_DRAW_PANEL_GAL (wxScrolledCanvas)
|
||
|
|
└── OPENGL_GAL (inherits from wxGLCanvas)
|
||
|
|
- Sized to match parent
|
||
|
|
- Child window positioned within parent
|
||
|
|
- Has its own GL context
|
||
|
|
```
|
||
|
|
|
||
|
|
Key KiCad files:
|
||
|
|
- `kicad/common/gal/opengl/opengl_gal.cpp` - GL rendering implementation
|
||
|
|
- `kicad/common/draw_panel_gal.cpp` - Creates OPENGL_GAL as child: `new OPENGL_GAL(..., this, ...)`
|
||
|
|
- `kicad/include/gal/hidpi_gl_canvas.h` - wxGLCanvas wrapper class
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Solution
|
||
|
|
|
||
|
|
wxGLCanvas should integrate with the existing WASM window system:
|
||
|
|
|
||
|
|
1. **Create its own canvas element** - Use `createWindow(id, needsCanvas=true)` in JavaScript
|
||
|
|
2. **Create WebGL context on that canvas** - Use selector `#window-{id} canvas`
|
||
|
|
3. **Handle positioning via window system** - `setWindowRect()` positions the canvas
|
||
|
|
|
||
|
|
### Key JavaScript Functions (already exist in wx.js)
|
||
|
|
|
||
|
|
```javascript
|
||
|
|
createWindow(id, needsCanvas, isVisible, classList)
|
||
|
|
setWindowRect(id, x, y, width, height)
|
||
|
|
setWindowVisibility(id, isVisible)
|
||
|
|
destroyWindow(id)
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Implementation
|
||
|
|
|
||
|
|
### Step 1: Modify Create() to create canvas element
|
||
|
|
|
||
|
|
In `wxwidgets/src/wasm/glcanvas.cpp`:
|
||
|
|
|
||
|
|
```cpp
|
||
|
|
bool wxGLCanvas::Create(wxWindow *parent, ...)
|
||
|
|
{
|
||
|
|
if ( !wxWindow::Create(parent, id, pos, size, style, name) )
|
||
|
|
return false;
|
||
|
|
|
||
|
|
// Create a window with canvas element in JavaScript
|
||
|
|
int cssId = GetCSSId();
|
||
|
|
EM_ASM({
|
||
|
|
createWindow($0, true, true, "glcanvas");
|
||
|
|
}, cssId);
|
||
|
|
|
||
|
|
// Position the canvas
|
||
|
|
wxPoint screenPos = GetScreenPosition();
|
||
|
|
wxSize clientSize = GetClientSize();
|
||
|
|
EM_ASM({
|
||
|
|
setWindowRect($0, $1, $2, $3, $4);
|
||
|
|
}, cssId, screenPos.x, screenPos.y, clientSize.GetWidth(), clientSize.GetHeight());
|
||
|
|
|
||
|
|
// Set dynamic canvas selector
|
||
|
|
m_canvasTarget = wxString::Format("#window-%d canvas", cssId).ToStdString();
|
||
|
|
|
||
|
|
return CreateWebGLContext(dispAttrs);
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### Step 2: Override DoSetSize for resize handling
|
||
|
|
|
||
|
|
```cpp
|
||
|
|
void wxGLCanvas::DoSetSize(int x, int y, int width, int height, int sizeFlags)
|
||
|
|
{
|
||
|
|
wxWindow::DoSetSize(x, y, width, height, sizeFlags);
|
||
|
|
|
||
|
|
wxPoint screenPos = GetScreenPosition();
|
||
|
|
wxSize clientSize = GetClientSize();
|
||
|
|
EM_ASM({
|
||
|
|
setWindowRect($0, $1, $2, $3, $4);
|
||
|
|
}, GetCSSId(), screenPos.x, screenPos.y, clientSize.GetWidth(), clientSize.GetHeight());
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### Step 3: Handle visibility
|
||
|
|
|
||
|
|
```cpp
|
||
|
|
void wxGLCanvas::DoShow(bool show)
|
||
|
|
{
|
||
|
|
wxWindow::DoShow(show);
|
||
|
|
EM_ASM({
|
||
|
|
setWindowVisibility($0, $1);
|
||
|
|
}, GetCSSId(), show);
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### Step 4: Clean up in destructor
|
||
|
|
|
||
|
|
```cpp
|
||
|
|
wxGLCanvas::~wxGLCanvas()
|
||
|
|
{
|
||
|
|
if ( m_webglContext > 0 )
|
||
|
|
{
|
||
|
|
emscripten_webgl_destroy_context(m_webglContext);
|
||
|
|
m_webglContext = 0;
|
||
|
|
}
|
||
|
|
EM_ASM({
|
||
|
|
destroyWindow($0);
|
||
|
|
}, GetCSSId());
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Files to Modify
|
||
|
|
|
||
|
|
| File | Changes |
|
||
|
|
|------|---------|
|
||
|
|
| `wxwidgets/src/wasm/glcanvas.cpp` | Main implementation - create own canvas, dynamic selector, resize/visibility handling |
|
||
|
|
| `wxwidgets/include/wx/wasm/glcanvas.h` | Add DoSetSize, DoShow declarations if needed |
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Testing
|
||
|
|
|
||
|
|
The existing test app in `tests/wasm-app/` with the OpenGL tab will verify:
|
||
|
|
1. Canvas element is created in DOM
|
||
|
|
2. WebGL context is on the correct canvas
|
||
|
|
3. GL rendering appears in the canvas area
|
||
|
|
4. Legacy GL emulation works (via `-sLEGACY_GL_EMULATION`)
|