289 lines
10 KiB
Markdown
289 lines
10 KiB
Markdown
# GAL Native Test Harness Architecture
|
|
|
|
> **ARCHIVED / HISTORICAL** — the WebGL-GAL backend described here was implemented (see `kicad/common/gal/webgl/`). Live test docs: [`tests/gal-regression/README.md`](../../../../tests/gal-regression/README.md). Kept for design rationale.
|
|
|
|
## Overview
|
|
|
|
The GAL native test harness is a standalone macOS application that compiles KiCad's actual `OPENGL_GAL` rendering engine against system wxWidgets. It generates baseline PNG screenshots for visual regression testing of WebGL rendering in the WASM build.
|
|
|
|
**Purpose**: Compare native OpenGL rendering (ground truth) against WebGL rendering in the browser to detect visual regressions.
|
|
|
|
---
|
|
|
|
## Architecture
|
|
|
|
```
|
|
┌─────────────────────────────────────────────────────────────────────┐
|
|
│ GAL Native Test │
|
|
├─────────────────────────────────────────────────────────────────────┤
|
|
│ gal_native_test.cpp (wxApp + wxFrame) │
|
|
│ └─ Creates OPENGL_GAL on wxGLCanvas │
|
|
│ └─ Runs 11 test scenarios │
|
|
│ └─ Captures FBO → PNG for each │
|
|
├─────────────────────────────────────────────────────────────────────┤
|
|
│ kicad_stubs.cpp │ gal_test_accessor.cpp │
|
|
│ • PGM_BASE singleton │ • Template accessor for │
|
|
│ • ADVANCED_CFG │ private OPENGL_GAL members │
|
|
│ • KIFONT stubs │ • FBO reading │
|
|
│ • Observable stubs │ │
|
|
├─────────────────────────────────────────────────────────────────────┤
|
|
│ KiCad OPENGL_GAL (from submodule) │
|
|
│ kicad/common/gal/opengl/*.cpp (18 source files) │
|
|
├─────────────────────────────────────────────────────────────────────┤
|
|
│ System Dependencies │
|
|
│ wxWidgets (via homebrew) │ GLEW │ OpenGL │
|
|
└─────────────────────────────────────────────────────────────────────┘
|
|
```
|
|
|
|
---
|
|
|
|
## GAL Code Source
|
|
|
|
The test harness compiles **KiCad's actual OPENGL_GAL** from the kicad submodule:
|
|
|
|
### Source Files (`/kicad/common/gal/opengl/`)
|
|
|
|
| File | Purpose |
|
|
|------|---------|
|
|
| `opengl_gal.cpp` | Main GAL implementation - drawing primitives |
|
|
| `opengl_compositor.cpp` | FBO management, layer compositing |
|
|
| `antialiasing.cpp` | SMAA antialiasing implementation |
|
|
| `vertex_manager.cpp` | Vertex buffer accumulation |
|
|
| `vertex_item.cpp` | Individual vertex items |
|
|
| `vertex_container.cpp` | Vertex storage base class |
|
|
| `cached_container.cpp` | Cached geometry container |
|
|
| `cached_container_gpu.cpp` | GPU-resident cached geometry |
|
|
| `cached_container_ram.cpp` | RAM-backed cached geometry |
|
|
| `noncached_container.cpp` | Per-frame geometry |
|
|
| `gpu_manager.cpp` | GPU buffer management |
|
|
| `shader.cpp` | GLSL shader compilation/linking |
|
|
| `utils.cpp` | OpenGL utility functions |
|
|
| `gl_resources.cpp` | OpenGL resource management |
|
|
| `hidpi_gl_canvas.cpp` | HiDPI-aware GL canvas |
|
|
| `graphics_abstraction_layer.cpp` | GAL base class |
|
|
| `color4d.cpp` | Color handling |
|
|
| `gal_display_options.cpp` | Display options |
|
|
|
|
### Build Configuration (`CMakeLists.txt`)
|
|
|
|
```cmake
|
|
# Links against system wxWidgets
|
|
find_program(WX_CONFIG_EXECUTABLE wx-config
|
|
HINTS /opt/homebrew/bin /usr/local/bin)
|
|
|
|
# Includes KiCad headers from submodule
|
|
target_include_directories(gal_native_test PRIVATE
|
|
${KICAD_SOURCE}/include
|
|
${KICAD_SOURCE}/include/gal
|
|
${KICAD_SOURCE}/include/gal/opengl
|
|
${KICAD_SOURCE}/libs/kimath/include
|
|
${KICAD_SOURCE}/libs/core/include
|
|
)
|
|
```
|
|
|
|
---
|
|
|
|
## Generated Files
|
|
|
|
### Location
|
|
|
|
`/tests/gal-regression/native/generated/`
|
|
|
|
### Purpose
|
|
|
|
GLSL shaders must be embedded as strings at runtime. `generate_shaders.py` converts shader source files to C++ hex arrays.
|
|
|
|
### Generator Script
|
|
|
|
`generate_shaders.py` reads from `/kicad/common/gal/shaders/` and creates:
|
|
|
|
| Shader | Generated Files | Purpose |
|
|
|--------|-----------------|---------|
|
|
| `kicad.frag` | `glsl_kicad_frag.cpp/h` | Fragment shader (coloring) |
|
|
| `kicad.vert` | `glsl_kicad_vert.cpp/h` | Vertex shader (transforms) |
|
|
| `smaa_base.glsl` | `glsl_smaa_base.cpp/h` | SMAA common structures |
|
|
| `smaa_pass_1_*.glsl` | 3 file pairs | SMAA edge detection |
|
|
| `smaa_pass_2_*.glsl` | 2 file pairs | SMAA blending weights |
|
|
| `smaa_pass_3_*.glsl` | 2 file pairs | SMAA neighborhood blending |
|
|
|
|
### Generated Code Structure
|
|
|
|
```cpp
|
|
// generated/glsl_kicad_frag.cpp
|
|
namespace KIGFX {
|
|
namespace BUILTIN_SHADERS {
|
|
static unsigned char glsl_kicad_frag_bytes[] = { 0x2f, 0x2a, ... };
|
|
std::string glsl_kicad_frag = std::string(
|
|
reinterpret_cast<char const*>(glsl_kicad_frag_bytes), 4233);
|
|
}}
|
|
```
|
|
|
|
---
|
|
|
|
## Test Harness Components
|
|
|
|
### `gal_native_test.cpp`
|
|
|
|
Main test driver with wxApp/wxFrame:
|
|
|
|
1. Creates `OPENGL_GAL` on a wxGLCanvas
|
|
2. Configures coordinate system for 1:1 world-to-screen mapping
|
|
3. Iterates through test scenarios
|
|
4. Captures FBO contents as PNG screenshots
|
|
|
|
Key configuration:
|
|
```cpp
|
|
// Critical: Set 1:1 world-to-screen mapping
|
|
// GAL default is for PCB nanometers (3.937e-8), which would
|
|
// compress pixel coordinates (0-800) to ~0.003 screen pixels
|
|
m_gal->SetWorldUnitLength(1.0 / ADVANCED_CFG::GetCfg().m_ScreenDPI);
|
|
```
|
|
|
|
### `kicad_stubs.cpp`
|
|
|
|
Minimal implementations for KiCad symbols not included in GAL:
|
|
|
|
- `PGM_BASE` singleton with `GL_CONTEXT_MANAGER`
|
|
- `ADVANCED_CFG` with `m_ScreenDPI = 91`
|
|
- `KIFONT` stubs (returns nullptr for fonts)
|
|
- `OBSERVABLE_BASE` observer pattern stubs
|
|
- UI dialog stubs (`DisplayError`, etc.)
|
|
|
|
### `gal_test_accessor.cpp`
|
|
|
|
Uses C++ template technique to access private members:
|
|
|
|
```cpp
|
|
// Access private OPENGL_GAL members without modifying headers
|
|
template<typename T, T> struct steal_impl;
|
|
template<typename T, T ptr>
|
|
struct steal_impl {
|
|
friend T get(steal_impl*) { return ptr; }
|
|
};
|
|
```
|
|
|
|
Provides:
|
|
- `GetCompositorMainBufferTexture()` - For screenshot reading
|
|
- `GetCompositorMainFBO()` - FBO ID access
|
|
- `ReadCompositorFBOPixels()` - Direct pixel readback
|
|
|
|
### `gal_test_scenarios.cpp`
|
|
|
|
11 rendering test scenarios using the GAL API.
|
|
|
|
---
|
|
|
|
## Test Coverage Analysis
|
|
|
|
### Current Scenarios
|
|
|
|
| # | Name | GAL Features Tested |
|
|
|---|------|-------------------|
|
|
| 0 | basic-lines | `DrawLine`, `SetStrokeColor`, `SetLineWidth` |
|
|
| 1 | line-widths | `DrawLine` with varying widths (0.5 to 12.0) |
|
|
| 2 | circles | `DrawCircle` (filled and stroked) |
|
|
| 3 | arcs | `DrawArc` |
|
|
| 4 | rectangles | `DrawRectangle` (filled and stroked) |
|
|
| 5 | polygons | `DrawPolygon`, `DrawPolyline` |
|
|
| 6 | alpha-blending | `SetFillColor` with alpha transparency |
|
|
| 7 | transforms | `Save`, `Restore`, `Rotate`, `Translate`, `Scale` |
|
|
| 8 | grid-cursor | `DrawGrid`, `DrawCursor` |
|
|
| 9 | segments | `DrawSegment` |
|
|
| 10 | complex-scene | `SetLayerDepth` (z-ordering) |
|
|
|
|
### Coverage: ~19% of GAL API (14 of 73 methods)
|
|
|
|
### Missing Coverage (Priority Order)
|
|
|
|
**High Priority** (Critical for KiCad functionality):
|
|
1. `DrawBitmap()` - Image/icon rendering
|
|
2. `DrawCurve()` - Bezier curves
|
|
3. `DrawGlyph()` / `BitmapText()` - Text rendering
|
|
4. `DrawSegmentChain()` - Complex paths
|
|
5. `DrawArcSegment()` - Filled arc segments
|
|
|
|
**Medium Priority** (Performance-critical features):
|
|
6. Group methods: `BeginGroup()`, `EndGroup()`, `DrawGroup()`, `ClearCache()`
|
|
7. Render targets: `SetTarget()`, offscreen rendering
|
|
8. `SetNegativeDrawMode()` - Gerber-style rendering
|
|
|
|
**Low Priority** (Already implicit or not relevant to WASM):
|
|
9. `EnableDepthTest()` - Implicit in complex-scene
|
|
10. Context locking - Single-threaded in WASM
|
|
|
|
---
|
|
|
|
## Code Quality Assessment
|
|
|
|
### Strengths
|
|
|
|
1. **Clean separation** - Stubs, accessor, scenarios in separate files
|
|
2. **Minimal stubs** - Only implements what's needed
|
|
3. **Safe accessor** - Template technique avoids `#define private public`
|
|
4. **Standard shader embedding** - Common practice for GL applications
|
|
|
|
### Architecture Decisions
|
|
|
|
| Decision | Rationale |
|
|
|----------|-----------|
|
|
| Compile actual KiCad GAL | Ground truth for comparison |
|
|
| System wxWidgets | Native OpenGL context |
|
|
| FBO reading | Clean screenshots without window capture |
|
|
| Hex shader embedding | Runtime shader loading like KiCad |
|
|
|
|
---
|
|
|
|
## Running the Test
|
|
|
|
### Build
|
|
|
|
```bash
|
|
./scripts/build-gal-native-test.sh
|
|
```
|
|
|
|
### Execute
|
|
|
|
```bash
|
|
./tests/gal-regression/native/build/gal_native_test --output ./baselines
|
|
```
|
|
|
|
### Options
|
|
|
|
| Flag | Description |
|
|
|------|-------------|
|
|
| `--output <dir>` | Output directory for PNG files |
|
|
| `--width <w>` | Canvas width (default: 800) |
|
|
| `--height <h>` | Canvas height (default: 600) |
|
|
| `--show` | Show window instead of headless |
|
|
|
|
---
|
|
|
|
## Next Steps
|
|
|
|
### Immediate: Expand Test Coverage
|
|
|
|
1. Add `DrawBitmap` scenario with simple test image
|
|
2. Add `DrawCurve` (Bezier) scenario
|
|
3. Add text rendering scenario (requires KIFONT implementation)
|
|
4. Add `DrawSegmentChain` scenario
|
|
5. Add `DrawArcSegment` scenario
|
|
|
|
### Future: WebGL Comparison
|
|
|
|
1. Run same scenarios in WASM build
|
|
2. Compare native vs WebGL screenshots
|
|
3. Automate regression detection
|
|
|
|
---
|
|
|
|
## File Reference
|
|
|
|
| File | Location |
|
|
|------|----------|
|
|
| Test driver | `tests/gal-regression/native/gal_native_test.cpp` |
|
|
| Stubs | `tests/gal-regression/native/kicad_stubs.cpp` |
|
|
| Private accessor | `tests/gal-regression/native/gal_test_accessor.cpp` |
|
|
| Test scenarios | `tests/gal-regression/scenarios/gal_test_scenarios.cpp` |
|
|
| CMake config | `tests/gal-regression/native/CMakeLists.txt` |
|
|
| Shader generator | `tests/gal-regression/native/generate_shaders.py` |
|
|
| Build script | `scripts/build-gal-native-test.sh` |
|