Switching OpenGL → raytracing → OpenGL could leave the viewer showing only the background gradient, with mid-session "[gl1] WebGL context changed" thrash and INVALID_OPERATION storms on BOTH WebGL contexts. Traced mechanism: the shim's FFP draw routing keyed on one process-global client-array flag; an interrupted fixed-function window (MODEL_3D::BeginDrawMulti loops, or the switch-back reload re-recording display lists with the GL context lock released across JSPI suspensions) left it set, after which the raytracer blit's and the 2D GAL's glDrawArrays were routed through the FFP pipeline — and one misrouted draw permanently repointed the VICTIM's own VAO attributes at shim buffers (the blit's attribute 0 collides with ATTR_POSITION), so both stayed broken/blank even after the flag cleared. Shim fixes (wasm/gl1): - Owner-context routing gate: __wrap_glDrawArrays/Elements route into the FFP pipeline only under the shim's owner context (adopted at the first FFP client-state mutation or programSync in a context); foreign-context draws always pass through — the 2D GAL can never be misrouted and the context guard can never thrash. - VAO isolation (ScopedDefaultVAO): draw executors do their attribute setup on VAO 0 and restore the caller's binding — a misrouted draw can no longer corrupt the caller. - contextSync() resets the whole client-array mirror on a context change (enables/pointers/VBO names all described the dead context). kicad pointer bump (d6e3dc1a87a): blit preamble disables the four client arrays (same-context firewall) + DoRePaint hidden-parent early return now clears m_is_currently_painting like its six siblings (a standalone sufficient cause of a permanently blank viewer). TDD (each observed red before its fix, green after; harness = authoritative): - T1 VAO corruption, T2 foreign-context routing + guard thrash, T3 stale client-state surviving context recreation — tests/e2e/3d-webgl.spec.ts over new harness choreography (appQuad/ffpMakeStale/createSecondContext/ quadDrawFresh). Parity stays 47/47 with zero drift. - tests/kicad/3d-viewer-engine-toggle.spec.ts (new, CI-skipped like the deadlock spec): real round-trip happy-path gate — board re-renders, zero [gl1] lines, zero INVALID_OPERATION. (The raytraced image itself never displays on the wasm build — the pre-existing inert-toggle KNOWN ISSUE in 3d-viewer-deadlock.spec.ts, out of scope here; the engine switch and the poisoning reload path run regardless.) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
203 lines
9.1 KiB
Makefile
203 lines
9.1 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','_appQuadInit','_appQuadDraw','_ffpMakeStale','_ffpClearStale','_createSecondContext','_useContext','_quadDrawFresh'] \
|
|
-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)
|