10 KiB
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. 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)
# 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
// 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:
- Creates
OPENGL_GALon a wxGLCanvas - Configures coordinate system for 1:1 world-to-screen mapping
- Iterates through test scenarios
- Captures FBO contents as PNG screenshots
Key configuration:
// 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_BASEsingleton withGL_CONTEXT_MANAGERADVANCED_CFGwithm_ScreenDPI = 91KIFONTstubs (returns nullptr for fonts)OBSERVABLE_BASEobserver pattern stubs- UI dialog stubs (
DisplayError, etc.)
gal_test_accessor.cpp
Uses C++ template technique to access private members:
// 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 readingGetCompositorMainFBO()- FBO ID accessReadCompositorFBOPixels()- 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):
DrawBitmap()- Image/icon renderingDrawCurve()- Bezier curvesDrawGlyph()/BitmapText()- Text renderingDrawSegmentChain()- Complex pathsDrawArcSegment()- 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
- Clean separation - Stubs, accessor, scenarios in separate files
- Minimal stubs - Only implements what's needed
- Safe accessor - Template technique avoids
#define private public - 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
./scripts/build-gal-native-test.sh
Execute
./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
- Add
DrawBitmapscenario with simple test image - Add
DrawCurve(Bezier) scenario - Add text rendering scenario (requires KIFONT implementation)
- Add
DrawSegmentChainscenario - Add
DrawArcSegmentscenario
Future: WebGL Comparison
- Run same scenarios in WASM build
- Compare native vs WebGL screenshots
- 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 |