Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Incremental rendering #1811

Merged
merged 30 commits into from
Apr 27, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
0f7abff
Draft (very) basic incremental rendering for `iced_tiny_skia`
hecrj Mar 21, 2023
6270c33
Keep playing with incremental rendering (still very slow)
hecrj Apr 4, 2023
f8cd1fa
Group damage regions by area increase
hecrj Apr 5, 2023
4cae262
Implement `PartialEq` and `Eq` for `image::Bytes`
hecrj Apr 5, 2023
1bba9a0
Fix `Svg` and `Image` primitives in `iced_tiny_skia`
hecrj Apr 5, 2023
4ede482
Present new frame only when damaged in `iced_tiny_skia`
hecrj Apr 5, 2023
e134a82
Switch debug mode to fade old primitives and display damage in `iced_…
hecrj Apr 5, 2023
92d61e5
Use `softbuffer` fork with owned pixel buffer
hecrj Apr 5, 2023
940a47e
Revert "Use `softbuffer` fork with owned pixel buffer"
hecrj Apr 5, 2023
3ee3673
Merge branch 'advanced-text' into incremental-rendering
hecrj Apr 8, 2023
16e6efe
Use `pixels` for presentation in `iced_tiny_skia` when possible
hecrj Apr 8, 2023
1872f7f
Use `*_from_env` helpers from `wgpu` in `iced_wgpu`
hecrj Apr 8, 2023
42b2e9b
Support `ICED_BACKEND` environment variable
hecrj Apr 8, 2023
c2249d2
Fix copy-pasted panic description in `iced_renderer`
hecrj Apr 8, 2023
ba07abe
Expand bounds of `Text` primitives a bit further
hecrj Apr 8, 2023
d206b82
Update `glyphon`
hecrj Apr 8, 2023
619ba92
Merge branch 'advanced-text' into incremental-rendering
hecrj Apr 17, 2023
435b54e
Revert "Use `pixels` for presentation in `iced_tiny_skia` when possible"
hecrj Apr 26, 2023
d6345ff
Remove `dbg!` statement leftover
hecrj Apr 26, 2023
f0fa5f7
Remove `debug` overlay from `iced_tiny_skia` for now
hecrj Apr 26, 2023
e63cc18
Fix `Candidate::build` in `compositor` of `iced_renderer`
hecrj Apr 26, 2023
9c63eb7
Update `tiny-skia` and `resvg`
hecrj Apr 26, 2023
d953d12
Fix incorrect `wgpu` version for Wasm builds in `iced_wgpu`
hecrj Apr 26, 2023
af0303f
Move damage tracking logic to `compositor` in `iced_tiny_skia`
hecrj Apr 27, 2023
92d808e
Fix double reference in `compositor` in `iced_tiny_skia`
hecrj Apr 27, 2023
88d3247
Fix build of `integration` example for Wasm target
hecrj Apr 27, 2023
200a29c
Fix unused import in `triangle` pipeline for Wasm target in `iced_wgpu`
hecrj Apr 27, 2023
38f82ab
Expand damage regions of `Clip` primitives a bit
hecrj Apr 27, 2023
eb1b2bf
Invalidate `last_primitives` on resize in `iced_tiny_skia`
hecrj Apr 27, 2023
a755472
Remove unnecessary `last_` prefix in `Surface` of `iced_tiny_skia`
hecrj Apr 27, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions core/src/image.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use std::path::PathBuf;
use std::sync::Arc;

/// A handle of some image data.
#[derive(Debug, Clone)]
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Handle {
id: u64,
data: Data,
Expand Down Expand Up @@ -110,6 +110,14 @@ impl std::hash::Hash for Bytes {
}
}

impl PartialEq for Bytes {
fn eq(&self, other: &Self) -> bool {
self.as_ref() == other.as_ref()
}
}

impl Eq for Bytes {}

impl AsRef<[u8]> for Bytes {
fn as_ref(&self) -> &[u8] {
self.0.as_ref().as_ref()
Expand All @@ -125,7 +133,7 @@ impl std::ops::Deref for Bytes {
}

/// The data of a raster image.
#[derive(Clone, Hash)]
#[derive(Clone, PartialEq, Eq, Hash)]
pub enum Data {
/// File data
Path(PathBuf),
Expand Down
48 changes: 48 additions & 0 deletions core/src/rectangle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,11 @@ impl Rectangle<f32> {
Size::new(self.width, self.height)
}

/// Returns the area of the [`Rectangle`].
pub fn area(&self) -> f32 {
self.width * self.height
}

/// Returns true if the given [`Point`] is contained in the [`Rectangle`].
pub fn contains(&self, point: Point) -> bool {
self.x <= point.x
Expand All @@ -74,6 +79,15 @@ impl Rectangle<f32> {
&& point.y <= self.y + self.height
}

/// Returns true if the current [`Rectangle`] is completely within the given
/// `container`.
pub fn is_within(&self, container: &Rectangle) -> bool {
container.contains(self.position())
&& container.contains(
self.position() + Vector::new(self.width, self.height),
)
}

/// Computes the intersection with the given [`Rectangle`].
pub fn intersection(
&self,
Expand All @@ -100,6 +114,30 @@ impl Rectangle<f32> {
}
}

/// Returns whether the [`Rectangle`] intersects with the given one.
pub fn intersects(&self, other: &Self) -> bool {
self.intersection(other).is_some()
}

/// Computes the union with the given [`Rectangle`].
pub fn union(&self, other: &Self) -> Self {
let x = self.x.min(other.x);
let y = self.y.min(other.y);

let lower_right_x = (self.x + self.width).max(other.x + other.width);
let lower_right_y = (self.y + self.height).max(other.y + other.height);

let width = lower_right_x - x;
let height = lower_right_y - y;

Rectangle {
x,
y,
width,
height,
}
}

/// Snaps the [`Rectangle`] to __unsigned__ integer coordinates.
pub fn snap(self) -> Rectangle<u32> {
Rectangle {
Expand All @@ -109,6 +147,16 @@ impl Rectangle<f32> {
height: self.height as u32,
}
}

/// Expands the [`Rectangle`] a given amount.
pub fn expand(self, amount: f32) -> Self {
Self {
x: self.x - amount,
y: self.y - amount,
width: self.width + amount * 2.0,
height: self.height + amount * 2.0,
}
}
}

impl std::ops::Mul<f32> for Rectangle<f32> {
Expand Down
4 changes: 2 additions & 2 deletions core/src/svg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use std::path::PathBuf;
use std::sync::Arc;

/// A handle of Svg data.
#[derive(Debug, Clone)]
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Handle {
id: u64,
data: Arc<Data>,
Expand Down Expand Up @@ -57,7 +57,7 @@ impl Hash for Handle {
}

/// The data of a vectorial image.
#[derive(Clone, Hash)]
#[derive(Clone, Hash, PartialEq, Eq)]
pub enum Data {
/// File data
Path(PathBuf),
Expand Down
3 changes: 2 additions & 1 deletion examples/integration/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@ pub fn main() -> Result<(), Box<dyn std::error::Error>> {
web_sys::window()
.and_then(|win| win.document())
.and_then(|doc| doc.get_element_by_id("iced_canvas"))
.and_then(|element| element.dyn_into::<HtmlCanvasElement>().ok())?
.and_then(|element| element.dyn_into::<HtmlCanvasElement>().ok())
.expect("Get canvas element")
};
#[cfg(not(target_arch = "wasm32"))]
env_logger::init();
Expand Down
2 changes: 1 addition & 1 deletion graphics/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ version = "0.9"
path = "../core"

[dependencies.tiny-skia]
version = "0.8"
version = "0.9"
optional = true

[dependencies.image]
Expand Down
142 changes: 142 additions & 0 deletions graphics/src/damage.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
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.expand(1.0)))
.collect();
} else {
return vec![bounds_a.expand(1.0), bounds_b.expand(1.0)];
}
}
(
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(&region);

if union.area() - current.area() - region.area() <= AREA_THRESHOLD {
current = union;
} else {
output.push(current);
current = region;
}
}

output.push(current);
}

output
}
1 change: 1 addition & 0 deletions graphics/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ mod viewport;

pub mod backend;
pub mod compositor;
pub mod damage;
pub mod primitive;
pub mod renderer;

Expand Down
69 changes: 65 additions & 4 deletions graphics/src/primitive.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use bytemuck::{Pod, Zeroable};
use std::sync::Arc;

/// A rendering primitive.
#[derive(Debug, Clone)]
#[derive(Debug, Clone, PartialEq)]
#[non_exhaustive]
pub enum Primitive {
/// A text primitive
Expand Down Expand Up @@ -147,10 +147,71 @@ impl Primitive {
content: Box::new(self),
}
}

pub fn bounds(&self) -> Rectangle {
match self {
Self::Text {
bounds,
horizontal_alignment,
vertical_alignment,
..
} => {
let mut bounds = *bounds;

bounds.x = match horizontal_alignment {
alignment::Horizontal::Left => bounds.x,
alignment::Horizontal::Center => {
bounds.x - bounds.width / 2.0
}
alignment::Horizontal::Right => bounds.x - bounds.width,
};

bounds.y = match vertical_alignment {
alignment::Vertical::Top => bounds.y,
alignment::Vertical::Center => {
bounds.y - bounds.height / 2.0
}
alignment::Vertical::Bottom => bounds.y - bounds.height,
};

bounds.expand(1.5)
}
Self::Quad { bounds, .. }
| Self::Image { bounds, .. }
| Self::Svg { bounds, .. } => bounds.expand(1.0),
Self::Clip { bounds, .. } => bounds.expand(1.0),
Self::SolidMesh { size, .. } | Self::GradientMesh { size, .. } => {
Rectangle::with_size(*size)
}
#[cfg(feature = "tiny-skia")]
Self::Fill { path, .. } | Self::Stroke { path, .. } => {
let bounds = path.bounds();

Rectangle {
x: bounds.x(),
y: bounds.y(),
width: bounds.width(),
height: bounds.height(),
}
.expand(1.0)
}
Self::Group { primitives } => primitives
.iter()
.map(Self::bounds)
.fold(Rectangle::with_size(Size::ZERO), |a, b| {
Rectangle::union(&a, &b)
}),
Self::Translate {
translation,
content,
} => content.bounds() + *translation,
Self::Cache { content } => content.bounds(),
}
}
}

/// A set of [`Vertex2D`] and indices representing a list of triangles.
#[derive(Clone, Debug)]
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Mesh2D<T> {
/// The vertices of the mesh
pub vertices: Vec<T>,
Expand All @@ -162,15 +223,15 @@ pub struct Mesh2D<T> {
}

/// A two-dimensional vertex.
#[derive(Copy, Clone, Debug, Zeroable, Pod)]
#[derive(Copy, Clone, Debug, PartialEq, Zeroable, Pod)]
#[repr(C)]
pub struct Vertex2D {
/// The vertex position in 2D space.
pub position: [f32; 2],
}

/// A two-dimensional vertex with a color.
#[derive(Copy, Clone, Debug, Zeroable, Pod)]
#[derive(Copy, Clone, Debug, PartialEq, Zeroable, Pod)]
#[repr(C)]
pub struct ColoredVertex2D {
/// The vertex position in 2D space.
Expand Down
Loading