Repository navigation
Viewer: shared materials, x-ray by material swap, merged component bodies - #33
Merged
Merged
Conversation
…nt bodies One node material per source material and use for the whole board, with a normal and an x-ray variant; the state texel comes from the geometry (merged layer meshes store the global texel instead of the feature ID). setXray swaps the meshes' materials, and the other mode is compiled in idle time after loading. The asset's component bodies are merged into one static mesh per material (large repeated meshes stay instanced); runtime models stay instanced and share their materials by content. Work in progress.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Work package "viewer materials" of the performance work (x-ray toggle and first-frame shader builds on big boards). No profile change. Viewer only (
packages/viewer), plus docs and CHANGELOG.Problem
On testcase1-RevC-full every x-ray toggle cost 1.5–1.7 s of main-thread JS, and the first frame spent 1.5 s building node materials.
BoardMaterialscreated oneNodeMaterialper mesh (a fresh TSL graph with the layer's state offset as a constant),setXrayflippedtransparent/depthWrite/needsUpdateon all of them, so three rebuilt every node material on every toggle. And since three 0.186 adds anInstancedMesh's uuid to the render-object cache key, the 204 componentInstancedMeshes (105 packages × body/pin-1) each got their own node build.What this does
materials.ts). One node material per (source material, use) for the whole board, with a normal and an x-ray variant (the tint overlay has one, the same in both modes). The graph reads the state texel from the geometry (_state,STATE_ATTRIBUTE), so it holds nothing per mesh and three builds each shader once. Runtime models share by material content (materialKey: KiCad models bring their own copies of a small palette); shared materials are reference-counted and disposed with their last mesh.FeatureRanges.scantakes the offset.setXrayswapsmesh.material; no material property changes, noneedsUpdate. The x-ray variant is set up as today's x-ray mode (opacitymix(xrayOpacity, 1, strength),transparent, no depth writes, the mask's polygon offset on both variants, tints).renderer.compileAsync(mesh, camera, scene)call, a mesh at a time in idle callbacks (several while the deadline allows). Per mesh, not the whole scene:compileAsyncwaits for each pipeline before building the next shader, and on SwiftShader that kept the first toggle at 59–138 ms; per mesh, all shaders are built shortly after load and pipelines compile in the background.bodies.ts). The asset's bodies (placeholders, pin-1 markers, embedded user models) are baked into one static mesh per material and vertex layout, in board coordinates, with a per-vertex state texel; mirrored placements get their winding restored. Runtime models replacing placeholders drop those instances out of the merged index buffer (draw range);modelsShown = false/clearModelsput them back. Picking tests each drawn instance's box, then its triangles. Component bounds are computed as before (transformed AABBs, float32 matrices), so framing and widgets don't move. Meshes whose copies would add more than 2¹⁸ vertices stay oneInstancedMesheach (texel per instance). Runtime models stay instanced, on a shallow copy of the shared geometry with an instanced_state.Numbers: testcase1-RevC-full (
tc1-full.glb, 277 MB)Method:
perf-prof.mjs(adapted: node builds counted on the sync and async build paths, draw calls per toggle, an extra profile of the idle time after the first frame), headless Chromium (Playwright 1.63 image) on docker-server, WebGPU on SwiftShader, CDP CPU profiles at 500 µs. Unminified dev pages built in the same dev-image call: master = origin/master b6932ae viewer sources, branch = this PR. Main-thread JS = samples outside(idle)/(program). Wall-clock toggle times are SwiftShader rendering and mean nothing.First-frame wall time on SwiftShader: master 28 s (223 pipelines compiled on the CPU), this PR 1.5–8 s (machine load varied; another session shared docker-server).
Numbers: KiCad royalblue54l-feather with runtime models (React demo)
A scratch Playwright spec (not committed) on the React demo site, master site (origin/master's
packages/viewer) and this PR's site built in the same dev-image call;?sample=royalblue54l-featherwith real KiCad models from gitlab.com (LIVE_GITLAB=1): 41 of 52 components, 11 models (6 not in KiCad's libraries); iso view, 8 s idle, four toggles with CDP profiles.navigator.gpuhidden)¹ gitlab.com rate-limited that run (36 models).
Screenshots (described, not committed). Normal view: identical to master (0.007 % of pixels differ by more than 8/255: the "Converted in" timing text). X-ray: the board translucent, copper at 0.6, bodies at 0.15, runtime models translucent like the placeholders, as on master; 0.24 % of pixels differ by more than 8/255 and 0.005 % by more than 40/255, on bodies (header pins, ICs). Cause: translucent blend order. The merged body mesh is sorted as one object against the translucent layers, where master sorted each package's instanced mesh separately. Same board without runtime models on the dev page: top/bottom/iso identical, x-ray 0.68 % / 0.006 %. I tried merging per mounting side: top bodies are then drawn after all layers (physically better), but 20 % of x-ray pixels differ slightly from master, so I kept one merged mesh per material. WebGL2 vs WebGPU on this PR differ only at anti-aliased edges (0.5 %), so the per-instance state texel works on both backends.
Spec changes
None.
Tests
materials.test.ts: sharing (same source, equal copies, other x-ray opacity, other material), the swap without material version changes, the variants'transparent/depthWrite/polygon offset, the overlay the same in both modes, assignment in x-ray mode, reference-counted release,materialKey.bodies.test.ts: merge into board coordinates with state texels, picking per instance, filtering (draw range, bounds, visibility) and restoring, mirrored matrices keep outward winding,batchBodiesmerging per material and layout and instancing large repeated meshes (source geometry untouched).board-model(merged batches),dense(11k components in two merged meshes),runtime-models(placeholders replaced and restored in the merged batches; model batches instanced).vitest.config.ts:hookTimeout60 s liketestTimeout(the dense fixture'sbeforeAllhit 10 s once under the parallelpnpm test; merging adds no measurable time:fromGltf277–312 ms vs master 270–301 ms for 11k components).cargo fmt --check, clippy,cargo test --workspaceand the conformance test (BOARDUI_REQUIRE_IPC_TESTCASES=1), the wasm build;pnpm install && pnpm lint && pnpm build && pnpm test: all pass. Demo e2e (both apps, as CI, with the IPC test cases): 56 passed, 14 skipped (the review/live specs); no screenshot changed beyond the tolerance, none updated.Known gaps and follow-ups
FrontSideculling. Rare in practice._stateinstead of_feature_id_0(internal).