Move damage tracking logic to compositor in iced_tiny_skia
This commit is contained in:
parent
d953d12c38
commit
af0303f95e
5 changed files with 183 additions and 182 deletions
140
graphics/src/damage.rs
Normal file
140
graphics/src/damage.rs
Normal file
|
|
@ -0,0 +1,140 @@
|
||||||
|
use crate::core::{Rectangle, Size};
|
||||||
|
use crate::Primitive;
|
||||||
|
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
pub fn regions(a: &Primitive, b: &Primitive) -> Vec<Rectangle> {
|
||||||
|
match (a, b) {
|
||||||
|
(
|
||||||
|
Primitive::Group {
|
||||||
|
primitives: primitives_a,
|
||||||
|
},
|
||||||
|
Primitive::Group {
|
||||||
|
primitives: primitives_b,
|
||||||
|
},
|
||||||
|
) => return list(primitives_a, primitives_b),
|
||||||
|
(
|
||||||
|
Primitive::Clip {
|
||||||
|
bounds: bounds_a,
|
||||||
|
content: content_a,
|
||||||
|
},
|
||||||
|
Primitive::Clip {
|
||||||
|
bounds: bounds_b,
|
||||||
|
content: content_b,
|
||||||
|
},
|
||||||
|
) => {
|
||||||
|
if bounds_a == bounds_b {
|
||||||
|
return regions(content_a, content_b)
|
||||||
|
.into_iter()
|
||||||
|
.filter_map(|r| r.intersection(bounds_a))
|
||||||
|
.collect();
|
||||||
|
} else {
|
||||||
|
return vec![*bounds_a, *bounds_b];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(
|
||||||
|
Primitive::Translate {
|
||||||
|
translation: translation_a,
|
||||||
|
content: content_a,
|
||||||
|
},
|
||||||
|
Primitive::Translate {
|
||||||
|
translation: translation_b,
|
||||||
|
content: content_b,
|
||||||
|
},
|
||||||
|
) => {
|
||||||
|
if translation_a == translation_b {
|
||||||
|
return regions(content_a, content_b)
|
||||||
|
.into_iter()
|
||||||
|
.map(|r| r + *translation_a)
|
||||||
|
.collect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(
|
||||||
|
Primitive::Cache { content: content_a },
|
||||||
|
Primitive::Cache { content: content_b },
|
||||||
|
) => {
|
||||||
|
if Arc::ptr_eq(content_a, content_b) {
|
||||||
|
return vec![];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ if a == b => return vec![],
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
let bounds_a = a.bounds();
|
||||||
|
let bounds_b = b.bounds();
|
||||||
|
|
||||||
|
if bounds_a == bounds_b {
|
||||||
|
vec![bounds_a]
|
||||||
|
} else {
|
||||||
|
vec![bounds_a, bounds_b]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn list(previous: &[Primitive], current: &[Primitive]) -> Vec<Rectangle> {
|
||||||
|
let damage = previous
|
||||||
|
.iter()
|
||||||
|
.zip(current)
|
||||||
|
.flat_map(|(a, b)| regions(a, b));
|
||||||
|
|
||||||
|
if previous.len() == current.len() {
|
||||||
|
damage.collect()
|
||||||
|
} else {
|
||||||
|
let (smaller, bigger) = if previous.len() < current.len() {
|
||||||
|
(previous, current)
|
||||||
|
} else {
|
||||||
|
(current, previous)
|
||||||
|
};
|
||||||
|
|
||||||
|
// Extend damage by the added/removed primitives
|
||||||
|
damage
|
||||||
|
.chain(bigger[smaller.len()..].iter().map(Primitive::bounds))
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn group(
|
||||||
|
mut damage: Vec<Rectangle>,
|
||||||
|
scale_factor: f32,
|
||||||
|
bounds: Size<u32>,
|
||||||
|
) -> Vec<Rectangle> {
|
||||||
|
use std::cmp::Ordering;
|
||||||
|
|
||||||
|
const AREA_THRESHOLD: f32 = 20_000.0;
|
||||||
|
|
||||||
|
let bounds = Rectangle {
|
||||||
|
x: 0.0,
|
||||||
|
y: 0.0,
|
||||||
|
width: bounds.width as f32,
|
||||||
|
height: bounds.height as f32,
|
||||||
|
};
|
||||||
|
|
||||||
|
damage.sort_by(|a, b| {
|
||||||
|
a.x.partial_cmp(&b.x)
|
||||||
|
.unwrap_or(Ordering::Equal)
|
||||||
|
.then_with(|| a.y.partial_cmp(&b.y).unwrap_or(Ordering::Equal))
|
||||||
|
});
|
||||||
|
|
||||||
|
let mut output = Vec::new();
|
||||||
|
let mut scaled = damage
|
||||||
|
.into_iter()
|
||||||
|
.filter_map(|region| (region * scale_factor).intersection(&bounds))
|
||||||
|
.filter(|region| region.width >= 1.0 && region.height >= 1.0);
|
||||||
|
|
||||||
|
if let Some(mut current) = scaled.next() {
|
||||||
|
for region in scaled {
|
||||||
|
let union = current.union(®ion);
|
||||||
|
|
||||||
|
if union.area() - current.area() - region.area() <= AREA_THRESHOLD {
|
||||||
|
current = union;
|
||||||
|
} else {
|
||||||
|
output.push(current);
|
||||||
|
current = region;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
output.push(current);
|
||||||
|
}
|
||||||
|
|
||||||
|
output
|
||||||
|
}
|
||||||
|
|
@ -28,6 +28,7 @@ mod viewport;
|
||||||
|
|
||||||
pub mod backend;
|
pub mod backend;
|
||||||
pub mod compositor;
|
pub mod compositor;
|
||||||
|
pub mod damage;
|
||||||
pub mod primitive;
|
pub mod primitive;
|
||||||
pub mod renderer;
|
pub mod renderer;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -208,96 +208,6 @@ impl Primitive {
|
||||||
Self::Cache { content } => content.bounds(),
|
Self::Cache { content } => content.bounds(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn damage(&self, other: &Self) -> Vec<Rectangle> {
|
|
||||||
match (self, other) {
|
|
||||||
(
|
|
||||||
Primitive::Group {
|
|
||||||
primitives: primitives_a,
|
|
||||||
},
|
|
||||||
Primitive::Group {
|
|
||||||
primitives: primitives_b,
|
|
||||||
},
|
|
||||||
) => return Self::damage_list(primitives_a, primitives_b),
|
|
||||||
(
|
|
||||||
Primitive::Clip {
|
|
||||||
bounds: bounds_a,
|
|
||||||
content: content_a,
|
|
||||||
},
|
|
||||||
Primitive::Clip {
|
|
||||||
bounds: bounds_b,
|
|
||||||
content: content_b,
|
|
||||||
},
|
|
||||||
) => {
|
|
||||||
if bounds_a == bounds_b {
|
|
||||||
return content_a
|
|
||||||
.damage(content_b)
|
|
||||||
.into_iter()
|
|
||||||
.filter_map(|r| r.intersection(bounds_a))
|
|
||||||
.collect();
|
|
||||||
} else {
|
|
||||||
return vec![*bounds_a, *bounds_b];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
(
|
|
||||||
Primitive::Translate {
|
|
||||||
translation: translation_a,
|
|
||||||
content: content_a,
|
|
||||||
},
|
|
||||||
Primitive::Translate {
|
|
||||||
translation: translation_b,
|
|
||||||
content: content_b,
|
|
||||||
},
|
|
||||||
) => {
|
|
||||||
if translation_a == translation_b {
|
|
||||||
return content_a
|
|
||||||
.damage(content_b)
|
|
||||||
.into_iter()
|
|
||||||
.map(|r| r + *translation_a)
|
|
||||||
.collect();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
(
|
|
||||||
Primitive::Cache { content: content_a },
|
|
||||||
Primitive::Cache { content: content_b },
|
|
||||||
) => {
|
|
||||||
if Arc::ptr_eq(content_a, content_b) {
|
|
||||||
return vec![];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ if self == other => return vec![],
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
|
|
||||||
let bounds_a = self.bounds();
|
|
||||||
let bounds_b = other.bounds();
|
|
||||||
|
|
||||||
if bounds_a == bounds_b {
|
|
||||||
vec![bounds_a]
|
|
||||||
} else {
|
|
||||||
vec![bounds_a, bounds_b]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn damage_list(previous: &[Self], current: &[Self]) -> Vec<Rectangle> {
|
|
||||||
let damage =
|
|
||||||
previous.iter().zip(current).flat_map(|(a, b)| a.damage(b));
|
|
||||||
|
|
||||||
if previous.len() == current.len() {
|
|
||||||
damage.collect()
|
|
||||||
} else {
|
|
||||||
let (smaller, bigger) = if previous.len() < current.len() {
|
|
||||||
(previous, current)
|
|
||||||
} else {
|
|
||||||
(current, previous)
|
|
||||||
};
|
|
||||||
|
|
||||||
// Extend damage by the added/removed primitives
|
|
||||||
damage
|
|
||||||
.chain(bigger[smaller.len()..].iter().map(Primitive::bounds))
|
|
||||||
.collect()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A set of [`Vertex2D`] and indices representing a list of triangles.
|
/// A set of [`Vertex2D`] and indices representing a list of triangles.
|
||||||
|
|
|
||||||
|
|
@ -16,10 +16,6 @@ pub struct Backend {
|
||||||
|
|
||||||
#[cfg(feature = "svg")]
|
#[cfg(feature = "svg")]
|
||||||
vector_pipeline: crate::vector::Pipeline,
|
vector_pipeline: crate::vector::Pipeline,
|
||||||
|
|
||||||
last_primitives: Vec<Primitive>,
|
|
||||||
last_background_color: Color,
|
|
||||||
last_size: Size<u32>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Backend {
|
impl Backend {
|
||||||
|
|
@ -34,10 +30,6 @@ impl Backend {
|
||||||
|
|
||||||
#[cfg(feature = "svg")]
|
#[cfg(feature = "svg")]
|
||||||
vector_pipeline: crate::vector::Pipeline::new(),
|
vector_pipeline: crate::vector::Pipeline::new(),
|
||||||
|
|
||||||
last_primitives: Vec::new(),
|
|
||||||
last_background_color: Color::BLACK,
|
|
||||||
last_size: Size::new(0, 0),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -47,31 +39,13 @@ impl Backend {
|
||||||
clip_mask: &mut tiny_skia::Mask,
|
clip_mask: &mut tiny_skia::Mask,
|
||||||
primitives: &[Primitive],
|
primitives: &[Primitive],
|
||||||
viewport: &Viewport,
|
viewport: &Viewport,
|
||||||
|
damage: &[Rectangle],
|
||||||
background_color: Color,
|
background_color: Color,
|
||||||
overlay: &[T],
|
overlay: &[T],
|
||||||
) -> bool {
|
) {
|
||||||
let physical_size = viewport.physical_size();
|
let physical_size = viewport.physical_size();
|
||||||
|
|
||||||
let damage = if self.last_background_color == background_color
|
|
||||||
&& self.last_size == physical_size
|
|
||||||
{
|
|
||||||
Primitive::damage_list(&self.last_primitives, primitives)
|
|
||||||
} else {
|
|
||||||
vec![Rectangle::with_size(viewport.logical_size())]
|
|
||||||
};
|
|
||||||
|
|
||||||
if damage.is_empty() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
self.last_primitives = primitives.to_vec();
|
|
||||||
self.last_background_color = background_color;
|
|
||||||
self.last_size = physical_size;
|
|
||||||
|
|
||||||
let scale_factor = viewport.scale_factor() as f32;
|
let scale_factor = viewport.scale_factor() as f32;
|
||||||
|
|
||||||
let damage = group_damage(damage, scale_factor, physical_size);
|
|
||||||
|
|
||||||
if !overlay.is_empty() {
|
if !overlay.is_empty() {
|
||||||
let path = tiny_skia::PathBuilder::from_rect(
|
let path = tiny_skia::PathBuilder::from_rect(
|
||||||
tiny_skia::Rect::from_xywh(
|
tiny_skia::Rect::from_xywh(
|
||||||
|
|
@ -99,7 +73,7 @@ impl Backend {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
for region in damage {
|
for ®ion in damage {
|
||||||
let path = tiny_skia::PathBuilder::from_rect(
|
let path = tiny_skia::PathBuilder::from_rect(
|
||||||
tiny_skia::Rect::from_xywh(
|
tiny_skia::Rect::from_xywh(
|
||||||
region.x,
|
region.x,
|
||||||
|
|
@ -164,8 +138,6 @@ impl Backend {
|
||||||
|
|
||||||
#[cfg(feature = "svg")]
|
#[cfg(feature = "svg")]
|
||||||
self.vector_pipeline.trim_cache();
|
self.vector_pipeline.trim_cache();
|
||||||
|
|
||||||
true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn draw_primitive(
|
fn draw_primitive(
|
||||||
|
|
@ -629,52 +601,6 @@ fn adjust_clip_mask(clip_mask: &mut tiny_skia::Mask, bounds: Rectangle) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn group_damage(
|
|
||||||
mut damage: Vec<Rectangle>,
|
|
||||||
scale_factor: f32,
|
|
||||||
bounds: Size<u32>,
|
|
||||||
) -> Vec<Rectangle> {
|
|
||||||
use std::cmp::Ordering;
|
|
||||||
|
|
||||||
const AREA_THRESHOLD: f32 = 20_000.0;
|
|
||||||
|
|
||||||
let bounds = Rectangle {
|
|
||||||
x: 0.0,
|
|
||||||
y: 0.0,
|
|
||||||
width: bounds.width as f32,
|
|
||||||
height: bounds.height as f32,
|
|
||||||
};
|
|
||||||
|
|
||||||
damage.sort_by(|a, b| {
|
|
||||||
a.x.partial_cmp(&b.x)
|
|
||||||
.unwrap_or(Ordering::Equal)
|
|
||||||
.then_with(|| a.y.partial_cmp(&b.y).unwrap_or(Ordering::Equal))
|
|
||||||
});
|
|
||||||
|
|
||||||
let mut output = Vec::new();
|
|
||||||
let mut scaled = damage
|
|
||||||
.into_iter()
|
|
||||||
.filter_map(|region| (region * scale_factor).intersection(&bounds))
|
|
||||||
.filter(|region| region.width >= 1.0 && region.height >= 1.0);
|
|
||||||
|
|
||||||
if let Some(mut current) = scaled.next() {
|
|
||||||
for region in scaled {
|
|
||||||
let union = current.union(®ion);
|
|
||||||
|
|
||||||
if union.area() - current.area() - region.area() <= AREA_THRESHOLD {
|
|
||||||
current = union;
|
|
||||||
} else {
|
|
||||||
output.push(current);
|
|
||||||
current = region;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
output.push(current);
|
|
||||||
}
|
|
||||||
|
|
||||||
output
|
|
||||||
}
|
|
||||||
|
|
||||||
impl iced_graphics::Backend for Backend {
|
impl iced_graphics::Backend for Backend {
|
||||||
fn trim_measurements(&mut self) {
|
fn trim_measurements(&mut self) {
|
||||||
self.text_pipeline.trim_measurement_cache();
|
self.text_pipeline.trim_measurement_cache();
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
use crate::core::Color;
|
use crate::core::{Color, Rectangle};
|
||||||
use crate::graphics::compositor::{self, Information, SurfaceError};
|
use crate::graphics::compositor::{self, Information, SurfaceError};
|
||||||
|
use crate::graphics::damage;
|
||||||
use crate::graphics::{Error, Primitive, Viewport};
|
use crate::graphics::{Error, Primitive, Viewport};
|
||||||
use crate::{Backend, Renderer, Settings};
|
use crate::{Backend, Renderer, Settings};
|
||||||
|
|
||||||
|
|
@ -14,6 +15,8 @@ pub struct Surface {
|
||||||
window: softbuffer::GraphicsContext,
|
window: softbuffer::GraphicsContext,
|
||||||
buffer: Vec<u32>,
|
buffer: Vec<u32>,
|
||||||
clip_mask: tiny_skia::Mask,
|
clip_mask: tiny_skia::Mask,
|
||||||
|
last_primitives: Vec<Primitive>,
|
||||||
|
last_background_color: Color,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Theme> crate::graphics::Compositor for Compositor<Theme> {
|
impl<Theme> crate::graphics::Compositor for Compositor<Theme> {
|
||||||
|
|
@ -45,6 +48,8 @@ impl<Theme> crate::graphics::Compositor for Compositor<Theme> {
|
||||||
buffer: vec![0; width as usize * height as usize],
|
buffer: vec![0; width as usize * height as usize],
|
||||||
clip_mask: tiny_skia::Mask::new(width, height)
|
clip_mask: tiny_skia::Mask::new(width, height)
|
||||||
.expect("Create clip mask"),
|
.expect("Create clip mask"),
|
||||||
|
last_primitives: Vec::new(),
|
||||||
|
last_background_color: Color::BLACK,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -57,6 +62,8 @@ impl<Theme> crate::graphics::Compositor for Compositor<Theme> {
|
||||||
surface.buffer.resize((width * height) as usize, 0);
|
surface.buffer.resize((width * height) as usize, 0);
|
||||||
surface.clip_mask =
|
surface.clip_mask =
|
||||||
tiny_skia::Mask::new(width, height).expect("Create clip mask");
|
tiny_skia::Mask::new(width, height).expect("Create clip mask");
|
||||||
|
|
||||||
|
surface.last_primitives.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fetch_information(&self) -> Information {
|
fn fetch_information(&self) -> Information {
|
||||||
|
|
@ -105,28 +112,45 @@ pub fn present<T: AsRef<str>>(
|
||||||
overlay: &[T],
|
overlay: &[T],
|
||||||
) -> Result<(), compositor::SurfaceError> {
|
) -> Result<(), compositor::SurfaceError> {
|
||||||
let physical_size = viewport.physical_size();
|
let physical_size = viewport.physical_size();
|
||||||
|
let scale_factor = viewport.scale_factor() as f32;
|
||||||
|
|
||||||
let drawn = backend.draw(
|
let mut pixels = &mut tiny_skia::PixmapMut::from_bytes(
|
||||||
&mut tiny_skia::PixmapMut::from_bytes(
|
bytemuck::cast_slice_mut(&mut surface.buffer),
|
||||||
bytemuck::cast_slice_mut(&mut surface.buffer),
|
physical_size.width,
|
||||||
physical_size.width,
|
physical_size.height,
|
||||||
physical_size.height,
|
)
|
||||||
)
|
.expect("Create pixel map");
|
||||||
.expect("Create pixel map"),
|
|
||||||
|
let damage = if surface.last_background_color == background_color {
|
||||||
|
damage::list(&surface.last_primitives, primitives)
|
||||||
|
} else {
|
||||||
|
vec![Rectangle::with_size(viewport.logical_size())]
|
||||||
|
};
|
||||||
|
|
||||||
|
if damage.is_empty() {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
surface.last_primitives = primitives.to_vec();
|
||||||
|
surface.last_background_color = background_color;
|
||||||
|
|
||||||
|
let damage = damage::group(damage, scale_factor, physical_size);
|
||||||
|
|
||||||
|
backend.draw(
|
||||||
|
&mut pixels,
|
||||||
&mut surface.clip_mask,
|
&mut surface.clip_mask,
|
||||||
primitives,
|
primitives,
|
||||||
viewport,
|
viewport,
|
||||||
|
&damage,
|
||||||
background_color,
|
background_color,
|
||||||
overlay,
|
overlay,
|
||||||
);
|
);
|
||||||
|
|
||||||
if drawn {
|
surface.window.set_buffer(
|
||||||
surface.window.set_buffer(
|
&surface.buffer,
|
||||||
&surface.buffer,
|
physical_size.width as u16,
|
||||||
physical_size.width as u16,
|
physical_size.height as u16,
|
||||||
physical_size.height as u16,
|
);
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue