refactor(webgl): Move WebGL GAL from test harness to KiCad source tree
Migrate WebGL GAL implementation from tests/gal-regression/wasm/webgl/ to kicad/common/gal/webgl/ and kicad/include/gal/webgl/. This integrates the WebGL GAL properly into KiCad's build system: - Update test Makefile to use sources from kicad/ instead of local copies - Update build scripts for new source locations - Add test-gal-webgl.sh script for running WebGL regression tests - Update Docker to Emscripten 4.0.22 The WebGL GAL passes all 28 regression tests (matching baseline). Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
5e74a001e7
commit
37d973f064
50 changed files with 403 additions and 28977 deletions
|
|
@ -8,6 +8,11 @@
|
|||
# Uses a Makefile with direct em++ calls (like build-wasm-test.sh)
|
||||
# to avoid emcmake Python 3.10+ requirement.
|
||||
#
|
||||
# Usage:
|
||||
# ./scripts/build-gal-webgl-test.sh # Clean build (default)
|
||||
# ./scripts/build-gal-webgl-test.sh --no-clean # Incremental build
|
||||
# ./scripts/build-gal-webgl-test.sh --debug # Debug build with source maps
|
||||
#
|
||||
|
||||
# Redirect all output to a log file (re-execs script with redirection)
|
||||
source "$(dirname "$0")/common/logging.sh"
|
||||
|
|
@ -37,18 +42,19 @@ fi
|
|||
echo " Emscripten: $(em++ --version 2>&1 | head -1)"
|
||||
|
||||
# Parse arguments
|
||||
# Default: clean build to avoid stale object file issues (header deps not tracked in old builds)
|
||||
DEBUG_BUILD=0
|
||||
CLEAN_BUILD=0
|
||||
CLEAN_BUILD=1
|
||||
|
||||
for arg in "$@"; do
|
||||
if [ "$arg" = "--debug" ]; then
|
||||
DEBUG_BUILD=1
|
||||
elif [ "$arg" = "--clean" ]; then
|
||||
CLEAN_BUILD=1
|
||||
elif [ "$arg" = "--no-clean" ]; then
|
||||
CLEAN_BUILD=0
|
||||
fi
|
||||
done
|
||||
|
||||
# Build using Makefile
|
||||
# Build using Makefile (compiles WebGL sources directly from kicad/)
|
||||
cd "$TEST_DIR"
|
||||
|
||||
if [ "$CLEAN_BUILD" = "1" ]; then
|
||||
|
|
@ -62,6 +68,7 @@ fi
|
|||
rm -f "$OUTPUT_DIR"/*.js "$OUTPUT_DIR"/*.wasm 2>/dev/null || true
|
||||
|
||||
# Generate shaders (converts GLSL 1.20 to GLSL ES 3.00)
|
||||
# TODO: Eventually these should come from KiCad's build
|
||||
echo ""
|
||||
echo "Generating WebGL shaders..."
|
||||
python3 generate_shaders.py
|
||||
|
|
|
|||
|
|
@ -239,7 +239,7 @@ emcmake cmake "${KICAD_DIR}" \
|
|||
-DCMAKE_POLICY_VERSION_MINIMUM=3.5 \
|
||||
-DCMAKE_CXX_FLAGS="${EXTRA_FLAGS} -pthread -sUSE_ZLIB=1 -DKICAD_USE_PLATFORM_WASM=1 -I${SYSROOT}/include -I${STUBS_DIR}" \
|
||||
-DCMAKE_C_FLAGS="${EXTRA_FLAGS} -pthread -sUSE_ZLIB=1 -I${SYSROOT}/include -I${STUBS_DIR}" \
|
||||
-DCMAKE_EXE_LINKER_FLAGS="${LINKER_DEBUG_FLAGS} -pthread -sUSE_ZLIB=1 -sASYNCIFY=1 -sDYNCALLS=1 -sASYNCIFY_STACK_SIZE=65536 -sUSE_PTHREADS=1 -sPTHREAD_POOL_SIZE='navigator.hardwareConcurrency' -sPTHREAD_POOL_SIZE_STRICT=0 -sALLOW_MEMORY_GROWTH=1 -sINITIAL_MEMORY=256MB -sMAXIMUM_MEMORY=4GB -sLEGACY_GL_EMULATION -sMAX_WEBGL_VERSION=2 -sEXPORTED_RUNTIME_METHODS=['ccall','cwrap','UTF8ToString','stringToUTF8','lengthBytesUTF8','dynCall'] -sDEFAULT_LIBRARY_FUNCS_TO_INCLUDE=['\$dynCall'] --bind -L${SYSROOT}/lib ${STUBS_BUILD}/libgit2_stub.a ${STUBS_BUILD}/libcurl_stub.a ${STUBS_BUILD}/libpcbnew_scripting_stub.a ${STUBS_BUILD}/libnng_stub.a ${STUBS_BUILD}/pcbnew_embind.o" \
|
||||
-DCMAKE_EXE_LINKER_FLAGS="${LINKER_DEBUG_FLAGS} -pthread -sUSE_ZLIB=1 -sASYNCIFY=1 -sDYNCALLS=1 -sASYNCIFY_STACK_SIZE=65536 -sUSE_PTHREADS=1 -sPTHREAD_POOL_SIZE='navigator.hardwareConcurrency' -sPTHREAD_POOL_SIZE_STRICT=0 -sALLOW_MEMORY_GROWTH=1 -sINITIAL_MEMORY=256MB -sMAXIMUM_MEMORY=4GB -sMAX_WEBGL_VERSION=2 -sFULL_ES3=1 -sEXPORTED_RUNTIME_METHODS=['ccall','cwrap','UTF8ToString','stringToUTF8','lengthBytesUTF8','dynCall'] -sDEFAULT_LIBRARY_FUNCS_TO_INCLUDE=['\$dynCall'] --bind -L${SYSROOT}/lib ${STUBS_BUILD}/libgit2_stub.a ${STUBS_BUILD}/libcurl_stub.a ${STUBS_BUILD}/libpcbnew_scripting_stub.a ${STUBS_BUILD}/libnng_stub.a ${STUBS_BUILD}/pcbnew_embind.o" \
|
||||
-DCMAKE_PREFIX_PATH="${SYSROOT};${WX_BUILD}" \
|
||||
-DwxWidgets_CONFIG_EXECUTABLE="${WX_BUILD}/wx-config" \
|
||||
\
|
||||
|
|
|
|||
|
|
@ -320,24 +320,18 @@ build_webgl() {
|
|||
log_header "Building WebGL Test"
|
||||
|
||||
if [ ! -f "$SCRIPT_DIR/build-gal-webgl-test.sh" ]; then
|
||||
log_step "WebGL build script not found (Phase 2)"
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Check if Emscripten is available
|
||||
if ! command -v emcmake &> /dev/null; then
|
||||
log_step "Emscripten not available - WebGL build requires emsdk or Docker"
|
||||
log_step "Skipping WebGL build (run inside Docker or activate emsdk first)"
|
||||
log_step "WebGL build script not found"
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Delegate to build-gal-webgl-test.sh (handles Emscripten check, clean build, etc.)
|
||||
log_step "Running scripts/build-gal-webgl-test.sh..."
|
||||
|
||||
if "$SCRIPT_DIR/build-gal-webgl-test.sh"; then
|
||||
log_success "WebGL build succeeded"
|
||||
else
|
||||
log_error "WebGL build failed (Emscripten may need Python 3.10+)"
|
||||
log_step "Try running inside Docker or with a compatible emsdk"
|
||||
log_error "WebGL build failed"
|
||||
log_step "Check if KiCad is built first: docker/build.sh"
|
||||
return 0 # Don't fail the whole test, just skip WebGL
|
||||
fi
|
||||
}
|
||||
|
|
|
|||
340
scripts/test-gal-webgl.sh
Executable file
340
scripts/test-gal-webgl.sh
Executable file
|
|
@ -0,0 +1,340 @@
|
|||
#!/bin/bash
|
||||
|
||||
# GAL WebGL Regression Test - Monitoring Script
|
||||
# ==============================================
|
||||
# Single entry point for WebGL GAL testing.
|
||||
# Always does a clean build, then runs tests and compares against baseline.
|
||||
#
|
||||
# Purpose: Detect regressions during WebGL GAL migration to KiCad.
|
||||
# Strategy: Lock in current rendering behavior as baseline, then monitor
|
||||
# for any changes as we integrate WebGL GAL into KiCad build.
|
||||
#
|
||||
# Usage:
|
||||
# ./scripts/test-gal-webgl.sh # Clean build and test (default)
|
||||
# ./scripts/test-gal-webgl.sh -v # Verbose output
|
||||
|
||||
# Redirect all output to a log file (re-execs script with redirection)
|
||||
source "$(dirname "$0")/common/logging.sh"
|
||||
|
||||
set -e
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_ROOT="$(dirname "$SCRIPT_DIR")"
|
||||
|
||||
# Directories
|
||||
GAL_REGRESSION_DIR="$PROJECT_ROOT/tests/gal-regression"
|
||||
BASELINE_WEBGL_DIR="$GAL_REGRESSION_DIR/baseline-webgl"
|
||||
OUTPUT_DIR="$GAL_REGRESSION_DIR/output"
|
||||
WEBGL_OUTPUT_DIR="$OUTPUT_DIR/webgl"
|
||||
|
||||
# Colors for output
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# Parse arguments
|
||||
VERBOSE=""
|
||||
|
||||
for arg in "$@"; do
|
||||
case $arg in
|
||||
-v|--verbose)
|
||||
VERBOSE="-v"
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# ============================================================================
|
||||
# Helper Functions
|
||||
# ============================================================================
|
||||
|
||||
log_header() {
|
||||
echo ""
|
||||
echo -e "${BLUE}════════════════════════════════════════════════════════════════${NC}"
|
||||
echo -e "${BLUE} $1${NC}"
|
||||
echo -e "${BLUE}════════════════════════════════════════════════════════════════${NC}"
|
||||
}
|
||||
|
||||
log_step() {
|
||||
echo -e "${YELLOW}>>> $1${NC}"
|
||||
}
|
||||
|
||||
log_success() {
|
||||
echo -e "${GREEN}✓ $1${NC}"
|
||||
}
|
||||
|
||||
log_error() {
|
||||
echo -e "${RED}✗ $1${NC}"
|
||||
}
|
||||
|
||||
# Compare WebGL output against baseline-webgl
|
||||
# Usage: compare_webgl_screenshots [threshold]
|
||||
# Returns 0 if match within threshold, 1 if different
|
||||
compare_webgl_screenshots() {
|
||||
local threshold="${1:-1.0}" # Default 1% difference allowed
|
||||
|
||||
echo ""
|
||||
echo "Comparing WebGL output against baseline:"
|
||||
echo " Baseline: $BASELINE_WEBGL_DIR"
|
||||
echo " Current: $WEBGL_OUTPUT_DIR"
|
||||
echo " Threshold: ${threshold}% pixel difference"
|
||||
echo ""
|
||||
|
||||
if [ ! -d "$BASELINE_WEBGL_DIR" ]; then
|
||||
log_error "Baseline directory not found: $BASELINE_WEBGL_DIR"
|
||||
log_step "Run full test suite first to create baseline: ./scripts/test-gal-regression.sh"
|
||||
return 1
|
||||
fi
|
||||
|
||||
if [ ! -d "$WEBGL_OUTPUT_DIR" ]; then
|
||||
log_error "WebGL output directory not found: $WEBGL_OUTPUT_DIR"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Check for ImageMagick
|
||||
if ! command -v compare &> /dev/null; then
|
||||
log_error "ImageMagick 'compare' command not found. Install with: brew install imagemagick"
|
||||
return 1
|
||||
fi
|
||||
|
||||
local total=0
|
||||
local matching=0
|
||||
local different=0
|
||||
local missing=0
|
||||
|
||||
# Create temp directory for normalized images
|
||||
local tmpdir=$(mktemp -d)
|
||||
trap "rm -rf '$tmpdir'" EXIT
|
||||
|
||||
# Files to exclude from comparison (documented as broken/dead code)
|
||||
local excluded_files="gal-transform-api.png"
|
||||
|
||||
# Compare all baseline screenshots
|
||||
for baseline in "$BASELINE_WEBGL_DIR"/*.png; do
|
||||
[ -e "$baseline" ] || continue # Handle no matches
|
||||
|
||||
local filename=$(basename "$baseline")
|
||||
|
||||
# Skip excluded files
|
||||
if echo "$excluded_files" | grep -q "$filename"; then
|
||||
if [ -n "$VERBOSE" ]; then
|
||||
echo " SKIPPED: $filename (excluded - see README.md)"
|
||||
fi
|
||||
continue
|
||||
fi
|
||||
|
||||
local current="$WEBGL_OUTPUT_DIR/$filename"
|
||||
total=$((total + 1))
|
||||
|
||||
if [ ! -f "$current" ]; then
|
||||
echo " MISSING: $filename"
|
||||
missing=$((missing + 1))
|
||||
continue
|
||||
fi
|
||||
|
||||
# Get dimensions
|
||||
local baseline_dims=$(identify -format "%wx%h" "$baseline" 2>/dev/null)
|
||||
local current_dims=$(identify -format "%wx%h" "$current" 2>/dev/null)
|
||||
|
||||
# If dimensions differ, resize current to match baseline
|
||||
local compare_file="$current"
|
||||
if [ "$baseline_dims" != "$current_dims" ]; then
|
||||
compare_file="$tmpdir/resized_$filename"
|
||||
convert "$current" -resize "$baseline_dims!" "$compare_file" 2>/dev/null
|
||||
if [ -n "$VERBOSE" ]; then
|
||||
echo " RESIZED: $filename ($current_dims -> $baseline_dims)"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Normalize both images to TrueColor RGB for consistent comparison
|
||||
local baseline_normalized="$tmpdir/baseline_$filename"
|
||||
local current_normalized="$tmpdir/current_$filename"
|
||||
convert "$baseline" -flatten -colorspace sRGB -type TrueColor "$baseline_normalized" 2>/dev/null
|
||||
convert "$compare_file" -flatten -colorspace sRGB -type TrueColor "$current_normalized" 2>/dev/null
|
||||
|
||||
# Compare with fuzz factor (allows small pixel differences from anti-aliasing)
|
||||
# Use AE (Absolute Error) metric - counts differing pixels
|
||||
local compare_output=$(compare -metric AE -fuzz 2% "$baseline_normalized" "$current_normalized" null: 2>&1 || true)
|
||||
local diff_pixels=$(echo "$compare_output" | awk '{print $1}')
|
||||
|
||||
# Handle scientific notation (e.g., 1.92e+06)
|
||||
if [[ "$diff_pixels" =~ [eE] ]]; then
|
||||
diff_pixels=$(printf "%.0f" "$diff_pixels")
|
||||
fi
|
||||
|
||||
# Handle error cases (non-numeric output)
|
||||
if ! [[ "$diff_pixels" =~ ^[0-9]+\.?[0-9]*$ ]]; then
|
||||
echo " ERROR: $filename (comparison failed: $compare_output)"
|
||||
different=$((different + 1))
|
||||
continue
|
||||
fi
|
||||
|
||||
# Calculate percentage
|
||||
local total_pixels=$(identify -format "%[fx:w*h]" "$baseline_normalized" 2>/dev/null)
|
||||
# Handle scientific notation
|
||||
if [[ "$total_pixels" =~ [eE] ]]; then
|
||||
total_pixels=$(printf "%.0f" "$total_pixels")
|
||||
fi
|
||||
if [ -z "$total_pixels" ] || [ "$total_pixels" = "0" ]; then
|
||||
total_pixels=1 # Avoid division by zero
|
||||
fi
|
||||
|
||||
# Use awk for floating point arithmetic
|
||||
local diff_pct=$(awk "BEGIN {printf \"%.4f\", ($diff_pixels * 100.0) / $total_pixels}")
|
||||
|
||||
# Compare against threshold
|
||||
local is_match=$(awk "BEGIN {print ($diff_pct < $threshold) ? 1 : 0}")
|
||||
|
||||
if [ "$is_match" -eq 1 ]; then
|
||||
matching=$((matching + 1))
|
||||
if [ -n "$VERBOSE" ] || [ "$diff_pixels" -gt 0 ]; then
|
||||
echo " MATCH: $filename (${diff_pct}% different)"
|
||||
fi
|
||||
else
|
||||
different=$((different + 1))
|
||||
echo " DIFFERENT: $filename (${diff_pct}% different, threshold: ${threshold}%)"
|
||||
|
||||
# Show diagnostic info for failing scenario
|
||||
if [ -n "$VERBOSE" ]; then
|
||||
echo " Content bounds:"
|
||||
local baseline_bounds=$(magick "$baseline" -flatten -fuzz 1% -trim -format "%w x %h at %O" info: 2>/dev/null || echo "N/A")
|
||||
local current_bounds=$(magick "$current" -fuzz 1% -trim -format "%w x %h at %O" info: 2>/dev/null || echo "N/A")
|
||||
echo " Baseline: $baseline_bounds"
|
||||
echo " Current: $current_bounds"
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
# Check for extra files
|
||||
local extra=0
|
||||
for current in "$WEBGL_OUTPUT_DIR"/*.png; do
|
||||
[ -e "$current" ] || continue
|
||||
local filename=$(basename "$current")
|
||||
local baseline="$BASELINE_WEBGL_DIR/$filename"
|
||||
if [ ! -f "$baseline" ]; then
|
||||
extra=$((extra + 1))
|
||||
echo " EXTRA: $filename (not in baseline)"
|
||||
fi
|
||||
done
|
||||
|
||||
echo ""
|
||||
echo " Results: $matching/$total matching, $different different, $missing missing, $extra extra"
|
||||
|
||||
if [ "$different" -gt 0 ] || [ "$missing" -gt 0 ]; then
|
||||
log_error "WebGL vs Baseline: FAILED"
|
||||
echo ""
|
||||
echo "MIGRATION REGRESSION DETECTED!"
|
||||
echo "WebGL output has changed from baseline. This indicates the migration"
|
||||
echo "has altered rendering behavior. Please investigate before proceeding."
|
||||
echo ""
|
||||
echo "To update baseline (if changes are intentional):"
|
||||
echo " cp $WEBGL_OUTPUT_DIR/*.png $BASELINE_WEBGL_DIR/"
|
||||
echo " git add $BASELINE_WEBGL_DIR/"
|
||||
echo " git commit -m 'Update WebGL baseline after migration'"
|
||||
return 1
|
||||
else
|
||||
log_success "WebGL vs Baseline: PASSED"
|
||||
return 0
|
||||
fi
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# Build Function
|
||||
# ============================================================================
|
||||
|
||||
build_webgl() {
|
||||
log_header "Building WebGL Test"
|
||||
|
||||
if [ ! -f "$SCRIPT_DIR/build-gal-webgl-test.sh" ]; then
|
||||
log_error "WebGL build script not found: $SCRIPT_DIR/build-gal-webgl-test.sh"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check if Emscripten is available
|
||||
if ! command -v emcmake &> /dev/null; then
|
||||
log_error "Emscripten not available - WebGL build requires emsdk or Docker"
|
||||
log_step "Activate emsdk first or run inside Docker container"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log_step "Running scripts/build-gal-webgl-test.sh..."
|
||||
|
||||
if "$SCRIPT_DIR/build-gal-webgl-test.sh"; then
|
||||
log_success "WebGL build succeeded"
|
||||
else
|
||||
log_error "WebGL build failed"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# Run Function
|
||||
# ============================================================================
|
||||
|
||||
run_webgl() {
|
||||
log_header "Running WebGL Test"
|
||||
|
||||
local spec_file="$PROJECT_ROOT/tests/e2e/gal-webgl.spec.ts"
|
||||
|
||||
if [ ! -f "$spec_file" ]; then
|
||||
log_error "WebGL test spec not found: $spec_file"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Create output directory
|
||||
mkdir -p "$WEBGL_OUTPUT_DIR"
|
||||
|
||||
log_step "Running Playwright test..."
|
||||
|
||||
cd "$PROJECT_ROOT/tests"
|
||||
if npx playwright test gal-webgl.spec.ts; then
|
||||
local count=$(ls -1 "$WEBGL_OUTPUT_DIR"/*.png 2>/dev/null | wc -l | tr -d ' ')
|
||||
log_success "WebGL test completed: $count screenshots generated"
|
||||
else
|
||||
log_error "WebGL test failed"
|
||||
exit 1
|
||||
fi
|
||||
cd "$PROJECT_ROOT"
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# Main
|
||||
# ============================================================================
|
||||
|
||||
log_header "GAL WebGL Regression Test (Monitoring)"
|
||||
echo "Project Root: $PROJECT_ROOT"
|
||||
echo "Baseline WebGL: $BASELINE_WEBGL_DIR"
|
||||
echo "Current Output: $WEBGL_OUTPUT_DIR"
|
||||
echo ""
|
||||
echo "Purpose: Detect regressions during WebGL GAL migration to KiCad"
|
||||
echo "Strategy: Compare current WebGL output against baseline-webgl"
|
||||
|
||||
# Build phase (always clean build to avoid stale object issues)
|
||||
build_webgl
|
||||
|
||||
# Run phase
|
||||
run_webgl
|
||||
|
||||
# Compare phase
|
||||
log_header "Comparing WebGL vs Baseline"
|
||||
if compare_webgl_screenshots; then
|
||||
COMPARE_STATUS=0
|
||||
else
|
||||
COMPARE_STATUS=1
|
||||
fi
|
||||
|
||||
# Final summary
|
||||
log_header "Final Result"
|
||||
|
||||
if [ "$COMPARE_STATUS" -eq 0 ]; then
|
||||
log_success "WebGL output matches baseline perfectly!"
|
||||
echo ""
|
||||
echo "Migration safety check PASSED. Rendering fidelity preserved."
|
||||
exit 0
|
||||
else
|
||||
log_error "WebGL output differs from baseline!"
|
||||
echo ""
|
||||
echo "Migration safety check FAILED. Review changes before proceeding."
|
||||
exit 1
|
||||
fi
|
||||
Loading…
Reference in a new issue