diff --git a/Cargo.lock b/Cargo.lock index d43e803b..798c012d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -359,6 +359,12 @@ dependencies = [ "dyn-any", ] +[[package]] +name = "boxcar" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38c99613cb3cd7429889a08dfcf651721ca971c86afa30798461f8eee994de47" + [[package]] name = "brotli" version = "3.3.4" @@ -1663,6 +1669,7 @@ dependencies = [ "autoquant", "bezier-rs", "borrow_stack", + "boxcar", "bytemuck", "compilation-client", "dyn-any", diff --git a/node-graph/gstd/Cargo.toml b/node-graph/gstd/Cargo.toml index 7ef93201..ff36d1cc 100644 --- a/node-graph/gstd/Cargo.toml +++ b/node-graph/gstd/Cargo.toml @@ -19,7 +19,7 @@ quantization = ["autoquant"] [dependencies] autoquant = { git = "https://github.com/truedoctor/autoquant", optional = true, features = ["fitting"] } -graphene-core = {path = "../gcore", features = ["async", "std" ], default-features = false} +graphene-core = {path = "../gcore", features = ["async", "std", "serde" ], default-features = false} borrow_stack = {path = "../borrow_stack"} dyn-any = {path = "../../libraries/dyn-any", features = ["derive"]} graph-craft = {path = "../graph-craft"} @@ -38,12 +38,13 @@ image = "*" dyn-clone = "1.0" log = "0.4" -bezier-rs = { path = "../../libraries/bezier-rs" } +bezier-rs = { path = "../../libraries/bezier-rs" , features = ["serde"] } kurbo = { git = "https://github.com/linebender/kurbo.git", features = [ "serde", ] } glam = { version = "0.22", features = ["serde"] } node-macro = { path="../node-macro" } +boxcar = "0.1.0" [dependencies.serde] version = "1.0" diff --git a/node-graph/gstd/src/memo.rs b/node-graph/gstd/src/memo.rs index d31c92e8..14c419ba 100644 --- a/node-graph/gstd/src/memo.rs +++ b/node-graph/gstd/src/memo.rs @@ -1,50 +1,77 @@ +use std::collections::hash_map::DefaultHasher; +use std::hash::{Hash, Hasher}; use std::marker::PhantomData; use graphene_core::Node; -use once_cell::sync::OnceCell; /// Caches the output of a given Node and acts as a proxy #[derive(Default)] pub struct CacheNode { - cache: OnceCell, + // We have to use an append only data structure to make sure the references + // to the cache entries are always valid + cache: boxcar::Vec<(u64, T)>, } -impl<'i, T: 'i> Node<'i, T> for CacheNode { +impl<'i, T: 'i + Hash> Node<'i, T> for CacheNode { type Output = &'i T; fn eval<'s: 'i>(&'s self, input: T) -> Self::Output { - self.cache.get_or_init(|| { - trace!("Creating new cache node"); - input - }) + let mut hasher = DefaultHasher::new(); + input.hash(&mut hasher); + let hash = hasher.finish(); + + if let Some((_, cached_value)) = self.cache.iter().find(|(h, _)| *h == hash) { + return cached_value; + } else { + trace!("Cache miss"); + let index = self.cache.push((hash, input)); + return &self.cache[index].1; + } } } impl CacheNode { - pub const fn new() -> CacheNode { - CacheNode { cache: OnceCell::new() } + pub fn new() -> CacheNode { + CacheNode { cache: boxcar::Vec::new() } } } /// Caches the output of a given Node and acts as a proxy +/// It provides two modes of operation, it can either be set +/// when calling the node with a `Some` variant or the last +/// value that was added is returned when calling it with `None` #[derive(Debug, Clone, PartialEq, Eq, Default)] pub struct LetNode { - cache: OnceCell, + // We have to use an append only data structure to make sure the references + // to the cache entries are always valid + // TODO: We only ever access the last value so there is not really a reason for us + // to store the previous entries. This should be reworked in the future + cache: boxcar::Vec<(u64, T)>, } -impl<'i, T: 'i> Node<'i, Option> for LetNode { +impl<'i, T: 'i + Hash> Node<'i, Option> for LetNode { type Output = &'i T; fn eval<'s: 'i>(&'s self, input: Option) -> Self::Output { match input { Some(input) => { - self.cache.set(input).unwrap_or_else(|_| error!("Let node was set twice but is not mutable")); - self.cache.get().unwrap() + let mut hasher = DefaultHasher::new(); + input.hash(&mut hasher); + let hash = hasher.finish(); + + if let Some((cached_hash, cached_value)) = self.cache.iter().last() { + if hash == *cached_hash { + return cached_value; + } + } + trace!("Cache miss"); + let index = self.cache.push((hash, input)); + return &self.cache[index].1; } - None => self.cache.get().expect("Let node was not initialized"), + None => &self.cache.iter().last().expect("Let node was not initialized").1, } } } impl LetNode { - pub const fn new() -> LetNode { - LetNode { cache: OnceCell::new() } + pub fn new() -> LetNode { + LetNode { cache: boxcar::Vec::new() } } }