Fix cache trimming loop in iced_wgpu::text

This commit is contained in:
Héctor Ramón Jiménez 2024-04-30 23:15:04 +02:00
parent c51b85e7ab
commit b276a603a1
No known key found for this signature in database
GPG key ID: 7CC46565708259A7
2 changed files with 52 additions and 9 deletions

View file

@ -15,7 +15,7 @@ impl<T> Cache<T> {
/// Creates a new empty [`Cache`]. /// Creates a new empty [`Cache`].
pub fn new() -> Self { pub fn new() -> Self {
Cache { Cache {
group: Group::unique(), group: Group::singleton(),
state: RefCell::new(State::Empty { previous: None }), state: RefCell::new(State::Empty { previous: None }),
} }
} }
@ -27,6 +27,11 @@ impl<T> Cache<T> {
/// You should generally group caches that are likely to change /// You should generally group caches that are likely to change
/// together. /// together.
pub fn with_group(group: Group) -> Self { pub fn with_group(group: Group) -> Self {
assert!(
!group.is_singleton(),
"The group {group:?} cannot be shared!"
);
Cache { Cache {
group, group,
state: RefCell::new(State::Empty { previous: None }), state: RefCell::new(State::Empty { previous: None }),
@ -75,14 +80,40 @@ impl<T> Cache<T> {
/// A cache group can be used to implement certain performance /// A cache group can be used to implement certain performance
/// optimizations during rendering, like batching or sharing atlases. /// optimizations during rendering, like batching or sharing atlases.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Group(u64); pub struct Group {
id: u64,
is_singleton: bool,
}
impl Group { impl Group {
/// Generates a new unique cache [`Group`]. /// Generates a new unique cache [`Group`].
pub fn unique() -> Self { pub fn unique() -> Self {
static NEXT: AtomicU64 = AtomicU64::new(0); static NEXT: AtomicU64 = AtomicU64::new(0);
Self(NEXT.fetch_add(1, atomic::Ordering::Relaxed)) Self {
id: NEXT.fetch_add(1, atomic::Ordering::Relaxed),
is_singleton: false,
}
}
/// Returns `true` if the [`Group`] can only ever have a
/// single [`Cache`] in it.
///
/// This is the default kind of [`Group`] assigned when using
/// [`Cache::new`].
///
/// Knowing that a [`Group`] will never be shared may be
/// useful for rendering backends to perform additional
/// optimizations.
pub fn is_singleton(self) -> bool {
self.is_singleton
}
fn singleton() -> Self {
Self {
is_singleton: true,
..Self::unique()
}
} }
} }

View file

@ -157,13 +157,16 @@ impl Storage {
target_size, 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.text = Rc::downgrade(&cache.text);
upload.version = cache.version; upload.version = cache.version;
upload.group_version = group.version; upload.group_version = group.version;
upload.transformation = new_transformation; upload.transformation = new_transformation;
upload.buffer_cache.trim(); upload.buffer_cache.trim();
group.should_trim = true;
} }
} }
hash_map::Entry::Vacant(entry) => { hash_map::Entry::Vacant(entry) => {
@ -213,19 +216,28 @@ impl Storage {
.retain(|_id, upload| upload.text.strong_count() > 0); .retain(|_id, upload| upload.text.strong_count() > 0);
self.groups.retain(|id, group| { 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:?}"); log::debug!("Dropping text atlas: {id:?}");
return false; return false;
} }
if group.should_trim { if id.is_singleton() || group.should_trim {
log::debug!("Trimming text atlas: {id:?}"); log::debug!(
"Trimming text atlas: {id:?} (uploads: {active_uploads})"
);
group.atlas.trim(); group.atlas.trim();
group.version += 1;
group.should_trim = false; 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 true