test(3d): screenshot-baseline TDD suite for the 3D viewer OpenGL->WebGL port — 47 native goldens + red-state WebGL harness
tests/3d-regression mirrors the gal-regression pattern at renderer scale: shared C++ scenarios call real KiCad 3D-viewer code (opengl_utils, display lists + DrawCulled stencil subtraction, MODEL_3D VBOs, private generators via a rob-template accessor, and full reload()+Redraw() composites over a synthetic BOARD_ADAPTER). A native macOS harness renders them on real OpenGL into 47 committed goldens (bit-deterministic, FBO capture); the wasm harness compiles the same TUs against wasm/stubs/gl_ffp_stub.c no-ops so every scenario renders blank — the TDD red state (parity meter: 47/47 changed). Comparisons use the CI pixelmatch engine via the new generic compare-dirs.ts (floors.json levels; manifest.json cmp-guards registry drift). Documents an upstream bug: appendPostMachiningGeometry's countersink path adds middle quads without normals, silently erasing the walls of any display list it is batched into (3d-post-machining.png keeps the lists separate to record it). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
1a24c2894e
commit
ce1473c9ab
85 changed files with 6797 additions and 1 deletions
55
scripts/build-3d-native-test.sh
Executable file
55
scripts/build-3d-native-test.sh
Executable file
|
|
@ -0,0 +1,55 @@
|
|||
#!/bin/bash
|
||||
#
|
||||
# Build script for the native 3D-renderer test harness (tests/3d-regression/native).
|
||||
#
|
||||
# Builds a standalone macOS application that renders the shared 3D scenarios
|
||||
# through KiCad's actual RENDER_3D_OPENGL code paths (real desktop OpenGL) and
|
||||
# writes golden baseline PNGs for the OpenGL->WebGL port regression suite.
|
||||
#
|
||||
# Modeled on scripts/build-gal-native-test.sh.
|
||||
#
|
||||
|
||||
set -e
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||||
TEST_DIR="$PROJECT_ROOT/tests/3d-regression/native"
|
||||
BUILD_DIR="$TEST_DIR/build"
|
||||
LOG_FILE="$PROJECT_ROOT/tests/logs/3d-native-build.log"
|
||||
|
||||
mkdir -p "$(dirname "$LOG_FILE")"
|
||||
|
||||
echo "Building 3D Renderer Native Test..."
|
||||
echo " Test dir: $TEST_DIR"
|
||||
echo " Build dir: $BUILD_DIR"
|
||||
echo " Log file: $LOG_FILE"
|
||||
|
||||
mkdir -p "$BUILD_DIR"
|
||||
|
||||
echo "Running CMake..."
|
||||
if ! cmake -S "$TEST_DIR" -B "$BUILD_DIR" >> "$LOG_FILE" 2>&1; then
|
||||
echo "CMake configuration failed! Check $LOG_FILE for details."
|
||||
echo ""
|
||||
echo "Last 50 lines of log:"
|
||||
tail -50 "$LOG_FILE"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Building..."
|
||||
if ! make -C "$BUILD_DIR" -j"$(sysctl -n hw.ncpu 2>/dev/null || echo 4)" >> "$LOG_FILE" 2>&1; then
|
||||
echo "Build failed! Check $LOG_FILE for details."
|
||||
echo ""
|
||||
echo "Last 100 lines of log:"
|
||||
tail -100 "$LOG_FILE"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Build successful!"
|
||||
echo "Executable: $BUILD_DIR/scene3d_native_test"
|
||||
echo ""
|
||||
echo "Options:"
|
||||
echo " --output <dir> Output directory for 3d-<name>.png"
|
||||
echo " --manifest <file> Write the scenario manifest JSON"
|
||||
echo " --filter <substr> Only run scenarios whose name contains <substr>"
|
||||
echo " --list Print scenario names and exit"
|
||||
echo " --show Keep the window open after rendering"
|
||||
75
scripts/build-3d-webgl-test.sh
Executable file
75
scripts/build-3d-webgl-test.sh
Executable file
|
|
@ -0,0 +1,75 @@
|
|||
#!/bin/bash
|
||||
#
|
||||
# Build script for the 3D-renderer WebGL test harness (WASM).
|
||||
#
|
||||
# Builds the shared 3D scenarios (tests/3d-regression/scenarios) + real KiCad
|
||||
# 3D-viewer TUs against the FFP no-op stubs (wasm/stubs/gl_ffp_stub.c) — the
|
||||
# TDD red state for the OpenGL->WebGL port. Output: tests/apps/3d-webgl/.
|
||||
#
|
||||
# Requires the wxWidgets WASM build (scripts/build-wx-wasm.sh) and the sysroot
|
||||
# headers (boost/glm) in build-wasm/sysroot.
|
||||
#
|
||||
# Usage:
|
||||
# ./scripts/build-3d-webgl-test.sh # Clean build (default)
|
||||
# ./scripts/build-3d-webgl-test.sh --no-clean # Incremental build
|
||||
# ./scripts/build-3d-webgl-test.sh --debug # Debug build with source maps
|
||||
#
|
||||
# Modeled on scripts/build-gal-webgl-test.sh (no shader-generation step — the
|
||||
# FFP renderer has no GLSL yet; the port will add one here).
|
||||
#
|
||||
|
||||
source "$(dirname "$0")/common/logging.sh"
|
||||
|
||||
set -e
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
|
||||
JOBS="${JOBS:-$(nproc 2>/dev/null || sysctl -n hw.ncpu 2>/dev/null || echo 4)}"
|
||||
|
||||
QUIET=1 source "$SCRIPT_DIR/common/env.sh"
|
||||
|
||||
TEST_DIR="$PROJECT_ROOT/tests/3d-regression/wasm"
|
||||
OUTPUT_DIR="$PROJECT_ROOT/tests/apps/3d-webgl"
|
||||
|
||||
echo "Building 3D WebGL Test (red-state harness)..."
|
||||
echo " Test dir: $TEST_DIR"
|
||||
echo " Output dir: $OUTPUT_DIR"
|
||||
|
||||
if ! command -v em++ &> /dev/null; then
|
||||
echo "ERROR: em++ not found. Run: ./scripts/setup-emsdk.sh"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -x "$PROJECT_ROOT/build-wasm/wxwidgets/wx-config" ]; then
|
||||
echo "ERROR: wxWidgets WASM build missing. Run: ./scripts/build-wx-wasm.sh"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo " Emscripten: $(em++ --version 2>&1 | head -1)"
|
||||
|
||||
DEBUG_BUILD=0
|
||||
CLEAN_BUILD=1
|
||||
|
||||
for arg in "$@"; do
|
||||
case "$arg" in
|
||||
--debug) DEBUG_BUILD=1 ;;
|
||||
--no-clean) CLEAN_BUILD=0 ;;
|
||||
esac
|
||||
done
|
||||
|
||||
cd "$TEST_DIR"
|
||||
|
||||
if [ "$CLEAN_BUILD" = "1" ]; then
|
||||
make clean || true
|
||||
fi
|
||||
|
||||
rm -f "$OUTPUT_DIR"/*.js "$OUTPUT_DIR"/*.wasm
|
||||
|
||||
if [ "$DEBUG_BUILD" = "1" ]; then
|
||||
make -j"$JOBS" DEBUG=1
|
||||
else
|
||||
make -j"$JOBS"
|
||||
fi
|
||||
|
||||
echo "Build successful: $OUTPUT_DIR/3d_webgl_test.{js,wasm,html}"
|
||||
echo "Serve with: cd tests && npm run serve -> /3d-webgl/3d_webgl_test.html"
|
||||
179
scripts/test-3d-regression.sh
Executable file
179
scripts/test-3d-regression.sh
Executable file
|
|
@ -0,0 +1,179 @@
|
|||
#!/bin/bash
|
||||
#
|
||||
# 3D-renderer regression suite orchestrator (tests/3d-regression).
|
||||
#
|
||||
# Usage:
|
||||
# ./scripts/test-3d-regression.sh # native: build -> run -> compare vs baseline;
|
||||
# # webgl phase too once the wasm harness exists
|
||||
# ./scripts/test-3d-regression.sh native # native phase only
|
||||
# ./scripts/test-3d-regression.sh webgl # wasm build -> playwright -> webgl-self + parity
|
||||
# ./scripts/test-3d-regression.sh compare # comparisons only (skip builds/runs)
|
||||
# ./scripts/test-3d-regression.sh promote # promote output/native -> baseline/ (byte-diff
|
||||
# # guarded) + manifest.json
|
||||
#
|
||||
# Comparison engine: the pixelmatch CI tooling (tests/tools/screenshots/compare-dirs.ts),
|
||||
# NOT ImageMagick. Levels/floors: tests/3d-regression/floors.json.
|
||||
#
|
||||
|
||||
source "$(dirname "${BASH_SOURCE[0]}")/common/logging.sh"
|
||||
|
||||
set -e
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||||
THREED_DIR="$PROJECT_ROOT/tests/3d-regression"
|
||||
BASELINE_DIR="$THREED_DIR/baseline"
|
||||
BASELINE_WEBGL_DIR="$THREED_DIR/baseline-webgl"
|
||||
NATIVE_OUT="$THREED_DIR/output/native"
|
||||
WEBGL_OUT="$THREED_DIR/output/webgl"
|
||||
NATIVE_BIN="$THREED_DIR/native/build/scene3d_native_test"
|
||||
|
||||
MODE="${1:-all}"
|
||||
|
||||
NATIVE_COMPARE_STATUS=0
|
||||
WEBGL_COMPARE_STATUS=0
|
||||
|
||||
run_native() {
|
||||
echo "=== Native phase: build + render ==="
|
||||
"$SCRIPT_DIR/build-3d-native-test.sh"
|
||||
|
||||
mkdir -p "$NATIVE_OUT"
|
||||
"$NATIVE_BIN" --output "$NATIVE_OUT" --manifest "$NATIVE_OUT/manifest.json"
|
||||
|
||||
# Anti-drift guard: the scenario registry (names/size) must match the
|
||||
# committed manifest; a mismatch means scenarios changed and the baselines
|
||||
# need review + re-promote.
|
||||
if [ -f "$THREED_DIR/manifest.json" ]; then
|
||||
if ! cmp -s "$THREED_DIR/manifest.json" "$NATIVE_OUT/manifest.json"; then
|
||||
echo "ERROR: scenario registry changed (manifest.json differs from committed copy)."
|
||||
echo "Review the change, then: ./scripts/test-3d-regression.sh promote"
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
echo "NOTE: no committed manifest yet (first run) — promote will create it."
|
||||
fi
|
||||
}
|
||||
|
||||
compare_native() {
|
||||
if [ ! -d "$BASELINE_DIR" ] || [ -z "$(ls -A "$BASELINE_DIR" 2>/dev/null)" ]; then
|
||||
echo "SKIP native-self compare: no committed baseline yet (run promote first)."
|
||||
return
|
||||
fi
|
||||
|
||||
echo "=== Compare: native-self (baseline/ vs output/native/) ==="
|
||||
( cd "$PROJECT_ROOT/tests" && npm run --silent 3d:check ) || NATIVE_COMPARE_STATUS=$?
|
||||
}
|
||||
|
||||
run_webgl() {
|
||||
if [ ! -f "$THREED_DIR/wasm/Makefile" ]; then
|
||||
echo "SKIP webgl phase: tests/3d-regression/wasm not present yet."
|
||||
return
|
||||
fi
|
||||
|
||||
if [ ! -x "$PROJECT_ROOT/build-wasm/wxwidgets/wx-config" ]; then
|
||||
echo "SKIP webgl phase: wxWidgets WASM build missing (scripts/build-wx-wasm.sh)."
|
||||
return
|
||||
fi
|
||||
|
||||
echo "=== WebGL phase: build + playwright capture ==="
|
||||
"$SCRIPT_DIR/build-3d-webgl-test.sh"
|
||||
( cd "$PROJECT_ROOT/tests" && npx playwright test e2e/3d-webgl.spec.ts )
|
||||
}
|
||||
|
||||
compare_webgl() {
|
||||
if [ ! -d "$WEBGL_OUT" ] || [ -z "$(ls -A "$WEBGL_OUT" 2>/dev/null)" ]; then
|
||||
echo "SKIP webgl compares: no webgl renders in output/webgl."
|
||||
return
|
||||
fi
|
||||
|
||||
if [ -d "$BASELINE_WEBGL_DIR" ] && [ -n "$(ls -A "$BASELINE_WEBGL_DIR" 2>/dev/null)" ]; then
|
||||
echo "=== Compare: webgl-self (baseline-webgl/ vs output/webgl/) ==="
|
||||
( cd "$PROJECT_ROOT/tests" && npm run --silent 3d:check:webgl ) || WEBGL_COMPARE_STATUS=$?
|
||||
else
|
||||
echo "SKIP webgl-self compare: no baseline-webgl yet."
|
||||
fi
|
||||
|
||||
# Port-parity is the TDD progress meter: informational, never gates.
|
||||
echo "=== Compare: parity (baseline/ vs output/webgl/, informational) ==="
|
||||
( cd "$PROJECT_ROOT/tests" && npm run --silent 3d:check:parity ) || true
|
||||
}
|
||||
|
||||
promote() {
|
||||
if [ ! -d "$NATIVE_OUT" ] || [ -z "$(ls -A "$NATIVE_OUT" 2>/dev/null)" ]; then
|
||||
echo "ERROR: nothing to promote — run the native phase first."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
mkdir -p "$BASELINE_DIR"
|
||||
local changed=0
|
||||
|
||||
for png in "$NATIVE_OUT"/3d-*.png; do
|
||||
local name
|
||||
name="$(basename "$png")"
|
||||
|
||||
if ! cmp -s "$png" "$BASELINE_DIR/$name"; then
|
||||
cp "$png" "$BASELINE_DIR/$name"
|
||||
echo " promoted: $name"
|
||||
changed=$((changed + 1))
|
||||
fi
|
||||
done
|
||||
|
||||
if ! cmp -s "$NATIVE_OUT/manifest.json" "$THREED_DIR/manifest.json"; then
|
||||
cp "$NATIVE_OUT/manifest.json" "$THREED_DIR/manifest.json"
|
||||
echo " promoted: manifest.json"
|
||||
changed=$((changed + 1))
|
||||
fi
|
||||
|
||||
# Baselines removed from the registry linger in baseline/ — report them.
|
||||
for png in "$BASELINE_DIR"/3d-*.png; do
|
||||
[ -e "$png" ] || continue
|
||||
if [ ! -f "$NATIVE_OUT/$(basename "$png")" ]; then
|
||||
echo " STALE baseline (not in registry anymore): $(basename "$png")"
|
||||
fi
|
||||
done
|
||||
|
||||
echo "Promote done: $changed file(s) updated (byte-diff guarded, zero churn)."
|
||||
echo "Review + git add tests/3d-regression/{baseline,manifest.json}."
|
||||
}
|
||||
|
||||
case "$MODE" in
|
||||
all)
|
||||
run_native
|
||||
compare_native
|
||||
run_webgl
|
||||
compare_webgl
|
||||
;;
|
||||
native)
|
||||
run_native
|
||||
compare_native
|
||||
;;
|
||||
webgl)
|
||||
run_webgl
|
||||
compare_webgl
|
||||
;;
|
||||
compare)
|
||||
compare_native
|
||||
compare_webgl
|
||||
;;
|
||||
promote)
|
||||
promote
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
echo "Usage: $0 [all|native|webgl|compare|promote]"
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
|
||||
echo ""
|
||||
if [ $NATIVE_COMPARE_STATUS -ne 0 ]; then
|
||||
echo "RESULT: FAIL (native-self comparison found changes — see tests/3d-regression/output/diff/native-self/)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ $WEBGL_COMPARE_STATUS -ne 0 ]; then
|
||||
echo "RESULT: FAIL (webgl-self comparison found changes — see tests/3d-regression/output/diff/webgl-self/)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "RESULT: PASS"
|
||||
Loading…
Reference in a new issue