Closing the viewer destroys its wxGLCanvas's WebGL context; reopening mints a
new one. The gl1 shim cached GL names (FFP program, stream/scratch VBOs) in
never-reset statics behind `if (!handle)` guards — in the new context every
draw died with INVALID_OPERATION and the viewer showed only the clear color
("Reload time 0.031 s" is benign: warm model caches make the rebuild fast).
contextSync() (gl1_state.cpp) now detects the context change in programSync()
— the one choke point every shim draw crosses, and a path the 2D GAL never
reaches (a first attempt checking in the glBindTexture wrap saw the GAL's
context and thrash-rebuilt the program 23x per run) — and drops the cached
names for lazy rebuild in the new context. Context identity is a monotonic id
stamped on Emscripten's per-context record: the numeric
EMSCRIPTEN_WEBGL_CONTEXT_HANDLE is recycled, so a destroy-then-create can
return the same number and a handle comparison detects nothing.
The lost-position half is a wxwidgets wasm fix (pointer bump: GetFromWindow
reports display 0; saved geometry used to carry display=(unsigned)-1, which
LoadWindowState treats as "display not found" and re-centres the frame).
TDD (red observed before each fix, green after):
- tests/kicad/3d-viewer-reopen.spec.ts (new, own worker like the deadlock
spec): load board, open viewer, render-gate, drag by the titlebar, close
via the x, reopen; asserts the board re-renders (was: 1 distinct colour for
90 s) and the window position is restored (was: re-centred to 0,0 after
closing at 40,90). Green run logs exactly one [gl1] context-change line.
- 3d-regression harness: recreateContext() destroys the context AND swaps in
a fresh canvas element (a browser canvas keeps its context for life, so
same-element recreation hands back the live old context and hides the bug);
the new 3d-webgl spec test renders redraw-mini-board-navigator before and
after recreation and requires pixel-identical output. Parity: 47/47, zero
drift.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
203 lines
8.9 KiB
Makefile
203 lines
8.9 KiB
Makefile
# Makefile for the 3D-renderer WebGL test harness (WASM).
|
|
#
|
|
# Compiles the SAME shared scenarios + real KiCad 3D-viewer TUs as the native
|
|
# golden generator (tests/3d-regression/native/CMakeLists.txt), linking the
|
|
# fixed-function GL surface against the wasm/gl1 GL1->WebGL2 emulation layer —
|
|
# exactly how the production kicad build satisfies RENDER_3D_OPENGL's link.
|
|
# `npm run 3d:check:parity` compares the browser renders against the native
|
|
# goldens (47/47 under floor since the port).
|
|
#
|
|
# Usage:
|
|
# make # Build
|
|
# make clean # Clean build artifacts
|
|
# make DEBUG=1 # Debug build with source maps
|
|
|
|
CXX = em++
|
|
CC = emcc
|
|
|
|
PROJECT_ROOT = ../../..
|
|
KICAD_ROOT = $(PROJECT_ROOT)/kicad
|
|
WX_BUILD = $(PROJECT_ROOT)/build-wasm/wxwidgets
|
|
|
|
OUTPUT_DIR = ../../apps/3d-webgl
|
|
|
|
# wxWidgets WASM (the KiCad TUs reference wxString/wxLogTrace/wxASSERT).
|
|
WXCONFIG = $(WX_BUILD)/wx-config
|
|
WX_CXXFLAGS := $(shell $(WXCONFIG) --cxxflags)
|
|
WX_LDFLAGS := $(shell $(WXCONFIG) --libs base,core,gl)
|
|
|
|
# Sysroot (boost, glm, ...)
|
|
SYSROOT = $(PROJECT_ROOT)/build-wasm/sysroot
|
|
|
|
# Mirrors the include set of tests/3d-regression/native/CMakeLists.txt.
|
|
KICAD_INCLUDES = -I../native \
|
|
-I../scenarios \
|
|
-I$(KICAD_ROOT)/include \
|
|
-I$(KICAD_ROOT)/3d-viewer \
|
|
-I$(KICAD_ROOT)/3d-viewer/3d_rendering \
|
|
-I$(KICAD_ROOT)/3d-viewer/3d_viewer \
|
|
-I$(KICAD_ROOT)/pcbnew \
|
|
-I$(KICAD_ROOT)/common \
|
|
-I$(KICAD_ROOT)/libs/kimath/include \
|
|
-I$(KICAD_ROOT)/libs/core/include \
|
|
-I$(KICAD_ROOT)/thirdparty/clipper2/Clipper2Lib/include \
|
|
-I$(KICAD_ROOT)/thirdparty/dynamic_bitset \
|
|
-I$(KICAD_ROOT)/thirdparty/rtree \
|
|
-I$(KICAD_ROOT)/thirdparty/magic_enum/magic_enum \
|
|
-I$(KICAD_ROOT)/thirdparty/expected/include \
|
|
-I$(KICAD_ROOT)/thirdparty/nlohmann_json \
|
|
-I$(KICAD_ROOT)/thirdparty \
|
|
-I$(PROJECT_ROOT)/wasm/stubs \
|
|
-I$(PROJECT_ROOT)/wasm/gl1/include \
|
|
-I$(SYSROOT)/include
|
|
|
|
ifdef DEBUG
|
|
OPT_FLAGS = -g -O0
|
|
DEBUG_LDFLAGS = -g -gsource-map
|
|
else
|
|
OPT_FLAGS = -O1
|
|
DEBUG_LDFLAGS =
|
|
endif
|
|
|
|
# Match the wx/KiCad WASM exception model (scripts/common/env.sh DEPS_EH_FLAGS).
|
|
DEPS_EH_FLAGS ?= -fwasm-exceptions -sSUPPORT_LONGJMP=wasm -sWASM_LEGACY_EXCEPTIONS=1
|
|
|
|
# USINGZ: KiCad builds clipper2 with it (PUBLIC compile definition).
|
|
CXXFLAGS = $(OPT_FLAGS) $(DEPS_EH_FLAGS) -DUSINGZ $(WX_CXXFLAGS) $(KICAD_INCLUDES) -std=c++20 -MMD -MP
|
|
CFLAGS = $(OPT_FLAGS) $(DEPS_EH_FLAGS) -I$(PROJECT_ROOT)/wasm/stubs
|
|
|
|
BASE_LDFLAGS = $(DEPS_EH_FLAGS) \
|
|
-sALLOW_MEMORY_GROWTH=1 \
|
|
-sERROR_ON_UNDEFINED_SYMBOLS=0 \
|
|
-sEXPORTED_FUNCTIONS=['_main','_runScenario','_getTotalScenarios','_getScenarioName','_getCanvasWidth','_getCanvasHeight','_recreateContext'] \
|
|
-sEXPORTED_RUNTIME_METHODS=['ccall','cwrap'] \
|
|
-sMODULARIZE=1 \
|
|
-sEXPORT_NAME='create3DTest' \
|
|
-sENVIRONMENT=web,worker
|
|
|
|
# Pure WebGL 2.0 context; the FFP surface is implemented by wasm/gl1 (no
|
|
# LEGACY_GL_EMULATION), same as the production kicad_editor build.
|
|
EM_GL_FLAGS = -sMAX_WEBGL_VERSION=2
|
|
|
|
LDFLAGS = $(DEBUG_LDFLAGS) $(BASE_LDFLAGS) $(EM_GL_FLAGS) $(WX_LDFLAGS) $(GL1_WRAP_LDFLAGS)
|
|
|
|
MAIN_SRCS = 3d_webgl_test.cpp
|
|
|
|
SCENARIO_SRCS = ../scenarios/scene3d_test_scenarios.cpp \
|
|
../scenarios/scene3d_test_ctx.cpp \
|
|
../scenarios/test_board_data.cpp \
|
|
$(wildcard ../scenarios/scenario_*.cpp)
|
|
|
|
# Test-impl TUs shared with the native harness (BOARD_ADAPTER seam, settings
|
|
# stub, link stubs, private-member accessor).
|
|
TESTIMPL_SRCS = ../native/board_adapter_test_impl.cpp \
|
|
../native/settings_3d_stub.cpp \
|
|
../native/kicad_stubs_3d.cpp \
|
|
../native/render3d_test_accessor.cpp
|
|
|
|
# Same real-KiCad TU list as the native CMakeLists (minus glad — Emscripten
|
|
# provides the modern GL surface, wasm/gl1 the legacy one).
|
|
KICAD_3D_SRCS = \
|
|
$(KICAD_ROOT)/3d-viewer/common_ogl/ogl_utils.cpp \
|
|
$(KICAD_ROOT)/3d-viewer/3d_rendering/image.cpp \
|
|
$(KICAD_ROOT)/3d-viewer/3d_rendering/buffers_debug.cpp \
|
|
$(KICAD_ROOT)/3d-viewer/3d_rendering/color_rgba.cpp \
|
|
$(KICAD_ROOT)/3d-viewer/3d_rendering/track_ball.cpp \
|
|
$(KICAD_ROOT)/3d-viewer/3d_rendering/trackball.cpp \
|
|
$(KICAD_ROOT)/common/gal/3d/camera.cpp \
|
|
$(KICAD_ROOT)/3d-viewer/3d_rendering/opengl/layer_triangles.cpp \
|
|
$(KICAD_ROOT)/3d-viewer/3d_rendering/opengl/opengl_utils.cpp \
|
|
$(KICAD_ROOT)/3d-viewer/3d_rendering/opengl/3d_model.cpp \
|
|
$(KICAD_ROOT)/3d-viewer/3d_rendering/opengl/3d_spheres_gizmo.cpp \
|
|
$(KICAD_ROOT)/3d-viewer/3d_rendering/raytracing/ray.cpp \
|
|
$(KICAD_ROOT)/3d-viewer/3d_rendering/raytracing/accelerators/container_2d.cpp \
|
|
$(KICAD_ROOT)/3d-viewer/3d_rendering/raytracing/shapes2D/object_2d.cpp \
|
|
$(KICAD_ROOT)/3d-viewer/3d_rendering/raytracing/shapes2D/bbox_2d.cpp \
|
|
$(KICAD_ROOT)/3d-viewer/3d_rendering/raytracing/shapes2D/round_segment_2d.cpp \
|
|
$(KICAD_ROOT)/3d-viewer/3d_rendering/raytracing/shapes2D/filled_circle_2d.cpp \
|
|
$(KICAD_ROOT)/3d-viewer/3d_rendering/raytracing/shapes3D/bbox_3d.cpp \
|
|
$(KICAD_ROOT)/libs/kimath/src/trigo.cpp \
|
|
$(KICAD_ROOT)/libs/kimath/src/geometry/eda_angle.cpp \
|
|
$(KICAD_ROOT)/libs/kimath/src/geometry/seg.cpp \
|
|
$(KICAD_ROOT)/libs/kimath/src/math/util.cpp \
|
|
$(KICAD_ROOT)/3d-viewer/3d_rendering/opengl/render_3d_opengl.cpp \
|
|
$(KICAD_ROOT)/3d-viewer/3d_rendering/opengl/create_scene.cpp \
|
|
$(KICAD_ROOT)/3d-viewer/3d_rendering/render_3d_base.cpp \
|
|
$(KICAD_ROOT)/3d-viewer/3d_rendering/raytracing/shapes2D/ring_2d.cpp \
|
|
$(KICAD_ROOT)/3d-viewer/3d_rendering/raytracing/shapes2D/triangle_2d.cpp \
|
|
$(KICAD_ROOT)/3d-viewer/3d_rendering/raytracing/shapes2D/4pt_polygon_2d.cpp \
|
|
$(KICAD_ROOT)/3d-viewer/3d_rendering/raytracing/shapes2D/polygon_2d.cpp \
|
|
$(KICAD_ROOT)/libs/kimath/src/geometry/shape_poly_set.cpp \
|
|
$(KICAD_ROOT)/libs/kimath/src/geometry/shape_line_chain.cpp \
|
|
$(KICAD_ROOT)/libs/kimath/src/geometry/shape.cpp \
|
|
$(KICAD_ROOT)/libs/kimath/src/geometry/shape_arc.cpp \
|
|
$(KICAD_ROOT)/libs/kimath/src/geometry/vertex_set.cpp \
|
|
$(KICAD_ROOT)/libs/kimath/src/geometry/geometry_utils.cpp \
|
|
$(KICAD_ROOT)/libs/kimath/src/convert_basic_shapes_to_polygon.cpp \
|
|
$(KICAD_ROOT)/libs/kimath/src/md5_hash.cpp \
|
|
$(KICAD_ROOT)/libs/kimath/src/bezier_curves.cpp \
|
|
$(KICAD_ROOT)/libs/kimath/src/geometry/circle.cpp \
|
|
$(KICAD_ROOT)/libs/kimath/src/geometry/arc_chord_params.cpp \
|
|
$(KICAD_ROOT)/libs/kimath/src/geometry/corner_operations.cpp \
|
|
$(KICAD_ROOT)/libs/kimath/src/geometry/shape_collisions.cpp \
|
|
$(KICAD_ROOT)/libs/kimath/src/geometry/shape_compound.cpp \
|
|
$(KICAD_ROOT)/libs/kimath/src/geometry/shape_rect.cpp \
|
|
$(KICAD_ROOT)/libs/kimath/src/geometry/shape_nearest_points.cpp \
|
|
$(KICAD_ROOT)/libs/kimath/src/geometry/half_line.cpp \
|
|
$(KICAD_ROOT)/libs/kimath/src/geometry/shape_utils.cpp \
|
|
$(KICAD_ROOT)/libs/kimath/src/geometry/roundrect.cpp \
|
|
$(KICAD_ROOT)/libs/kimath/src/geometry/line.cpp \
|
|
$(KICAD_ROOT)/libs/kimath/src/geometry/shape_segment.cpp \
|
|
$(KICAD_ROOT)/libs/kimath/src/math/vector2.cpp \
|
|
$(KICAD_ROOT)/common/layer_id.cpp \
|
|
$(KICAD_ROOT)/common/gal/color4d.cpp \
|
|
$(KICAD_ROOT)/common/kicad_gl/gl_context_mgr.cpp \
|
|
$(KICAD_ROOT)/common/lset.cpp \
|
|
$(KICAD_ROOT)/libs/core/utf8.cpp \
|
|
$(KICAD_ROOT)/thirdparty/clipper2/Clipper2Lib/src/clipper.engine.cpp \
|
|
$(KICAD_ROOT)/thirdparty/clipper2/Clipper2Lib/src/clipper.offset.cpp \
|
|
$(KICAD_ROOT)/thirdparty/clipper2/Clipper2Lib/src/clipper.rectclip.cpp
|
|
|
|
# The GL1->WebGL2 emulation layer (wasm/gl1): implements the FFP surface the
|
|
# renderer needs and --wrap-intercepts the Emscripten-owned names it must
|
|
# observe. sources.txt / wrapped_symbols.txt are shared with the production
|
|
# link site (scripts/kicad/build-kicad-target.sh).
|
|
GL1_DIR = $(PROJECT_ROOT)/wasm/gl1
|
|
GL1_SRCS := $(addprefix $(GL1_DIR)/src/,$(shell cat $(GL1_DIR)/sources.txt))
|
|
|
|
comma := ,
|
|
GL1_WRAP_LDFLAGS := $(foreach sym,$(shell cat $(GL1_DIR)/wrapped_symbols.txt),-Wl$(comma)--wrap=$(sym))
|
|
|
|
SRCS = $(MAIN_SRCS) $(SCENARIO_SRCS) $(TESTIMPL_SRCS) $(KICAD_3D_SRCS) $(GL1_SRCS)
|
|
OBJS = $(SRCS:.cpp=.o)
|
|
DEPS = $(SRCS:.cpp=.d)
|
|
|
|
TARGET = $(OUTPUT_DIR)/3d_webgl_test.js
|
|
|
|
all: $(OUTPUT_DIR) $(TARGET)
|
|
|
|
$(OUTPUT_DIR):
|
|
mkdir -p $(OUTPUT_DIR)
|
|
|
|
%.o: %.cpp
|
|
$(CXX) -c $(CXXFLAGS) $< -o $@
|
|
|
|
%.o: %.c
|
|
$(CC) -c $(CFLAGS) $< -o $@
|
|
|
|
$(TARGET): $(OBJS)
|
|
@echo "Linking $(words $(OBJS)) objects..."
|
|
$(CXX) $(OBJS) $(LDFLAGS) -o $@
|
|
cp 3d_webgl_test.html $(OUTPUT_DIR)/
|
|
|
|
clean:
|
|
rm -f $(OBJS) $(DEPS)
|
|
rm -f $(OUTPUT_DIR)/3d_webgl_test.js
|
|
rm -f $(OUTPUT_DIR)/3d_webgl_test.wasm
|
|
rm -f $(OUTPUT_DIR)/3d_webgl_test.html
|
|
rm -f ../scenarios/*.o ../scenarios/*.d ../native/*.o ../native/*.d
|
|
rm -f $(GL1_DIR)/src/*.o $(GL1_DIR)/src/*.d
|
|
|
|
.PHONY: all clean
|
|
|
|
-include $(DEPS)
|