Make the dynamic node graph execution asynchronous (#1218)

* Make node graph execution async

Make node macro generate async node implementations

Start propagating async through the node system

Async checkpoint

Make Any<'i> Send + Sync

Determine node io type using panic node

Fix types for raster_node macro

Finish porting node registry?

Fix lifetime errors

Remove Send + Sync requirements and start making node construction async

Async MVP

Fix tests

Clippy fix

* Fix nodes

* Simplify lifetims for node macro + make node macro more modular

* Reenable more nodes

* Fix pasting images

* Remove http test from brush node

* Fix output type for cache node

* Fix types for let scope

* Fix formatting
This commit is contained in:
Dennis Kobert
2023-05-27 11:48:57 +02:00
committed by Keavon Chambers
parent 5c7211cb30
commit 4bd9fbd073
40 changed files with 834 additions and 471 deletions

View File

@@ -1,3 +1,5 @@
use futures::Future;
use graphene_core::Node;
use std::hash::{Hash, Hasher};
@@ -15,25 +17,30 @@ pub struct CacheNode<T, CachedNode> {
cache: boxcar::Vec<(u64, T, AtomicBool)>,
node: CachedNode,
}
impl<'i, T: 'i, I: 'i + Hash, CachedNode: 'i> Node<'i, I> for CacheNode<T, CachedNode>
impl<'i, T: 'i + Clone, I: 'i + Hash, CachedNode: 'i> Node<'i, I> for CacheNode<T, CachedNode>
where
CachedNode: for<'any_input> Node<'any_input, I, Output = T>,
CachedNode: for<'any_input> Node<'any_input, I>,
for<'a> <CachedNode as Node<'a, I>>::Output: core::future::Future<Output = T> + 'a,
{
type Output = &'i T;
// TODO: This should return a reference to the cached cached_value
// but that requires a lot of lifetime magic <- This was suggested by copilot but is pretty acurate xD
type Output = Pin<Box<dyn Future<Output = T> + 'i>>;
fn eval(&'i self, input: I) -> Self::Output {
let mut hasher = Xxh3::new();
input.hash(&mut hasher);
let hash = hasher.finish();
Box::pin(async move {
let mut hasher = Xxh3::new();
input.hash(&mut hasher);
let hash = hasher.finish();
if let Some((_, cached_value, keep)) = self.cache.iter().find(|(h, _, _)| *h == hash) {
keep.store(true, std::sync::atomic::Ordering::Relaxed);
cached_value
} else {
trace!("Cache miss");
let output = self.node.eval(input);
let index = self.cache.push((hash, output, AtomicBool::new(true)));
&self.cache[index].1
}
if let Some((_, cached_value, keep)) = self.cache.iter().find(|(h, _, _)| *h == hash) {
keep.store(true, std::sync::atomic::Ordering::Relaxed);
cached_value.clone()
} else {
trace!("Cache miss");
let output = self.node.eval(input).await;
let index = self.cache.push((hash, output, AtomicBool::new(true)));
self.cache[index].1.clone()
}
})
}
fn reset(mut self: Pin<&mut Self>) {
@@ -151,11 +158,12 @@ pub struct RefNode<T, Let> {
let_node: Let,
_t: PhantomData<T>,
}
impl<'i, T: 'i, Let> Node<'i, ()> for RefNode<T, Let>
where
Let: for<'a> Node<'a, Option<T>, Output = &'a T>,
Let: for<'a> Node<'a, Option<T>>,
{
type Output = &'i T;
type Output = <Let as Node<'i, Option<T>>>::Output;
fn eval(&'i self, _: ()) -> Self::Output {
self.let_node.eval(None)
}