fix(text): grip-drag no longer hides text (#316)

Dragging text by a grip hid it until release. The dragged entity is
hidden from the base wire set and shown as a preview overlay, but its
SDF glyph quads (text_verts) were never uploaded for the preview, so
the text vanished mid-drag.

Add a per-frame preview_text GPU buffer (sibling to preview_wires). A
rigid whole-entity move of pure text (TEXT/MTEXT, Square insertion grip)
slides the drag-start glyphs by the drag delta instead of re-shaping the
run every move; reshape grips (MTEXT width), dimensions and wire
entities re-tessellate and feed the same buffer.

Also fold preview_text into render_signature: a pure-text slide leaves
preview_wires empty, so without it the scene-render cache froze the
dragged text at its first frame (grip moved but text stayed, then
jumped to place on release).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Hakan Seven 2026-07-10 10:39:44 +03:00
commit 67fe363775
9 changed files with 176 additions and 5 deletions

View file

@ -90,6 +90,14 @@ pub struct Pipeline {
/// analogue of the selected-wire xray overlay). Rebuilt on selection change.
text_highlight_vbuf: Option<wgpu::Buffer>,
text_highlight_vcount: u32,
/// Live grip-drag / command-preview SDF glyph quads. The base text buffer
/// only re-uploads on a geometry-epoch change, but a grip drag hides the
/// dragged entity from the base wire set and shows it as a per-frame
/// overlay — so its glyphs ride here, re-uploaded every frame like
/// `gpu_preview_wires`, or the dragged text vanishes until release re-tesses
/// it back into the base set (issue #316).
text_preview_vbuf: Option<wgpu::Buffer>,
text_preview_vcount: u32,
/// Last requested render size (the full viewport rect, in pixels). The
/// geometry passes render at this size; the blit UV is scaled by
/// `depth_texture_size / alloc_size` so it samples only the filled region.
@ -1161,6 +1169,8 @@ impl Pipeline {
text_vcount: 0,
text_highlight_vbuf: None,
text_highlight_vcount: 0,
text_preview_vbuf: None,
text_preview_vcount: 0,
mesh_pipeline,
mesh_transparent_pipeline,
mesh_highlight_pipeline,
@ -1383,6 +1393,39 @@ impl Pipeline {
};
}
/// Upload the live grip-drag / command-preview SDF glyph quads. Re-uploaded
/// every frame they're present, kept separate from the epoch-cached base
/// text buffer so a drag never re-uploads the (potentially huge) resident
/// text set — and so the dragged entity's glyphs (hidden from the base set
/// while the drag is live) still reach the GPU each frame (issue #316).
pub fn upload_preview_text(
&mut self,
device: &wgpu::Device,
queue: &wgpu::Queue,
verts: &[text_gpu::TextVertex],
) {
if verts.is_empty() {
self.text_preview_vbuf = None;
self.text_preview_vcount = 0;
return;
}
// Ensure the glyph atlas exists / is current even when the base text
// pass didn't run this frame (e.g. the dragged text is the only text).
if let Ok(mut atlas) = crate::scene::text::sdf_atlas::text_atlas().lock() {
if self.text_atlas_gpu.is_none() || atlas.is_dirty() {
self.text_atlas_gpu = Some(text_gpu::TextAtlasGpu::upload(
device,
queue,
&atlas,
&self.text_atlas_bgl,
));
atlas.clear_dirty();
}
}
self.text_preview_vbuf = text_gpu::upload_vertices(device, verts);
self.text_preview_vcount = verts.len() as u32;
}
/// Recompute pixel scissor rects for viewport-clipped wires from the current view_proj.
/// Called every frame from prepare() because scissor pixels shift with pan/zoom.
pub fn compute_wire_scissors(&mut self, view_rot: glam::Mat4, eye: glam::DVec3, clip_w: u32, clip_h: u32) {
@ -2143,7 +2186,9 @@ impl Pipeline {
if let Some(atlas) = &self.text_atlas_gpu {
let have_base = self.text_vbuf.is_some() && self.text_vcount > 0;
let have_hl = self.text_highlight_vbuf.is_some() && self.text_highlight_vcount > 0;
if have_base || have_hl {
let have_preview =
self.text_preview_vbuf.is_some() && self.text_preview_vcount > 0;
if have_base || have_hl || have_preview {
let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
label: Some("text.render_pass"),
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
@ -2182,6 +2227,13 @@ impl Pipeline {
pass.draw(0..self.text_highlight_vcount, 0..1);
}
}
// Grip-drag / command-preview glyphs, drawn over the base text.
if let Some(pbuf) = &self.text_preview_vbuf {
if self.text_preview_vcount > 0 {
pass.set_vertex_buffer(0, pbuf.slice(..));
pass.draw(0..self.text_preview_vcount, 0..1);
}
}
}
}