1
0
mirror of https://github.com/emilk/egui.git synced 2026-09-01 06:10:06 -04:00

Keep track of dependencies between GalleyCaches and keep them alive

This commit is contained in:
Hubert Głuchowski
2025-04-01 18:14:39 +02:00
parent 9d61f9c927
commit ff9dfdeec1

View File

@@ -728,6 +728,10 @@ impl FontsImpl {
struct CachedGalley { struct CachedGalley {
/// When it was last used /// When it was last used
last_used: u32, last_used: u32,
/// Hashes of all other entries this one depends on for quick re-layout.
/// Their `last_used`s should be updated alongside this one to make sure they're
/// not evicted.
children: Option<Arc<[u64]>>,
galley: Arc<Galley>, galley: Arc<Galley>,
} }
@@ -739,12 +743,12 @@ struct GalleyCache {
} }
impl GalleyCache { impl GalleyCache {
fn layout( fn layout_internal(
&mut self, &mut self,
fonts: &mut FontsImpl, fonts: &mut FontsImpl,
mut job: LayoutJob, mut job: LayoutJob,
allow_split_paragraphs: bool, allow_split_paragraphs: bool,
) -> Arc<Galley> { ) -> (u64, Arc<Galley>) {
if job.wrap.max_width.is_finite() { if job.wrap.max_width.is_finite() {
// Protect against rounding errors in egui layout code. // Protect against rounding errors in egui layout code.
@@ -772,32 +776,56 @@ impl GalleyCache {
let hash = crate::util::hash(&job); // TODO(emilk): even faster hasher? let hash = crate::util::hash(&job); // TODO(emilk): even faster hasher?
match self.cache.entry(hash) { (hash, match self.cache.entry(hash) {
std::collections::hash_map::Entry::Occupied(entry) => { std::collections::hash_map::Entry::Occupied(entry) => {
// The job was found in cache - no need to re-layout. // The job was found in cache - no need to re-layout.
let cached = entry.into_mut(); let cached = entry.into_mut();
cached.last_used = self.generation; cached.last_used = self.generation;
cached.galley.clone()
let galley = cached.galley.clone();
if let Some(children) = &cached.children {
for child_hash in children.clone().iter() {
if let Some(cached_child) = self.cache.get_mut(child_hash) {
cached_child.last_used = self.generation;
}
}
}
galley
} }
std::collections::hash_map::Entry::Vacant(entry) => { std::collections::hash_map::Entry::Vacant(entry) => {
let job = Arc::new(job); let job = Arc::new(job);
if allow_split_paragraphs && should_cache_each_paragraph_individually(&job) { if allow_split_paragraphs && should_cache_each_paragraph_individually(&job) {
let galley = self.layout_each_paragraph_individuallly(fonts, job); let mut child_hashes = Vec::new();
// TODO(afishhh): This Galley cannot be added directly into the cache without taking let galley = self.layout_each_paragraph_individuallly(fonts, job, &mut child_hashes);
// extra precautions to make sure all component paragraph Galleys are not invalidated let galley = Arc::new(galley);
// immediately next frame (since their `last_used` will not be updated). self.cache.insert(hash, CachedGalley {
Arc::new(galley) last_used: self.generation,
children: Some(child_hashes.into()),
galley: galley.clone()
});
galley
} else { } else {
let galley = super::layout(fonts, job); let galley = super::layout(fonts, job);
let galley = Arc::new(galley); let galley = Arc::new(galley);
entry.insert(CachedGalley { entry.insert(CachedGalley {
last_used: self.generation, last_used: self.generation,
children: None,
galley: galley.clone(), galley: galley.clone(),
}); });
galley galley
} }
} }
} })
}
fn layout(
&mut self,
fonts: &mut FontsImpl,
job: LayoutJob,
allow_split_paragraphs: bool,
) -> Arc<Galley> {
self.layout_internal(fonts, job, allow_split_paragraphs).1
} }
/// Split on `\n` and lay out (and cache) each paragraph individually. /// Split on `\n` and lay out (and cache) each paragraph individually.
@@ -805,6 +833,7 @@ impl GalleyCache {
&mut self, &mut self,
fonts: &mut FontsImpl, fonts: &mut FontsImpl,
job: Arc<LayoutJob>, job: Arc<LayoutJob>,
child_hashes: &mut Vec<u64>
) -> Galley { ) -> Galley {
profiling::function_scope!(); profiling::function_scope!();
@@ -878,7 +907,8 @@ impl GalleyCache {
} }
// TODO(emilk): we could lay out each paragraph in parallel to get a nice speedup on multicore machines. // TODO(emilk): we could lay out each paragraph in parallel to get a nice speedup on multicore machines.
let galley = self.layout(fonts, paragraph_job, false); let (hash, galley) = self.layout_internal(fonts, paragraph_job, false);
child_hashes.push(hash);
// This will prevent us from invalidating cache entries unnecessarily: // This will prevent us from invalidating cache entries unnecessarily:
if max_rows_remaining != usize::MAX { if max_rows_remaining != usize::MAX {