fix: render command previews above wipeouts

This commit is contained in:
gianlucafiore committed 2026-10-07 20:14:08 -03:00
1 parent 99fff30c53
commit 3ecf1951c0
1 file changed
+104 -49
+104 -49
View File
@@ -4818,44 +4818,6 @@ analytic={:.1} regular={:.1} blocks={:.1}",
}
}
}
// Live overlay wires (command preview / interim / grip drag) always
// on top: the xray pipeline (depth_compare=Always, no depth write)
// keeps them visible through any occluding geometry — a 3D solid, or
// 2D geometry drawn in front — so a command preview is never hidden.
// No scissor.
if self.gpu_preview_wires.iter().any(|pw| pw.instance_count > 0) {
pass.set_pipeline(&self.wire_xray_pipeline);
for pw in &self.gpu_preview_wires {
if pw.instance_count > 0 {
if let Some(bg) = &pw.const_bind_group {
pass.set_bind_group(1, bg.as_ref(), &[]);
}
pass.set_vertex_buffer(0, pw.instance_buffer.slice(..));
pass.draw(
0..6,
pw.first_instance..pw.first_instance + pw.instance_count,
);
}
}
}
if self.gpu_preview_circles.iter().any(|cg| cg.instance_count > 0) {
pass.set_pipeline(&self.circle_xray_pipeline);
for cg in &self.gpu_preview_circles {
if cg.instance_count > 0 {
pass.set_vertex_buffer(0, cg.instance_buffer.slice(..));
pass.draw(0..6, 0..cg.instance_count);
}
}
}
if self.gpu_preview_ellipses.iter().any(|eg| eg.instance_count > 0) {
pass.set_pipeline(&self.ellipse_xray_pipeline);
for eg in &self.gpu_preview_ellipses {
if eg.instance_count > 0 {
pass.set_vertex_buffer(0, eg.instance_buffer.slice(..));
pass.draw(0..6, 0..eg.instance_count);
}
}
}
}
// ── Pass 5c: SDF text quads (drawn over wires) ────────────────────
@@ -4864,9 +4826,7 @@ analytic={:.1} regular={:.1} blocks={:.1}",
if let Some(atlas) = &self.text_atlas_gpu {
let have_base = !self.text_gpu.is_empty();
let have_blocks = !self.block_text_gpu.is_empty();
let have_preview =
!self.text_preview_gpu.is_empty();
if have_base || have_blocks || have_preview {
if have_base || have_blocks {
let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
label: Some("text.render_pass"),
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
@@ -4907,14 +4867,6 @@ analytic={:.1} regular={:.1} blocks={:.1}",
pass.draw(0..text.vertex_count, 0..text.instance_count);
}
}
// Grip-drag / command-preview glyphs, drawn over the base text.
if have_preview {
pass.set_pipeline(&self.text_pipeline);
for text in &self.text_preview_gpu {
pass.set_vertex_buffer(0, text.vertex_buffer.slice(..));
pass.draw(0..text.vertex_count, 0..1);
}
}
}
}
@@ -4958,6 +4910,109 @@ analytic={:.1} regular={:.1} blocks={:.1}",
}
}
// ── Pass 6b: live command/grip preview overlay ────────────────────
// Wipeouts intentionally mask resident drawing geometry, but they must
// never mask temporary construction geometry belonging to an active
// command or grip edit. Draw those previews after the wipeout pass.
//
// The xray variants keep previews independent of resident depth while
// the shared content stencil still respects viewport/XCLIP clipping.
let have_preview_overlay =
self.gpu_preview_wires.iter().any(|wire| wire.instance_count > 0)
|| self.gpu_preview_circles.iter().any(|circle| circle.instance_count > 0)
|| self.gpu_preview_ellipses.iter().any(|ellipse| ellipse.instance_count > 0)
|| !self.text_preview_gpu.is_empty();
if have_preview_overlay {
let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
label: Some("preview_overlay.render_pass"),
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
view: msaa,
depth_slice: None,
resolve_target: None,
ops: wgpu::Operations {
load: wgpu::LoadOp::Load,
store: wgpu::StoreOp::Store,
},
})],
depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
view: &self.depth_view,
depth_ops: Some(wgpu::Operations {
load: wgpu::LoadOp::Load,
store: wgpu::StoreOp::Store,
}),
stencil_ops: Some(wgpu::Operations {
load: wgpu::LoadOp::Load,
store: wgpu::StoreOp::Store,
}),
}),
timestamp_writes: None,
occlusion_query_set: None,
multiview_mask: None,
});
pass.set_viewport(
raster.x,
raster.y,
raster.width,
raster.height,
0.0,
1.0,
);
pass.set_bind_group(0, &self.uniform_bind_group, &[]);
pass.set_stencil_reference(stencil_ref);
if self.gpu_preview_wires.iter().any(|wire| wire.instance_count > 0) {
pass.set_pipeline(&self.wire_xray_pipeline);
for wire in &self.gpu_preview_wires {
if wire.instance_count == 0 {
continue;
}
if let Some(bg) = &wire.const_bind_group {
pass.set_bind_group(1, bg.as_ref(), &[]);
}
pass.set_vertex_buffer(0, wire.instance_buffer.slice(..));
pass.draw(
0..6,
wire.first_instance..wire.first_instance + wire.instance_count,
);
}
}
if self.gpu_preview_circles.iter().any(|circle| circle.instance_count > 0) {
pass.set_pipeline(&self.circle_xray_pipeline);
for circle in &self.gpu_preview_circles {
if circle.instance_count > 0 {
pass.set_vertex_buffer(0, circle.instance_buffer.slice(..));
pass.draw(0..6, 0..circle.instance_count);
}
}
}
if self.gpu_preview_ellipses.iter().any(|ellipse| ellipse.instance_count > 0) {
pass.set_pipeline(&self.ellipse_xray_pipeline);
for ellipse in &self.gpu_preview_ellipses {
if ellipse.instance_count > 0 {
pass.set_vertex_buffer(0, ellipse.instance_buffer.slice(..));
pass.draw(0..6, 0..ellipse.instance_count);
}
}
}
if let Some(atlas) = &self.text_atlas_gpu {
if !self.text_preview_gpu.is_empty() {
// Same glyph shader as ordinary text, but depth-independent:
// previews are temporary UI geometry, not resident drawing.
pass.set_pipeline(&self.text_highlight_pipeline);
pass.set_bind_group(1, &atlas.bind_group, &[]);
for text in &self.text_preview_gpu {
pass.set_vertex_buffer(0, text.vertex_buffer.slice(..));
pass.draw(0..text.vertex_count, 0..1);
}
}
}
}
// ── Pass 7: selection overlay pass ───────────────────────────────
// Redraws selected wires and text with depth_compare=Always so both
// appear on top of all other geometry at full brightness — unless