pcbjam/tests/gal-regression/wasm/gal_webgl_test.html
Viktor Vaczi c23bc974be feat(webgl): Wire up WEBGL_GAL test harness with proper canvas setup
- Add wasm_stubs.cpp with WASM-specific stub implementations for
  COLOR4D::BLACK/WHITE, GLU tesselator, PGM_BASE, and other KiCad
  dependencies
- Update Makefile to include KiCad sources (GAL base class, display
  options, HiDPI canvas) and test scenarios
- Fix kiglew.h to define GLEW guard (__glew_h__) preventing conflicts
  with Emscripten's GLEW header
- Update test HTML to create canvas before module load (MODULARIZE
  requires passing canvas in Module config, not preRun)
- Update Playwright tests to find canvas elements correctly
- Add #window-container for wxWidgets GL canvas support

Build produces 8.4MB WASM with full WEBGL_GAL implementation.
Tests pass but rendering still shows dark output (debugging in progress).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-07 21:32:53 +01:00

257 lines
8 KiB
HTML

<!DOCTYPE html>
<html lang="en-us">
<head>
<meta charset="utf-8">
<title>GAL WebGL Test</title>
<style>
body {
margin: 0;
padding: 0;
width: 100%;
height: 100%;
overflow: hidden;
background: #1a1a26;
color: #fff;
font-family: monospace;
}
/* wxWidgets window styling */
.window {
position: absolute;
pointer-events: none;
z-index: 10;
background-color: black;
overflow: hidden;
width: 0;
height: 0;
}
.window-canvas {
position: absolute;
top: 0;
left: 0;
pointer-events: none;
}
/* GL canvas styling */
.gl-canvas {
position: absolute;
z-index: 100;
pointer-events: none;
}
/* Controls overlay */
#controls-overlay {
position: fixed;
top: 10px;
left: 10px;
z-index: 1000;
background: rgba(0,0,0,0.8);
padding: 10px;
border-radius: 4px;
}
#status {
margin-top: 10px;
padding: 5px;
background: #2a2a3a;
border-radius: 4px;
}
button, select {
padding: 5px 10px;
margin-right: 5px;
}
</style>
</head>
<body>
<!-- wxWidgets main window container -->
<div id="main-window"></div>
<!-- wxWidgets window container (for popups and GL canvases) -->
<div id="window-container"></div>
<!-- Controls overlay -->
<div id="controls-overlay">
<select id="scenario-select">
<option value="-1">Select scenario...</option>
</select>
<button id="run-btn" disabled>Run</button>
<button id="run-all-btn" disabled>All</button>
<div id="status">Loading WASM...</div>
</div>
<script>
// Scenario names (matching native test)
const SCENARIO_NAMES = [
'basic-lines', // 0
'line-widths', // 1
'circles', // 2
'arcs', // 3
'rectangles', // 4
'polygons', // 5
'alpha-blending', // 6
'transforms', // 7
'grid-cursor', // 8
'segments', // 9
'complex-scene', // 10
'bezier-curves', // 11
'arc-segments', // 12
'segment-chain', // 13
'group-caching', // 14
'polylines-multi', // 15
'hole-walls', // 16
'grid-native', // 17
'cursor-native', // 18
'render-targets', // 19
'screen-transform', // 20
'clear-colors', // 21
'depth-testing', // 22
'negative-mode', // 23
'text-attrs', // 24
'glyphs', // 25
'bitmap', // 26
'transform-api' // 27
];
var wasmModule = null;
// Populate scenario dropdown
function populateScenarios() {
const select = document.getElementById('scenario-select');
SCENARIO_NAMES.forEach((name, index) => {
const option = document.createElement('option');
option.value = index;
option.textContent = `${index}: ${name}`;
select.appendChild(option);
});
}
// Run a single scenario
function runScenario(index) {
if (!wasmModule) return;
const result = wasmModule.ccall('runScenario', 'number', ['number'], [index]);
if (result === 0) {
setStatus(`Rendered: ${SCENARIO_NAMES[index]}`);
} else {
setStatus(`ERROR: Failed scenario ${index}`);
}
}
// Run all scenarios
async function runAllScenarios() {
if (!wasmModule) return;
const total = wasmModule.ccall('getTotalScenarios', 'number', [], []);
setStatus(`Running all ${total} scenarios...`);
for (let i = 0; i < total; i++) {
runScenario(i);
await new Promise(r => setTimeout(r, 100));
}
setStatus(`Completed all ${total} scenarios`);
}
// Update status
function setStatus(text) {
document.getElementById('status').textContent = text;
console.log('[GAL Test]', text);
}
// Get the GL canvas for screenshots
function getGLCanvas() {
// wxGLCanvas creates canvases with class 'gl-canvas' or id 'glcanvas-N'
const glCanvases = document.querySelectorAll('.gl-canvas');
if (glCanvases.length > 0) {
return glCanvases[0];
}
// Fallback to any canvas in window-container
const containerCanvases = document.querySelectorAll('#window-container canvas');
if (containerCanvases.length > 0) {
return containerCanvases[0];
}
// Fallback to main canvas
return document.getElementById('canvas');
}
// Export for Playwright
window.galTest = {
runScenario,
runAllScenarios,
getScenarioName: (index) => SCENARIO_NAMES[index],
getTotalScenarios: () => SCENARIO_NAMES.length,
getGLCanvas: getGLCanvas
};
// Create canvas immediately (before module loads)
var canvas = document.createElement('canvas');
canvas.id = 'canvas';
canvas.style.display = 'block';
canvas.style.position = 'absolute';
canvas.style.left = '0';
canvas.style.top = '0';
canvas.width = 800;
canvas.height = 600;
canvas.style.width = '800px';
canvas.style.height = '600px';
canvas.oncontextmenu = function(e) { e.preventDefault(); };
canvas.addEventListener("webglcontextlost", function(e) {
setStatus('WebGL context lost!');
e.preventDefault();
}, false);
document.getElementById('main-window').appendChild(canvas);
// Module configuration for MODULARIZE
var Module = {
canvas: canvas, // Pass the canvas we created
print: function(text) {
console.log(text);
},
printErr: function(text) {
console.error(text);
},
setStatus: function(text) {
setStatus(text || 'Ready');
},
onRuntimeInitialized: function() {
setStatus('WASM initialized');
}
};
// Setup UI after module loads
function onModuleReady(module) {
wasmModule = module;
setStatus('Ready. Select a scenario.');
document.getElementById('run-btn').disabled = false;
document.getElementById('run-all-btn').disabled = false;
document.getElementById('run-btn').onclick = () => {
const select = document.getElementById('scenario-select');
const index = parseInt(select.value);
if (index >= 0) {
runScenario(index);
}
};
document.getElementById('run-all-btn').onclick = runAllScenarios;
document.getElementById('scenario-select').onchange = (e) => {
const index = parseInt(e.target.value);
if (index >= 0) {
runScenario(index);
}
};
// Dispatch ready event for Playwright
window.dispatchEvent(new CustomEvent('gal-test-ready'));
}
// Initialize
populateScenarios();
</script>
<script src="gal_webgl_test.js" onload="createGALTest(Module).then(onModuleReady).catch(e => setStatus('Load error: ' + e.message))"></script>
</body>
</html>