forked from 1b-agent/opencadstudio-upstream
perf: let one tessellation serve every background
Switching to a Layout re-tessellated the whole model — `memo_miss=186455`, 2034 ms — to arrive at geometry identical to what was already in the memo. The only thing that differed was colour. `local_wires_for` already knows this: it stores the raw colour and drops its background argument with `let _ = bg_color;`, noting that `Batches::finalize` applies `adapt_to_bg` last, "so the cache no longer has to rebuild on BACKGROUND / layout-switch". The tessellation memo's guard had not been told, and still mixed the background in, so every entry was discarded on a switch. `finalize` now records what its colour resolution consumed — the raw colour, the raw glyph colours, and the three flags it branches on — as `WireModel::bg_adapt`, and `resolve_colors_for_bg` replays it for another background. The guard drops the background, so a memo entry serves any layout. The resolution recomputes from the recorded raw colour rather than from the resolved one, which makes it idempotent and therefore safe over a set that mixes memo hits with freshly tessellated misses. Re-adapting the resolved colour would not be safe: `adapt_to_bg` snaps a near-white colour to pure black on a light background, and pure black comes back pure *white* on a dark one, losing the tint. A test pins that asymmetry so the raw colour is not optimised away later. The contract is tested against the real `finalize` rather than a copy of its logic: for every background pair, building under one and resolving for the other lands exactly where building under the other would have. `bg_adapt` is `None` for wires built anywhere else, and the resolution skips them, so nothing outside the block cache changes colour. It is also `None` when the resolution would be a no-op — a preserved colour with no canvas fill looks the same under every background — which keeps the common case at no cost. Ordering is unchanged: the pass runs inside the builder, before `append_scene_markers` and `apply_refedit_fade`, both of which have their own separate background dependency. `wires-build` gains `colors=` so the pass has a number of its own. Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
1 parent
0e4f755e6f
commit
391a74a615
40 files changed
+308
-9
No files matched your search
@@ -140,6 +140,40 @@ impl PointMarker {
|
||||
///
|
||||
/// Linetype is encoded as a GPU-side dash pattern so the CPU never needs to
|
||||
/// split wires into per-dash segments. `pattern_length = 0.0` means solid.
|
||||
/// Everything needed to resolve a wire's display colour against a background,
|
||||
/// kept so the resolution can be redone for another background without
|
||||
/// re-tessellating.
|
||||
///
|
||||
/// `None` — the default — means the wire did not record its inputs and a
|
||||
/// resolution pass must leave it alone. Only `Batches::finalize` fills it in.
|
||||
pub type BgAdapt = Option<Box<BgAdaptInputs>>;
|
||||
|
||||
/// The inputs `Batches::finalize` consumes when it resolves a colour.
|
||||
///
|
||||
/// The flags are independent — a wire can paint the background itself while
|
||||
/// its glyphs still adapt — so this mirrors that code's branches rather than
|
||||
/// collapsing them into one enum.
|
||||
#[derive(Clone, Debug, Default, PartialEq)]
|
||||
pub struct BgAdaptInputs {
|
||||
/// Colour before adaptation. Resolution always recomputes from here rather
|
||||
/// than from `WireModel::color`, so it is idempotent and safe over a set
|
||||
/// mixing memo hits with freshly tessellated wires. `adapt_to_bg` is
|
||||
/// **not** re-appliable: a near-white colour snaps to pure black on a
|
||||
/// light background, and pure black comes back pure *white* on a dark one,
|
||||
/// losing the tint.
|
||||
pub raw_color: [f32; 4],
|
||||
/// Glyph colours before adaptation, parallel to `WireModel::text_verts`.
|
||||
/// Empty when the wire has no glyphs or their colour never adapts.
|
||||
pub text_raw_colors: Vec<[f32; 4]>,
|
||||
/// Adapt against this instead of the live background: an MTEXT carrying
|
||||
/// its own background fill.
|
||||
pub contrast_bg: Option<[f32; 4]>,
|
||||
/// The wire paints the background; its colour *is* the live background.
|
||||
pub canvas_color: bool,
|
||||
/// Authored colour. The background must not touch it, glyphs included.
|
||||
pub preserve_color: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct WireModel {
|
||||
/// Unique identifier — the handle value as a decimal string.
|
||||
@@ -156,6 +190,9 @@ pub struct WireModel {
|
||||
pub point_marker: Option<PointMarker>,
|
||||
/// RGBA colour in [0, 1].
|
||||
pub color: [f32; 4],
|
||||
/// How `color` and `text_verts` follow the background, or `None` when this
|
||||
/// wire's colour must not be re-resolved.
|
||||
pub bg_adapt: BgAdapt,
|
||||
/// Whether this wire is currently selected.
|
||||
#[allow(dead_code)]
|
||||
pub selected: bool,
|
||||
@@ -315,6 +352,7 @@ impl WireModel {
|
||||
/// Create a solid wire (no dash pattern, 1px weight).
|
||||
pub fn solid(name: String, points: Vec<[f32; 3]>, color: [f32; 4], selected: bool) -> Self {
|
||||
Self {
|
||||
bg_adapt: None,
|
||||
point_marker: None,
|
||||
taper_widths: Vec::new(),
|
||||
pattern_stations: Vec::new(),
|
||||
@@ -688,6 +726,7 @@ impl Default for WireModel {
|
||||
points: Vec::new(),
|
||||
points_low: Vec::new(),
|
||||
color: Self::WHITE,
|
||||
bg_adapt: None,
|
||||
selected: false,
|
||||
pattern_length: 0.0,
|
||||
pattern: [0.0; 8],
|
||||
|
||||
Reference in new issue
Block a user