From 2bb53ad6e7ea2689f2f56662e5840a8d363b3108 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Fri, 29 Mar 2024 04:02:24 +0100 Subject: Use a `StagingBelt` in `iced_wgpu` for regular buffer uploads --- wgpu/src/triangle.rs | 35 ++++++++++++++++++++++++++--------- 1 file changed, 26 insertions(+), 9 deletions(-) (limited to 'wgpu/src/triangle.rs') diff --git a/wgpu/src/triangle.rs b/wgpu/src/triangle.rs index 2bb6f307..b6be54d4 100644 --- a/wgpu/src/triangle.rs +++ b/wgpu/src/triangle.rs @@ -48,7 +48,8 @@ impl Layer { fn prepare( &mut self, device: &wgpu::Device, - queue: &wgpu::Queue, + encoder: &mut wgpu::CommandEncoder, + belt: &mut wgpu::util::StagingBelt, solid: &solid::Pipeline, gradient: &gradient::Pipeline, meshes: &[Mesh<'_>], @@ -103,33 +104,47 @@ impl Layer { let uniforms = Uniforms::new(transformation * mesh.transformation()); - index_offset += - self.index_buffer.write(queue, index_offset, indices); + index_offset += self.index_buffer.write( + device, + encoder, + belt, + index_offset, + indices, + ); + self.index_strides.push(indices.len() as u32); match mesh { Mesh::Solid { buffers, .. } => { solid_vertex_offset += self.solid.vertices.write( - queue, + device, + encoder, + belt, solid_vertex_offset, &buffers.vertices, ); solid_uniform_offset += self.solid.uniforms.write( - queue, + device, + encoder, + belt, solid_uniform_offset, &[uniforms], ); } Mesh::Gradient { buffers, .. } => { gradient_vertex_offset += self.gradient.vertices.write( - queue, + device, + encoder, + belt, gradient_vertex_offset, &buffers.vertices, ); gradient_uniform_offset += self.gradient.uniforms.write( - queue, + device, + encoder, + belt, gradient_uniform_offset, &[uniforms], ); @@ -237,7 +252,8 @@ impl Pipeline { pub fn prepare( &mut self, device: &wgpu::Device, - queue: &wgpu::Queue, + encoder: &mut wgpu::CommandEncoder, + belt: &mut wgpu::util::StagingBelt, meshes: &[Mesh<'_>], transformation: Transformation, ) { @@ -252,7 +268,8 @@ impl Pipeline { let layer = &mut self.layers[self.prepare_layer]; layer.prepare( device, - queue, + encoder, + belt, &self.solid, &self.gradient, meshes, -- cgit From b05e61f5c8ae61c9f3c7cc08cded53901ebbccfd Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Wed, 3 Apr 2024 21:07:54 +0200 Subject: Redesign `iced_wgpu` layering architecture --- wgpu/src/triangle.rs | 396 +++++++++++++++++++++++++++++++++++---------------- 1 file changed, 276 insertions(+), 120 deletions(-) (limited to 'wgpu/src/triangle.rs') diff --git a/wgpu/src/triangle.rs b/wgpu/src/triangle.rs index b6be54d4..6df97a7b 100644 --- a/wgpu/src/triangle.rs +++ b/wgpu/src/triangle.rs @@ -1,14 +1,16 @@ //! Draw meshes of triangles. mod msaa; -use crate::core::{Size, Transformation}; +use crate::core::{Rectangle, Size, Transformation}; +use crate::graphics::mesh::{self, Mesh}; use crate::graphics::Antialiasing; -use crate::layer::mesh::{self, Mesh}; use crate::Buffer; const INITIAL_INDEX_COUNT: usize = 1_000; const INITIAL_VERTEX_COUNT: usize = 1_000; +pub type Batch = Vec; + #[derive(Debug)] pub struct Pipeline { blit: Option, @@ -18,8 +20,270 @@ pub struct Pipeline { prepare_layer: usize, } +impl Pipeline { + pub fn new( + device: &wgpu::Device, + format: wgpu::TextureFormat, + antialiasing: Option, + ) -> Pipeline { + Pipeline { + blit: antialiasing.map(|a| msaa::Blit::new(device, format, a)), + solid: solid::Pipeline::new(device, format, antialiasing), + gradient: gradient::Pipeline::new(device, format, antialiasing), + layers: Vec::new(), + prepare_layer: 0, + } + } + + pub fn prepare_batch( + &mut self, + device: &wgpu::Device, + encoder: &mut wgpu::CommandEncoder, + belt: &mut wgpu::util::StagingBelt, + meshes: &Batch, + transformation: Transformation, + ) { + if self.layers.len() <= self.prepare_layer { + self.layers + .push(Layer::new(device, &self.solid, &self.gradient)); + } + + let layer = &mut self.layers[self.prepare_layer]; + layer.prepare( + device, + encoder, + belt, + &self.solid, + &self.gradient, + meshes, + transformation, + ); + + self.prepare_layer += 1; + } + + pub fn prepare_cache( + &mut self, + device: &wgpu::Device, + encoder: &mut wgpu::CommandEncoder, + belt: &mut wgpu::util::StagingBelt, + cache: &mut Cache, + transformation: Transformation, + ) { + match cache { + Cache::Staged(_) => { + let Cache::Staged(batch) = + std::mem::replace(cache, Cache::Staged(Batch::default())) + else { + unreachable!() + }; + + let mut layer = Layer::new(device, &self.solid, &self.gradient); + layer.prepare( + device, + encoder, + belt, + &self.solid, + &self.gradient, + &batch, + transformation, + ); + + *cache = Cache::Uploaded { + layer, + batch, + needs_reupload: false, + } + } + + Cache::Uploaded { + batch, + layer, + needs_reupload, + } => { + if *needs_reupload { + layer.prepare( + device, + encoder, + belt, + &self.solid, + &self.gradient, + batch, + transformation, + ); + + *needs_reupload = false; + } + } + } + } + + pub fn render_batch( + &mut self, + device: &wgpu::Device, + encoder: &mut wgpu::CommandEncoder, + target: &wgpu::TextureView, + layer: usize, + target_size: Size, + meshes: &Batch, + bounds: Rectangle, + scale_factor: f32, + ) { + Self::render( + device, + encoder, + target, + self.blit.as_mut(), + &self.solid, + &self.gradient, + &self.layers[layer], + target_size, + meshes, + bounds, + scale_factor, + ); + } + + pub fn render_cache( + &mut self, + device: &wgpu::Device, + encoder: &mut wgpu::CommandEncoder, + target: &wgpu::TextureView, + target_size: Size, + cache: &Cache, + bounds: Rectangle, + scale_factor: f32, + ) { + let Cache::Uploaded { batch, layer, .. } = cache else { + return; + }; + + Self::render( + device, + encoder, + target, + self.blit.as_mut(), + &self.solid, + &self.gradient, + layer, + target_size, + batch, + bounds, + scale_factor, + ); + } + + fn render( + device: &wgpu::Device, + encoder: &mut wgpu::CommandEncoder, + target: &wgpu::TextureView, + mut blit: Option<&mut msaa::Blit>, + solid: &solid::Pipeline, + gradient: &gradient::Pipeline, + layer: &Layer, + target_size: Size, + meshes: &Batch, + bounds: Rectangle, + scale_factor: f32, + ) { + { + let (attachment, resolve_target, load) = if let Some(blit) = + &mut blit + { + let (attachment, resolve_target) = + blit.targets(device, target_size.width, target_size.height); + + ( + attachment, + Some(resolve_target), + wgpu::LoadOp::Clear(wgpu::Color::TRANSPARENT), + ) + } else { + (target, None, wgpu::LoadOp::Load) + }; + + let mut render_pass = + encoder.begin_render_pass(&wgpu::RenderPassDescriptor { + label: Some("iced_wgpu.triangle.render_pass"), + color_attachments: &[Some( + wgpu::RenderPassColorAttachment { + view: attachment, + resolve_target, + ops: wgpu::Operations { + load, + store: wgpu::StoreOp::Store, + }, + }, + )], + depth_stencil_attachment: None, + timestamp_writes: None, + occlusion_query_set: None, + }); + + layer.render( + solid, + gradient, + meshes, + bounds, + scale_factor, + &mut render_pass, + ); + } + + if let Some(blit) = blit { + blit.draw(encoder, target); + } + } + + pub fn end_frame(&mut self) { + self.prepare_layer = 0; + } +} + +#[derive(Debug)] +pub enum Cache { + Staged(Batch), + Uploaded { + batch: Batch, + layer: Layer, + needs_reupload: bool, + }, +} + +impl Cache { + pub fn is_empty(&self) -> bool { + match self { + Cache::Staged(batch) | Cache::Uploaded { batch, .. } => { + batch.is_empty() + } + } + } + + pub fn update(&mut self, new_batch: Batch) { + match self { + Self::Staged(batch) => { + *batch = new_batch; + } + Self::Uploaded { + batch, + needs_reupload, + .. + } => { + *batch = new_batch; + *needs_reupload = true; + } + } + } +} + +impl Default for Cache { + fn default() -> Self { + Self::Staged(Batch::default()) + } +} + #[derive(Debug)] -struct Layer { +pub struct Layer { index_buffer: Buffer, index_strides: Vec, solid: solid::Layer, @@ -52,7 +316,7 @@ impl Layer { belt: &mut wgpu::util::StagingBelt, solid: &solid::Pipeline, gradient: &gradient::Pipeline, - meshes: &[Mesh<'_>], + meshes: &Batch, transformation: Transformation, ) { // Count the total amount of vertices & indices we need to handle @@ -100,7 +364,6 @@ impl Layer { for mesh in meshes { let indices = mesh.indices(); - let uniforms = Uniforms::new(transformation * mesh.transformation()); @@ -157,7 +420,8 @@ impl Layer { &'a self, solid: &'a solid::Pipeline, gradient: &'a gradient::Pipeline, - meshes: &[Mesh<'_>], + meshes: &Batch, + layer_bounds: Rectangle, scale_factor: f32, render_pass: &mut wgpu::RenderPass<'a>, ) { @@ -166,7 +430,12 @@ impl Layer { let mut last_is_solid = None; for (index, mesh) in meshes.iter().enumerate() { - let clip_bounds = (mesh.clip_bounds() * scale_factor).snap(); + let Some(clip_bounds) = Rectangle::::from(layer_bounds) + .intersection(&(mesh.clip_bounds() * scale_factor)) + .map(Rectangle::snap) + else { + continue; + }; if clip_bounds.width < 1 || clip_bounds.height < 1 { continue; @@ -234,119 +503,6 @@ impl Layer { } } -impl Pipeline { - pub fn new( - device: &wgpu::Device, - format: wgpu::TextureFormat, - antialiasing: Option, - ) -> Pipeline { - Pipeline { - blit: antialiasing.map(|a| msaa::Blit::new(device, format, a)), - solid: solid::Pipeline::new(device, format, antialiasing), - gradient: gradient::Pipeline::new(device, format, antialiasing), - layers: Vec::new(), - prepare_layer: 0, - } - } - - pub fn prepare( - &mut self, - device: &wgpu::Device, - encoder: &mut wgpu::CommandEncoder, - belt: &mut wgpu::util::StagingBelt, - meshes: &[Mesh<'_>], - transformation: Transformation, - ) { - #[cfg(feature = "tracing")] - let _ = tracing::info_span!("Wgpu::Triangle", "PREPARE").entered(); - - if self.layers.len() <= self.prepare_layer { - self.layers - .push(Layer::new(device, &self.solid, &self.gradient)); - } - - let layer = &mut self.layers[self.prepare_layer]; - layer.prepare( - device, - encoder, - belt, - &self.solid, - &self.gradient, - meshes, - transformation, - ); - - self.prepare_layer += 1; - } - - pub fn render( - &mut self, - device: &wgpu::Device, - encoder: &mut wgpu::CommandEncoder, - target: &wgpu::TextureView, - layer: usize, - target_size: Size, - meshes: &[Mesh<'_>], - scale_factor: f32, - ) { - #[cfg(feature = "tracing")] - let _ = tracing::info_span!("Wgpu::Triangle", "DRAW").entered(); - - { - let (attachment, resolve_target, load) = if let Some(blit) = - &mut self.blit - { - let (attachment, resolve_target) = - blit.targets(device, target_size.width, target_size.height); - - ( - attachment, - Some(resolve_target), - wgpu::LoadOp::Clear(wgpu::Color::TRANSPARENT), - ) - } else { - (target, None, wgpu::LoadOp::Load) - }; - - let mut render_pass = - encoder.begin_render_pass(&wgpu::RenderPassDescriptor { - label: Some("iced_wgpu.triangle.render_pass"), - color_attachments: &[Some( - wgpu::RenderPassColorAttachment { - view: attachment, - resolve_target, - ops: wgpu::Operations { - load, - store: wgpu::StoreOp::Store, - }, - }, - )], - depth_stencil_attachment: None, - timestamp_writes: None, - occlusion_query_set: None, - }); - - let layer = &mut self.layers[layer]; - - layer.render( - &self.solid, - &self.gradient, - meshes, - scale_factor, - &mut render_pass, - ); - } - - if let Some(blit) = &mut self.blit { - blit.draw(encoder, target); - } - } - - pub fn end_frame(&mut self) { - self.prepare_layer = 0; - } -} - fn fragment_target( texture_format: wgpu::TextureFormat, ) -> wgpu::ColorTargetState { -- cgit From 88b72de282441367092b07f8075eb931eff495ad Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Wed, 3 Apr 2024 22:13:00 +0200 Subject: Implement preliminary cache grouping for mesh primitives Due to AA, it's very expensive to render every cached layer independently. --- wgpu/src/triangle.rs | 63 +++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 45 insertions(+), 18 deletions(-) (limited to 'wgpu/src/triangle.rs') diff --git a/wgpu/src/triangle.rs b/wgpu/src/triangle.rs index 6df97a7b..9cd02f72 100644 --- a/wgpu/src/triangle.rs +++ b/wgpu/src/triangle.rs @@ -136,14 +136,13 @@ impl Pipeline { self.blit.as_mut(), &self.solid, &self.gradient, - &self.layers[layer], target_size, - meshes, - bounds, + std::iter::once((&self.layers[layer], meshes, bounds)), scale_factor, ); } + #[allow(dead_code)] pub fn render_cache( &mut self, device: &wgpu::Device, @@ -165,25 +164,51 @@ impl Pipeline { self.blit.as_mut(), &self.solid, &self.gradient, - layer, target_size, - batch, - bounds, + std::iter::once((layer, batch, bounds)), scale_factor, ); } - fn render( + pub fn render_cache_group<'a>( + &mut self, + device: &wgpu::Device, + encoder: &mut wgpu::CommandEncoder, + target: &wgpu::TextureView, + target_size: Size, + group: impl Iterator)>, + scale_factor: f32, + ) { + let group = group.filter_map(|(cache, bounds)| { + if let Cache::Uploaded { batch, layer, .. } = cache { + Some((layer, batch, bounds)) + } else { + None + } + }); + + Self::render( + device, + encoder, + target, + self.blit.as_mut(), + &self.solid, + &self.gradient, + target_size, + group, + scale_factor, + ); + } + + fn render<'a>( device: &wgpu::Device, encoder: &mut wgpu::CommandEncoder, target: &wgpu::TextureView, mut blit: Option<&mut msaa::Blit>, solid: &solid::Pipeline, gradient: &gradient::Pipeline, - layer: &Layer, target_size: Size, - meshes: &Batch, - bounds: Rectangle, + group: impl Iterator)>, scale_factor: f32, ) { { @@ -220,14 +245,16 @@ impl Pipeline { occlusion_query_set: None, }); - layer.render( - solid, - gradient, - meshes, - bounds, - scale_factor, - &mut render_pass, - ); + for (layer, meshes, bounds) in group { + layer.render( + solid, + gradient, + meshes, + bounds, + scale_factor, + &mut render_pass, + ); + } } if let Some(blit) = blit { -- cgit From e2c129c057b521009d451e474a6820601877cf11 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Wed, 3 Apr 2024 23:14:16 +0200 Subject: Fix `geometry::Cache` not reusing previous geometry --- wgpu/src/triangle.rs | 1 - 1 file changed, 1 deletion(-) (limited to 'wgpu/src/triangle.rs') diff --git a/wgpu/src/triangle.rs b/wgpu/src/triangle.rs index 9cd02f72..be7bb867 100644 --- a/wgpu/src/triangle.rs +++ b/wgpu/src/triangle.rs @@ -95,7 +95,6 @@ impl Pipeline { needs_reupload: false, } } - Cache::Uploaded { batch, layer, -- cgit From 394e599c3a096b036aabdd6f850c4a7c518d44fa Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Fri, 5 Apr 2024 00:40:39 +0200 Subject: Fix layer transformations --- wgpu/src/triangle.rs | 72 ++++++++++++++++++++++++++++++++++------------------ 1 file changed, 47 insertions(+), 25 deletions(-) (limited to 'wgpu/src/triangle.rs') diff --git a/wgpu/src/triangle.rs b/wgpu/src/triangle.rs index be7bb867..3a184da2 100644 --- a/wgpu/src/triangle.rs +++ b/wgpu/src/triangle.rs @@ -68,8 +68,11 @@ impl Pipeline { encoder: &mut wgpu::CommandEncoder, belt: &mut wgpu::util::StagingBelt, cache: &mut Cache, - transformation: Transformation, + new_projection: Transformation, + new_transformation: Transformation, ) { + let new_projection = new_projection * new_transformation; + match cache { Cache::Staged(_) => { let Cache::Staged(batch) = @@ -86,21 +89,25 @@ impl Pipeline { &self.solid, &self.gradient, &batch, - transformation, + new_projection, ); *cache = Cache::Uploaded { layer, batch, + transformation: new_transformation, + projection: new_projection, needs_reupload: false, } } Cache::Uploaded { batch, layer, + transformation, + projection, needs_reupload, } => { - if *needs_reupload { + if *needs_reupload || new_projection != *projection { layer.prepare( device, encoder, @@ -108,9 +115,11 @@ impl Pipeline { &self.solid, &self.gradient, batch, - transformation, + new_projection, ); + *transformation = new_transformation; + *projection = new_projection; *needs_reupload = false; } } @@ -126,7 +135,7 @@ impl Pipeline { target_size: Size, meshes: &Batch, bounds: Rectangle, - scale_factor: f32, + transformation: &Transformation, ) { Self::render( device, @@ -136,8 +145,12 @@ impl Pipeline { &self.solid, &self.gradient, target_size, - std::iter::once((&self.layers[layer], meshes, bounds)), - scale_factor, + std::iter::once(( + &self.layers[layer], + meshes, + transformation, + bounds, + )), ); } @@ -150,9 +163,14 @@ impl Pipeline { target_size: Size, cache: &Cache, bounds: Rectangle, - scale_factor: f32, ) { - let Cache::Uploaded { batch, layer, .. } = cache else { + let Cache::Uploaded { + batch, + layer, + transformation, + .. + } = cache + else { return; }; @@ -164,8 +182,7 @@ impl Pipeline { &self.solid, &self.gradient, target_size, - std::iter::once((layer, batch, bounds)), - scale_factor, + std::iter::once((layer, batch, transformation, bounds)), ); } @@ -176,11 +193,16 @@ impl Pipeline { target: &wgpu::TextureView, target_size: Size, group: impl Iterator)>, - scale_factor: f32, ) { let group = group.filter_map(|(cache, bounds)| { - if let Cache::Uploaded { batch, layer, .. } = cache { - Some((layer, batch, bounds)) + if let Cache::Uploaded { + batch, + layer, + transformation, + .. + } = cache + { + Some((layer, batch, transformation, bounds)) } else { None } @@ -195,7 +217,6 @@ impl Pipeline { &self.gradient, target_size, group, - scale_factor, ); } @@ -207,8 +228,9 @@ impl Pipeline { solid: &solid::Pipeline, gradient: &gradient::Pipeline, target_size: Size, - group: impl Iterator)>, - scale_factor: f32, + group: impl Iterator< + Item = (&'a Layer, &'a Batch, &'a Transformation, Rectangle), + >, ) { { let (attachment, resolve_target, load) = if let Some(blit) = @@ -244,13 +266,13 @@ impl Pipeline { occlusion_query_set: None, }); - for (layer, meshes, bounds) in group { + for (layer, meshes, transformation, bounds) in group { layer.render( solid, gradient, meshes, bounds, - scale_factor, + *transformation, &mut render_pass, ); } @@ -272,6 +294,8 @@ pub enum Cache { Uploaded { batch: Batch, layer: Layer, + transformation: Transformation, + projection: Transformation, needs_reupload: bool, }, } @@ -390,6 +414,7 @@ impl Layer { for mesh in meshes { let indices = mesh.indices(); + let uniforms = Uniforms::new(transformation * mesh.transformation()); @@ -448,7 +473,7 @@ impl Layer { gradient: &'a gradient::Pipeline, meshes: &Batch, layer_bounds: Rectangle, - scale_factor: f32, + transformation: Transformation, render_pass: &mut wgpu::RenderPass<'a>, ) { let mut num_solids = 0; @@ -457,15 +482,12 @@ impl Layer { for (index, mesh) in meshes.iter().enumerate() { let Some(clip_bounds) = Rectangle::::from(layer_bounds) - .intersection(&(mesh.clip_bounds() * scale_factor)) - .map(Rectangle::snap) + .intersection(&(mesh.clip_bounds() * transformation)) else { continue; }; - if clip_bounds.width < 1 || clip_bounds.height < 1 { - continue; - } + let clip_bounds = clip_bounds.snap(); render_pass.set_scissor_rect( clip_bounds.x, -- cgit From 6d3e1d835e1688fbc58622a03a784ed25ed3f0e1 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Fri, 5 Apr 2024 23:59:21 +0200 Subject: Decouple caching from layering and simplify everything --- wgpu/src/triangle.rs | 503 ++++++++++++++++++++++++++------------------------- 1 file changed, 253 insertions(+), 250 deletions(-) (limited to 'wgpu/src/triangle.rs') diff --git a/wgpu/src/triangle.rs b/wgpu/src/triangle.rs index 3a184da2..a08b6987 100644 --- a/wgpu/src/triangle.rs +++ b/wgpu/src/triangle.rs @@ -6,10 +6,136 @@ use crate::graphics::mesh::{self, Mesh}; use crate::graphics::Antialiasing; use crate::Buffer; +use rustc_hash::{FxHashMap, FxHashSet}; +use std::collections::hash_map; +use std::rc::Rc; +use std::sync::atomic::{self, AtomicU64}; + const INITIAL_INDEX_COUNT: usize = 1_000; const INITIAL_VERTEX_COUNT: usize = 1_000; -pub type Batch = Vec; +pub type Batch = Vec; + +pub enum Item { + Group { + transformation: Transformation, + meshes: Vec, + }, + Cached { + transformation: Transformation, + cache: Cache, + }, +} + +#[derive(Debug, Clone)] +pub struct Cache { + id: Id, + batch: Rc<[Mesh]>, + version: usize, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct Id(u64); + +impl Cache { + pub fn new(meshes: Vec) -> Self { + static NEXT_ID: AtomicU64 = AtomicU64::new(0); + + Self { + id: Id(NEXT_ID.fetch_add(1, atomic::Ordering::Relaxed)), + batch: Rc::from(meshes), + version: 0, + } + } + + pub fn update(&mut self, meshes: Vec) { + self.batch = Rc::from(meshes); + self.version += 1; + } +} + +#[derive(Debug)] +struct Upload { + layer: Layer, + transformation: Transformation, + version: usize, +} + +#[derive(Debug, Default)] +pub struct Storage { + uploads: FxHashMap, + recently_used: FxHashSet, +} + +impl Storage { + pub fn new() -> Self { + Self::default() + } + + fn get(&self, id: Id) -> Option<&Upload> { + self.uploads.get(&id) + } + + fn prepare( + &mut self, + device: &wgpu::Device, + encoder: &mut wgpu::CommandEncoder, + belt: &mut wgpu::util::StagingBelt, + solid: &solid::Pipeline, + gradient: &gradient::Pipeline, + cache: &Cache, + new_transformation: Transformation, + ) { + match self.uploads.entry(cache.id) { + hash_map::Entry::Occupied(entry) => { + let upload = entry.into_mut(); + + if upload.version != cache.version + || upload.transformation != new_transformation + { + upload.layer.prepare( + device, + encoder, + belt, + solid, + gradient, + &cache.batch, + new_transformation, + ); + + upload.version = cache.version; + upload.transformation = new_transformation; + } + } + hash_map::Entry::Vacant(entry) => { + let mut layer = Layer::new(device, solid, gradient); + + layer.prepare( + device, + encoder, + belt, + solid, + gradient, + &cache.batch, + new_transformation, + ); + + let _ = entry.insert(Upload { + layer, + transformation: new_transformation, + version: 0, + }); + } + } + + let _ = self.recently_used.insert(cache.id); + } + + pub fn trim(&mut self) { + self.uploads.retain(|id, _| self.recently_used.contains(id)); + self.recently_used.clear(); + } +} #[derive(Debug)] pub struct Pipeline { @@ -35,180 +161,103 @@ impl Pipeline { } } - pub fn prepare_batch( - &mut self, - device: &wgpu::Device, - encoder: &mut wgpu::CommandEncoder, - belt: &mut wgpu::util::StagingBelt, - meshes: &Batch, - transformation: Transformation, - ) { - if self.layers.len() <= self.prepare_layer { - self.layers - .push(Layer::new(device, &self.solid, &self.gradient)); - } - - let layer = &mut self.layers[self.prepare_layer]; - layer.prepare( - device, - encoder, - belt, - &self.solid, - &self.gradient, - meshes, - transformation, - ); - - self.prepare_layer += 1; - } - - pub fn prepare_cache( + pub fn prepare( &mut self, device: &wgpu::Device, encoder: &mut wgpu::CommandEncoder, belt: &mut wgpu::util::StagingBelt, - cache: &mut Cache, - new_projection: Transformation, - new_transformation: Transformation, + storage: &mut Storage, + items: &[Item], + projection: Transformation, ) { - let new_projection = new_projection * new_transformation; - - match cache { - Cache::Staged(_) => { - let Cache::Staged(batch) = - std::mem::replace(cache, Cache::Staged(Batch::default())) - else { - unreachable!() - }; - - let mut layer = Layer::new(device, &self.solid, &self.gradient); - layer.prepare( - device, - encoder, - belt, - &self.solid, - &self.gradient, - &batch, - new_projection, - ); + for item in items { + match item { + Item::Group { + transformation, + meshes, + } => { + if self.layers.len() <= self.prepare_layer { + self.layers.push(Layer::new( + device, + &self.solid, + &self.gradient, + )); + } - *cache = Cache::Uploaded { - layer, - batch, - transformation: new_transformation, - projection: new_projection, - needs_reupload: false, - } - } - Cache::Uploaded { - batch, - layer, - transformation, - projection, - needs_reupload, - } => { - if *needs_reupload || new_projection != *projection { + let layer = &mut self.layers[self.prepare_layer]; layer.prepare( device, encoder, belt, &self.solid, &self.gradient, - batch, - new_projection, + meshes, + projection * *transformation, ); - *transformation = new_transformation; - *projection = new_projection; - *needs_reupload = false; + self.prepare_layer += 1; + } + Item::Cached { + transformation, + cache, + } => { + storage.prepare( + device, + encoder, + belt, + &self.solid, + &self.gradient, + cache, + projection * *transformation, + ); } } } } - pub fn render_batch( - &mut self, - device: &wgpu::Device, - encoder: &mut wgpu::CommandEncoder, - target: &wgpu::TextureView, - layer: usize, - target_size: Size, - meshes: &Batch, - bounds: Rectangle, - transformation: &Transformation, - ) { - Self::render( - device, - encoder, - target, - self.blit.as_mut(), - &self.solid, - &self.gradient, - target_size, - std::iter::once(( - &self.layers[layer], - meshes, - transformation, - bounds, - )), - ); - } - - #[allow(dead_code)] - pub fn render_cache( + pub fn render( &mut self, device: &wgpu::Device, encoder: &mut wgpu::CommandEncoder, target: &wgpu::TextureView, + storage: &Storage, + start: usize, + batch: &Batch, target_size: Size, - cache: &Cache, - bounds: Rectangle, - ) { - let Cache::Uploaded { - batch, - layer, - transformation, - .. - } = cache - else { - return; - }; + bounds: Rectangle, + screen_transformation: Transformation, + ) -> usize { + let mut layer_count = 0; - Self::render( - device, - encoder, - target, - self.blit.as_mut(), - &self.solid, - &self.gradient, - target_size, - std::iter::once((layer, batch, transformation, bounds)), - ); - } + let items = batch.iter().filter_map(|item| match item { + Item::Group { + transformation, + meshes, + } => { + let layer = &self.layers[start + layer_count]; + layer_count += 1; - pub fn render_cache_group<'a>( - &mut self, - device: &wgpu::Device, - encoder: &mut wgpu::CommandEncoder, - target: &wgpu::TextureView, - target_size: Size, - group: impl Iterator)>, - ) { - let group = group.filter_map(|(cache, bounds)| { - if let Cache::Uploaded { - batch, - layer, + Some(( + layer, + meshes.as_slice(), + screen_transformation * *transformation, + )) + } + Item::Cached { transformation, - .. - } = cache - { - Some((layer, batch, transformation, bounds)) - } else { - None + cache, + } => { + let upload = storage.get(cache.id)?; + + Some(( + &upload.layer, + &cache.batch, + screen_transformation * *transformation, + )) } }); - Self::render( + render( device, encoder, target, @@ -216,71 +265,11 @@ impl Pipeline { &self.solid, &self.gradient, target_size, - group, + bounds, + items, ); - } - - fn render<'a>( - device: &wgpu::Device, - encoder: &mut wgpu::CommandEncoder, - target: &wgpu::TextureView, - mut blit: Option<&mut msaa::Blit>, - solid: &solid::Pipeline, - gradient: &gradient::Pipeline, - target_size: Size, - group: impl Iterator< - Item = (&'a Layer, &'a Batch, &'a Transformation, Rectangle), - >, - ) { - { - let (attachment, resolve_target, load) = if let Some(blit) = - &mut blit - { - let (attachment, resolve_target) = - blit.targets(device, target_size.width, target_size.height); - - ( - attachment, - Some(resolve_target), - wgpu::LoadOp::Clear(wgpu::Color::TRANSPARENT), - ) - } else { - (target, None, wgpu::LoadOp::Load) - }; - - let mut render_pass = - encoder.begin_render_pass(&wgpu::RenderPassDescriptor { - label: Some("iced_wgpu.triangle.render_pass"), - color_attachments: &[Some( - wgpu::RenderPassColorAttachment { - view: attachment, - resolve_target, - ops: wgpu::Operations { - load, - store: wgpu::StoreOp::Store, - }, - }, - )], - depth_stencil_attachment: None, - timestamp_writes: None, - occlusion_query_set: None, - }); - - for (layer, meshes, transformation, bounds) in group { - layer.render( - solid, - gradient, - meshes, - bounds, - *transformation, - &mut render_pass, - ); - } - } - if let Some(blit) = blit { - blit.draw(encoder, target); - } + layer_count } pub fn end_frame(&mut self) { @@ -288,47 +277,61 @@ impl Pipeline { } } -#[derive(Debug)] -pub enum Cache { - Staged(Batch), - Uploaded { - batch: Batch, - layer: Layer, - transformation: Transformation, - projection: Transformation, - needs_reupload: bool, - }, -} - -impl Cache { - pub fn is_empty(&self) -> bool { - match self { - Cache::Staged(batch) | Cache::Uploaded { batch, .. } => { - batch.is_empty() - } - } - } +fn render<'a>( + device: &wgpu::Device, + encoder: &mut wgpu::CommandEncoder, + target: &wgpu::TextureView, + mut blit: Option<&mut msaa::Blit>, + solid: &solid::Pipeline, + gradient: &gradient::Pipeline, + target_size: Size, + bounds: Rectangle, + group: impl Iterator, +) { + { + let (attachment, resolve_target, load) = if let Some(blit) = &mut blit { + let (attachment, resolve_target) = + blit.targets(device, target_size.width, target_size.height); + + ( + attachment, + Some(resolve_target), + wgpu::LoadOp::Clear(wgpu::Color::TRANSPARENT), + ) + } else { + (target, None, wgpu::LoadOp::Load) + }; - pub fn update(&mut self, new_batch: Batch) { - match self { - Self::Staged(batch) => { - *batch = new_batch; - } - Self::Uploaded { - batch, - needs_reupload, - .. - } => { - *batch = new_batch; - *needs_reupload = true; - } + let mut render_pass = + encoder.begin_render_pass(&wgpu::RenderPassDescriptor { + label: Some("iced_wgpu.triangle.render_pass"), + color_attachments: &[Some(wgpu::RenderPassColorAttachment { + view: attachment, + resolve_target, + ops: wgpu::Operations { + load, + store: wgpu::StoreOp::Store, + }, + })], + depth_stencil_attachment: None, + timestamp_writes: None, + occlusion_query_set: None, + }); + + for (layer, meshes, transformation) in group { + layer.render( + solid, + gradient, + meshes, + bounds, + transformation, + &mut render_pass, + ); } } -} -impl Default for Cache { - fn default() -> Self { - Self::Staged(Batch::default()) + if let Some(blit) = blit { + blit.draw(encoder, target); } } @@ -366,7 +369,7 @@ impl Layer { belt: &mut wgpu::util::StagingBelt, solid: &solid::Pipeline, gradient: &gradient::Pipeline, - meshes: &Batch, + meshes: &[Mesh], transformation: Transformation, ) { // Count the total amount of vertices & indices we need to handle @@ -471,8 +474,8 @@ impl Layer { &'a self, solid: &'a solid::Pipeline, gradient: &'a gradient::Pipeline, - meshes: &Batch, - layer_bounds: Rectangle, + meshes: &[Mesh], + bounds: Rectangle, transformation: Transformation, render_pass: &mut wgpu::RenderPass<'a>, ) { @@ -481,8 +484,8 @@ impl Layer { let mut last_is_solid = None; for (index, mesh) in meshes.iter().enumerate() { - let Some(clip_bounds) = Rectangle::::from(layer_bounds) - .intersection(&(mesh.clip_bounds() * transformation)) + let Some(clip_bounds) = + bounds.intersection(&(mesh.clip_bounds() * transformation)) else { continue; }; -- cgit From 441aac25995290a83162a4728f22492ff69a5f4d Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Sat, 6 Apr 2024 03:06:40 +0200 Subject: Avoid generating empty caches in `iced_wgpu` --- wgpu/src/triangle.rs | 31 +++++++++++++++++++++++-------- 1 file changed, 23 insertions(+), 8 deletions(-) (limited to 'wgpu/src/triangle.rs') diff --git a/wgpu/src/triangle.rs b/wgpu/src/triangle.rs index a08b6987..de6c026a 100644 --- a/wgpu/src/triangle.rs +++ b/wgpu/src/triangle.rs @@ -38,14 +38,18 @@ pub struct Cache { pub struct Id(u64); impl Cache { - pub fn new(meshes: Vec) -> Self { + pub fn new(meshes: Vec) -> Option { static NEXT_ID: AtomicU64 = AtomicU64::new(0); - Self { + if meshes.is_empty() { + return None; + } + + Some(Self { id: Id(NEXT_ID.fetch_add(1, atomic::Ordering::Relaxed)), batch: Rc::from(meshes), version: 0, - } + }) } pub fn update(&mut self, meshes: Vec) { @@ -72,8 +76,12 @@ impl Storage { Self::default() } - fn get(&self, id: Id) -> Option<&Upload> { - self.uploads.get(&id) + fn get(&self, cache: &Cache) -> Option<&Upload> { + if cache.batch.is_empty() { + return None; + } + + self.uploads.get(&cache.id) } fn prepare( @@ -90,8 +98,9 @@ impl Storage { hash_map::Entry::Occupied(entry) => { let upload = entry.into_mut(); - if upload.version != cache.version - || upload.transformation != new_transformation + if !cache.batch.is_empty() + && (upload.version != cache.version + || upload.transformation != new_transformation) { upload.layer.prepare( device, @@ -125,6 +134,12 @@ impl Storage { transformation: new_transformation, version: 0, }); + + log::info!( + "New mesh upload: {} (total: {})", + cache.id.0, + self.uploads.len() + ); } } @@ -247,7 +262,7 @@ impl Pipeline { transformation, cache, } => { - let upload = storage.get(cache.id)?; + let upload = storage.get(cache)?; Some(( &upload.layer, -- cgit From 288f62bfb691a91e01b9ddbce9dbdc560ee9036a Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Sun, 7 Apr 2024 18:45:30 +0200 Subject: Share `msaa::Blit` texture between multiple windows --- wgpu/src/triangle.rs | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) (limited to 'wgpu/src/triangle.rs') diff --git a/wgpu/src/triangle.rs b/wgpu/src/triangle.rs index 06c0ef02..98ba41b3 100644 --- a/wgpu/src/triangle.rs +++ b/wgpu/src/triangle.rs @@ -184,8 +184,16 @@ impl Pipeline { belt: &mut wgpu::util::StagingBelt, storage: &mut Storage, items: &[Item], - projection: Transformation, + scale: Transformation, + target_size: Size, ) { + let projection = if let Some(blit) = &mut self.blit { + blit.prepare(device, encoder, belt, target_size) * scale + } else { + Transformation::orthographic(target_size.width, target_size.height) + * scale + }; + for item in items { match item { Item::Group { @@ -233,13 +241,11 @@ impl Pipeline { pub fn render( &mut self, - device: &wgpu::Device, encoder: &mut wgpu::CommandEncoder, target: &wgpu::TextureView, storage: &Storage, start: usize, batch: &Batch, - target_size: Size, bounds: Rectangle, screen_transformation: Transformation, ) -> usize { @@ -274,13 +280,11 @@ impl Pipeline { }); render( - device, encoder, target, self.blit.as_mut(), &self.solid, &self.gradient, - target_size, bounds, items, ); @@ -294,20 +298,17 @@ impl Pipeline { } fn render<'a>( - device: &wgpu::Device, encoder: &mut wgpu::CommandEncoder, target: &wgpu::TextureView, mut blit: Option<&mut msaa::Blit>, solid: &solid::Pipeline, gradient: &gradient::Pipeline, - target_size: Size, bounds: Rectangle, group: impl Iterator, ) { { let (attachment, resolve_target, load) = if let Some(blit) = &mut blit { - let (attachment, resolve_target) = - blit.targets(device, target_size.width, target_size.height); + let (attachment, resolve_target) = blit.targets(); ( attachment, -- cgit From d922b478156488a7bc03c6e791e05c040d702634 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Mon, 8 Apr 2024 15:04:35 +0200 Subject: Reintroduce support for custom primitives in `iced_wgpu` --- wgpu/src/triangle.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'wgpu/src/triangle.rs') diff --git a/wgpu/src/triangle.rs b/wgpu/src/triangle.rs index 98ba41b3..8470ea39 100644 --- a/wgpu/src/triangle.rs +++ b/wgpu/src/triangle.rs @@ -501,14 +501,13 @@ impl Layer { let mut last_is_solid = None; for (index, mesh) in meshes.iter().enumerate() { - let Some(clip_bounds) = - bounds.intersection(&(mesh.clip_bounds() * transformation)) + let Some(clip_bounds) = bounds + .intersection(&(mesh.clip_bounds() * transformation)) + .and_then(Rectangle::snap) else { continue; }; - let clip_bounds = clip_bounds.snap(); - render_pass.set_scissor_rect( clip_bounds.x, clip_bounds.y, -- cgit From 2dcd4f916e0ea71f925212c8277498c6f995155b Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Sat, 27 Apr 2024 14:16:12 +0200 Subject: Retain caches in `iced_wgpu` as long as `Rc` values are alive This allows reusing a `canvas::Cache` at no cost even if it is not presented every frame. --- wgpu/src/triangle.rs | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) (limited to 'wgpu/src/triangle.rs') diff --git a/wgpu/src/triangle.rs b/wgpu/src/triangle.rs index 8470ea39..53a5502a 100644 --- a/wgpu/src/triangle.rs +++ b/wgpu/src/triangle.rs @@ -6,9 +6,9 @@ use crate::graphics::mesh::{self, Mesh}; use crate::graphics::Antialiasing; use crate::Buffer; -use rustc_hash::{FxHashMap, FxHashSet}; +use rustc_hash::FxHashMap; use std::collections::hash_map; -use std::rc::Rc; +use std::rc::{self, Rc}; use std::sync::atomic::{self, AtomicU64}; const INITIAL_INDEX_COUNT: usize = 1_000; @@ -64,12 +64,12 @@ struct Upload { layer: Layer, transformation: Transformation, version: usize, + batch: rc::Weak<[Mesh]>, } #[derive(Debug, Default)] pub struct Storage { uploads: FxHashMap, - recently_used: FxHashSet, } impl Storage { @@ -134,6 +134,7 @@ impl Storage { layer, transformation: new_transformation, version: 0, + batch: Rc::downgrade(&cache.batch), }); log::info!( @@ -143,13 +144,11 @@ impl Storage { ); } } - - let _ = self.recently_used.insert(cache.id); } pub fn trim(&mut self) { - self.uploads.retain(|id, _| self.recently_used.contains(id)); - self.recently_used.clear(); + self.uploads + .retain(|_id, upload| upload.batch.strong_count() > 0); } } -- cgit From 24501fd73b5ae884367a2d112ff44625058b876b Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 30 Apr 2024 05:13:24 +0200 Subject: Fix `text` and `triangle` uploads being dropped on `canvas` cache clears --- wgpu/src/triangle.rs | 1 + 1 file changed, 1 insertion(+) (limited to 'wgpu/src/triangle.rs') diff --git a/wgpu/src/triangle.rs b/wgpu/src/triangle.rs index 53a5502a..ca36de82 100644 --- a/wgpu/src/triangle.rs +++ b/wgpu/src/triangle.rs @@ -113,6 +113,7 @@ impl Storage { new_transformation, ); + upload.batch = Rc::downgrade(&cache.batch); upload.version = cache.version; upload.transformation = new_transformation; } -- cgit From c51b85e7ab067f5e7411eccd10a5ae192e6ee0a8 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 30 Apr 2024 21:59:46 +0200 Subject: Invalidate text uploads after atlas trimming --- wgpu/src/triangle.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'wgpu/src/triangle.rs') diff --git a/wgpu/src/triangle.rs b/wgpu/src/triangle.rs index ca36de82..b0551f55 100644 --- a/wgpu/src/triangle.rs +++ b/wgpu/src/triangle.rs @@ -138,7 +138,7 @@ impl Storage { batch: Rc::downgrade(&cache.batch), }); - log::info!( + log::debug!( "New mesh upload: {} (total: {})", cache.id.0, self.uploads.len() -- cgit