Build the selection highlight in one pass instead of four

Highlighting a whole-drawing selection took 299 ms, and it runs when the
selection changes — directly between the click that completes a selection and
seeing it.

Most of that was asking the same question twice. Each wire went through the
circle and ellipse extractors at its draw depth to build the analytic
instances, then through both again at depth 0.0 purely to ask *whether* it is
a circle or an ellipse, allocating a vector each time and dropping it.
Neither extractor's `Some`/`None` answer depends on the depth: every
`return None` in them turns on the geometry — which `TangentGeom` variant,
the radius, the axis lengths, the parameters — and the depth is only written
into the instance, never read to decide. So the extraction that builds the
instances already answers the classification. One pass now does both, and
fans out across cores, since it reads a wire and writes nothing shared. That
took 173 ms of shape extraction and splitting down to 19 ms, and the whole
highlight from 299 ms to 150 ms.

Gathering the highlighted wires also cloned each handle's slot vector and
sorted the copy — one allocation and one sort per selected entity. The slots
are already ascending: both builders walk the wires in order and push as they
go, and `patch_handle_index` shifts a handle's slots by a constant, which
preserves it. A `debug_assert` on the slots actually read holds that in
place. The compat path of `from_highlight_refs` emitted its instances
sequentially while the native path beside it fans out; it now matches.

Nothing renders in a test, so a wrong bucket in the classification would
surface only as circles drawn wrong when selected. Two tests cover it: each
wire lands where the old predicate put it, and the colour override — always
for hover, and the configured tint or `SELECTED` for a selection — reaches
the instances.

`wire-highlight-detail` reports the phases: gathering, classifying,
uploading the analytic instances, the regular wires and the block ones.
This commit is contained in:
Panos committed 2026-09-09 10:36:45 +03:00
1 parent d5bf919414
commit f128ed3eaf
2 files changed
+236 -77

No files matched your search

+7 -1
View File
@@ -1148,8 +1148,14 @@ impl WireGpu {
let _ = const_bgl;
let max_packed_instances = super::gpu_budget::max_elements::<PackedWireInstance>(device);
// Same fan-out the native path above uses: emitting a wire reads that
// wire and the depth map and writes nothing shared, and a selection
// large enough to be slow is exactly where it pays. `collect` on an
// indexed parallel iterator keeps the order, which the instance indices
// depend on.
use crate::par::prelude::*;
let per: Vec<Vec<PackedWireInstance>> = wires
.iter()
.par_iter()
.map(|wire| {
emit_wire_packed(wire, color, wire_draw_depth(wire, depth_map))
})