Skip to content

Commit

Permalink
Implement caching in TaffyLayout
Browse files Browse the repository at this point in the history
  • Loading branch information
nicoburns committed Nov 22, 2023
1 parent 3080b4f commit 63fe0dd
Show file tree
Hide file tree
Showing 2 changed files with 74 additions and 25 deletions.
11 changes: 10 additions & 1 deletion src/view/taffy_layout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -119,12 +119,21 @@ impl<T, A, VT: ViewSequence<T, A>> View<T, A> for TaffyLayout<T, A, VT> {
.rebuild(cx, &prev.children, state, &mut splice)
});

if self.background_color != prev.background_color {
element.background_color = self.background_color;
flags |= ChangeFlags::PAINT
}

if self.style != prev.style {
element.style = self.style.clone();
element.background_color = self.background_color;
flags |= ChangeFlags::LAYOUT | ChangeFlags::PAINT;
}

// Clear layout cache if the layout ChangeFlag is set
if flags.contains(ChangeFlags::LAYOUT) || flags.contains(ChangeFlags::TREE) {
element.cache.clear()
}

flags
}

Expand Down
88 changes: 64 additions & 24 deletions src/widget/taffy_layout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,19 +36,16 @@ type DummyMeasureFunction =
/// widget.
pub struct TaffyLayout {
pub children: Vec<Pod>,
pub child_caches: Vec<taffy::Cache>,
pub cache: taffy::Cache,
pub style: taffy::Style,
pub background_color: Option<Color>,
}

impl TaffyLayout {
pub fn new(children: Vec<Pod>, style: taffy::Style, background_color: Option<Color>) -> Self {
let len = children.len();
let mut caches = Vec::new();
caches.resize_with(len, taffy::Cache::new);
TaffyLayout {
children,
child_caches: caches,
cache: taffy::Cache::new(),
style,
background_color,
}
Expand Down Expand Up @@ -181,9 +178,20 @@ fn to_box_constraints(input: &taffy::LayoutInput) -> BoxConstraints {

struct TaffyLayoutCtx<'w, 'a, 'b> {
widget: &'w mut TaffyLayout,
dummy_child_cache: taffy::Cache,
cx: &'w mut LayoutCx<'a, 'b>,
}

impl<'w, 'a, 'b> TaffyLayoutCtx<'w, 'a, 'b> {
fn new(widget: &'w mut TaffyLayout, cx: &'w mut LayoutCx<'a, 'b>) -> Self {
TaffyLayoutCtx {
widget,
cx,
dummy_child_cache: taffy::Cache::new(),
}
}
}

impl<'w, 'a, 'b> taffy::PartialLayoutTree for TaffyLayoutCtx<'w, 'a, 'b> {
type ChildIter<'c> = ChildIter where Self: 'c;

Expand Down Expand Up @@ -222,11 +230,19 @@ impl<'w, 'a, 'b> taffy::PartialLayoutTree for TaffyLayoutCtx<'w, 'a, 'b> {
x: layout.location.x as f64,
y: layout.location.y as f64,
},
)
);
}

fn get_cache_mut(&mut self, node_id: taffy::NodeId) -> &mut ::taffy::tree::Cache {
&mut self.widget.child_caches[usize::from(node_id)]
fn get_cache_mut(&mut self, node_id: taffy::NodeId) -> &mut taffy::Cache {
let node_id = usize::from(node_id);
let child = &mut self.widget.children[node_id];
match child.downcast_mut::<TaffyLayout>() {
Some(child_widget) => &mut child_widget.cache,
None => {
self.dummy_child_cache.clear();
&mut self.dummy_child_cache
}
}
}

fn compute_child_layout(
Expand Down Expand Up @@ -292,12 +308,6 @@ impl Widget for TaffyLayout {
}

fn layout(&mut self, cx: &mut LayoutCx, bc: &BoxConstraints) -> Size {
// We run the following wrapped in "compute_cached_layout", which will check the cache for an entry matching the node and inputs and:
// - Return that entry if exists
// - Else call the passed closure (below) to compute the result
//
// If there was no cache match and a new result needs to be computed then that result will be added to the cache
// compute_cached_layout(self, node, inputs, |widget, node, inputs| {
let display_mode = self.style.display;
let has_children = !self.children.is_empty();

Expand All @@ -309,8 +319,21 @@ impl Widget for TaffyLayout {
);
let node_id = taffy::NodeId::from(usize::MAX);

// Check for cached layout. And return it if found.
if let Some(cached_output) = self.cache.get(
inputs.known_dimensions,
inputs.available_space,
taffy::RunMode::PerformLayout,
) {
let max = bc.max();
return Size {
width: (cached_output.size.width as f64).min(max.width),
height: (cached_output.size.height as f64).min(max.height),
};
}

// Dispatch to a layout algorithm based on the node's display style and whether the node has children or not.
let mut layout_ctx = TaffyLayoutCtx { widget: self, cx };
let mut layout_ctx = TaffyLayoutCtx::new(self, cx);
let output = match (display_mode, has_children) {
(taffy::Display::None, _) => taffy::compute_hidden_layout(&mut layout_ctx, node_id),
(taffy::Display::Block, true) => {
Expand All @@ -328,23 +351,24 @@ impl Widget for TaffyLayout {
}
};

// Save output to cache
self.cache.store(
inputs.known_dimensions,
inputs.available_space,
taffy::RunMode::PerformLayout,
output,
);

cx.request_paint();

let max = bc.max();
Size {
width: (output.size.width as f64).min(max.width),
height: (output.size.height as f64).min(max.height),
}
// })
}

fn compute_max_intrinsic(&mut self, axis: Axis, cx: &mut LayoutCx, bc: &BoxConstraints) -> f64 {
// We run the following wrapped in "compute_cached_layout", which will check the cache for an entry matching the node and inputs and:
// - Return that entry if exists
// - Else call the passed closure (below) to compute the result
//
// If there was no cache match and a new result needs to be computed then that result will be added to the cache
// compute_cached_layout(self, node, inputs, |widget, node, inputs| {
let display_mode = self.style.display;
let has_children = !self.children.is_empty();

Expand All @@ -357,8 +381,17 @@ impl Widget for TaffyLayout {
taffy::SizingMode::InherentSize, // TODO: Support SizingMode::ContentSize
);

// Check for cached size. And return it if found.
if let Some(cached_output) = self.cache.get(
inputs.known_dimensions,
inputs.available_space,
taffy::RunMode::ComputeSize,
) {
return cached_output.size.get_abs(taffy_axis) as f64;
}

// Dispatch to a layout algorithm based on the node's display style and whether the node has children or not.
let mut layout_ctx = TaffyLayoutCtx { widget: self, cx };
let mut layout_ctx = TaffyLayoutCtx::new(self, cx);
let output = match (display_mode, has_children) {
(taffy::Display::None, _) => taffy::compute_hidden_layout(&mut layout_ctx, node_id),
(taffy::Display::Block, true) => {
Expand All @@ -376,8 +409,15 @@ impl Widget for TaffyLayout {
}
};

// Save output to cache
self.cache.store(
inputs.known_dimensions,
inputs.available_space,
taffy::RunMode::ComputeSize,
output,
);

output.size.get_abs(taffy_axis) as f64
// })
}

fn accessibility(&mut self, cx: &mut AccessCx) {
Expand Down

0 comments on commit 63fe0dd

Please sign in to comment.