forked from 1b-agent/opencadstudio-upstream
fix(text): bake draw-order depth into glyphs so fills can't cover text
Fill triangles (DIMTFILL backgrounds, MLEADER masks, hatches) carry the entity's draw-order rank, but SDF glyph quads were pushed at neutral 0.0 — any entity in the front half of the draw order had its own background fill painted over its text. gather_text_verts now adds the host wire's wire_draw_depth to each glyph: a text background lands at the same biased depth (glyphs draw after fills and win the LessEqual test), and text respects draw order against other entities' fills too. The #403 glyph-reuse fast path gains a rank-stability gate: adding or removing an entity shifts every sibling's bias (the rank denominator changes), so reuse is only safe across all-Modified edits. Co-Authored-By: Claude Fable 5 <[email protected]>
This commit is contained in:
1 parent
f407835543
commit
94609fc54a
2 files changed
+43
-4
No files matched your search
@@ -1416,6 +1416,17 @@ impl Scene {
|
||||
/// references (their baked text moves with the instance) — an edit to any of
|
||||
/// those, or any removal, invalidates it; a plain line / arc / polyline edit
|
||||
/// does not.
|
||||
/// Whether the per-entity draw-order ranks are unchanged since
|
||||
/// `last_epoch`: every delta is a Modify (an Add / Remove changes the rank
|
||||
/// denominator and shifts every sibling's bias — same rule as the
|
||||
/// `draw_depth_map` cache).
|
||||
pub(super) fn draw_ranks_stable(&self, last_epoch: u64) -> bool {
|
||||
match self.replay_since(last_epoch) {
|
||||
Some(deltas) => deltas.iter().all(|&(_, k)| k == ChangeKind::Modified),
|
||||
None => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn text_unchanged(&self, last_epoch: u64) -> bool {
|
||||
match self.replay_since(last_epoch) {
|
||||
Some(deltas) => deltas.iter().all(|&(h, k)| {
|
||||
|
||||
@@ -998,6 +998,7 @@ impl Scene {
|
||||
wires: &[WireModel],
|
||||
wire_content_id: u64,
|
||||
source_key: u64,
|
||||
depth_map: &rustc_hash::FxHashMap<u64, [f32; 2]>,
|
||||
) -> std::sync::Arc<Vec<crate::scene::pipeline::text_gpu::TextVertex>> {
|
||||
use std::sync::Arc;
|
||||
{
|
||||
@@ -1010,12 +1011,19 @@ impl Scene {
|
||||
// text-bearing entity changed since the last build, the glyphs are
|
||||
// identical, so reuse them instead of re-walking every wire. Reuse is
|
||||
// per `source_key`: another source's glyphs are a different wire set,
|
||||
// not an older build of this one (#403).
|
||||
// not an older build of this one (#403). The glyphs also bake the
|
||||
// entity draw-order depth (below), which shifts whenever an entity is
|
||||
// added or removed — reuse only across rank-stable (all-Modified)
|
||||
// edits.
|
||||
{
|
||||
let reuse = {
|
||||
let last = self.last_sdf_text.borrow();
|
||||
match last.get(&source_key) {
|
||||
Some((epoch, arc)) if self.text_unchanged(*epoch) => Some(arc.clone()),
|
||||
Some((epoch, arc))
|
||||
if self.text_unchanged(*epoch) && self.draw_ranks_stable(*epoch) =>
|
||||
{
|
||||
Some(arc.clone())
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
};
|
||||
@@ -1032,7 +1040,22 @@ impl Scene {
|
||||
let mut out: Vec<crate::scene::pipeline::text_gpu::TextVertex> = Vec::new();
|
||||
for w in wires {
|
||||
if !w.text_verts.is_empty() {
|
||||
out.extend_from_slice(&w.text_verts);
|
||||
// Bake the host wire's draw-order depth into its glyphs so
|
||||
// text layers like the rest of the entity: its own background
|
||||
// fill lands at the same biased depth (glyphs draw after fills
|
||||
// and win the LessEqual test), and a hatch later in draw order
|
||||
// still covers the text. A tessellation-time value (block-local
|
||||
// compose) survives as an offset on top of the wire's depth.
|
||||
let d = crate::scene::pipeline::wire_gpu::wire_draw_depth(w, depth_map);
|
||||
if d == 0.0 {
|
||||
out.extend_from_slice(&w.text_verts);
|
||||
} else {
|
||||
out.extend(w.text_verts.iter().map(|tv| {
|
||||
let mut tv = *tv;
|
||||
tv.draw_depth += d;
|
||||
tv
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
let verts = Arc::new(out);
|
||||
@@ -1370,7 +1393,12 @@ impl Scene {
|
||||
} else {
|
||||
0x3000_0000_0000_0000 | inst.handle.value()
|
||||
};
|
||||
let text_verts = self.gather_text_verts(&all_wires, wire_content_id, text_source_key);
|
||||
let text_verts = self.gather_text_verts(
|
||||
&all_wires,
|
||||
wire_content_id,
|
||||
text_source_key,
|
||||
&self.draw_depth_map(),
|
||||
);
|
||||
// Grip-drag / command-preview glyphs, excluded from the epoch-cached base
|
||||
// gather above. Two sources, both tiny (one operation's worth) and walked
|
||||
// per frame: the overlay wires' own glyphs (MOVE / COPY / ROTATE / SCALE /
|
||||
|
||||
Reference in new issue
Block a user