From 493c36ac712ef04523065b94988a88cc4db16b1a Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Wed, 24 Apr 2024 21:29:30 +0200 Subject: Make image `Cache` eviction strategy less aggressive in `iced_wgpu` Instead of trimming unconditionally at the end of a frame, we now trim the cache only when there is a cache miss. This way, images that are not visible but still a part of the layout will stay cached. Eviction will only happen when the images are not a part of the UI for two consectuive frames. --- wgpu/src/image/atlas.rs | 15 +++++++++++---- wgpu/src/image/atlas/allocator.rs | 4 ++++ wgpu/src/image/atlas/layer.rs | 8 ++++++++ wgpu/src/image/mod.rs | 2 +- wgpu/src/image/raster.rs | 9 +++++++++ wgpu/src/image/vector.rs | 8 ++++++++ 6 files changed, 41 insertions(+), 5 deletions(-) (limited to 'wgpu/src') diff --git a/wgpu/src/image/atlas.rs b/wgpu/src/image/atlas.rs index ea36e06d..ae43c3b4 100644 --- a/wgpu/src/image/atlas.rs +++ b/wgpu/src/image/atlas.rs @@ -94,7 +94,7 @@ impl Atlas { entry }; - log::info!("Allocated atlas entry: {entry:?}"); + log::debug!("Allocated atlas entry: {entry:?}"); // It is a webgpu requirement that: // BufferCopyView.layout.bytes_per_row % wgpu::COPY_BYTES_PER_ROW_ALIGNMENT == 0 @@ -147,13 +147,20 @@ impl Atlas { } } - log::info!("Current atlas: {self:?}"); + if log::log_enabled!(log::Level::Debug) { + log::debug!( + "Atlas layers: {} (busy: {}, allocations: {})", + self.layer_count(), + self.layers.iter().filter(|layer| !layer.is_empty()).count(), + self.layers.iter().map(Layer::allocations).sum::(), + ); + } Some(entry) } pub fn remove(&mut self, entry: &Entry) { - log::info!("Removing atlas entry: {entry:?}"); + log::debug!("Removing atlas entry: {entry:?}"); match entry { Entry::Contiguous(allocation) => { @@ -266,7 +273,7 @@ impl Atlas { } fn deallocate(&mut self, allocation: &Allocation) { - log::info!("Deallocating atlas: {allocation:?}"); + log::debug!("Deallocating atlas: {allocation:?}"); match allocation { Allocation::Full { layer } => { diff --git a/wgpu/src/image/atlas/allocator.rs b/wgpu/src/image/atlas/allocator.rs index 204a5c26..a51ac1f5 100644 --- a/wgpu/src/image/atlas/allocator.rs +++ b/wgpu/src/image/atlas/allocator.rs @@ -33,6 +33,10 @@ impl Allocator { pub fn is_empty(&self) -> bool { self.allocations == 0 } + + pub fn allocations(&self) -> usize { + self.allocations + } } pub struct Region { diff --git a/wgpu/src/image/atlas/layer.rs b/wgpu/src/image/atlas/layer.rs index cf089601..fd6788d9 100644 --- a/wgpu/src/image/atlas/layer.rs +++ b/wgpu/src/image/atlas/layer.rs @@ -11,4 +11,12 @@ impl Layer { pub fn is_empty(&self) -> bool { matches!(self, Layer::Empty) } + + pub fn allocations(&self) -> usize { + match self { + Layer::Empty => 0, + Layer::Busy(allocator) => allocator.allocations(), + Layer::Full => 1, + } + } } diff --git a/wgpu/src/image/mod.rs b/wgpu/src/image/mod.rs index 86731cbf..8b831a3c 100644 --- a/wgpu/src/image/mod.rs +++ b/wgpu/src/image/mod.rs @@ -277,7 +277,7 @@ impl Pipeline { let texture_version = cache.layer_count(); if self.texture_version != texture_version { - log::info!("Atlas has grown. Recreating bind group..."); + log::debug!("Atlas has grown. Recreating bind group..."); self.texture = cache.create_bind_group(device, &self.texture_layout); diff --git a/wgpu/src/image/raster.rs b/wgpu/src/image/raster.rs index 441b294f..7a837f28 100644 --- a/wgpu/src/image/raster.rs +++ b/wgpu/src/image/raster.rs @@ -40,6 +40,7 @@ impl Memory { pub struct Cache { map: FxHashMap, hits: FxHashSet, + should_trim: bool, } impl Cache { @@ -55,6 +56,8 @@ impl Cache { Err(_) => Memory::Invalid, }; + self.should_trim = true; + self.insert(handle, memory); self.get(handle).unwrap() } @@ -86,6 +89,11 @@ impl Cache { /// Trim cache misses from cache pub fn trim(&mut self, atlas: &mut Atlas) { + // Only trim if new entries have landed in the `Cache` + if !self.should_trim { + return; + } + let hits = &self.hits; self.map.retain(|k, memory| { @@ -101,6 +109,7 @@ impl Cache { }); self.hits.clear(); + self.should_trim = false; } fn get(&mut self, handle: &image::Handle) -> Option<&mut Memory> { diff --git a/wgpu/src/image/vector.rs b/wgpu/src/image/vector.rs index d681b2e6..c6d829af 100644 --- a/wgpu/src/image/vector.rs +++ b/wgpu/src/image/vector.rs @@ -37,6 +37,7 @@ pub struct Cache { rasterized: FxHashMap<(u64, u32, u32, ColorFilter), atlas::Entry>, svg_hits: FxHashSet, rasterized_hits: FxHashSet<(u64, u32, u32, ColorFilter)>, + should_trim: bool, } type ColorFilter = Option<[u8; 4]>; @@ -76,6 +77,8 @@ impl Cache { } } + self.should_trim = true; + let _ = self.svgs.insert(handle.id(), svg); self.svgs.get(&handle.id()).unwrap() } @@ -176,6 +179,10 @@ impl Cache { /// Load svg and upload raster data pub fn trim(&mut self, atlas: &mut Atlas) { + if !self.should_trim { + return; + } + let svg_hits = &self.svg_hits; let rasterized_hits = &self.rasterized_hits; @@ -191,6 +198,7 @@ impl Cache { }); self.svg_hits.clear(); self.rasterized_hits.clear(); + self.should_trim = false; } } -- 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/text.rs | 13 ++++++------- wgpu/src/triangle.rs | 13 ++++++------- 2 files changed, 12 insertions(+), 14 deletions(-) (limited to 'wgpu/src') diff --git a/wgpu/src/text.rs b/wgpu/src/text.rs index 0d01faca..f20db026 100644 --- a/wgpu/src/text.rs +++ b/wgpu/src/text.rs @@ -4,9 +4,9 @@ use crate::graphics::color; use crate::graphics::text::cache::{self, Cache as BufferCache}; use crate::graphics::text::{font_system, to_color, Editor, Paragraph}; -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}; use std::sync::Arc; @@ -69,12 +69,12 @@ struct Upload { buffer_cache: BufferCache, transformation: Transformation, version: usize, + text: rc::Weak<[Text]>, } #[derive(Default)] pub struct Storage { uploads: FxHashMap, - recently_used: FxHashSet, } impl Storage { @@ -162,6 +162,7 @@ impl Storage { buffer_cache, transformation: new_transformation, version: 0, + text: Rc::downgrade(&cache.text), }); log::info!( @@ -171,13 +172,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.text.strong_count() > 0); } } 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/text.rs | 1 + wgpu/src/triangle.rs | 1 + 2 files changed, 2 insertions(+) (limited to 'wgpu/src') diff --git a/wgpu/src/text.rs b/wgpu/src/text.rs index f20db026..38712660 100644 --- a/wgpu/src/text.rs +++ b/wgpu/src/text.rs @@ -122,6 +122,7 @@ impl Storage { target_size, ); + upload.text = Rc::downgrade(&cache.text); upload.version = cache.version; upload.transformation = new_transformation; 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 b5b78d505e22cafccb4ecbf57dc61f536ca558ca Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 30 Apr 2024 07:57:54 +0200 Subject: Introduce `canvas::Cache` grouping Caches with the same `Group` will share their text atlas! --- wgpu/src/geometry.rs | 13 ++++++--- wgpu/src/text.rs | 74 +++++++++++++++++++++++++++++++++++++++------------- 2 files changed, 65 insertions(+), 22 deletions(-) (limited to 'wgpu/src') diff --git a/wgpu/src/geometry.rs b/wgpu/src/geometry.rs index 60967082..f6213e1d 100644 --- a/wgpu/src/geometry.rs +++ b/wgpu/src/geometry.rs @@ -3,6 +3,7 @@ use crate::core::text::LineHeight; use crate::core::{ Pixels, Point, Radians, Rectangle, Size, Transformation, Vector, }; +use crate::graphics::cache::{self, Cached}; use crate::graphics::color; use crate::graphics::geometry::fill::{self, Fill}; use crate::graphics::geometry::{ @@ -10,7 +11,7 @@ use crate::graphics::geometry::{ }; use crate::graphics::gradient::{self, Gradient}; use crate::graphics::mesh::{self, Mesh}; -use crate::graphics::{self, Cached, Text}; +use crate::graphics::{self, Text}; use crate::text; use crate::triangle; @@ -38,7 +39,11 @@ impl Cached for Geometry { Geometry::Cached(cache.clone()) } - fn cache(self, previous: Option) -> Self::Cache { + fn cache( + self, + group: cache::Group, + previous: Option, + ) -> Self::Cache { match self { Self::Live { meshes, text } => { if let Some(mut previous) = previous { @@ -51,14 +56,14 @@ impl Cached for Geometry { if let Some(cache) = &mut previous.text { cache.update(text); } else { - previous.text = text::Cache::new(text); + previous.text = text::Cache::new(group, text); } previous } else { Cache { meshes: triangle::Cache::new(meshes), - text: text::Cache::new(text), + text: text::Cache::new(group, text), } } } diff --git a/wgpu/src/text.rs b/wgpu/src/text.rs index 38712660..5806863c 100644 --- a/wgpu/src/text.rs +++ b/wgpu/src/text.rs @@ -1,7 +1,8 @@ use crate::core::alignment; use crate::core::{Rectangle, Size, Transformation}; +use crate::graphics::cache; use crate::graphics::color; -use crate::graphics::text::cache::{self, Cache as BufferCache}; +use crate::graphics::text::cache::{self as text_cache, Cache as BufferCache}; use crate::graphics::text::{font_system, to_color, Editor, Paragraph}; use rustc_hash::FxHashMap; @@ -35,6 +36,7 @@ pub enum Item { #[derive(Debug, Clone)] pub struct Cache { id: Id, + group: cache::Group, text: Rc<[Text]>, version: usize, } @@ -43,7 +45,7 @@ pub struct Cache { pub struct Id(u64); impl Cache { - pub fn new(text: Vec) -> Option { + pub fn new(group: cache::Group, text: Vec) -> Option { static NEXT_ID: AtomicU64 = AtomicU64::new(0); if text.is_empty() { @@ -52,6 +54,7 @@ impl Cache { Some(Self { id: Id(NEXT_ID.fetch_add(1, atomic::Ordering::Relaxed)), + group, text: Rc::from(text), version: 0, }) @@ -65,29 +68,39 @@ impl Cache { struct Upload { renderer: glyphon::TextRenderer, - atlas: glyphon::TextAtlas, buffer_cache: BufferCache, transformation: Transformation, version: usize, text: rc::Weak<[Text]>, + _atlas: rc::Weak<()>, } #[derive(Default)] pub struct Storage { + groups: FxHashMap, uploads: FxHashMap, } +struct Group { + atlas: glyphon::TextAtlas, + previous_uploads: usize, + handle: Rc<()>, +} + impl Storage { pub fn new() -> Self { Self::default() } - fn get(&self, cache: &Cache) -> Option<&Upload> { + fn get(&self, cache: &Cache) -> Option<(&glyphon::TextAtlas, &Upload)> { if cache.text.is_empty() { return None; } - self.uploads.get(&cache.id) + self.groups + .get(&cache.group) + .map(|group| &group.atlas) + .zip(self.uploads.get(&cache.id)) } fn prepare( @@ -101,6 +114,20 @@ impl Storage { bounds: Rectangle, target_size: Size, ) { + let group_count = self.groups.len(); + + let group = self.groups.entry(cache.group).or_insert_with(|| { + log::info!("New text atlas created (total: {})", group_count + 1); + + Group { + atlas: glyphon::TextAtlas::with_color_mode( + device, queue, format, COLOR_MODE, + ), + previous_uploads: 0, + handle: Rc::new(()), + } + }); + match self.uploads.entry(cache.id) { hash_map::Entry::Occupied(entry) => { let upload = entry.into_mut(); @@ -114,7 +141,7 @@ impl Storage { queue, encoder, &mut upload.renderer, - &mut upload.atlas, + &mut group.atlas, &mut upload.buffer_cache, &cache.text, bounds, @@ -127,16 +154,11 @@ impl Storage { upload.transformation = new_transformation; upload.buffer_cache.trim(); - upload.atlas.trim(); } } hash_map::Entry::Vacant(entry) => { - let mut atlas = glyphon::TextAtlas::with_color_mode( - device, queue, format, COLOR_MODE, - ); - let mut renderer = glyphon::TextRenderer::new( - &mut atlas, + &mut group.atlas, device, wgpu::MultisampleState::default(), None, @@ -149,7 +171,7 @@ impl Storage { queue, encoder, &mut renderer, - &mut atlas, + &mut group.atlas, &mut buffer_cache, &cache.text, bounds, @@ -159,11 +181,11 @@ impl Storage { let _ = entry.insert(Upload { renderer, - atlas, buffer_cache, transformation: new_transformation, version: 0, text: Rc::downgrade(&cache.text), + _atlas: Rc::downgrade(&group.handle), }); log::info!( @@ -178,6 +200,22 @@ impl Storage { pub fn trim(&mut self) { self.uploads .retain(|_id, upload| upload.text.strong_count() > 0); + + self.groups.retain(|_id, group| { + let uploads_alive = Rc::weak_count(&group.handle); + + if uploads_alive == 0 { + return false; + } + + if uploads_alive < group.previous_uploads { + group.atlas.trim(); + } + + group.previous_uploads = uploads_alive; + + true + }); } } @@ -306,10 +344,10 @@ impl Pipeline { layer_count += 1; } Item::Cached { cache, .. } => { - if let Some(upload) = storage.get(cache) { + if let Some((atlas, upload)) = storage.get(cache) { upload .renderer - .render(&upload.atlas, render_pass) + .render(atlas, render_pass) .expect("Render cached text"); } } @@ -345,7 +383,7 @@ fn prepare( enum Allocation { Paragraph(Paragraph), Editor(Editor), - Cache(cache::KeyHash), + Cache(text_cache::KeyHash), Raw(Arc), } @@ -369,7 +407,7 @@ fn prepare( } => { let (key, _) = buffer_cache.allocate( font_system, - cache::Key { + text_cache::Key { content, size: f32::from(*size), line_height: f32::from(*line_height), -- 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/text.rs | 36 +++++++++++++++++++++++++----------- wgpu/src/triangle.rs | 2 +- 2 files changed, 26 insertions(+), 12 deletions(-) (limited to 'wgpu/src') diff --git a/wgpu/src/text.rs b/wgpu/src/text.rs index 5806863c..8ec1d56a 100644 --- a/wgpu/src/text.rs +++ b/wgpu/src/text.rs @@ -71,6 +71,7 @@ struct Upload { buffer_cache: BufferCache, transformation: Transformation, version: usize, + group_version: usize, text: rc::Weak<[Text]>, _atlas: rc::Weak<()>, } @@ -83,8 +84,9 @@ pub struct Storage { struct Group { atlas: glyphon::TextAtlas, - previous_uploads: usize, - handle: Rc<()>, + version: usize, + should_trim: bool, + handle: Rc<()>, // Keeps track of active uploads } impl Storage { @@ -117,13 +119,18 @@ impl Storage { let group_count = self.groups.len(); let group = self.groups.entry(cache.group).or_insert_with(|| { - log::info!("New text atlas created (total: {})", group_count + 1); + log::debug!( + "New text atlas: {:?} (total: {})", + cache.group, + group_count + 1 + ); Group { atlas: glyphon::TextAtlas::with_color_mode( device, queue, format, COLOR_MODE, ), - previous_uploads: 0, + version: 0, + should_trim: false, handle: Rc::new(()), } }); @@ -134,6 +141,7 @@ impl Storage { if !cache.text.is_empty() && (upload.version != cache.version + || upload.group_version != group.version || upload.transformation != new_transformation) { let _ = prepare( @@ -151,9 +159,11 @@ impl Storage { upload.text = Rc::downgrade(&cache.text); upload.version = cache.version; + upload.group_version = group.version; upload.transformation = new_transformation; upload.buffer_cache.trim(); + group.should_trim = true; } } hash_map::Entry::Vacant(entry) => { @@ -184,11 +194,12 @@ impl Storage { buffer_cache, transformation: new_transformation, version: 0, + group_version: group.version, text: Rc::downgrade(&cache.text), _atlas: Rc::downgrade(&group.handle), }); - log::info!( + log::debug!( "New text upload: {} (total: {})", cache.id.0, self.uploads.len() @@ -201,18 +212,21 @@ impl Storage { self.uploads .retain(|_id, upload| upload.text.strong_count() > 0); - self.groups.retain(|_id, group| { - let uploads_alive = Rc::weak_count(&group.handle); + self.groups.retain(|id, group| { + if Rc::weak_count(&group.handle) == 0 { + log::debug!("Dropping text atlas: {id:?}"); - if uploads_alive == 0 { return false; } - if uploads_alive < group.previous_uploads { + if group.should_trim { + log::debug!("Trimming text atlas: {id:?}"); + group.atlas.trim(); - } - group.previous_uploads = uploads_alive; + group.version += 1; + group.should_trim = false; + } true }); 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 From b276a603a17eda219b32f207aa53e2b6a1321a9f Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 30 Apr 2024 23:15:04 +0200 Subject: Fix cache trimming loop in `iced_wgpu::text` --- wgpu/src/text.rs | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) (limited to 'wgpu/src') diff --git a/wgpu/src/text.rs b/wgpu/src/text.rs index 8ec1d56a..2508906f 100644 --- a/wgpu/src/text.rs +++ b/wgpu/src/text.rs @@ -157,13 +157,16 @@ impl Storage { target_size, ); + // Only trim if glyphs have changed + group.should_trim = + group.should_trim || upload.version != cache.version; + upload.text = Rc::downgrade(&cache.text); upload.version = cache.version; upload.group_version = group.version; upload.transformation = new_transformation; upload.buffer_cache.trim(); - group.should_trim = true; } } hash_map::Entry::Vacant(entry) => { @@ -213,19 +216,28 @@ impl Storage { .retain(|_id, upload| upload.text.strong_count() > 0); self.groups.retain(|id, group| { - if Rc::weak_count(&group.handle) == 0 { + let active_uploads = Rc::weak_count(&group.handle); + + if active_uploads == 0 { log::debug!("Dropping text atlas: {id:?}"); return false; } - if group.should_trim { - log::debug!("Trimming text atlas: {id:?}"); + if id.is_singleton() || group.should_trim { + log::debug!( + "Trimming text atlas: {id:?} (uploads: {active_uploads})" + ); group.atlas.trim(); - - group.version += 1; group.should_trim = false; + + // We only need to worry about glyph fighting + // when the atlas may be shared by multiple + // uploads. + if !id.is_singleton() { + group.version += 1; + } } true -- cgit From 7e2d0dc931a4409e661851a1c7dffdcfd5b2f93a Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 30 Apr 2024 23:51:00 +0200 Subject: Keep text atlases alive during temporary empty uploads --- wgpu/src/text.rs | 76 ++++++++++++++++++++++++++++++++------------------------ 1 file changed, 44 insertions(+), 32 deletions(-) (limited to 'wgpu/src') diff --git a/wgpu/src/text.rs b/wgpu/src/text.rs index 2508906f..7e683c77 100644 --- a/wgpu/src/text.rs +++ b/wgpu/src/text.rs @@ -61,6 +61,10 @@ impl Cache { } pub fn update(&mut self, text: Vec) { + if self.text.is_empty() && text.is_empty() { + return; + } + self.text = Rc::from(text); self.version += 1; } @@ -139,23 +143,24 @@ impl Storage { hash_map::Entry::Occupied(entry) => { let upload = entry.into_mut(); - if !cache.text.is_empty() - && (upload.version != cache.version - || upload.group_version != group.version - || upload.transformation != new_transformation) + if upload.version != cache.version + || upload.group_version != group.version + || upload.transformation != new_transformation { - let _ = prepare( - device, - queue, - encoder, - &mut upload.renderer, - &mut group.atlas, - &mut upload.buffer_cache, - &cache.text, - bounds, - new_transformation, - target_size, - ); + if !cache.text.is_empty() { + let _ = prepare( + device, + queue, + encoder, + &mut upload.renderer, + &mut group.atlas, + &mut upload.buffer_cache, + &cache.text, + bounds, + new_transformation, + target_size, + ); + } // Only trim if glyphs have changed group.should_trim = @@ -179,18 +184,20 @@ impl Storage { let mut buffer_cache = BufferCache::new(); - let _ = prepare( - device, - queue, - encoder, - &mut renderer, - &mut group.atlas, - &mut buffer_cache, - &cache.text, - bounds, - new_transformation, - target_size, - ); + if !cache.text.is_empty() { + let _ = prepare( + device, + queue, + encoder, + &mut renderer, + &mut group.atlas, + &mut buffer_cache, + &cache.text, + bounds, + new_transformation, + target_size, + ); + } let _ = entry.insert(Upload { renderer, @@ -202,6 +209,8 @@ impl Storage { _atlas: Rc::downgrade(&group.handle), }); + group.should_trim = cache.group.is_singleton(); + log::debug!( "New text upload: {} (total: {})", cache.id.0, @@ -224,10 +233,8 @@ impl Storage { return false; } - if id.is_singleton() || group.should_trim { - log::debug!( - "Trimming text atlas: {id:?} (uploads: {active_uploads})" - ); + if group.should_trim { + log::trace!("Trimming text atlas: {id:?}"); group.atlas.trim(); group.should_trim = false; @@ -236,6 +243,11 @@ impl Storage { // when the atlas may be shared by multiple // uploads. if !id.is_singleton() { + log::debug!( + "Invalidating text atlas: {id:?} \ + (uploads: {active_uploads})" + ); + group.version += 1; } } -- cgit From 45254ab88c6ca76759523069c2fb8734de626f02 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Wed, 1 May 2024 00:55:49 +0200 Subject: Use `Bytes` as the `Container` of `ImageBuffer` Since we don't need to mutate images once loaded, we avoid unnecessary extra allocations. --- wgpu/src/image/raster.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'wgpu/src') diff --git a/wgpu/src/image/raster.rs b/wgpu/src/image/raster.rs index 7a837f28..60e9cbad 100644 --- a/wgpu/src/image/raster.rs +++ b/wgpu/src/image/raster.rs @@ -10,7 +10,7 @@ use rustc_hash::{FxHashMap, FxHashSet}; #[derive(Debug)] pub enum Memory { /// Image data on host - Host(image_rs::ImageBuffer, Vec>), + Host(image_rs::ImageBuffer, image::Bytes>), /// Storage entry Device(atlas::Entry), /// Image not found @@ -51,7 +51,7 @@ impl Cache { } let memory = match graphics::image::load(handle) { - Ok(image) => Memory::Host(image.to_rgba8()), + Ok(image) => Memory::Host(image), Err(image_rs::error::ImageError::IoError(_)) => Memory::NotFound, Err(_) => Memory::Invalid, }; -- cgit From b52c7bb610f593fffc624d461dca17ac50c81626 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Wed, 1 May 2024 01:39:43 +0200 Subject: Use an opaque `Id` type for `image::Handle` Hashing pointers is a terrible idea. --- wgpu/src/image/raster.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'wgpu/src') diff --git a/wgpu/src/image/raster.rs b/wgpu/src/image/raster.rs index 60e9cbad..4d3c3125 100644 --- a/wgpu/src/image/raster.rs +++ b/wgpu/src/image/raster.rs @@ -38,8 +38,8 @@ impl Memory { /// Caches image raster data #[derive(Debug, Default)] pub struct Cache { - map: FxHashMap, - hits: FxHashSet, + map: FxHashMap, + hits: FxHashSet, should_trim: bool, } -- cgit From ffa6614026cfc0dbdd636ad5a334008c1ee5532d Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Thu, 2 May 2024 08:44:09 +0200 Subject: Fix panic in `wgpu::color::convert` --- wgpu/src/color.rs | 68 ++++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 50 insertions(+), 18 deletions(-) (limited to 'wgpu/src') diff --git a/wgpu/src/color.rs b/wgpu/src/color.rs index 890f3f89..9d593d9c 100644 --- a/wgpu/src/color.rs +++ b/wgpu/src/color.rs @@ -1,5 +1,7 @@ use std::borrow::Cow; +use wgpu::util::DeviceExt; + pub fn convert( device: &wgpu::Device, encoder: &mut wgpu::CommandEncoder, @@ -15,28 +17,58 @@ pub fn convert( ..wgpu::SamplerDescriptor::default() }); - //sampler in 0 - let sampler_layout = + #[derive(Debug, Clone, Copy, bytemuck::Zeroable, bytemuck::Pod)] + #[repr(C)] + struct Ratio { + u: f32, + v: f32, + } + + let ratio = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { + label: Some("iced-wgpu::triangle::msaa ratio"), + contents: bytemuck::bytes_of(&Ratio { u: 1.0, v: 1.0 }), + usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::UNIFORM, + }); + + let constant_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { label: Some("iced_wgpu.offscreen.blit.sampler_layout"), - entries: &[wgpu::BindGroupLayoutEntry { - binding: 0, - visibility: wgpu::ShaderStages::FRAGMENT, - ty: wgpu::BindingType::Sampler( - wgpu::SamplerBindingType::NonFiltering, - ), - count: None, - }], + entries: &[ + wgpu::BindGroupLayoutEntry { + binding: 0, + visibility: wgpu::ShaderStages::FRAGMENT, + ty: wgpu::BindingType::Sampler( + wgpu::SamplerBindingType::NonFiltering, + ), + count: None, + }, + wgpu::BindGroupLayoutEntry { + binding: 1, + visibility: wgpu::ShaderStages::VERTEX, + ty: wgpu::BindingType::Buffer { + ty: wgpu::BufferBindingType::Uniform, + has_dynamic_offset: false, + min_binding_size: None, + }, + count: None, + }, + ], }); - let sampler_bind_group = + let constant_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { label: Some("iced_wgpu.offscreen.sampler.bind_group"), - layout: &sampler_layout, - entries: &[wgpu::BindGroupEntry { - binding: 0, - resource: wgpu::BindingResource::Sampler(&sampler), - }], + layout: &constant_layout, + entries: &[ + wgpu::BindGroupEntry { + binding: 0, + resource: wgpu::BindingResource::Sampler(&sampler), + }, + wgpu::BindGroupEntry { + binding: 1, + resource: ratio.as_entire_binding(), + }, + ], }); let texture_layout = @@ -59,7 +91,7 @@ pub fn convert( let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { label: Some("iced_wgpu.offscreen.blit.pipeline_layout"), - bind_group_layouts: &[&sampler_layout, &texture_layout], + bind_group_layouts: &[&constant_layout, &texture_layout], push_constant_ranges: &[], }); @@ -152,7 +184,7 @@ pub fn convert( }); pass.set_pipeline(&pipeline); - pass.set_bind_group(0, &sampler_bind_group, &[]); + pass.set_bind_group(0, &constant_bind_group, &[]); pass.set_bind_group(1, &texture_bind_group, &[]); pass.draw(0..6, 0..1); -- cgit From aae8e4f5cfabfc3725ac938023fa29a6737a380c Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Thu, 2 May 2024 17:23:32 +0200 Subject: Fix `clippy` lints for new `1.78` stable toolchain --- wgpu/src/primitive.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'wgpu/src') diff --git a/wgpu/src/primitive.rs b/wgpu/src/primitive.rs index 1313e752..8641f27a 100644 --- a/wgpu/src/primitive.rs +++ b/wgpu/src/primitive.rs @@ -67,7 +67,7 @@ pub struct Storage { impl Storage { /// Returns `true` if `Storage` contains a type `T`. pub fn has(&self) -> bool { - self.pipelines.get(&TypeId::of::()).is_some() + self.pipelines.contains_key(&TypeId::of::()) } /// Inserts the data `T` in to [`Storage`]. -- cgit From 09a6bcfffc24f5abdc8709403bab7ae1e01563f1 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Thu, 2 May 2024 13:15:17 +0200 Subject: Add `Image` rotation support Co-authored-by: DKolter <68352124+DKolter@users.noreply.github.com> --- wgpu/src/image/mod.rs | 65 +++++++++++++++++++++++++++++++++++++++------- wgpu/src/layer.rs | 13 ++++++++-- wgpu/src/lib.rs | 22 ++++++++++++++-- wgpu/src/shader/image.wgsl | 37 ++++++++++++++++++++------ 4 files changed, 116 insertions(+), 21 deletions(-) (limited to 'wgpu/src') diff --git a/wgpu/src/image/mod.rs b/wgpu/src/image/mod.rs index 8b831a3c..69f8a8ca 100644 --- a/wgpu/src/image/mod.rs +++ b/wgpu/src/image/mod.rs @@ -135,14 +135,20 @@ impl Pipeline { attributes: &wgpu::vertex_attr_array!( // Position 0 => Float32x2, - // Scale + // Center 1 => Float32x2, - // Atlas position + // Image size 2 => Float32x2, + // Rotation + 3 => Float32, + // Scale + 4 => Float32x2, + // Atlas position + 5 => Float32x2, // Atlas scale - 3 => Float32x2, + 6 => Float32x2, // Layer - 4 => Sint32, + 7 => Sint32, ), }], }, @@ -208,9 +214,10 @@ impl Pipeline { belt: &mut wgpu::util::StagingBelt, images: &Batch, transformation: Transformation, - scale: f32, + global_scale: f32, ) { - let transformation = transformation * Transformation::scale(scale); + let transformation = + transformation * Transformation::scale(global_scale); let nearest_instances: &mut Vec = &mut Vec::new(); let linear_instances: &mut Vec = &mut Vec::new(); @@ -224,6 +231,8 @@ impl Pipeline { handle, filter_method, bounds, + rotation, + scale, } => { if let Some(atlas_entry) = cache.upload_raster(device, encoder, handle) @@ -231,6 +240,8 @@ impl Pipeline { add_instances( [bounds.x, bounds.y], [bounds.width, bounds.height], + *rotation, + [scale.width, scale.height], atlas_entry, match filter_method { crate::core::image::FilterMethod::Nearest => { @@ -251,15 +262,24 @@ impl Pipeline { handle, color, bounds, + rotation, + scale, } => { let size = [bounds.width, bounds.height]; if let Some(atlas_entry) = cache.upload_vector( - device, encoder, handle, *color, size, scale, + device, + encoder, + handle, + *color, + size, + global_scale, ) { add_instances( [bounds.x, bounds.y], size, + *rotation, + [scale.width, scale.height], atlas_entry, nearest_instances, ); @@ -487,7 +507,10 @@ impl Data { #[derive(Debug, Clone, Copy, Zeroable, Pod)] struct Instance { _position: [f32; 2], + _center: [f32; 2], _size: [f32; 2], + _rotation: f32, + _scale: [f32; 2], _position_in_atlas: [f32; 2], _size_in_atlas: [f32; 2], _layer: u32, @@ -506,12 +529,27 @@ struct Uniforms { fn add_instances( image_position: [f32; 2], image_size: [f32; 2], + rotation: f32, + scale: [f32; 2], entry: &atlas::Entry, instances: &mut Vec, ) { + let center = [ + image_position[0] + image_size[0] / 2.0, + image_position[1] + image_size[1] / 2.0, + ]; + match entry { atlas::Entry::Contiguous(allocation) => { - add_instance(image_position, image_size, allocation, instances); + add_instance( + image_position, + center, + image_size, + rotation, + scale, + allocation, + instances, + ); } atlas::Entry::Fragmented { fragments, size } => { let scaling_x = image_size[0] / size.width as f32; @@ -537,7 +575,10 @@ fn add_instances( fragment_height as f32 * scaling_y, ]; - add_instance(position, size, allocation, instances); + add_instance( + position, center, size, rotation, scale, allocation, + instances, + ); } } } @@ -546,7 +587,10 @@ fn add_instances( #[inline] fn add_instance( position: [f32; 2], + center: [f32; 2], size: [f32; 2], + rotation: f32, + scale: [f32; 2], allocation: &atlas::Allocation, instances: &mut Vec, ) { @@ -556,7 +600,10 @@ fn add_instance( let instance = Instance { _position: position, + _center: center, _size: size, + _rotation: rotation, + _scale: scale, _position_in_atlas: [ (x as f32 + 0.5) / atlas::SIZE as f32, (y as f32 + 0.5) / atlas::SIZE as f32, diff --git a/wgpu/src/layer.rs b/wgpu/src/layer.rs index 9526c5a8..648ec476 100644 --- a/wgpu/src/layer.rs +++ b/wgpu/src/layer.rs @@ -1,5 +1,6 @@ -use crate::core::renderer; -use crate::core::{Background, Color, Point, Rectangle, Transformation}; +use crate::core::{ + renderer, Background, Color, Point, Rectangle, Size, Transformation, +}; use crate::graphics; use crate::graphics::color; use crate::graphics::layer; @@ -117,11 +118,15 @@ impl Layer { filter_method: crate::core::image::FilterMethod, bounds: Rectangle, transformation: Transformation, + rotation: f32, + scale: Size, ) { let image = Image::Raster { handle, filter_method, bounds: bounds * transformation, + rotation, + scale, }; self.images.push(image); @@ -133,11 +138,15 @@ impl Layer { color: Option, bounds: Rectangle, transformation: Transformation, + rotation: f32, + scale: Size, ) { let svg = Image::Vector { handle, color, bounds: bounds * transformation, + rotation, + scale, }; self.images.push(svg); diff --git a/wgpu/src/lib.rs b/wgpu/src/lib.rs index 178522de..a42d71c3 100644 --- a/wgpu/src/lib.rs +++ b/wgpu/src/lib.rs @@ -517,9 +517,18 @@ impl core::image::Renderer for Renderer { handle: Self::Handle, filter_method: core::image::FilterMethod, bounds: Rectangle, + rotation: f32, + scale: Size, ) { let (layer, transformation) = self.layers.current_mut(); - layer.draw_image(handle, filter_method, bounds, transformation); + layer.draw_image( + handle, + filter_method, + bounds, + transformation, + rotation, + scale, + ); } } @@ -534,9 +543,18 @@ impl core::svg::Renderer for Renderer { handle: core::svg::Handle, color_filter: Option, bounds: Rectangle, + rotation: f32, + scale: Size, ) { let (layer, transformation) = self.layers.current_mut(); - layer.draw_svg(handle, color_filter, bounds, transformation); + layer.draw_svg( + handle, + color_filter, + bounds, + transformation, + rotation, + scale, + ); } } diff --git a/wgpu/src/shader/image.wgsl b/wgpu/src/shader/image.wgsl index 7b2e5238..de962098 100644 --- a/wgpu/src/shader/image.wgsl +++ b/wgpu/src/shader/image.wgsl @@ -9,10 +9,13 @@ struct Globals { struct VertexInput { @builtin(vertex_index) vertex_index: u32, @location(0) pos: vec2, - @location(1) scale: vec2, - @location(2) atlas_pos: vec2, - @location(3) atlas_scale: vec2, - @location(4) layer: i32, + @location(1) center: vec2, + @location(2) image_size: vec2, + @location(3) rotation: f32, + @location(4) scale: vec2, + @location(5) atlas_pos: vec2, + @location(6) atlas_scale: vec2, + @location(7) layer: i32, } struct VertexOutput { @@ -25,24 +28,42 @@ struct VertexOutput { fn vs_main(input: VertexInput) -> VertexOutput { var out: VertexOutput; - let v_pos = vertex_position(input.vertex_index); + // Generate a vertex position in the range [0, 1] from the vertex index. + var v_pos = vertex_position(input.vertex_index); + // Map the vertex position to the atlas texture. out.uv = vec2(v_pos * input.atlas_scale + input.atlas_pos); out.layer = f32(input.layer); - var transform: mat4x4 = mat4x4( + // Calculate the vertex position and move the center to the origin + v_pos = input.pos + v_pos * input.image_size - input.center; + + // Apply the rotation around the center of the image + let cos_rot = cos(input.rotation); + let sin_rot = sin(input.rotation); + let rotate = mat4x4( + vec4(cos_rot, sin_rot, 0.0, 0.0), + vec4(-sin_rot, cos_rot, 0.0, 0.0), + vec4(0.0, 0.0, 1.0, 0.0), + vec4(0.0, 0.0, 0.0, 1.0) + ); + + // Scale the image and then translate to the final position by moving the center to the position + let scale_translate = mat4x4( vec4(input.scale.x, 0.0, 0.0, 0.0), vec4(0.0, input.scale.y, 0.0, 0.0), vec4(0.0, 0.0, 1.0, 0.0), - vec4(input.pos, 0.0, 1.0) + vec4(input.center, 0.0, 1.0) ); - out.position = globals.transform * transform * vec4(v_pos, 0.0, 1.0); + // Calculate the final position of the vertex + out.position = globals.transform * scale_translate * rotate * vec4(v_pos, 0.0, 1.0); return out; } @fragment fn fs_main(input: VertexOutput) -> @location(0) vec4 { + // Sample the texture at the given UV coordinate and layer. return textureSample(u_texture, u_sampler, input.uv, i32(input.layer)); } -- cgit From a57313b23ecb9843856ca0ea08635b6121fcb2cb Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Thu, 2 May 2024 15:21:22 +0200 Subject: Simplify image rotation API and its internals --- wgpu/src/image/mod.rs | 24 ++++++------------------ wgpu/src/layer.rs | 10 +++------- wgpu/src/lib.rs | 20 +++++--------------- wgpu/src/shader/image.wgsl | 21 ++++++--------------- 4 files changed, 20 insertions(+), 55 deletions(-) (limited to 'wgpu/src') diff --git a/wgpu/src/image/mod.rs b/wgpu/src/image/mod.rs index 69f8a8ca..3ec341fc 100644 --- a/wgpu/src/image/mod.rs +++ b/wgpu/src/image/mod.rs @@ -141,14 +141,12 @@ impl Pipeline { 2 => Float32x2, // Rotation 3 => Float32, - // Scale - 4 => Float32x2, // Atlas position - 5 => Float32x2, + 4 => Float32x2, // Atlas scale - 6 => Float32x2, + 5 => Float32x2, // Layer - 7 => Sint32, + 6 => Sint32, ), }], }, @@ -232,7 +230,6 @@ impl Pipeline { filter_method, bounds, rotation, - scale, } => { if let Some(atlas_entry) = cache.upload_raster(device, encoder, handle) @@ -240,8 +237,7 @@ impl Pipeline { add_instances( [bounds.x, bounds.y], [bounds.width, bounds.height], - *rotation, - [scale.width, scale.height], + f32::from(*rotation), atlas_entry, match filter_method { crate::core::image::FilterMethod::Nearest => { @@ -263,7 +259,6 @@ impl Pipeline { color, bounds, rotation, - scale, } => { let size = [bounds.width, bounds.height]; @@ -278,8 +273,7 @@ impl Pipeline { add_instances( [bounds.x, bounds.y], size, - *rotation, - [scale.width, scale.height], + f32::from(*rotation), atlas_entry, nearest_instances, ); @@ -510,7 +504,6 @@ struct Instance { _center: [f32; 2], _size: [f32; 2], _rotation: f32, - _scale: [f32; 2], _position_in_atlas: [f32; 2], _size_in_atlas: [f32; 2], _layer: u32, @@ -530,7 +523,6 @@ fn add_instances( image_position: [f32; 2], image_size: [f32; 2], rotation: f32, - scale: [f32; 2], entry: &atlas::Entry, instances: &mut Vec, ) { @@ -546,7 +538,6 @@ fn add_instances( center, image_size, rotation, - scale, allocation, instances, ); @@ -576,8 +567,7 @@ fn add_instances( ]; add_instance( - position, center, size, rotation, scale, allocation, - instances, + position, center, size, rotation, allocation, instances, ); } } @@ -590,7 +580,6 @@ fn add_instance( center: [f32; 2], size: [f32; 2], rotation: f32, - scale: [f32; 2], allocation: &atlas::Allocation, instances: &mut Vec, ) { @@ -603,7 +592,6 @@ fn add_instance( _center: center, _size: size, _rotation: rotation, - _scale: scale, _position_in_atlas: [ (x as f32 + 0.5) / atlas::SIZE as f32, (y as f32 + 0.5) / atlas::SIZE as f32, diff --git a/wgpu/src/layer.rs b/wgpu/src/layer.rs index 648ec476..e0242c59 100644 --- a/wgpu/src/layer.rs +++ b/wgpu/src/layer.rs @@ -1,5 +1,5 @@ use crate::core::{ - renderer, Background, Color, Point, Rectangle, Size, Transformation, + renderer, Background, Color, Point, Radians, Rectangle, Transformation, }; use crate::graphics; use crate::graphics::color; @@ -118,15 +118,13 @@ impl Layer { filter_method: crate::core::image::FilterMethod, bounds: Rectangle, transformation: Transformation, - rotation: f32, - scale: Size, + rotation: Radians, ) { let image = Image::Raster { handle, filter_method, bounds: bounds * transformation, rotation, - scale, }; self.images.push(image); @@ -138,15 +136,13 @@ impl Layer { color: Option, bounds: Rectangle, transformation: Transformation, - rotation: f32, - scale: Size, + rotation: Radians, ) { let svg = Image::Vector { handle, color, bounds: bounds * transformation, rotation, - scale, }; self.images.push(svg); diff --git a/wgpu/src/lib.rs b/wgpu/src/lib.rs index a42d71c3..6920067b 100644 --- a/wgpu/src/lib.rs +++ b/wgpu/src/lib.rs @@ -61,7 +61,8 @@ pub use settings::Settings; pub use geometry::Geometry; use crate::core::{ - Background, Color, Font, Pixels, Point, Rectangle, Size, Transformation, + Background, Color, Font, Pixels, Point, Radians, Rectangle, Size, + Transformation, Vector, }; use crate::graphics::text::{Editor, Paragraph}; use crate::graphics::Viewport; @@ -378,7 +379,6 @@ impl Renderer { use crate::core::alignment; use crate::core::text::Renderer as _; use crate::core::Renderer as _; - use crate::core::Vector; self.with_layer( Rectangle::with_size(viewport.logical_size()), @@ -517,8 +517,7 @@ impl core::image::Renderer for Renderer { handle: Self::Handle, filter_method: core::image::FilterMethod, bounds: Rectangle, - rotation: f32, - scale: Size, + rotation: Radians, ) { let (layer, transformation) = self.layers.current_mut(); layer.draw_image( @@ -527,7 +526,6 @@ impl core::image::Renderer for Renderer { bounds, transformation, rotation, - scale, ); } } @@ -543,18 +541,10 @@ impl core::svg::Renderer for Renderer { handle: core::svg::Handle, color_filter: Option, bounds: Rectangle, - rotation: f32, - scale: Size, + rotation: Radians, ) { let (layer, transformation) = self.layers.current_mut(); - layer.draw_svg( - handle, - color_filter, - bounds, - transformation, - rotation, - scale, - ); + layer.draw_svg(handle, color_filter, bounds, transformation, rotation); } } diff --git a/wgpu/src/shader/image.wgsl b/wgpu/src/shader/image.wgsl index de962098..71bf939c 100644 --- a/wgpu/src/shader/image.wgsl +++ b/wgpu/src/shader/image.wgsl @@ -10,12 +10,11 @@ struct VertexInput { @builtin(vertex_index) vertex_index: u32, @location(0) pos: vec2, @location(1) center: vec2, - @location(2) image_size: vec2, + @location(2) scale: vec2, @location(3) rotation: f32, - @location(4) scale: vec2, - @location(5) atlas_pos: vec2, - @location(6) atlas_scale: vec2, - @location(7) layer: i32, + @location(4) atlas_pos: vec2, + @location(5) atlas_scale: vec2, + @location(6) layer: i32, } struct VertexOutput { @@ -36,7 +35,7 @@ fn vs_main(input: VertexInput) -> VertexOutput { out.layer = f32(input.layer); // Calculate the vertex position and move the center to the origin - v_pos = input.pos + v_pos * input.image_size - input.center; + v_pos = input.pos + v_pos * input.scale - input.center; // Apply the rotation around the center of the image let cos_rot = cos(input.rotation); @@ -48,16 +47,8 @@ fn vs_main(input: VertexInput) -> VertexOutput { vec4(0.0, 0.0, 0.0, 1.0) ); - // Scale the image and then translate to the final position by moving the center to the position - let scale_translate = mat4x4( - vec4(input.scale.x, 0.0, 0.0, 0.0), - vec4(0.0, input.scale.y, 0.0, 0.0), - vec4(0.0, 0.0, 1.0, 0.0), - vec4(input.center, 0.0, 1.0) - ); - // Calculate the final position of the vertex - out.position = globals.transform * scale_translate * rotate * vec4(v_pos, 0.0, 1.0); + out.position = globals.transform * (vec4(input.center, 0.0, 0.0) + rotate * vec4(v_pos, 0.0, 1.0)); return out; } -- cgit From 610394b6957d9424aec1c50d927e34a0fb3fe5fd Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Thu, 2 May 2024 15:28:46 +0200 Subject: Rename `global_scale` to `scale` in `wgpu::image` --- wgpu/src/image/mod.rs | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) (limited to 'wgpu/src') diff --git a/wgpu/src/image/mod.rs b/wgpu/src/image/mod.rs index 3ec341fc..285eb2f6 100644 --- a/wgpu/src/image/mod.rs +++ b/wgpu/src/image/mod.rs @@ -212,10 +212,9 @@ impl Pipeline { belt: &mut wgpu::util::StagingBelt, images: &Batch, transformation: Transformation, - global_scale: f32, + scale: f32, ) { - let transformation = - transformation * Transformation::scale(global_scale); + let transformation = transformation * Transformation::scale(scale); let nearest_instances: &mut Vec = &mut Vec::new(); let linear_instances: &mut Vec = &mut Vec::new(); @@ -263,12 +262,7 @@ impl Pipeline { let size = [bounds.width, bounds.height]; if let Some(atlas_entry) = cache.upload_vector( - device, - encoder, - handle, - *color, - size, - global_scale, + device, encoder, handle, *color, size, scale, ) { add_instances( [bounds.x, bounds.y], -- cgit From 15057a05c118dafcb8cf90d4119e66caaa6026c5 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Fri, 3 May 2024 09:11:46 +0200 Subject: Introduce `center` widget helper ... and also make `center_x` and `center_y` set `width` and `height` to `Length::Fill`, respectively. This targets the most common use case when centering things and removes a bunch of boilerplate as a result. --- wgpu/src/lib.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'wgpu/src') diff --git a/wgpu/src/lib.rs b/wgpu/src/lib.rs index 6920067b..eb600dde 100644 --- a/wgpu/src/lib.rs +++ b/wgpu/src/lib.rs @@ -61,8 +61,8 @@ pub use settings::Settings; pub use geometry::Geometry; use crate::core::{ - Background, Color, Font, Pixels, Point, Radians, Rectangle, Size, - Transformation, Vector, + Background, Color, Font, Pixels, Point, Rectangle, Size, Transformation, + Vector, }; use crate::graphics::text::{Editor, Paragraph}; use crate::graphics::Viewport; @@ -517,7 +517,7 @@ impl core::image::Renderer for Renderer { handle: Self::Handle, filter_method: core::image::FilterMethod, bounds: Rectangle, - rotation: Radians, + rotation: core::Radians, ) { let (layer, transformation) = self.layers.current_mut(); layer.draw_image( @@ -541,7 +541,7 @@ impl core::svg::Renderer for Renderer { handle: core::svg::Handle, color_filter: Option, bounds: Rectangle, - rotation: Radians, + rotation: core::Radians, ) { let (layer, transformation) = self.layers.current_mut(); layer.draw_svg(handle, color_filter, bounds, transformation, rotation); -- cgit From fa9e1d96ea1924b51749b775ea0e67e69bc8a305 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Fri, 3 May 2024 13:25:58 +0200 Subject: Introduce dynamic `opacity` support for `Image` and `Svg` --- wgpu/src/image/mod.rs | 22 +++++++++++++++++----- wgpu/src/layer.rs | 4 ++++ wgpu/src/lib.rs | 12 +++++++++++- wgpu/src/shader/image.wgsl | 11 +++++++---- 4 files changed, 39 insertions(+), 10 deletions(-) (limited to 'wgpu/src') diff --git a/wgpu/src/image/mod.rs b/wgpu/src/image/mod.rs index 285eb2f6..063822aa 100644 --- a/wgpu/src/image/mod.rs +++ b/wgpu/src/image/mod.rs @@ -137,16 +137,18 @@ impl Pipeline { 0 => Float32x2, // Center 1 => Float32x2, - // Image size + // Scale 2 => Float32x2, // Rotation 3 => Float32, + // Opacity + 4 => Float32, // Atlas position - 4 => Float32x2, - // Atlas scale 5 => Float32x2, + // Atlas scale + 6 => Float32x2, // Layer - 6 => Sint32, + 7 => Sint32, ), }], }, @@ -229,6 +231,7 @@ impl Pipeline { filter_method, bounds, rotation, + opacity, } => { if let Some(atlas_entry) = cache.upload_raster(device, encoder, handle) @@ -237,6 +240,7 @@ impl Pipeline { [bounds.x, bounds.y], [bounds.width, bounds.height], f32::from(*rotation), + *opacity, atlas_entry, match filter_method { crate::core::image::FilterMethod::Nearest => { @@ -258,6 +262,7 @@ impl Pipeline { color, bounds, rotation, + opacity, } => { let size = [bounds.width, bounds.height]; @@ -268,6 +273,7 @@ impl Pipeline { [bounds.x, bounds.y], size, f32::from(*rotation), + *opacity, atlas_entry, nearest_instances, ); @@ -498,6 +504,7 @@ struct Instance { _center: [f32; 2], _size: [f32; 2], _rotation: f32, + _opacity: f32, _position_in_atlas: [f32; 2], _size_in_atlas: [f32; 2], _layer: u32, @@ -517,6 +524,7 @@ fn add_instances( image_position: [f32; 2], image_size: [f32; 2], rotation: f32, + opacity: f32, entry: &atlas::Entry, instances: &mut Vec, ) { @@ -532,6 +540,7 @@ fn add_instances( center, image_size, rotation, + opacity, allocation, instances, ); @@ -561,7 +570,8 @@ fn add_instances( ]; add_instance( - position, center, size, rotation, allocation, instances, + position, center, size, rotation, opacity, allocation, + instances, ); } } @@ -574,6 +584,7 @@ fn add_instance( center: [f32; 2], size: [f32; 2], rotation: f32, + opacity: f32, allocation: &atlas::Allocation, instances: &mut Vec, ) { @@ -586,6 +597,7 @@ fn add_instance( _center: center, _size: size, _rotation: rotation, + _opacity: opacity, _position_in_atlas: [ (x as f32 + 0.5) / atlas::SIZE as f32, (y as f32 + 0.5) / atlas::SIZE as f32, diff --git a/wgpu/src/layer.rs b/wgpu/src/layer.rs index e0242c59..9551311d 100644 --- a/wgpu/src/layer.rs +++ b/wgpu/src/layer.rs @@ -119,12 +119,14 @@ impl Layer { bounds: Rectangle, transformation: Transformation, rotation: Radians, + opacity: f32, ) { let image = Image::Raster { handle, filter_method, bounds: bounds * transformation, rotation, + opacity, }; self.images.push(image); @@ -137,12 +139,14 @@ impl Layer { bounds: Rectangle, transformation: Transformation, rotation: Radians, + opacity: f32, ) { let svg = Image::Vector { handle, color, bounds: bounds * transformation, rotation, + opacity, }; self.images.push(svg); diff --git a/wgpu/src/lib.rs b/wgpu/src/lib.rs index eb600dde..4c168029 100644 --- a/wgpu/src/lib.rs +++ b/wgpu/src/lib.rs @@ -518,6 +518,7 @@ impl core::image::Renderer for Renderer { filter_method: core::image::FilterMethod, bounds: Rectangle, rotation: core::Radians, + opacity: f32, ) { let (layer, transformation) = self.layers.current_mut(); layer.draw_image( @@ -526,6 +527,7 @@ impl core::image::Renderer for Renderer { bounds, transformation, rotation, + opacity, ); } } @@ -542,9 +544,17 @@ impl core::svg::Renderer for Renderer { color_filter: Option, bounds: Rectangle, rotation: core::Radians, + opacity: f32, ) { let (layer, transformation) = self.layers.current_mut(); - layer.draw_svg(handle, color_filter, bounds, transformation, rotation); + layer.draw_svg( + handle, + color_filter, + bounds, + transformation, + rotation, + opacity, + ); } } diff --git a/wgpu/src/shader/image.wgsl b/wgpu/src/shader/image.wgsl index 71bf939c..accefc17 100644 --- a/wgpu/src/shader/image.wgsl +++ b/wgpu/src/shader/image.wgsl @@ -12,15 +12,17 @@ struct VertexInput { @location(1) center: vec2, @location(2) scale: vec2, @location(3) rotation: f32, - @location(4) atlas_pos: vec2, - @location(5) atlas_scale: vec2, - @location(6) layer: i32, + @location(4) opacity: f32, + @location(5) atlas_pos: vec2, + @location(6) atlas_scale: vec2, + @location(7) layer: i32, } struct VertexOutput { @builtin(position) position: vec4, @location(0) uv: vec2, @location(1) layer: f32, // this should be an i32, but naga currently reads that as requiring interpolation. + @location(2) opacity: f32, } @vertex @@ -33,6 +35,7 @@ fn vs_main(input: VertexInput) -> VertexOutput { // Map the vertex position to the atlas texture. out.uv = vec2(v_pos * input.atlas_scale + input.atlas_pos); out.layer = f32(input.layer); + out.opacity = input.opacity; // Calculate the vertex position and move the center to the origin v_pos = input.pos + v_pos * input.scale - input.center; @@ -56,5 +59,5 @@ fn vs_main(input: VertexInput) -> VertexOutput { @fragment fn fs_main(input: VertexOutput) -> @location(0) vec4 { // Sample the texture at the given UV coordinate and layer. - return textureSample(u_texture, u_sampler, input.uv, i32(input.layer)); + return textureSample(u_texture, u_sampler, input.uv, i32(input.layer)) * vec4(1.0, 1.0, 1.0, input.opacity); } -- cgit From 547446f0de076149a4c61e6a4179308b266fd9fd Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Mon, 6 May 2024 12:14:42 +0200 Subject: Fix windows fighting over shared `image::Cache` Image caches are local to each window now. --- wgpu/src/engine.rs | 7 +++++-- wgpu/src/image/atlas.rs | 38 +++++++++++++++++++++++++++++++++++--- wgpu/src/image/cache.rs | 43 +++++++++++-------------------------------- wgpu/src/image/mod.rs | 38 +++++++++++--------------------------- wgpu/src/lib.rs | 15 +++++++++++---- wgpu/src/window/compositor.rs | 1 + 6 files changed, 74 insertions(+), 68 deletions(-) (limited to 'wgpu/src') diff --git a/wgpu/src/engine.rs b/wgpu/src/engine.rs index 96cd6db8..782fd58c 100644 --- a/wgpu/src/engine.rs +++ b/wgpu/src/engine.rs @@ -59,8 +59,11 @@ impl Engine { } #[cfg(any(feature = "image", feature = "svg"))] - pub fn image_cache(&self) -> &crate::image::cache::Shared { - self.image_pipeline.cache() + pub fn create_image_cache( + &self, + device: &wgpu::Device, + ) -> crate::image::Cache { + self.image_pipeline.create_cache(device) } pub fn submit( diff --git a/wgpu/src/image/atlas.rs b/wgpu/src/image/atlas.rs index ae43c3b4..a1ec0d7b 100644 --- a/wgpu/src/image/atlas.rs +++ b/wgpu/src/image/atlas.rs @@ -15,15 +15,23 @@ pub const SIZE: u32 = 2048; use crate::core::Size; use crate::graphics::color; +use std::sync::Arc; + #[derive(Debug)] pub struct Atlas { texture: wgpu::Texture, texture_view: wgpu::TextureView, + texture_bind_group: wgpu::BindGroup, + texture_layout: Arc, layers: Vec, } impl Atlas { - pub fn new(device: &wgpu::Device, backend: wgpu::Backend) -> Self { + pub fn new( + device: &wgpu::Device, + backend: wgpu::Backend, + texture_layout: Arc, + ) -> Self { let layers = match backend { // On the GL backend we start with 2 layers, to help wgpu figure // out that this texture is `GL_TEXTURE_2D_ARRAY` rather than `GL_TEXTURE_2D` @@ -60,15 +68,27 @@ impl Atlas { ..Default::default() }); + let texture_bind_group = + device.create_bind_group(&wgpu::BindGroupDescriptor { + label: Some("iced_wgpu::image texture atlas bind group"), + layout: &texture_layout, + entries: &[wgpu::BindGroupEntry { + binding: 0, + resource: wgpu::BindingResource::TextureView(&texture_view), + }], + }); + Atlas { texture, texture_view, + texture_bind_group, + texture_layout, layers, } } - pub fn view(&self) -> &wgpu::TextureView { - &self.texture_view + pub fn bind_group(&self) -> &wgpu::BindGroup { + &self.texture_bind_group } pub fn layer_count(&self) -> usize { @@ -421,5 +441,17 @@ impl Atlas { dimension: Some(wgpu::TextureViewDimension::D2Array), ..Default::default() }); + + self.texture_bind_group = + device.create_bind_group(&wgpu::BindGroupDescriptor { + label: Some("iced_wgpu::image texture atlas bind group"), + layout: &self.texture_layout, + entries: &[wgpu::BindGroupEntry { + binding: 0, + resource: wgpu::BindingResource::TextureView( + &self.texture_view, + ), + }], + }); } } diff --git a/wgpu/src/image/cache.rs b/wgpu/src/image/cache.rs index 32118ed6..94f7071d 100644 --- a/wgpu/src/image/cache.rs +++ b/wgpu/src/image/cache.rs @@ -1,8 +1,7 @@ use crate::core::{self, Size}; use crate::image::atlas::{self, Atlas}; -use std::cell::{RefCell, RefMut}; -use std::rc::Rc; +use std::sync::Arc; #[derive(Debug)] pub struct Cache { @@ -14,9 +13,13 @@ pub struct Cache { } impl Cache { - pub fn new(device: &wgpu::Device, backend: wgpu::Backend) -> Self { + pub fn new( + device: &wgpu::Device, + backend: wgpu::Backend, + layout: Arc, + ) -> Self { Self { - atlas: Atlas::new(device, backend), + atlas: Atlas::new(device, backend, layout), #[cfg(feature = "image")] raster: crate::image::raster::Cache::default(), #[cfg(feature = "svg")] @@ -24,6 +27,10 @@ impl Cache { } } + pub fn bind_group(&self) -> &wgpu::BindGroup { + self.atlas.bind_group() + } + pub fn layer_count(&self) -> usize { self.atlas.layer_count() } @@ -69,21 +76,6 @@ impl Cache { ) } - pub fn create_bind_group( - &self, - device: &wgpu::Device, - layout: &wgpu::BindGroupLayout, - ) -> wgpu::BindGroup { - device.create_bind_group(&wgpu::BindGroupDescriptor { - label: Some("iced_wgpu::image texture atlas bind group"), - layout, - entries: &[wgpu::BindGroupEntry { - binding: 0, - resource: wgpu::BindingResource::TextureView(self.atlas.view()), - }], - }) - } - pub fn trim(&mut self) { #[cfg(feature = "image")] self.raster.trim(&mut self.atlas); @@ -92,16 +84,3 @@ impl Cache { self.vector.trim(&mut self.atlas); } } - -#[derive(Debug, Clone)] -pub struct Shared(Rc>); - -impl Shared { - pub fn new(cache: Cache) -> Self { - Self(Rc::new(RefCell::new(cache))) - } - - pub fn lock(&self) -> RefMut<'_, Cache> { - self.0.borrow_mut() - } -} diff --git a/wgpu/src/image/mod.rs b/wgpu/src/image/mod.rs index 063822aa..daa2fe16 100644 --- a/wgpu/src/image/mod.rs +++ b/wgpu/src/image/mod.rs @@ -13,7 +13,9 @@ use crate::core::{Rectangle, Size, Transformation}; use crate::Buffer; use bytemuck::{Pod, Zeroable}; + use std::mem; +use std::sync::Arc; pub use crate::graphics::Image; @@ -22,13 +24,11 @@ pub type Batch = Vec; #[derive(Debug)] pub struct Pipeline { pipeline: wgpu::RenderPipeline, + backend: wgpu::Backend, nearest_sampler: wgpu::Sampler, linear_sampler: wgpu::Sampler, - texture: wgpu::BindGroup, - texture_version: usize, - texture_layout: wgpu::BindGroupLayout, + texture_layout: Arc, constant_layout: wgpu::BindGroupLayout, - cache: cache::Shared, layers: Vec, prepare_layer: usize, } @@ -186,25 +186,20 @@ impl Pipeline { multiview: None, }); - let cache = Cache::new(device, backend); - let texture = cache.create_bind_group(device, &texture_layout); - Pipeline { pipeline, + backend, nearest_sampler, linear_sampler, - texture, - texture_version: cache.layer_count(), - texture_layout, + texture_layout: Arc::new(texture_layout), constant_layout, - cache: cache::Shared::new(cache), layers: Vec::new(), prepare_layer: 0, } } - pub fn cache(&self) -> &cache::Shared { - &self.cache + pub fn create_cache(&self, device: &wgpu::Device) -> Cache { + Cache::new(device, self.backend, self.texture_layout.clone()) } pub fn prepare( @@ -212,6 +207,7 @@ impl Pipeline { device: &wgpu::Device, encoder: &mut wgpu::CommandEncoder, belt: &mut wgpu::util::StagingBelt, + cache: &mut Cache, images: &Batch, transformation: Transformation, scale: f32, @@ -221,8 +217,6 @@ impl Pipeline { let nearest_instances: &mut Vec = &mut Vec::new(); let linear_instances: &mut Vec = &mut Vec::new(); - let mut cache = self.cache.lock(); - for image in images { match &image { #[cfg(feature = "image")] @@ -288,16 +282,6 @@ impl Pipeline { return; } - let texture_version = cache.layer_count(); - - if self.texture_version != texture_version { - log::debug!("Atlas has grown. Recreating bind group..."); - - self.texture = - cache.create_bind_group(device, &self.texture_layout); - self.texture_version = texture_version; - } - if self.layers.len() <= self.prepare_layer { self.layers.push(Layer::new( device, @@ -323,6 +307,7 @@ impl Pipeline { pub fn render<'a>( &'a self, + cache: &'a Cache, layer: usize, bounds: Rectangle, render_pass: &mut wgpu::RenderPass<'a>, @@ -337,14 +322,13 @@ impl Pipeline { bounds.height, ); - render_pass.set_bind_group(1, &self.texture, &[]); + render_pass.set_bind_group(1, cache.bind_group(), &[]); layer.render(render_pass); } } pub fn end_frame(&mut self) { - self.cache.lock().trim(); self.prepare_layer = 0; } } diff --git a/wgpu/src/lib.rs b/wgpu/src/lib.rs index 4c168029..35da3211 100644 --- a/wgpu/src/lib.rs +++ b/wgpu/src/lib.rs @@ -67,6 +67,8 @@ use crate::core::{ use crate::graphics::text::{Editor, Paragraph}; use crate::graphics::Viewport; +use std::cell::RefCell; + /// A [`wgpu`] graphics renderer for [`iced`]. /// /// [`wgpu`]: https://github.com/gfx-rs/wgpu-rs @@ -82,11 +84,12 @@ pub struct Renderer { // TODO: Centralize all the image feature handling #[cfg(any(feature = "svg", feature = "image"))] - image_cache: image::cache::Shared, + image_cache: RefCell, } impl Renderer { pub fn new( + _device: &wgpu::Device, _engine: &Engine, default_font: Font, default_text_size: Pixels, @@ -100,7 +103,7 @@ impl Renderer { text_storage: text::Storage::new(), #[cfg(any(feature = "svg", feature = "image"))] - image_cache: _engine.image_cache().clone(), + image_cache: RefCell::new(_engine.create_image_cache(_device)), } } @@ -191,6 +194,7 @@ impl Renderer { device, encoder, &mut engine.staging_belt, + &mut self.image_cache.borrow_mut(), &layer.images, viewport.projection(), scale_factor, @@ -246,6 +250,8 @@ impl Renderer { #[cfg(any(feature = "svg", feature = "image"))] let mut image_layer = 0; + #[cfg(any(feature = "svg", feature = "image"))] + let image_cache = self.image_cache.borrow(); let scale_factor = viewport.scale_factor() as f32; let physical_bounds = Rectangle::::from(Rectangle::with_size( @@ -359,6 +365,7 @@ impl Renderer { #[cfg(any(feature = "svg", feature = "image"))] if !layer.images.is_empty() { engine.image_pipeline.render( + &image_cache, image_layer, scissor_rect, &mut render_pass, @@ -509,7 +516,7 @@ impl core::image::Renderer for Renderer { type Handle = core::image::Handle; fn measure_image(&self, handle: &Self::Handle) -> Size { - self.image_cache.lock().measure_image(handle) + self.image_cache.borrow_mut().measure_image(handle) } fn draw_image( @@ -535,7 +542,7 @@ impl core::image::Renderer for Renderer { #[cfg(feature = "svg")] impl core::svg::Renderer for Renderer { fn measure_svg(&self, handle: &core::svg::Handle) -> Size { - self.image_cache.lock().measure_svg(handle) + self.image_cache.borrow_mut().measure_svg(handle) } fn draw_svg( diff --git a/wgpu/src/window/compositor.rs b/wgpu/src/window/compositor.rs index 095afd48..baf9f315 100644 --- a/wgpu/src/window/compositor.rs +++ b/wgpu/src/window/compositor.rs @@ -290,6 +290,7 @@ impl graphics::Compositor for Compositor { fn create_renderer(&self) -> Self::Renderer { Renderer::new( + &self.device, &self.engine, self.settings.default_font, self.settings.default_text_size, -- cgit From ea64e4f63af7a7af1bde869ff6acd9203122b151 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Mon, 6 May 2024 12:17:43 +0200 Subject: Trim `image::Cache` after `wgpu::Renderer::present` --- wgpu/src/lib.rs | 3 +++ 1 file changed, 3 insertions(+) (limited to 'wgpu/src') diff --git a/wgpu/src/lib.rs b/wgpu/src/lib.rs index 35da3211..ad8ac591 100644 --- a/wgpu/src/lib.rs +++ b/wgpu/src/lib.rs @@ -125,6 +125,9 @@ impl Renderer { self.triangle_storage.trim(); self.text_storage.trim(); + + #[cfg(any(feature = "svg", feature = "image"))] + self.image_cache.borrow_mut().trim(); } fn prepare( -- cgit From 2645524f88414393d8b3ca9c6fe801b32b5ebd33 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 7 May 2024 15:50:18 +0200 Subject: Update `winit` to `0.30` --- wgpu/src/lib.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'wgpu/src') diff --git a/wgpu/src/lib.rs b/wgpu/src/lib.rs index ad8ac591..1fbdbe9a 100644 --- a/wgpu/src/lib.rs +++ b/wgpu/src/lib.rs @@ -67,8 +67,6 @@ use crate::core::{ use crate::graphics::text::{Editor, Paragraph}; use crate::graphics::Viewport; -use std::cell::RefCell; - /// A [`wgpu`] graphics renderer for [`iced`]. /// /// [`wgpu`]: https://github.com/gfx-rs/wgpu-rs @@ -84,7 +82,7 @@ pub struct Renderer { // TODO: Centralize all the image feature handling #[cfg(any(feature = "svg", feature = "image"))] - image_cache: RefCell, + image_cache: std::cell::RefCell, } impl Renderer { @@ -103,7 +101,9 @@ impl Renderer { text_storage: text::Storage::new(), #[cfg(any(feature = "svg", feature = "image"))] - image_cache: RefCell::new(_engine.create_image_cache(_device)), + image_cache: std::cell::RefCell::new( + _engine.create_image_cache(_device), + ), } } -- cgit From 8a0701a0d95989769341846b05ffcc705ae4ee5f Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 7 May 2024 21:05:29 +0200 Subject: Introduce `ICED_PRESENT_MODE` env var to pick a `wgpu::PresentMode` --- wgpu/src/settings.rs | 14 ++++++++++++++ wgpu/src/window/compositor.rs | 25 +++++++++++++++---------- 2 files changed, 29 insertions(+), 10 deletions(-) (limited to 'wgpu/src') diff --git a/wgpu/src/settings.rs b/wgpu/src/settings.rs index a6aea0a5..5e2603ee 100644 --- a/wgpu/src/settings.rs +++ b/wgpu/src/settings.rs @@ -51,3 +51,17 @@ impl From for Settings { } } } + +pub fn present_mode_from_env() -> Option { + let present_mode = std::env::var("ICED_PRESENT_MODE").ok()?; + + match present_mode.to_lowercase().as_str() { + "vsync" => Some(wgpu::PresentMode::AutoVsync), + "no_vsync" => Some(wgpu::PresentMode::AutoNoVsync), + "immediate" => Some(wgpu::PresentMode::Immediate), + "fifo" => Some(wgpu::PresentMode::Fifo), + "fifo_relaxed" => Some(wgpu::PresentMode::FifoRelaxed), + "mailbox" => Some(wgpu::PresentMode::Mailbox), + _ => None, + } +} diff --git a/wgpu/src/window/compositor.rs b/wgpu/src/window/compositor.rs index baf9f315..2e938c77 100644 --- a/wgpu/src/window/compositor.rs +++ b/wgpu/src/window/compositor.rs @@ -4,7 +4,8 @@ use crate::graphics::color; use crate::graphics::compositor; use crate::graphics::error; use crate::graphics::{self, Viewport}; -use crate::{Engine, Renderer, Settings}; +use crate::settings::{self, Settings}; +use crate::{Engine, Renderer}; /// A window graphics backend for iced powered by `wgpu`. #[allow(missing_debug_implementations)] @@ -270,15 +271,19 @@ impl graphics::Compositor for Compositor { backend: Option<&str>, ) -> Result { match backend { - None | Some("wgpu") => Ok(new( - Settings { - backends: wgpu::util::backend_bits_from_env() - .unwrap_or(wgpu::Backends::all()), - ..settings.into() - }, - compatible_window, - ) - .await?), + None | Some("wgpu") => { + let mut settings = Settings::from(settings); + + if let Some(backends) = wgpu::util::backend_bits_from_env() { + settings.backends = backends; + } + + if let Some(present_mode) = settings::present_mode_from_env() { + settings.present_mode = present_mode; + } + + Ok(new(settings, compatible_window).await?) + } Some(backend) => Err(graphics::Error::GraphicsAdapterNotFound { backend: "wgpu", reason: error::Reason::DidNotMatch { -- cgit From 5b6f3499e114c1694a5878466f8d46e7022e1bba Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 7 May 2024 21:13:51 +0200 Subject: Document `present_mode_from_env` in `iced_wgpu` --- wgpu/src/settings.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'wgpu/src') diff --git a/wgpu/src/settings.rs b/wgpu/src/settings.rs index 5e2603ee..b3c3cf6a 100644 --- a/wgpu/src/settings.rs +++ b/wgpu/src/settings.rs @@ -52,6 +52,18 @@ impl From for Settings { } } +/// Obtains a [`wgpu::PresentMode`] from the current environment +/// configuration, if set. +/// +/// The value returned by this function can be changed by setting +/// the `ICED_PRESENT_MODE` env variable. The possible values are: +/// +/// - `vsync` → [`wgpu::PresentMode::AutoVsync`] +/// - `no_vsync` → [`wgpu::PresentMode::AutoNoVsync`] +/// - `immediate` → [`wgpu::PresentMode::Immediate`] +/// - `fifo` → [`wgpu::PresentMode::Fifo`] +/// - `fifo_relaxed` → [`wgpu::PresentMode::FifoRelaxed`] +/// - `mailbox` → [`wgpu::PresentMode::Mailbox`] pub fn present_mode_from_env() -> Option { let present_mode = std::env::var("ICED_PRESENT_MODE").ok()?; -- cgit