Skip to content

Commit af0303f

Browse files
committed
Move damage tracking logic to compositor in iced_tiny_skia
1 parent d953d12 commit af0303f

5 files changed

Lines changed: 183 additions & 182 deletions

File tree

graphics/src/damage.rs

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
use crate::core::{Rectangle, Size};
2+
use crate::Primitive;
3+
4+
use std::sync::Arc;
5+
6+
pub fn regions(a: &Primitive, b: &Primitive) -> Vec<Rectangle> {
7+
match (a, b) {
8+
(
9+
Primitive::Group {
10+
primitives: primitives_a,
11+
},
12+
Primitive::Group {
13+
primitives: primitives_b,
14+
},
15+
) => return list(primitives_a, primitives_b),
16+
(
17+
Primitive::Clip {
18+
bounds: bounds_a,
19+
content: content_a,
20+
},
21+
Primitive::Clip {
22+
bounds: bounds_b,
23+
content: content_b,
24+
},
25+
) => {
26+
if bounds_a == bounds_b {
27+
return regions(content_a, content_b)
28+
.into_iter()
29+
.filter_map(|r| r.intersection(bounds_a))
30+
.collect();
31+
} else {
32+
return vec![*bounds_a, *bounds_b];
33+
}
34+
}
35+
(
36+
Primitive::Translate {
37+
translation: translation_a,
38+
content: content_a,
39+
},
40+
Primitive::Translate {
41+
translation: translation_b,
42+
content: content_b,
43+
},
44+
) => {
45+
if translation_a == translation_b {
46+
return regions(content_a, content_b)
47+
.into_iter()
48+
.map(|r| r + *translation_a)
49+
.collect();
50+
}
51+
}
52+
(
53+
Primitive::Cache { content: content_a },
54+
Primitive::Cache { content: content_b },
55+
) => {
56+
if Arc::ptr_eq(content_a, content_b) {
57+
return vec![];
58+
}
59+
}
60+
_ if a == b => return vec![],
61+
_ => {}
62+
}
63+
64+
let bounds_a = a.bounds();
65+
let bounds_b = b.bounds();
66+
67+
if bounds_a == bounds_b {
68+
vec![bounds_a]
69+
} else {
70+
vec![bounds_a, bounds_b]
71+
}
72+
}
73+
74+
pub fn list(previous: &[Primitive], current: &[Primitive]) -> Vec<Rectangle> {
75+
let damage = previous
76+
.iter()
77+
.zip(current)
78+
.flat_map(|(a, b)| regions(a, b));
79+
80+
if previous.len() == current.len() {
81+
damage.collect()
82+
} else {
83+
let (smaller, bigger) = if previous.len() < current.len() {
84+
(previous, current)
85+
} else {
86+
(current, previous)
87+
};
88+
89+
// Extend damage by the added/removed primitives
90+
damage
91+
.chain(bigger[smaller.len()..].iter().map(Primitive::bounds))
92+
.collect()
93+
}
94+
}
95+
96+
pub fn group(
97+
mut damage: Vec<Rectangle>,
98+
scale_factor: f32,
99+
bounds: Size<u32>,
100+
) -> Vec<Rectangle> {
101+
use std::cmp::Ordering;
102+
103+
const AREA_THRESHOLD: f32 = 20_000.0;
104+
105+
let bounds = Rectangle {
106+
x: 0.0,
107+
y: 0.0,
108+
width: bounds.width as f32,
109+
height: bounds.height as f32,
110+
};
111+
112+
damage.sort_by(|a, b| {
113+
a.x.partial_cmp(&b.x)
114+
.unwrap_or(Ordering::Equal)
115+
.then_with(|| a.y.partial_cmp(&b.y).unwrap_or(Ordering::Equal))
116+
});
117+
118+
let mut output = Vec::new();
119+
let mut scaled = damage
120+
.into_iter()
121+
.filter_map(|region| (region * scale_factor).intersection(&bounds))
122+
.filter(|region| region.width >= 1.0 && region.height >= 1.0);
123+
124+
if let Some(mut current) = scaled.next() {
125+
for region in scaled {
126+
let union = current.union(&region);
127+
128+
if union.area() - current.area() - region.area() <= AREA_THRESHOLD {
129+
current = union;
130+
} else {
131+
output.push(current);
132+
current = region;
133+
}
134+
}
135+
136+
output.push(current);
137+
}
138+
139+
output
140+
}

graphics/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ mod viewport;
2828

2929
pub mod backend;
3030
pub mod compositor;
31+
pub mod damage;
3132
pub mod primitive;
3233
pub mod renderer;
3334

graphics/src/primitive.rs

Lines changed: 0 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -208,96 +208,6 @@ impl Primitive {
208208
Self::Cache { content } => content.bounds(),
209209
}
210210
}
211-
212-
pub fn damage(&self, other: &Self) -> Vec<Rectangle> {
213-
match (self, other) {
214-
(
215-
Primitive::Group {
216-
primitives: primitives_a,
217-
},
218-
Primitive::Group {
219-
primitives: primitives_b,
220-
},
221-
) => return Self::damage_list(primitives_a, primitives_b),
222-
(
223-
Primitive::Clip {
224-
bounds: bounds_a,
225-
content: content_a,
226-
},
227-
Primitive::Clip {
228-
bounds: bounds_b,
229-
content: content_b,
230-
},
231-
) => {
232-
if bounds_a == bounds_b {
233-
return content_a
234-
.damage(content_b)
235-
.into_iter()
236-
.filter_map(|r| r.intersection(bounds_a))
237-
.collect();
238-
} else {
239-
return vec![*bounds_a, *bounds_b];
240-
}
241-
}
242-
(
243-
Primitive::Translate {
244-
translation: translation_a,
245-
content: content_a,
246-
},
247-
Primitive::Translate {
248-
translation: translation_b,
249-
content: content_b,
250-
},
251-
) => {
252-
if translation_a == translation_b {
253-
return content_a
254-
.damage(content_b)
255-
.into_iter()
256-
.map(|r| r + *translation_a)
257-
.collect();
258-
}
259-
}
260-
(
261-
Primitive::Cache { content: content_a },
262-
Primitive::Cache { content: content_b },
263-
) => {
264-
if Arc::ptr_eq(content_a, content_b) {
265-
return vec![];
266-
}
267-
}
268-
_ if self == other => return vec![],
269-
_ => {}
270-
}
271-
272-
let bounds_a = self.bounds();
273-
let bounds_b = other.bounds();
274-
275-
if bounds_a == bounds_b {
276-
vec![bounds_a]
277-
} else {
278-
vec![bounds_a, bounds_b]
279-
}
280-
}
281-
282-
pub fn damage_list(previous: &[Self], current: &[Self]) -> Vec<Rectangle> {
283-
let damage =
284-
previous.iter().zip(current).flat_map(|(a, b)| a.damage(b));
285-
286-
if previous.len() == current.len() {
287-
damage.collect()
288-
} else {
289-
let (smaller, bigger) = if previous.len() < current.len() {
290-
(previous, current)
291-
} else {
292-
(current, previous)
293-
};
294-
295-
// Extend damage by the added/removed primitives
296-
damage
297-
.chain(bigger[smaller.len()..].iter().map(Primitive::bounds))
298-
.collect()
299-
}
300-
}
301211
}
302212

303213
/// A set of [`Vertex2D`] and indices representing a list of triangles.

tiny_skia/src/backend.rs

Lines changed: 3 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,6 @@ pub struct Backend {
1616

1717
#[cfg(feature = "svg")]
1818
vector_pipeline: crate::vector::Pipeline,
19-
20-
last_primitives: Vec<Primitive>,
21-
last_background_color: Color,
22-
last_size: Size<u32>,
2319
}
2420

2521
impl Backend {
@@ -34,10 +30,6 @@ impl Backend {
3430

3531
#[cfg(feature = "svg")]
3632
vector_pipeline: crate::vector::Pipeline::new(),
37-
38-
last_primitives: Vec::new(),
39-
last_background_color: Color::BLACK,
40-
last_size: Size::new(0, 0),
4133
}
4234
}
4335

@@ -47,31 +39,13 @@ impl Backend {
4739
clip_mask: &mut tiny_skia::Mask,
4840
primitives: &[Primitive],
4941
viewport: &Viewport,
42+
damage: &[Rectangle],
5043
background_color: Color,
5144
overlay: &[T],
52-
) -> bool {
45+
) {
5346
let physical_size = viewport.physical_size();
54-
55-
let damage = if self.last_background_color == background_color
56-
&& self.last_size == physical_size
57-
{
58-
Primitive::damage_list(&self.last_primitives, primitives)
59-
} else {
60-
vec![Rectangle::with_size(viewport.logical_size())]
61-
};
62-
63-
if damage.is_empty() {
64-
return false;
65-
}
66-
67-
self.last_primitives = primitives.to_vec();
68-
self.last_background_color = background_color;
69-
self.last_size = physical_size;
70-
7147
let scale_factor = viewport.scale_factor() as f32;
7248

73-
let damage = group_damage(damage, scale_factor, physical_size);
74-
7549
if !overlay.is_empty() {
7650
let path = tiny_skia::PathBuilder::from_rect(
7751
tiny_skia::Rect::from_xywh(
@@ -99,7 +73,7 @@ impl Backend {
9973
);
10074
}
10175

102-
for region in damage {
76+
for &region in damage {
10377
let path = tiny_skia::PathBuilder::from_rect(
10478
tiny_skia::Rect::from_xywh(
10579
region.x,
@@ -164,8 +138,6 @@ impl Backend {
164138

165139
#[cfg(feature = "svg")]
166140
self.vector_pipeline.trim_cache();
167-
168-
true
169141
}
170142

171143
fn draw_primitive(
@@ -629,52 +601,6 @@ fn adjust_clip_mask(clip_mask: &mut tiny_skia::Mask, bounds: Rectangle) {
629601
);
630602
}
631603

632-
fn group_damage(
633-
mut damage: Vec<Rectangle>,
634-
scale_factor: f32,
635-
bounds: Size<u32>,
636-
) -> Vec<Rectangle> {
637-
use std::cmp::Ordering;
638-
639-
const AREA_THRESHOLD: f32 = 20_000.0;
640-
641-
let bounds = Rectangle {
642-
x: 0.0,
643-
y: 0.0,
644-
width: bounds.width as f32,
645-
height: bounds.height as f32,
646-
};
647-
648-
damage.sort_by(|a, b| {
649-
a.x.partial_cmp(&b.x)
650-
.unwrap_or(Ordering::Equal)
651-
.then_with(|| a.y.partial_cmp(&b.y).unwrap_or(Ordering::Equal))
652-
});
653-
654-
let mut output = Vec::new();
655-
let mut scaled = damage
656-
.into_iter()
657-
.filter_map(|region| (region * scale_factor).intersection(&bounds))
658-
.filter(|region| region.width >= 1.0 && region.height >= 1.0);
659-
660-
if let Some(mut current) = scaled.next() {
661-
for region in scaled {
662-
let union = current.union(&region);
663-
664-
if union.area() - current.area() - region.area() <= AREA_THRESHOLD {
665-
current = union;
666-
} else {
667-
output.push(current);
668-
current = region;
669-
}
670-
}
671-
672-
output.push(current);
673-
}
674-
675-
output
676-
}
677-
678604
impl iced_graphics::Backend for Backend {
679605
fn trim_measurements(&mut self) {
680606
self.text_pipeline.trim_measurement_cache();

0 commit comments

Comments
 (0)