1
0
Fork 0
forked from wry/wry

renderer: fix rendering flickering window rendering

This commit is contained in:
kossLAN 2026-04-11 00:57:16 -04:00
parent bd3128c516
commit 4e9b6def83
No known key found for this signature in database
3 changed files with 108 additions and 41 deletions

View file

@ -189,6 +189,51 @@ fn run_ops(fb: &Framebuffer, ops: &[GfxApiOpt]) -> Option<FdSync> {
let copy_tex = &mut *copy_tex; let copy_tex = &mut *copy_tex;
let mut triangles = state.triangles.borrow_mut(); let mut triangles = state.triangles.borrow_mut();
let triangles = &mut *triangles; 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; let mut i = 0;
while i < ops.len() { while i < ops.len() {
macro_rules! has_ops { macro_rules! has_ops {
@ -218,47 +263,19 @@ fn run_ops(fb: &Framebuffer, ops: &[GfxApiOpt]) -> Option<FdSync> {
i += 1; i += 1;
} }
GfxApiOpt::RoundedFillRect(rf) => { GfxApiOpt::RoundedFillRect(rf) => {
flush_fills!();
render_rounded_fill(&fb.ctx, rf); render_rounded_fill(&fb.ctx, rf);
i += 1; i += 1;
} }
GfxApiOpt::RoundedCopyTexture(ct) => { GfxApiOpt::RoundedCopyTexture(ct) => {
flush_textures!();
render_rounded_texture(&fb.ctx, ct); render_rounded_texture(&fb.ctx, ct);
i += 1; i += 1;
} }
} }
} }
if fill_rect.is_not_empty() { flush_fills!();
fill_rect.sort_unstable_by_key(|f| f.color); flush_textures!();
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);
}
} }
if fb.ctx.ctx.dpy.explicit_sync { if fb.ctx.ctx.dpy.explicit_sync {
let file = match fb.ctx.ctx.export_sync_file() { let file = match fb.ctx.ctx.export_sync_file() {

View file

@ -923,20 +923,19 @@ impl VulkanRenderer {
#[derive(Eq, PartialEq, PartialOrd, Ord)] #[derive(Eq, PartialEq, PartialOrd, Ord)]
enum Key { enum Key {
Fill { color: [u32; 4] }, Fill { color: [u32; 4] },
Tex(usize),
RoundedFill { z_order: u32, color: [u32; 4] }, RoundedFill { z_order: u32, color: [u32; 4] },
RoundedTex(usize), Tex(usize),
} }
match o { match o {
VulkanOp::Fill(f) => Key::Fill { VulkanOp::Fill(f) => Key::Fill {
color: f.color.map(|c| c.to_bits()), color: f.color.map(|c| c.to_bits()),
}, },
VulkanOp::Tex(t) => Key::Tex(t.index),
VulkanOp::RoundedFill(f) => Key::RoundedFill { VulkanOp::RoundedFill(f) => Key::RoundedFill {
z_order: f.z_order, z_order: f.z_order,
color: f.color.map(|c| c.to_bits()), 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]; 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] { for opt in &memory.ops[pass] {
match opt { match opt {
VulkanOp::Fill(r) => { VulkanOp::Fill(r) => {
@ -1768,7 +1791,7 @@ impl VulkanRenderer {
0, 0,
uapi::as_bytes(&push), uapi::as_bytes(&push),
); );
dev.cmd_draw(buf, 4, 1, 0, 0); draw_rounded!(r.target);
} }
} else { } else {
let push = LegacyRoundedFillPushConstants { let push = LegacyRoundedFillPushConstants {
@ -1791,7 +1814,7 @@ impl VulkanRenderer {
0, 0,
uapi::as_bytes(&push), uapi::as_bytes(&push),
); );
dev.cmd_draw(buf, 4, 1, 0, 0); draw_rounded!(r.target);
} }
} }
} }
@ -1837,7 +1860,7 @@ impl VulkanRenderer {
0, 0,
uapi::as_bytes(&push), uapi::as_bytes(&push),
); );
dev.cmd_draw(buf, 4, 1, 0, 0); draw_rounded!(c.target);
} }
} else { } else {
let write_descriptor_set = WriteDescriptorSet::default() let write_descriptor_set = WriteDescriptorSet::default()
@ -1872,7 +1895,7 @@ impl VulkanRenderer {
0, 0,
uapi::as_bytes(&push), 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 { fn constrain(&self, pos: &mut Point, tex_pos: Option<&mut Point>) -> bool {
zone!("constrain"); zone!("constrain");
let mut npos = *pos; let mut npos = *pos;

View file

@ -215,6 +215,9 @@ impl Renderer<'_> {
}; };
} }
render_stacked!(self.state.root.stacked); 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() { if fullscreen.is_none() {
render_layer!(output.layers[2]); render_layer!(output.layers[2]);
} }
@ -289,7 +292,7 @@ impl Renderer<'_> {
let bar_height = tab_bar.height; let bar_height = tab_bar.height;
let render_scale = tab_bar.render_scale; 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: // We use:
// FillRect tiny strip for Vulkan paint regions (hidden) // FillRect tiny strip for Vulkan paint regions (hidden)
// RoundedFillRect z0 solid rounded bg // RoundedFillRect z0 solid rounded bg
@ -466,6 +469,7 @@ impl Renderer<'_> {
.node .node
.node_render(self, x + content.x1(), y + content.y1(), Some(&body)); .node_render(self, x + content.x1(), y + content.y1(), Some(&body));
self.stretch = None; self.stretch = None;
self.corner_radius = None;
} else { } else {
let gap = self.state.theme.sizes.gap.get(); let gap = self.state.theme.sizes.gap.get();
let (srgb_srgb, bw, border_color, focused_border_color) = if gap != 0 { let (srgb_srgb, bw, border_color, focused_border_color) = if gap != 0 {
@ -545,6 +549,7 @@ impl Renderer<'_> {
.node .node
.node_render(self, x + content.x1(), y + content.y1(), Some(&body)); .node_render(self, x + content.x1(), y + content.y1(), Some(&body));
self.stretch = None; self.stretch = None;
self.corner_radius = None;
} }
} }
@ -630,6 +635,9 @@ impl Renderer<'_> {
is_subsurface: bool, is_subsurface: bool,
) { ) {
let stretch = self.stretch.take(); 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 children = surface.children.borrow();
let buffer = match surface.buffer.get() { let buffer = match surface.buffer.get() {
Some(b) => b, Some(b) => b,
@ -680,9 +688,12 @@ impl Renderer<'_> {
}; };
} }
render!(&children.below); 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); self.render_buffer(surface, &buffer, x, y, tpoints, size, bounds);
render!(&children.above); render!(&children.above);
} else { } else {
self.corner_radius = corner_radius;
self.render_buffer(surface, &buffer, x, y, tpoints, size, bounds); self.render_buffer(surface, &buffer, x, y, tpoints, size, bounds);
} }
} }
@ -827,6 +838,7 @@ impl Renderer<'_> {
self.corner_radius = Some(inner_cr); self.corner_radius = Some(inner_cr);
} }
child.node_render(self, body.x1(), body.y1(), Some(&scissor_body)); 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) { pub fn render_layer_surface(&mut self, surface: &ZwlrLayerSurfaceV1, x: i32, y: i32) {