From 01af4308d30419b58696d897a0687fdc4ff5ea78 Mon Sep 17 00:00:00 2001 From: Laurenz Date: Fri, 27 Sep 2024 10:24:59 +0200 Subject: [PATCH] Better block caching (#5046) --- crates/typst/src/layout/flow/collect.rs | 108 ++++++++++++++++++++---- crates/typst/src/layout/flow/mod.rs | 2 +- 2 files changed, 93 insertions(+), 17 deletions(-) diff --git a/crates/typst/src/layout/flow/collect.rs b/crates/typst/src/layout/flow/collect.rs index e316a3efc..9cd9bdc4e 100644 --- a/crates/typst/src/layout/flow/collect.rs +++ b/crates/typst/src/layout/flow/collect.rs @@ -4,12 +4,15 @@ use std::hash::Hash; use bumpalo::boxed::Box as BumpBox; use bumpalo::Bump; +use comemo::{Track, Tracked, TrackedMut}; use once_cell::unsync::Lazy; use crate::diag::{bail, SourceResult}; -use crate::engine::Engine; +use crate::engine::{Engine, Route, Sink, Traced}; use crate::foundations::{Packed, Resolve, Smart, StyleChain}; -use crate::introspection::{Locator, SplitLocator, Tag, TagElem}; +use crate::introspection::{ + Introspector, Locator, LocatorLink, SplitLocator, Tag, TagElem, +}; use crate::layout::{ layout_frame, Abs, AlignElem, Alignment, Axes, BlockElem, ColbreakElem, FixedAlignment, FlushElem, Fr, Fragment, Frame, PagebreakElem, PlaceElem, @@ -18,6 +21,7 @@ use crate::layout::{ use crate::model::ParElem; use crate::realize::Pair; use crate::text::TextElem; +use crate::World; /// Collects all elements of the flow into prepared children. These are much /// simpler to handle than the raw elements. @@ -324,13 +328,49 @@ impl SingleChild<'_> { /// Build the child's frame given the region's base size. pub fn layout(&self, engine: &mut Engine, base: Size) -> SourceResult { self.cell.get_or_init(base, |base| { - self.elem - .layout_single(engine, self.locator.relayout(), self.styles, base) - .map(|frame| frame.post_processed(self.styles)) + layout_single_impl( + engine.world, + engine.introspector, + engine.traced, + TrackedMut::reborrow_mut(&mut engine.sink), + engine.route.track(), + self.elem, + self.locator.track(), + self.styles, + base, + ) }) } } +/// The cached, internal implementation of [`SingleChild::layout`]. +#[comemo::memoize] +#[allow(clippy::too_many_arguments)] +fn layout_single_impl( + world: Tracked, + introspector: Tracked, + traced: Tracked, + sink: TrackedMut, + route: Tracked, + elem: &Packed, + locator: Tracked, + styles: StyleChain, + base: Size, +) -> SourceResult { + let link = LocatorLink::new(locator); + let locator = Locator::link(&link); + let mut engine = Engine { + world, + introspector, + traced, + sink, + route: Route::extend(route), + }; + + elem.layout_single(&mut engine, locator, styles, base) + .map(|frame| frame.post_processed(styles)) +} + /// A child that encapsulates a prepared breakable block. #[derive(Debug)] pub struct MultiChild<'a> { @@ -349,7 +389,7 @@ impl<'a> MultiChild<'a> { engine: &mut Engine, regions: Regions, ) -> SourceResult<(Frame, Option>)> { - let fragment = self.layout_impl(engine, regions)?; + let fragment = self.layout_full(engine, regions)?; // Extract the first frame. let mut frames = fragment.into_iter(); @@ -371,7 +411,7 @@ impl<'a> MultiChild<'a> { /// The shared internal implementation of [`Self::layout`] and /// [`MultiSpill::layout`]. - fn layout_impl( + fn layout_full( &self, engine: &mut Engine, regions: Regions, @@ -379,18 +419,54 @@ impl<'a> MultiChild<'a> { self.cell.get_or_init(regions, |mut regions| { // Vertical expansion is only kept if this block is the only child. regions.expand.y &= self.alone; - self.elem - .layout_multiple(engine, self.locator.relayout(), self.styles, regions) - .map(|mut fragment| { - for frame in &mut fragment { - frame.post_process(self.styles); - } - fragment - }) + layout_multi_impl( + engine.world, + engine.introspector, + engine.traced, + TrackedMut::reborrow_mut(&mut engine.sink), + engine.route.track(), + self.elem, + self.locator.track(), + self.styles, + regions, + ) }) } } +/// The cached, internal implementation of [`MultiChild::layout_full`]. +#[comemo::memoize] +#[allow(clippy::too_many_arguments)] +fn layout_multi_impl( + world: Tracked, + introspector: Tracked, + traced: Tracked, + sink: TrackedMut, + route: Tracked, + elem: &Packed, + locator: Tracked, + styles: StyleChain, + regions: Regions, +) -> SourceResult { + let link = LocatorLink::new(locator); + let locator = Locator::link(&link); + let mut engine = Engine { + world, + introspector, + traced, + sink, + route: Route::extend(route), + }; + + elem.layout_multiple(&mut engine, locator, styles, regions) + .map(|mut fragment| { + for frame in &mut fragment { + frame.post_process(styles); + } + fragment + }) +} + /// The spilled remains of a `MultiChild` that broke across two regions. #[derive(Debug, Clone)] pub struct MultiSpill<'a, 'b> { @@ -433,7 +509,7 @@ impl MultiSpill<'_, '_> { // Extract the not-yet-processed frames. let mut frames = self .multi - .layout_impl(engine, pod)? + .layout_full(engine, pod)? .into_iter() .skip(self.backlog.len()); diff --git a/crates/typst/src/layout/flow/mod.rs b/crates/typst/src/layout/flow/mod.rs index f664e9706..8a10b05dd 100644 --- a/crates/typst/src/layout/flow/mod.rs +++ b/crates/typst/src/layout/flow/mod.rs @@ -97,7 +97,7 @@ pub fn layout_frame( .map(Fragment::into_frame) } -/// The internal implementation of [`layout_fragment`]. +/// The cached, internal implementation of [`layout_fragment`]. #[comemo::memoize] #[allow(clippy::too_many_arguments)] fn layout_fragment_impl(