Spatial Performance Checks¶
Spatial member sampling is cached in canonical model units, shared by the viewport, Member Detail, envelopes and report diagrams. Each solver snapshot owns a least-recently-used cache with a 32 MiB sample-array budget. Keys separate snapshots, combinations, members, geometry/roll/type and load-boundary stations; unit, selection and display-only changes reuse read-only arrays. Fresh analyses use fresh caches, and solver serialization drops cached arrays. There is no change to numerical sampling density, one-sided load jumps or solver signs.
Run the optional analytical benchmark from the repository root:
uv run --locked python -B tests/benchmark_spatial.py
It solves 10-, 50- and 100-segment spatial cantilevers and emits JSON with solve, cold sampling, five warm reads, station counts and cache bytes. On this Linux Python 3.12.15 run (2026-10-09), the 100-member cold read took 0.85 seconds and warm reads took 0.15-0.21 milliseconds, retaining 585,600 sample-array bytes. These are sampling-only measurements, not complete UI redraw or solver speedups.
For optional large-model desktop rendering checks, use the same separate Playwright installation as above:
VIEWPORT_BENCHMARK=1 node tests/viewport3d.spec.cjs
The ordinary interaction suite still runs. The benchmark additionally draws
144-, 616- and 1,580-member lattices at 1280x720 with labels on/off, checks canvas
pixels, framing, orbit interaction and graphics-resource counts after model
replacement, and records scene rebuild and frame timings. Screenshots and
desktop-benchmark.json use the same output directory. Synthetic lattice data
benchmarks rendering only, not structural analysis. The harness uses Chromium's
SwiftShader software renderer; record the reported driver when comparing runs.
Timings are observations, not hardware-independent pass/fail thresholds.
The pre-batching baseline rebuilt the 1,580-member geometry in about 152 ms,
or 520 ms with labels enabled, with roughly 2,600 draw calls. Base frame rendering
now uses two instanced meshes (one unit cylinder shape for members and one unit
sphere shape for nodes) with per-instance transforms/colours/IDs, and the grid
uses one line-segment batch. Picking, node-dragging and drawing snaps resolve
instance IDs back to model IDs. Disposal releases instance buffers and deduplicates
geometry/material/texture disposal. Supports, labels, loads and analytical
overlays remain independent objects; their rebuilding and JSON/payload costs
are not removed by geometry batching or the analytical cache. The benchmark
enforces fewer than 30 unlabelled draw calls and 20 geometries for the lattices,
in addition to visible model pixels and resource-replacement checks.
The post-batching run recorded 18 unlabelled draw calls and seven geometries at
all three sizes. The 1,580-member unlabelled rebuild took about 14 ms (12 ms on
replacement), while labels still brought rebuilds into the hundreds of
milliseconds. SwiftShader frame timings remained variable; these measurements
demonstrate lower allocation/submission costs, not a guaranteed frame-rate gain.
Selection and tool-mode changes now recolour the two base instance buffers and update interaction settings without rebuilding supports, loads, deformation or force overlays. The complete remaining payload is compared, so geometry, work-plane, units, theme, visibility and result changes still rebuild. When local axes are shown, selection also rebuilds because it changes the displayed axes. JSON serialization and native payload construction still scale with model size.
Label textures are shared by exact text/colour through a least-recently-used cache capped at 512 entries and an 8 MiB estimated RGBA texture budget (including a mipmap allowance). Active sprites pin their entries. Labels beyond the budget use transient textures that are disposed with their sprites. Hiding ordinary labels clears unused entries; required diagram values remain independent. Context loss and page teardown clear retained entries and dispose scene objects; browser history suspension retains its scene for restoration. The cache bounds retained resources, not the memory required by all live labels in a large scene.
The interaction harness includes real-canvas cache lifetime/eviction checks, selection colours, all six force-overlay refreshes, support-anchor preservation, geometry/theme/visibility/local-axis rebuilds, and context-loss cleanup. Large-model benchmarks also record selection/tool refresh times and enforce no scene rebuilds, additional graphics resources or cache-budget overruns for those display-only updates.
A hardware comparison on 2026-10-09 used the Codex in-app Chromium browser, WebGL 2 / ANGLE Metal on Apple M4 Max, a 552x853 viewport, the same 144/616/1,580 member lattices and five selection/tool updates per case. Median labelled selection refreshes changed from 5.8/22.3/76.9 ms to 0.6/0.9/1.3 ms; tool changes changed from 5.7/24.6/71.7 ms to 0.5/0.8/1.0 ms. Unlabelled refresh times stayed around 0.4-1.5 ms. Resource counts stayed constant across the new refreshes; the largest scenes retained 512 label entries with an estimated 6,856,128 bytes. These are payload-to-scene update observations, not complete native GUI latency, solver timings, full-rebuild speedups or guaranteed frame rates. The targeted browser contracts and context-loss check passed on this hardware. The standalone Playwright interaction suite could not launch here because the macOS sandbox denied Chromium's Mach bootstrap registration; the recorded hardware comparison does not substitute for that suite's gesture coverage.
Source: GUIDE.md. This manual follows the current repository; the published rc1 wheel has a smaller feature set.