Skip to main content

motion_content/vector/
deferred.rs

1//! Shared deferred-content pipeline: compile key, compiled vector tree, selection, reveal.
2
3use super::{VectorTree, WriteOptions};
4use motion_core::sync::MutexExt;
5use motion_core::{Node, ValidationError};
6use std::{
7    collections::{BTreeMap, BTreeSet},
8    sync::{Arc, Mutex},
9};
10
11/// Most results one cache retains before it starts over.
12const MAX_SLOTS: usize = 8;
13
14/// A compile cache created from one immutable key.
15///
16/// Compilation inputs live in the key, so changing one produces a new, empty
17/// cache through [`CompileCache::map_key`]; nothing is ever cleared by hand.
18/// Clones share their results. A variant distinguishes environments that are
19/// not part of the key, such as the font snapshot a paragraph is shaped with.
20pub struct CompileCache<K, V> {
21    key: Arc<K>,
22    slots: Arc<Mutex<BTreeMap<u64, V>>>,
23}
24
25impl<K, V> Clone for CompileCache<K, V> {
26    fn clone(&self) -> Self {
27        Self {
28            key: self.key.clone(),
29            slots: self.slots.clone(),
30        }
31    }
32}
33
34impl<K: std::fmt::Debug, V> std::fmt::Debug for CompileCache<K, V> {
35    fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
36        formatter
37            .debug_struct("CompileCache")
38            .field("key", &self.key)
39            .finish_non_exhaustive()
40    }
41}
42
43impl<K, V: Clone> CompileCache<K, V> {
44    /// Creates an empty cache for a key.
45    pub fn new(key: K) -> Self {
46        Self {
47            key: Arc::new(key),
48            slots: Arc::default(),
49        }
50    }
51
52    /// The immutable key every cached result was compiled from.
53    pub fn key(&self) -> &K {
54        &self.key
55    }
56
57    /// Whether two caches share their results, as clones and unchanged keys do.
58    pub fn shares(&self, other: &Self) -> bool {
59        Arc::ptr_eq(&self.slots, &other.slots)
60    }
61
62    /// Whether any result has been compiled yet.
63    pub fn is_compiled(&self) -> bool {
64        !self.slots.lock_unpoisoned().is_empty()
65    }
66
67    /// Returns the result for a variant, compiling it once from the key.
68    /// The compiler runs while the cache is locked, so concurrent callers
69    /// never compile the same variant twice.
70    pub fn get_or_compile(&self, variant: u64, compile: impl FnOnce(&K) -> V) -> V {
71        let mut slots = self.slots.lock_unpoisoned();
72        if let Some(result) = slots.get(&variant) {
73            return result.clone();
74        }
75        let result = compile(&self.key);
76        if slots.len() >= MAX_SLOTS {
77            slots.clear();
78        }
79        slots.insert(variant, result.clone());
80        result
81    }
82}
83
84impl<K: Clone + PartialEq, V: Clone> CompileCache<K, V> {
85    /// Applies a change to a copy of the key. An unchanged key keeps sharing
86    /// this cache; a changed key gets a new one.
87    pub fn map_key(&self, change: impl FnOnce(&mut K)) -> Self {
88        let mut key = (*self.key).clone();
89        change(&mut key);
90        if key == *self.key {
91            self.clone()
92        } else {
93            Self::new(key)
94        }
95    }
96}
97
98type Modify = Arc<dyn Fn(Node) -> Node + Send + Sync>;
99
100/// Authored modifier applied to the original vector groups at known addresses.
101#[derive(Clone)]
102pub struct VectorSelection {
103    addresses: Arc<BTreeSet<Vec<usize>>>,
104    modify: Modify,
105}
106
107impl VectorSelection {
108    /// Selects original tree addresses and the ordinary modifier to apply to them.
109    pub fn new(
110        addresses: impl IntoIterator<Item = Vec<usize>>,
111        modify: impl Fn(Node) -> Node + Send + Sync + 'static,
112    ) -> Self {
113        Self {
114            addresses: Arc::new(addresses.into_iter().collect()),
115            modify: Arc::new(modify),
116        }
117    }
118
119    /// Selects addresses with an already shared modifier.
120    pub fn shared(addresses: impl IntoIterator<Item = Vec<usize>>, modify: &Modify) -> Self {
121        Self {
122            addresses: Arc::new(addresses.into_iter().collect()),
123            modify: modify.clone(),
124        }
125    }
126}
127
128impl VectorTree {
129    /// Selection then reveal: applies each selection in declaration order to its
130    /// original groups, and optionally writes the result by cumulative length.
131    pub fn reveal(
132        &self,
133        selections: &[VectorSelection],
134        write: Option<f64>,
135    ) -> Result<Node, ValidationError> {
136        let map = |address: &[usize], mut node: Node| {
137            for selection in selections {
138                if selection.addresses.contains(address) {
139                    node = (selection.modify)(node);
140                }
141            }
142            node
143        };
144        match write {
145            Some(progress) => self.write_mapped(progress, WriteOptions::default(), &map),
146            None => Ok(self.node_mapped(&map)),
147        }
148    }
149}