From 4e9b6def8348e53afde31b561752e77185115a83 Mon Sep 17 00:00:00 2001 From: kossLAN Date: Sat, 11 Apr 2026 00:57:16 -0400 Subject: [PATCH] renderer: fix rendering flickering window rendering --- src/gfx_apis/gl.rs | 81 ++++++++++++++++++++------------- src/gfx_apis/vulkan/renderer.rs | 54 ++++++++++++++++++---- src/renderer.rs | 14 +++++- 3 files changed, 108 insertions(+), 41 deletions(-) diff --git a/src/gfx_apis/gl.rs b/src/gfx_apis/gl.rs index bafd11ec..ef256a6b 100644 --- a/src/gfx_apis/gl.rs +++ b/src/gfx_apis/gl.rs @@ -189,6 +189,51 @@ fn run_ops(fb: &Framebuffer, ops: &[GfxApiOpt]) -> Option { let copy_tex = &mut *copy_tex; let mut triangles = state.triangles.borrow_mut(); let triangles = &mut *triangles; + // Flush accumulated fill rects (sorted by color for batching). + macro_rules! flush_fills { + () => { + if fill_rect.is_not_empty() { + fill_rect.sort_unstable_by_key(|f| f.color); + let mut j = 0; + while j < fill_rect.len() { + triangles.clear(); + let mut color = None; + while j < fill_rect.len() { + let fr = &fill_rect[j]; + match color { + None => color = Some(fr.color), + Some(c) if c == fr.color => {} + _ => break, + } + let [top_right, top_left, bottom_right, bottom_left] = + fr.rect.to_points(); + triangles.extend_from_slice(&[ + top_right, + top_left, + bottom_left, + top_right, + bottom_left, + bottom_right, + ]); + j += 1; + } + if let Some(color) = color { + fill_boxes3(&fb.ctx, triangles, &color); + } + } + fill_rect.clear(); + } + }; + } + // Flush accumulated texture copies (preserving insertion order). + macro_rules! flush_textures { + () => { + for tex in copy_tex.iter() { + render_texture(&fb.ctx, tex); + } + copy_tex.clear(); + }; + } let mut i = 0; while i < ops.len() { macro_rules! has_ops { @@ -218,47 +263,19 @@ fn run_ops(fb: &Framebuffer, ops: &[GfxApiOpt]) -> Option { i += 1; } GfxApiOpt::RoundedFillRect(rf) => { + flush_fills!(); render_rounded_fill(&fb.ctx, rf); i += 1; } GfxApiOpt::RoundedCopyTexture(ct) => { + flush_textures!(); render_rounded_texture(&fb.ctx, ct); i += 1; } } } - if fill_rect.is_not_empty() { - fill_rect.sort_unstable_by_key(|f| f.color); - let mut i = 0; - while i < fill_rect.len() { - triangles.clear(); - let mut color = None; - while i < fill_rect.len() { - let fr = &fill_rect[i]; - match color { - None => color = Some(fr.color), - Some(c) if c == fr.color => {} - _ => break, - } - let [top_right, top_left, bottom_right, bottom_left] = fr.rect.to_points(); - triangles.extend_from_slice(&[ - top_right, - top_left, - bottom_left, - top_right, - bottom_left, - bottom_right, - ]); - i += 1; - } - if let Some(color) = color { - fill_boxes3(&fb.ctx, triangles, &color); - } - } - } - for tex in &*copy_tex { - render_texture(&fb.ctx, tex); - } + flush_fills!(); + flush_textures!(); } if fb.ctx.ctx.dpy.explicit_sync { let file = match fb.ctx.ctx.export_sync_file() { diff --git a/src/gfx_apis/vulkan/renderer.rs b/src/gfx_apis/vulkan/renderer.rs index 9caa0d78..c920b570 100644 --- a/src/gfx_apis/vulkan/renderer.rs +++ b/src/gfx_apis/vulkan/renderer.rs @@ -923,20 +923,19 @@ impl VulkanRenderer { #[derive(Eq, PartialEq, PartialOrd, Ord)] enum Key { Fill { color: [u32; 4] }, - Tex(usize), RoundedFill { z_order: u32, color: [u32; 4] }, - RoundedTex(usize), + Tex(usize), } match o { VulkanOp::Fill(f) => Key::Fill { color: f.color.map(|c| c.to_bits()), }, - VulkanOp::Tex(t) => Key::Tex(t.index), VulkanOp::RoundedFill(f) => Key::RoundedFill { z_order: f.z_order, color: f.color.map(|c| c.to_bits()), }, - VulkanOp::RoundedTex(t) => Key::RoundedTex(t.index), + VulkanOp::Tex(t) => Key::Tex(t.index), + VulkanOp::RoundedTex(t) => Key::Tex(t.index), } }); let mops = &mut memory.ops[pass]; @@ -1638,6 +1637,30 @@ impl VulkanRenderer { } } }; + let full_scissor = Rect2D { + offset: Default::default(), + extent: Extent2D { + width: target.width, + height: target.height, + }, + }; + let paint_regions = &memory.paint_regions[pass]; + // Draws a rounded op once per intersecting paint region using scissor + // rects. Unlike CopyTexture/FillRect which clip their geometry to each + // paint region, rounded ops must keep their full geometry so the shader + // computes correct corner rounding. Scissor rects achieve this. + macro_rules! draw_rounded { + ($target_point:expr) => { + for region in paint_regions { + if region.intersects(&$target_point) { + let scissor = region.to_scissor(target); + dev.cmd_set_scissor(buf, 0, slice::from_ref(&scissor)); + dev.cmd_draw(buf, 4, 1, 0, 0); + } + } + dev.cmd_set_scissor(buf, 0, slice::from_ref(&full_scissor)); + }; + } for opt in &memory.ops[pass] { match opt { VulkanOp::Fill(r) => { @@ -1768,7 +1791,7 @@ impl VulkanRenderer { 0, uapi::as_bytes(&push), ); - dev.cmd_draw(buf, 4, 1, 0, 0); + draw_rounded!(r.target); } } else { let push = LegacyRoundedFillPushConstants { @@ -1791,7 +1814,7 @@ impl VulkanRenderer { 0, uapi::as_bytes(&push), ); - dev.cmd_draw(buf, 4, 1, 0, 0); + draw_rounded!(r.target); } } } @@ -1837,7 +1860,7 @@ impl VulkanRenderer { 0, uapi::as_bytes(&push), ); - dev.cmd_draw(buf, 4, 1, 0, 0); + draw_rounded!(c.target); } } else { let write_descriptor_set = WriteDescriptorSet::default() @@ -1872,7 +1895,7 @@ impl VulkanRenderer { 0, uapi::as_bytes(&push), ); - dev.cmd_draw(buf, 4, 1, 0, 0); + draw_rounded!(c.target); } } } @@ -2716,6 +2739,21 @@ impl PaintRegion { } } + fn to_scissor(&self, target: &VulkanImage) -> Rect2D { + let from_norm = |c: f32, max: u32| ((c + 1.0) * 0.5 * max as f32).round() as i32; + let x1 = from_norm(self.x1, target.width).max(0); + let y1 = from_norm(self.y1, target.height).max(0); + let x2 = from_norm(self.x2, target.width).min(target.width as i32); + let y2 = from_norm(self.y2, target.height).min(target.height as i32); + Rect2D { + offset: Offset2D { x: x1, y: y1 }, + extent: Extent2D { + width: (x2 - x1).max(0) as u32, + height: (y2 - y1).max(0) as u32, + }, + } + } + fn constrain(&self, pos: &mut Point, tex_pos: Option<&mut Point>) -> bool { zone!("constrain"); let mut npos = *pos; diff --git a/src/renderer.rs b/src/renderer.rs index af64f1a0..0eeb670f 100644 --- a/src/renderer.rs +++ b/src/renderer.rs @@ -215,6 +215,9 @@ impl Renderer<'_> { }; } render_stacked!(self.state.root.stacked); + // Flush RoundedFillRect ops from container/float borders so they don't + // sort after (and render on top of) layer-shell CopyTexture ops. + self.base.sync(); if fullscreen.is_none() { render_layer!(output.layers[2]); } @@ -289,7 +292,7 @@ impl Renderer<'_> { let bar_height = tab_bar.height; let render_scale = tab_bar.render_scale; - // Vulkan sorts ops: Fill < Tex < RoundedFill (by z_order, color) < RoundedTex. + // Vulkan sorts ops: Fill < RoundedFill (by z_order, color) < Tex/RoundedTex (by index). // We use: // FillRect – tiny strip for Vulkan paint regions (hidden) // RoundedFillRect z0 – solid rounded bg @@ -466,6 +469,7 @@ impl Renderer<'_> { .node .node_render(self, x + content.x1(), y + content.y1(), Some(&body)); self.stretch = None; + self.corner_radius = None; } else { let gap = self.state.theme.sizes.gap.get(); let (srgb_srgb, bw, border_color, focused_border_color) = if gap != 0 { @@ -545,6 +549,7 @@ impl Renderer<'_> { .node .node_render(self, x + content.x1(), y + content.y1(), Some(&body)); self.stretch = None; + self.corner_radius = None; } } @@ -630,6 +635,9 @@ impl Renderer<'_> { is_subsurface: bool, ) { let stretch = self.stretch.take(); + // Take corner_radius early so it is never leaked on early return + // and so that below-subsurfaces cannot steal it from the main surface. + let corner_radius = self.corner_radius.take(); let children = surface.children.borrow(); let buffer = match surface.buffer.get() { Some(b) => b, @@ -680,9 +688,12 @@ impl Renderer<'_> { }; } render!(&children.below); + // Restore corner_radius only for the main surface's render_buffer call. + self.corner_radius = corner_radius; self.render_buffer(surface, &buffer, x, y, tpoints, size, bounds); render!(&children.above); } else { + self.corner_radius = corner_radius; self.render_buffer(surface, &buffer, x, y, tpoints, size, bounds); } } @@ -827,6 +838,7 @@ impl Renderer<'_> { self.corner_radius = Some(inner_cr); } child.node_render(self, body.x1(), body.y1(), Some(&scissor_body)); + self.corner_radius = None; } pub fn render_layer_surface(&mut self, surface: &ZwlrLayerSurfaceV1, x: i32, y: i32) {