Introduce draw_mesh_cache in mesh::Renderer

This commit is contained in:
Héctor Ramón Jiménez 2025-11-29 17:33:01 +01:00
parent 42bc4af972
commit afb3b8fce6
No known key found for this signature in database
GPG key ID: 7CC46565708259A7
8 changed files with 119 additions and 69 deletions

View file

@ -100,7 +100,7 @@ mod rainbow {
let mesh = Mesh::Solid { let mesh = Mesh::Solid {
buffers: mesh::Indexed { buffers: mesh::Indexed {
vertices: [ vertices: vec![
SolidVertex2D { SolidVertex2D {
position: posn_center, position: posn_center,
color: color::pack([1.0, 1.0, 1.0, 1.0]), color: color::pack([1.0, 1.0, 1.0, 1.0]),
@ -137,9 +137,8 @@ mod rainbow {
position: posn_l, position: posn_l,
color: color::pack(color_v), color: color::pack(color_v),
}, },
] ],
.into(), indices: vec![
indices: [
0, 1, 2, // TL 0, 1, 2, // TL
0, 2, 3, // T 0, 2, 3, // T
0, 3, 4, // TR 0, 3, 4, // TR
@ -148,8 +147,7 @@ mod rainbow {
0, 6, 7, // B 0, 6, 7, // B
0, 7, 8, // BL 0, 7, 8, // BL
0, 8, 1, // L 0, 8, 1, // L
] ],
.into(),
}, },
transformation: Transformation::IDENTITY, transformation: Transformation::IDENTITY,
clip_bounds: Rectangle::INFINITE, clip_bounds: Rectangle::INFINITE,

View file

@ -5,7 +5,8 @@ use crate::gradient;
use bytemuck::{Pod, Zeroable}; use bytemuck::{Pod, Zeroable};
use std::sync::Arc; use std::sync::atomic::{self, AtomicU64};
use std::sync::{Arc, Weak};
/// A low-level primitive to render a mesh of triangles. /// A low-level primitive to render a mesh of triangles.
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
@ -72,12 +73,12 @@ impl Mesh {
#[derive(Clone, Debug, PartialEq, Eq)] #[derive(Clone, Debug, PartialEq, Eq)]
pub struct Indexed<T> { pub struct Indexed<T> {
/// The vertices of the mesh /// The vertices of the mesh
pub vertices: Arc<[T]>, pub vertices: Vec<T>,
/// The list of vertex indices that defines the triangles of the mesh. /// The list of vertex indices that defines the triangles of the mesh.
/// ///
/// Therefore, this list should always have a length that is a multiple of 3. /// Therefore, this list should always have a length that is a multiple of 3.
pub indices: Arc<[u32]>, pub indices: Vec<u32>,
} }
/// A two-dimensional vertex with a color. /// A two-dimensional vertex with a color.
@ -143,8 +144,67 @@ pub fn attribute_count_of(meshes: &[Mesh]) -> AttributeCount {
}) })
} }
/// A cache of multiple meshes.
#[derive(Debug, Clone)]
pub struct Cache {
id: Id,
batch: Arc<[Mesh]>,
version: usize,
}
/// The unique id of a [`Cache`].
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Id(u64);
impl Cache {
/// Creates a new [`Cache`] for the given meshes.
pub fn new(meshes: Arc<[Mesh]>) -> Self {
static NEXT_ID: AtomicU64 = AtomicU64::new(0);
Self {
id: Id(NEXT_ID.fetch_add(1, atomic::Ordering::Relaxed)),
batch: meshes,
version: 0,
}
}
/// Returns the [`Id`] of the [`Cache`].
pub fn id(&self) -> Id {
self.id
}
/// Returns the current version of the [`Cache`].
pub fn version(&self) -> usize {
self.version
}
/// Returns the batch of meshes in the [`Cache`].
pub fn batch(&self) -> &[Mesh] {
&self.batch
}
/// Returns a [`Weak`] reference to the contents of the [`Cache`].
pub fn downgrade(&self) -> Weak<[Mesh]> {
Arc::downgrade(&self.batch)
}
/// Returns true if the [`Cache`] is empty.
pub fn is_empty(&self) -> bool {
self.batch.is_empty()
}
/// Updates the [`Cache`] with the given meshes.
pub fn update(&mut self, meshes: Arc<[Mesh]>) {
self.batch = meshes;
self.version += 1;
}
}
/// A renderer capable of drawing a [`Mesh`]. /// A renderer capable of drawing a [`Mesh`].
pub trait Renderer { pub trait Renderer {
/// Draws the given [`Mesh`]. /// Draws the given [`Mesh`].
fn draw_mesh(&mut self, mesh: Mesh); fn draw_mesh(&mut self, mesh: Mesh);
/// Draws the given [`Cache`].
fn draw_mesh_cache(&mut self, cache: Cache);
} }

View file

@ -224,6 +224,10 @@ where
fn draw_mesh(&mut self, mesh: graphics::Mesh) { fn draw_mesh(&mut self, mesh: graphics::Mesh) {
delegate!(self, renderer, renderer.draw_mesh(mesh)); delegate!(self, renderer, renderer.draw_mesh(mesh));
} }
fn draw_mesh_cache(&mut self, cache: mesh::Cache) {
delegate!(self, renderer, renderer.draw_mesh_cache(cache));
}
} }
/// A compositor `A` with a fallback strategy `B`. /// A compositor `A` with a fallback strategy `B`.

View file

@ -373,6 +373,10 @@ impl graphics::mesh::Renderer for Renderer {
fn draw_mesh(&mut self, _mesh: graphics::Mesh) { fn draw_mesh(&mut self, _mesh: graphics::Mesh) {
log::warn!("iced_tiny_skia does not support drawing meshes"); log::warn!("iced_tiny_skia does not support drawing meshes");
} }
fn draw_mesh_cache(&mut self, _cache: iced_graphics::mesh::Cache) {
log::warn!("iced_tiny_skia does not support drawing meshes");
}
} }
#[cfg(feature = "image")] #[cfg(feature = "image")]

View file

@ -13,7 +13,6 @@ use crate::graphics::gradient::{self, Gradient};
use crate::graphics::mesh::{self, Mesh}; use crate::graphics::mesh::{self, Mesh};
use crate::graphics::{Image, Text}; use crate::graphics::{Image, Text};
use crate::text; use crate::text;
use crate::triangle;
use lyon::geom::euclid; use lyon::geom::euclid;
use lyon::tessellation; use lyon::tessellation;
@ -33,7 +32,7 @@ pub enum Geometry {
#[derive(Debug, Clone, Default)] #[derive(Debug, Clone, Default)]
pub struct Cache { pub struct Cache {
pub meshes: Option<triangle::Cache>, pub meshes: Option<mesh::Cache>,
pub images: Option<Arc<[Image]>>, pub images: Option<Arc<[Image]>>,
pub text: Option<text::Cache>, pub text: Option<text::Cache>,
} }
@ -62,11 +61,17 @@ impl Cached for Geometry {
Some(Arc::from(images)) Some(Arc::from(images))
}; };
let meshes = Arc::from(meshes);
if let Some(mut previous) = previous { if let Some(mut previous) = previous {
if let Some(cache) = &mut previous.meshes { if let Some(cache) = &mut previous.meshes {
cache.update(meshes); cache.update(meshes);
} else { } else {
previous.meshes = triangle::Cache::new(meshes); previous.meshes = if meshes.is_empty() {
None
} else {
Some(mesh::Cache::new(meshes))
};
} }
if let Some(cache) = &mut previous.text { if let Some(cache) = &mut previous.text {
@ -80,7 +85,11 @@ impl Cached for Geometry {
previous previous
} else { } else {
Cache { Cache {
meshes: triangle::Cache::new(meshes), meshes: if meshes.is_empty() {
None
} else {
Some(mesh::Cache::new(meshes))
},
images, images,
text: text::Cache::new(group, text), text: text::Cache::new(group, text),
} }
@ -547,8 +556,8 @@ impl BufferStack {
Buffer::Solid(buffer) if !buffer.indices.is_empty() => { Buffer::Solid(buffer) if !buffer.indices.is_empty() => {
Some(Mesh::Solid { Some(Mesh::Solid {
buffers: mesh::Indexed { buffers: mesh::Indexed {
vertices: buffer.vertices.into(), vertices: buffer.vertices,
indices: buffer.indices.into(), indices: buffer.indices,
}, },
clip_bounds, clip_bounds,
transformation: Transformation::IDENTITY, transformation: Transformation::IDENTITY,
@ -557,8 +566,8 @@ impl BufferStack {
Buffer::Gradient(buffer) if !buffer.indices.is_empty() => { Buffer::Gradient(buffer) if !buffer.indices.is_empty() => {
Some(Mesh::Gradient { Some(Mesh::Gradient {
buffers: mesh::Indexed { buffers: mesh::Indexed {
vertices: buffer.vertices.into(), vertices: buffer.vertices,
indices: buffer.indices.into(), indices: buffer.indices,
}, },
clip_bounds, clip_bounds,
transformation: Transformation::IDENTITY, transformation: Transformation::IDENTITY,

View file

@ -5,6 +5,7 @@ use crate::graphics;
use crate::graphics::Mesh; use crate::graphics::Mesh;
use crate::graphics::color; use crate::graphics::color;
use crate::graphics::layer; use crate::graphics::layer;
use crate::graphics::mesh;
use crate::graphics::text::{Editor, Paragraph}; use crate::graphics::text::{Editor, Paragraph};
use crate::image::{self, Image}; use crate::image::{self, Image};
use crate::primitive::{self, Primitive}; use crate::primitive::{self, Primitive};
@ -230,7 +231,7 @@ impl Layer {
pub fn draw_mesh_cache( pub fn draw_mesh_cache(
&mut self, &mut self,
cache: triangle::Cache, cache: mesh::Cache,
transformation: Transformation, transformation: Transformation,
) { ) {
self.flush_meshes(); self.flush_meshes();

View file

@ -65,6 +65,7 @@ use crate::core::renderer;
use crate::core::{ use crate::core::{
Background, Color, Font, Pixels, Point, Rectangle, Size, Transformation, Background, Color, Font, Pixels, Point, Rectangle, Size, Transformation,
}; };
use crate::graphics::mesh;
use crate::graphics::text::{Editor, Paragraph}; use crate::graphics::text::{Editor, Paragraph};
use crate::graphics::{Shell, Viewport}; use crate::graphics::{Shell, Viewport};
@ -845,6 +846,11 @@ impl graphics::mesh::Renderer for Renderer {
let (layer, transformation) = self.layers.current_mut(); let (layer, transformation) = self.layers.current_mut();
layer.draw_mesh(mesh, transformation); layer.draw_mesh(mesh, transformation);
} }
fn draw_mesh_cache(&mut self, cache: mesh::Cache) {
let (layer, transformation) = self.layers.current_mut();
layer.draw_mesh_cache(cache, transformation);
}
} }
#[cfg(feature = "geometry")] #[cfg(feature = "geometry")]

View file

@ -8,8 +8,7 @@ use crate::graphics::mesh::{self, Mesh};
use rustc_hash::FxHashMap; use rustc_hash::FxHashMap;
use std::collections::hash_map; use std::collections::hash_map;
use std::sync::atomic::{self, AtomicU64}; use std::sync::Weak;
use std::sync::{self, Arc};
const INITIAL_INDEX_COUNT: usize = 1_000; const INITIAL_INDEX_COUNT: usize = 1_000;
const INITIAL_VERTEX_COUNT: usize = 1_000; const INITIAL_VERTEX_COUNT: usize = 1_000;
@ -24,52 +23,21 @@ pub enum Item {
}, },
Cached { Cached {
transformation: Transformation, transformation: Transformation,
cache: Cache, cache: mesh::Cache,
}, },
} }
#[derive(Debug, Clone)]
pub struct Cache {
id: Id,
batch: Arc<[Mesh]>,
version: usize,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Id(u64);
impl Cache {
pub fn new(meshes: Vec<Mesh>) -> Option<Self> {
static NEXT_ID: AtomicU64 = AtomicU64::new(0);
if meshes.is_empty() {
return None;
}
Some(Self {
id: Id(NEXT_ID.fetch_add(1, atomic::Ordering::Relaxed)),
batch: Arc::from(meshes),
version: 0,
})
}
pub fn update(&mut self, meshes: Vec<Mesh>) {
self.batch = Arc::from(meshes);
self.version += 1;
}
}
#[derive(Debug)] #[derive(Debug)]
struct Upload { struct Upload {
layer: Layer, layer: Layer,
transformation: Transformation, transformation: Transformation,
version: usize, version: usize,
batch: sync::Weak<[Mesh]>, batch: Weak<[Mesh]>,
} }
#[derive(Debug, Default)] #[derive(Debug, Default)]
pub struct Storage { pub struct Storage {
uploads: FxHashMap<Id, Upload>, uploads: FxHashMap<mesh::Id, Upload>,
} }
impl Storage { impl Storage {
@ -77,12 +45,12 @@ impl Storage {
Self::default() Self::default()
} }
fn get(&self, cache: &Cache) -> Option<&Upload> { fn get(&self, cache: &mesh::Cache) -> Option<&Upload> {
if cache.batch.is_empty() { if cache.is_empty() {
return None; return None;
} }
self.uploads.get(&cache.id) self.uploads.get(&cache.id())
} }
fn prepare( fn prepare(
@ -92,15 +60,15 @@ impl Storage {
belt: &mut wgpu::util::StagingBelt, belt: &mut wgpu::util::StagingBelt,
solid: &solid::Pipeline, solid: &solid::Pipeline,
gradient: &gradient::Pipeline, gradient: &gradient::Pipeline,
cache: &Cache, cache: &mesh::Cache,
new_transformation: Transformation, new_transformation: Transformation,
) { ) {
match self.uploads.entry(cache.id) { match self.uploads.entry(cache.id()) {
hash_map::Entry::Occupied(entry) => { hash_map::Entry::Occupied(entry) => {
let upload = entry.into_mut(); let upload = entry.into_mut();
if !cache.batch.is_empty() if !cache.is_empty()
&& (upload.version != cache.version && (upload.version != cache.version()
|| upload.transformation != new_transformation) || upload.transformation != new_transformation)
{ {
upload.layer.prepare( upload.layer.prepare(
@ -109,12 +77,12 @@ impl Storage {
belt, belt,
solid, solid,
gradient, gradient,
&cache.batch, cache.batch(),
new_transformation, new_transformation,
); );
upload.batch = Arc::downgrade(&cache.batch); upload.batch = cache.downgrade();
upload.version = cache.version; upload.version = cache.version();
upload.transformation = new_transformation; upload.transformation = new_transformation;
} }
} }
@ -127,7 +95,7 @@ impl Storage {
belt, belt,
solid, solid,
gradient, gradient,
&cache.batch, cache.batch(),
new_transformation, new_transformation,
); );
@ -135,12 +103,12 @@ impl Storage {
layer, layer,
transformation: new_transformation, transformation: new_transformation,
version: 0, version: 0,
batch: Arc::downgrade(&cache.batch), batch: cache.downgrade(),
}); });
log::debug!( log::debug!(
"New mesh upload: {} (total: {})", "New mesh upload: {:?} (total: {})",
cache.id.0, cache.id(),
self.uploads.len() self.uploads.len()
); );
} }
@ -278,7 +246,7 @@ impl State {
Some(( Some((
&upload.layer, &upload.layer,
&cache.batch, cache.batch(),
screen_transformation * *transformation, screen_transformation * *transformation,
)) ))
} }