Repository navigation
Identical dielectric sheets share one mesh (profile 0.10) - #35
Merged
Merged
Conversation
midub
marked this pull request as ready for review
October 7, 2026 16:33
… 0.10) A dielectric whose sheet repeats an earlier dielectric's (same crossing drills, same colour) references that layer's mesh with a node transform that scales and moves it along Y, and its feature table. The validator accepts exactly that and still rejects other transforms and other shared meshes. New hand-written sample stacked-sheets.
midub
force-pushed
the
perf/shared-sheet-mesh
branch
from
October 8, 2026 12:22
a3924e4 to
ffe47e7
Compare
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.
Stacked on #34 (base
perf/dielectric-sheets; retarget tomasteronce #34 is merged).Problem
testcase1-RevC-full (IPC consortium Testcase1) has 11 dielectric layers with the same sheet (only through holes): 7.0 MB each, 77 MB of the 277 MB GLB, 4.2 M of 17.25 M triangles (
perf-findings.md). Spec §4 required identity transforms on layer nodes, and each primitive names its own layer's feature table, so every sheet was stored again. #34 computes the sheet once; this PR stores it once.Design (profile 0.10)
layersorder) owns the mesh: identity transform, mesh in board coordinates at its own Z range, exactly as before. A later dielectric with the same sheet (a repeated sheet) uses the same mesh withtranslation [0, t, 0]andscale [1, s, 1](s > 0, no rotation, no matrix) that maps the owner's[zMin, zMax]onto its own within float32 precision. Its geometry in board coordinates is the mesh transformed by its node. I rejected a unit-Z-range mesh with a transform on every user: no node would hold board coordinates any more and the shared case would differ from all other layers.propertyTable, and this keepsEXT_mesh_featuresconsistent (every primitive's table is thefeatureTableof every layer that shows it). The tables were identical anyway (oneSHEETrow), and feature IDs derive from the layer, not the table (§5), sofeat/PREPREG_2/0stays. The alternative (keep a table per layer and letBOARDUI_boardoverride the primitive) would hand genericEXT_mesh_featuresreaders the other layer's table.Implementation
boardui-gltf:LayerAsset::repeats: Option<usize>; the writer gives a repeat a node with the owner's mesh, the Y transform and the owner's table, and panics on an invalid repeat (documented).PROFILE_VERSION0.10, schema version 0.10,featureTabledescription.boardui-convertpipeline: a dielectric with the same crossing drills as an earlier one (Compute each distinct dielectric sheet once (same output) #34), the same colour and valid Z ranges is a repeat and isn't extruded; if the owner's sheet failed to mesh, each repeat is extruded on its own as before (so each layer keeps its warning). Stats still count the board's geometry (a shared mesh counts for each layer that shows it), so snapshot counts don't change.LayerMesh::restackedfrom Compute each distinct dielectric sheet once (same output) #34 is removed again: nothing moves meshes any more.boardui validate: a layer node whose mesh an earlier layer node uses is a repeat; both must be dielectrics, the transform must be exactly the Y scale and translation mapping the Z ranges, and the feature table must be the owner's. Every other non-identity layer/drill transform is still rejected, and a layer or drill mesh used by any other node is now an error. Unit tests for each case.mergeLayeralready bakesmatrixWorldinto board coordinates, and three 0.186'sGLTFLoaderclones a shared mesh per node and keeps their node associations apart. New tests on the new sample: merged geometry and feature boxes of each sheet at its own Z range,describe, shared table, and picking each sheet from the side. Profile ≤ 0.9 GLBs still load (the viewer only checks the major version; the fixture writes 0.2); the version test expects 0.10.stacked-sheets: 4 copper layers, two through vias and a blind via TOP–IN1. PREPREG_2 repeats the core (scale 0.2), PREPREG_1 has its own sheet.Spec changes (please check)
node/featureTableof a repeat; two layers have the samefeatureTableonly then.boardui validatechecks for repeats.profileVersion0.10,spec/schema/structural-metadata.jsonversion 0.10,BOARDUI_board.schema.jsonfeatureTabledescription; ADR 0016.Numbers
GLB size (converter output, byte sizes from the conformance run and the CLI):
"0.10")Conversion of testcase1-RevC-full, docker-server (8 cores, load average 6–8 during this series), master b6932ae / #34 / this PR, alternating runs, medians:
This PR saves the 10 sheet copies and 74 MB of writing: native −0.16 s and −170 MB, WASM −60 MB memory; its WASM total is within noise of #34 (cut and sheets is the same code in both and varied by 0.12 s between series).
Viewer (same viewer, master GLB vs this PR's GLB):
perf-prof.mjsload profiles in headless Chromium were too noisy on the shared machine (load average 18–33, other sessions' builds; the unrelated node-material build varied 1.4–2.1 s between runs of the same GLB), so I timed the viewer's model build in Node instead (loadGltf+BoardModel.fromGltffrompackages/viewer/dist, fresh process each, 5 alternating runs): GLB parse 336 → 251 ms (median), model build (mostlymergeLayer) 681 → 667 ms, i.e. unchanged: the repeats are still merged per layer, now with a matrix per vertex. Heap after first frame in Chromium 894 → 793 MB. Both GLBs load, render and become pickable in the browser.Test evidence
boardui-dev:1:cargo fmt --all --check,cargo clippy --workspace --all-targets --locked -- -D warnings,BOARDUI_REQUIRE_IPC_TESTCASES=1 cargo test --workspace --locked(conformance 52/52 incl. the new sample and the 5 fetched IPC test cases),cargo build -p boardui-wasm --target wasm32-unknown-unknown --release --locked;pnpm install && pnpm lint && pnpm build && pnpm test(viewer 168 tests incl. the 2 new ones).INSTA_UPDATE=always BOARDUI_BLESS=1: snapshots of blind-buried-vias, royalblue54l-feather and testcase11 change only by the new " mesh of layer/…" note on repeated sheets (counts, Z ranges and areas unchanged); newstacked-sheets.snap; the hand-written GLBs change by the profile version (and stacked-sheets is new).boardui validateon testcase1-RevC-full from this PR: valid, 0 errors, 0 warnings. Khronos glTF Validator 2.0.0-dev.3.10 (as CI) on testcase1-RevC-full, stacked-sheets, blind-buried-vias, royalblue54l-feather and testcase11: 0 errors, 0 warnings (only the known infos).stacked-sheets, not committed): the board edge shows the upper prepreg, the core and the shared lower prepreg as a continuous stack with no gap; the lower prepreg (core mesh scaled by 0.2) sits at its own height.Known gaps / follow-ups
perf-findings.md) is a viewer follow-up.