diff --git a/Cargo.lock b/Cargo.lock index 932f4478a..588c0c2b6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1659,6 +1659,7 @@ dependencies = [ "compilation-client", "dyn-any", "dyn-clone", + "futures", "glam", "gpu-compiler-bin-wrapper", "gpu-executor", @@ -1671,6 +1672,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", + "reqwest", "serde", "serde_json", "syn 1.0.109", @@ -2140,6 +2142,7 @@ dependencies = [ "borrow_stack", "dyn-any", "dyn-clone", + "futures", "glam", "graph-craft", "graphene-core", @@ -2148,6 +2151,7 @@ dependencies = [ "num-traits", "once_cell", "serde", + "tokio", ] [[package]] diff --git a/editor/src/messages/portfolio/document/document_message_handler.rs b/editor/src/messages/portfolio/document/document_message_handler.rs index 6824bef2a..5a4d0218b 100644 --- a/editor/src/messages/portfolio/document/document_message_handler.rs +++ b/editor/src/messages/portfolio/document/document_message_handler.rs @@ -1505,7 +1505,7 @@ impl DocumentMessageHandler { /// When working with an insert index, deleting the layers may cause the insert index to point to a different location (if the layer being deleted was located before the insert index). /// /// This function updates the insert index so that it points to the same place after the specified `layers` are deleted. - fn update_insert_index<'a>(&self, layers: &[&'a [LayerId]], path: &[LayerId], insert_index: isize, reverse_index: bool) -> Result { + fn update_insert_index(&self, layers: &[&[LayerId]], path: &[LayerId], insert_index: isize, reverse_index: bool) -> Result { let folder = self.document_legacy.folder(path)?; let insert_index = if reverse_index { folder.layer_ids.len() as isize - insert_index } else { insert_index }; let layer_ids_above = if insert_index < 0 { &folder.layer_ids } else { &folder.layer_ids[..(insert_index as usize)] }; diff --git a/editor/src/messages/portfolio/document/node_graph/node_graph_message_handler/document_node_types.rs b/editor/src/messages/portfolio/document/node_graph/node_graph_message_handler/document_node_types.rs index 97dc7ef7d..b5c8ee9cc 100644 --- a/editor/src/messages/portfolio/document/node_graph/node_graph_message_handler/document_node_types.rs +++ b/editor/src/messages/portfolio/document/node_graph/node_graph_message_handler/document_node_types.rs @@ -96,7 +96,7 @@ fn static_nodes() -> Vec { vec![ DocumentNodeType { name: "Identity", - category: "General", + category: "Structural", identifier: NodeImplementation::proto("graphene_core::ops::IdNode"), inputs: vec![DocumentInputType { name: "In", @@ -108,7 +108,7 @@ fn static_nodes() -> Vec { }, DocumentNodeType { name: "Monitor", - category: "General", + category: "Structural", identifier: NodeImplementation::proto("graphene_core::ops::IdNode"), inputs: vec![DocumentInputType { name: "In", @@ -186,7 +186,7 @@ fn static_nodes() -> Vec { category: "Ignore", identifier: NodeImplementation::DocumentNode(NodeNetwork { inputs: vec![0], - outputs: vec![NodeOutput::new(2, 0)], + outputs: vec![NodeOutput::new(1, 0)], nodes: [ DocumentNode { name: "Downres".to_string(), @@ -200,12 +200,13 @@ fn static_nodes() -> Vec { implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_std::memo::CacheNode")), ..Default::default() }, - DocumentNode { + // We currently just clone by default + /*DocumentNode { name: "Clone".to_string(), inputs: vec![NodeInput::node(1, 0)], implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::ops::CloneNode<_>")), ..Default::default() - }, + },*/ ] .into_iter() .enumerate() @@ -263,10 +264,10 @@ fn static_nodes() -> Vec { }, DocumentNodeType { name: "Begin Scope", - category: "Structural", + category: "Ignore", identifier: NodeImplementation::DocumentNode(NodeNetwork { inputs: vec![0, 2], - outputs: vec![NodeOutput::new(1, 0), NodeOutput::new(3, 0)], + outputs: vec![NodeOutput::new(1, 0), NodeOutput::new(2, 0)], nodes: [ DocumentNode { name: "SetNode".to_string(), @@ -286,12 +287,6 @@ fn static_nodes() -> Vec { implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_std::memo::RefNode<_, _>")), ..Default::default() }, - DocumentNode { - name: "CloneNode".to_string(), - inputs: vec![NodeInput::node(2, 0)], - implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::ops::CloneNode<_>")), - ..Default::default() - }, ] .into_iter() .enumerate() @@ -319,7 +314,7 @@ fn static_nodes() -> Vec { }, DocumentNodeType { name: "End Scope", - category: "Structural", + category: "Ignore", identifier: NodeImplementation::proto("graphene_std::memo::EndLetNode<_>"), inputs: vec![ DocumentInputType { @@ -596,7 +591,7 @@ fn static_nodes() -> Vec { }, DocumentNodeType { name: "Gaussian Blur", - category: "Image Filters", + category: "Ignore", identifier: NodeImplementation::DocumentNode(NodeNetwork { inputs: vec![0, 1, 1], outputs: vec![NodeOutput::new(1, 0)], diff --git a/editor/src/messages/tool/common_functionality/overlay_renderer.rs b/editor/src/messages/tool/common_functionality/overlay_renderer.rs index 56403751f..1d8eb0d0c 100644 --- a/editor/src/messages/tool/common_functionality/overlay_renderer.rs +++ b/editor/src/messages/tool/common_functionality/overlay_renderer.rs @@ -24,7 +24,7 @@ struct ManipulatorGroupOverlays { pub out_line: Option>, } impl ManipulatorGroupOverlays { - pub fn iter<'a>(&'a self) -> impl Iterator>> { + pub fn iter(&self) -> impl Iterator>> { [&self.anchor, &self.in_handle, &self.in_line, &self.out_handle, &self.out_line].into_iter() } } diff --git a/editor/src/messages/tool/common_functionality/shape_editor.rs b/editor/src/messages/tool/common_functionality/shape_editor.rs index faa54c849..977d7c12f 100644 --- a/editor/src/messages/tool/common_functionality/shape_editor.rs +++ b/editor/src/messages/tool/common_functionality/shape_editor.rs @@ -145,7 +145,7 @@ impl ShapeState { } /// Provide the currently selected points by reference. - pub fn selected_points<'a>(&'a self) -> impl Iterator { + pub fn selected_points(&self) -> impl Iterator { self.selected_shape_state.values().flat_map(|state| &state.selected_points) } diff --git a/editor/src/node_graph_executor.rs b/editor/src/node_graph_executor.rs index 63abb4787..d3d47d928 100644 --- a/editor/src/node_graph_executor.rs +++ b/editor/src/node_graph_executor.rs @@ -23,6 +23,7 @@ use interpreted_executor::executor::DynamicExecutor; use glam::{DAffine2, DVec2}; use std::borrow::Cow; use std::cell::RefCell; +use std::rc::Rc; use std::sync::mpsc::{Receiver, Sender}; use std::sync::Arc; @@ -58,7 +59,7 @@ pub(crate) struct GenerationResponse { } thread_local! { - static NODE_RUNTIME: RefCell> = RefCell::new(None); + pub(crate) static NODE_RUNTIME: Rc>> = Rc::new(RefCell::new(None)); } impl NodeRuntime { @@ -72,7 +73,7 @@ impl NodeRuntime { thumbnails: Default::default(), } } - pub fn run(&mut self) { + pub async fn run(&mut self) { let mut requests = self.receiver.try_iter().collect::>(); // TODO: Currently we still render the document after we submit the node graph execution request. // This should be avoided in the future. @@ -94,7 +95,7 @@ impl NodeRuntime { }) => { let (network, monitor_nodes) = Self::wrap_network(graph); - let result = self.execute_network(network, image_frame); + let result = self.execute_network(network, image_frame).await; let mut responses = VecDeque::new(); self.update_thumbnails(&path, monitor_nodes, &mut responses); let response = GenerationResponse { @@ -125,7 +126,7 @@ impl NodeRuntime { (scoped_network, monitor_nodes) } - fn execute_network<'a>(&'a mut self, scoped_network: NodeNetwork, image_frame: Option>) -> Result { + async fn execute_network<'a>(&'a mut self, scoped_network: NodeNetwork, image_frame: Option>) -> Result { let editor_api = EditorApi { font_cache: Some(&self.font_cache), image_frame, @@ -137,7 +138,7 @@ impl NodeRuntime { let proto_network = c.compile_single(scoped_network, true)?; assert_ne!(proto_network.nodes.len(), 0, "No protonodes exist?"); - if let Err(e) = self.executor.update(proto_network) { + if let Err(e) = self.executor.update(proto_network).await { error!("Failed to update executor:\n{}", e); return Err(e); } @@ -146,8 +147,8 @@ impl NodeRuntime { use graph_craft::executor::Executor; let result = match self.executor.input_type() { - Some(t) if t == concrete!(EditorApi) => self.executor.execute(editor_api.into_dyn()).map_err(|e| e.to_string()), - Some(t) if t == concrete!(()) => self.executor.execute(().into_dyn()).map_err(|e| e.to_string()), + Some(t) if t == concrete!(EditorApi) => self.executor.execute(editor_api.into_dyn()).await.map_err(|e| e.to_string()), + Some(t) if t == concrete!(()) => self.executor.execute(().into_dyn()).await.map_err(|e| e.to_string()), _ => Err("Invalid input type".to_string()), }; match result { @@ -200,13 +201,21 @@ impl NodeRuntime { } } -pub fn run_node_graph() { - NODE_RUNTIME.with(|runtime| { - let mut runtime = runtime.borrow_mut(); - if let Some(runtime) = runtime.as_mut() { - runtime.run(); +pub async fn run_node_graph() { + let result = NODE_RUNTIME.try_with(|runtime| { + let runtime = runtime.clone(); + async move { + let mut runtime = runtime.try_borrow_mut(); + if let Ok(ref mut runtime) = runtime { + if let Some(ref mut runtime) = runtime.as_mut() { + runtime.run().await; + } + } } }); + if let Ok(result) = result { + result.await; + } } #[derive(Debug)] @@ -231,8 +240,7 @@ impl Default for NodeGraphExecutor { let (request_sender, request_reciever) = std::sync::mpsc::channel(); let (response_sender, response_reciever) = std::sync::mpsc::channel(); NODE_RUNTIME.with(|runtime| { - let mut runtime = runtime.borrow_mut(); - *runtime = Some(NodeRuntime::new(request_reciever, response_sender)); + runtime.borrow_mut().replace(NodeRuntime::new(request_reciever, response_sender)); }); Self { diff --git a/frontend/src-tauri/src/main.rs b/frontend/src-tauri/src/main.rs index a1004096f..22d037e5e 100644 --- a/frontend/src-tauri/src/main.rs +++ b/frontend/src-tauri/src/main.rs @@ -12,12 +12,15 @@ use axum::routing::get; use axum::Router; use fern::colors::{Color, ColoredLevelConfig}; use http::{Response, StatusCode}; +use std::cell::RefCell; use std::collections::HashMap; use std::sync::Arc; use std::sync::Mutex; static IMAGES: Mutex>> = Mutex::new(None); -static EDITOR: Mutex> = Mutex::new(None); +thread_local! { + static EDITOR: RefCell> = RefCell::new(None); +} async fn respond_to(id: Path) -> impl IntoResponse { let builder = Response::builder().header("Access-Control-Allow-Origin", "*").status(StatusCode::OK); @@ -55,7 +58,7 @@ async fn main() { *(IMAGES.lock().unwrap()) = Some(HashMap::new()); graphite_editor::application::set_uuid_seed(0); - *(EDITOR.lock().unwrap()) = Some(Editor::new()); + EDITOR.with(|editor| editor.borrow_mut().replace(Editor::new())); let app = Router::new().route("/", get(|| async { "Hello, World!" })).route("/image/:id", get(respond_to)); // run it with hyper on localhost:3000 @@ -84,9 +87,10 @@ fn handle_message(message: String) -> String { let Ok(message) = ron::from_str::(&message) else { panic!("Error parsing message: {}", message) }; - let mut guard = EDITOR.lock().unwrap(); - let editor = (*guard).as_mut().unwrap(); - let responses = editor.handle_message(message); + let responses = EDITOR.with(|editor| { + let mut editor = editor.borrow_mut(); + editor.as_mut().unwrap().handle_message(message) + }); // Sends a FrontendMessage to JavaScript fn send_frontend_message_to_js(message: FrontendMessage) -> FrontendMessage { diff --git a/frontend/wasm/src/editor_api.rs b/frontend/wasm/src/editor_api.rs index b8a84dfca..de8882bd0 100644 --- a/frontend/wasm/src/editor_api.rs +++ b/frontend/wasm/src/editor_api.rs @@ -59,16 +59,17 @@ fn window() -> web_sys::Window { } fn request_animation_frame(f: &Closure) { + //window().request_idle_callback(f.as_ref().unchecked_ref()).unwrap(); window().request_animation_frame(f.as_ref().unchecked_ref()).expect("should register `requestAnimationFrame` OK"); } // Sends a message to the dispatcher in the Editor Backend -fn poll_node_graph_evaluation() { +async fn poll_node_graph_evaluation() { // Process no further messages after a crash to avoid spamming the console if EDITOR_HAS_CRASHED.load(Ordering::SeqCst) { return; } - editor::node_graph_executor::run_node_graph(); + editor::node_graph_executor::run_node_graph().await; // Get the editor instances, dispatch the message, and store the `FrontendMessage` queue response EDITOR_INSTANCES.with(|instances| { @@ -218,7 +219,7 @@ impl JsEditorHandle { let g = f.clone(); *g.borrow_mut() = Some(Closure::new(move || { - poll_node_graph_evaluation(); + wasm_bindgen_futures::spawn_local(poll_node_graph_evaluation()); // Schedule ourself for another requestAnimationFrame callback. request_animation_frame(f.borrow().as_ref().unwrap()); @@ -788,7 +789,7 @@ impl JsEditorHandle { Some(message_data) }) }); - frontend_messages.unwrap().unwrap_or_default().into() + frontend_messages.unwrap().unwrap_or_default() } } diff --git a/libraries/bezier-rs/src/subpath/transform.rs b/libraries/bezier-rs/src/subpath/transform.rs index fe7d6f1f1..887d0ff3d 100644 --- a/libraries/bezier-rs/src/subpath/transform.rs +++ b/libraries/bezier-rs/src/subpath/transform.rs @@ -637,7 +637,7 @@ mod tests { let outline = subpath.outline(10., crate::Join::Round, crate::Cap::Round).0; assert!(outline.manipulator_groups.windows(2).all(|pair| !pair[0].anchor.abs_diff_eq(pair[1].anchor, MAX_ABSOLUTE_DIFFERENCE))); - assert_eq!(outline.closed(), true); + assert!(outline.closed()); } #[test] diff --git a/libraries/dyn-any/src/lib.rs b/libraries/dyn-any/src/lib.rs index 4259366d1..b962defd2 100644 --- a/libraries/dyn-any/src/lib.rs +++ b/libraries/dyn-any/src/lib.rs @@ -193,9 +193,18 @@ unsafe impl StaticType for [T; N] { type Static = [::Static; N]; } -unsafe impl<'a> StaticType for dyn DynAny<'a> + '_ { +unsafe impl StaticType for dyn for<'i> DynAny<'_> + '_ { type Static = dyn DynAny<'static>; } +unsafe impl StaticType for dyn for<'i> DynAny<'_> + Send + Sync + '_ { + type Static = dyn DynAny<'static> + Send + Sync; +} +unsafe impl StaticType for dyn core::future::Future + Send + Sync + '_ { + type Static = dyn core::future::Future + Send + Sync; +} +unsafe impl StaticType for dyn core::future::Future + '_ { + type Static = dyn core::future::Future; +} #[cfg(feature = "alloc")] pub trait IntoDynAny<'n>: Sized + StaticType + 'n { fn into_dyn(self) -> Box + 'n> { @@ -228,13 +237,14 @@ use core::{ mem::{ManuallyDrop, MaybeUninit}, num::Wrapping, ops::Range, + pin::Pin, time::Duration, }; impl_type!( Option, Result, Cell, UnsafeCell, RefCell, MaybeUninit, ManuallyDrop, PhantomData, PhantomPinned, Empty, Range, - Wrapping, Duration, bool, f32, f64, char, + Wrapping, Pin, Duration, bool, f32, f64, char, u8, AtomicU8, u16, AtomicU16, u32, AtomicU32, u64, usize, AtomicUsize, i8, AtomicI8, i16, AtomicI16, i32, AtomicI32, i64, isize, AtomicIsize, i128, u128, AtomicBool, AtomicPtr diff --git a/node-graph/gcore/src/lib.rs b/node-graph/gcore/src/lib.rs index b3e62a68d..9c0edeccc 100644 --- a/node-graph/gcore/src/lib.rs +++ b/node-graph/gcore/src/lib.rs @@ -60,6 +60,10 @@ where Self::Output: 'i + StaticTypeSized, Input: 'i + StaticTypeSized, { + fn node_name(&self) -> &'static str { + core::any::type_name::() + } + fn input_type(&self) -> TypeId { TypeId::of::() } @@ -121,6 +125,13 @@ impl<'i, I: 'i, O: 'i> Node<'i, I> for Pin Node<'a, I, Output = (**self).eval(input) } } +impl<'i, I: 'i, O: 'i> Node<'i, I> for Pin<&'i (dyn NodeIO<'i, I, Output = O> + 'i)> { + type Output = O; + + fn eval(&'i self, input: I) -> Self::Output { + (**self).eval(input) + } +} #[cfg(feature = "alloc")] pub use crate::raster::image::{EditorApi, ExtractImageFrame}; diff --git a/node-graph/gcore/src/ops.rs b/node-graph/gcore/src/ops.rs index 59642761c..57eb585a6 100644 --- a/node-graph/gcore/src/ops.rs +++ b/node-graph/gcore/src/ops.rs @@ -22,7 +22,7 @@ pub struct AddParameterNode { second: Second, } -#[node_macro::node_fn(AddParameterNode)] +#[node_macro::node_new(AddParameterNode)] fn add_parameter(first: U, second: T) -> >::Output where U: Add, @@ -30,6 +30,24 @@ where first + second } +#[automatically_derived] +impl<'input, U: 'input, T: 'input, S0: 'input> Node<'input, U> for AddParameterNode +where + U: Add, + S0: Node<'input, (), Output = T>, +{ + type Output = >::Output; + #[inline] + fn eval(&'input self, first: U) -> Self::Output { + let second = self.second.eval(()); + { + { + first + second + } + } + } +} + pub struct MulParameterNode { second: Second, } @@ -181,7 +199,7 @@ pub struct MapResultNode { } #[node_macro::node_fn(MapResultNode<_I, _E>)] -fn flat_map<_I, _E, N>(input: Result<_I, _E>, node: &'any_input N) -> Result<>::Output, _E> +fn flat_map<_I, _E, N>(input: Result<_I, _E>, node: &'input N) -> Result<>::Output, _E> where N: for<'a> Node<'a, _I>, { @@ -195,7 +213,7 @@ pub struct FlatMapResultNode { } #[node_macro::node_fn(FlatMapResultNode<_I, _O, _E>)] -fn flat_map<_I, _O, _E, N>(input: Result<_I, _E>, node: &'any_input N) -> Result<_O, _E> +fn flat_map<_I, _O, _E, N>(input: Result<_I, _E>, node: &'input N) -> Result<_O, _E> where N: for<'a> Node<'a, _I, Output = Result<_O, _E>>, { diff --git a/node-graph/gcore/src/raster.rs b/node-graph/gcore/src/raster.rs index 01b3004cc..075abeb03 100644 --- a/node-graph/gcore/src/raster.rs +++ b/node-graph/gcore/src/raster.rs @@ -221,7 +221,7 @@ pub struct MapNode { } #[node_macro::node_fn(MapNode)] -fn map_node<_Iter: Iterator, MapFnNode>(input: _Iter, map_fn: &'any_input MapFnNode) -> MapFnIterator<'input, _Iter, MapFnNode> +fn map_node<_Iter: Iterator, MapFnNode>(input: _Iter, map_fn: &'input MapFnNode) -> MapFnIterator<'input, _Iter, MapFnNode> where MapFnNode: for<'any_input> Node<'any_input, _Iter::Item>, { @@ -425,7 +425,7 @@ pub struct MapSndNode { } #[node_macro::node_fn(MapSndNode< _First, _Second>)] -fn map_snd_node(input: (_First, _Second), map_fn: &'any_input MapFn) -> (_First, >::Output) +fn map_snd_node(input: (_First, _Second), map_fn: &'input MapFn) -> (_First, >::Output) where MapFn: for<'any_input> Node<'any_input, _Second>, { @@ -449,7 +449,7 @@ pub struct ForEachNode { } #[node_macro::node_fn(ForEachNode)] -fn map_node<_Iter: Iterator, MapNode>(input: _Iter, map_node: &'any_input MapNode) -> () +fn map_node<_Iter: Iterator, MapNode>(input: _Iter, map_node: &'input MapNode) -> () where MapNode: for<'any_input> Node<'any_input, _Iter::Item, Output = ()> + 'input, { diff --git a/node-graph/gcore/src/raster/image.rs b/node-graph/gcore/src/raster/image.rs index ca87b91b3..2793a8fce 100644 --- a/node-graph/gcore/src/raster/image.rs +++ b/node-graph/gcore/src/raster/image.rs @@ -329,12 +329,13 @@ impl<'a> AsRef> for EditorApi<'a> { } } +#[derive(Debug, Clone, Copy, Default)] pub struct ExtractImageFrame; -impl<'a: 'input, 'input> Node<'input, EditorApi<'a>> for ExtractImageFrame { +impl<'a: 'input, 'input> Node<'input, &'a EditorApi<'a>> for ExtractImageFrame { type Output = ImageFrame; - fn eval(&'input self, mut editor_api: EditorApi<'a>) -> Self::Output { - editor_api.image_frame.take().unwrap_or(ImageFrame::identity()) + fn eval(&'input self, editor_api: &'a EditorApi<'a>) -> Self::Output { + editor_api.image_frame.clone().unwrap_or(ImageFrame::identity()) } } diff --git a/node-graph/gcore/src/storage.rs b/node-graph/gcore/src/storage.rs index b6cb76c1e..3396ec83e 100644 --- a/node-graph/gcore/src/storage.rs +++ b/node-graph/gcore/src/storage.rs @@ -11,7 +11,7 @@ struct SetNode { } #[node_macro::node_fn(SetNode<_S, _I>)] -fn set_node(value: T, storage: &'any_input mut _S, index: _I) +fn set_node(value: T, storage: &'input mut _S, index: _I) where _S: IndexMut<_I>, _S::Output: DerefMut + Sized, @@ -25,7 +25,7 @@ struct GetNode { } #[node_macro::node_fn(GetNode<_S>)] -fn get_node<_S, I>(index: I, storage: &'any_input _S) -> &'input _S::Output +fn get_node<_S, I>(index: I, storage: &'input _S) -> &'input _S::Output where _S: Index, _S::Output: Sized, diff --git a/node-graph/gcore/src/structural.rs b/node-graph/gcore/src/structural.rs index 314f63c79..57a8b9f4b 100644 --- a/node-graph/gcore/src/structural.rs +++ b/node-graph/gcore/src/structural.rs @@ -32,6 +32,39 @@ where } } +#[derive(Clone)] +pub struct AsyncComposeNode { + first: First, + second: Second, + phantom: PhantomData, +} + +impl<'i, 'f: 'i, 's: 'i, Input: 'static, First, Second> Node<'i, Input> for AsyncComposeNode +where + First: Node<'i, Input>, + First::Output: core::future::Future, + Second: Node<'i, <>::Output as core::future::Future>::Output> + 'i, +{ + type Output = core::pin::Pin>::Output as core::future::Future>::Output>>::Output> + 'i>>; + fn eval(&'i self, input: Input) -> Self::Output { + Box::pin(async move { + let arg = self.first.eval(input).await; + self.second.eval(arg) + }) + } +} + +impl<'i, First, Second, Input: 'i> AsyncComposeNode +where + First: Node<'i, Input>, + First::Output: core::future::Future, + Second: Node<'i, <>::Output as core::future::Future>::Output> + 'i, +{ + pub const fn new(first: First, second: Second) -> Self { + AsyncComposeNode:: { first, second, phantom: PhantomData } + } +} + pub trait Then<'i, Input: 'i>: Sized { fn then(self, second: Second) -> ComposeNode where @@ -44,6 +77,19 @@ pub trait Then<'i, Input: 'i>: Sized { impl<'i, First: Node<'i, Input>, Input: 'i> Then<'i, Input> for First {} +pub trait AndThen<'i, Input: 'i>: Sized { + fn and_then(self, second: Second) -> AsyncComposeNode + where + Self: Node<'i, Input>, + Self::Output: core::future::Future, + Second: Node<'i, <>::Output as core::future::Future>::Output> + 'i, + { + AsyncComposeNode::new(self, second) + } +} + +impl<'i, First: Node<'i, Input>, Input: 'i> AndThen<'i, Input> for First {} + pub struct ConsNode, Root>(pub Root, PhantomData); impl<'i, Root, Input: 'i, I: 'i + From<()>> Node<'i, Input> for ConsNode diff --git a/node-graph/gcore/src/text.rs b/node-graph/gcore/src/text.rs index 44414712b..446e6afb8 100644 --- a/node-graph/gcore/src/text.rs +++ b/node-graph/gcore/src/text.rs @@ -15,7 +15,7 @@ pub struct TextGenerator { } #[node_fn(TextGenerator)] -fn generate_text<'a: 'input>(editor: EditorApi<'a>, text: String, font_name: Font, font_size: f64) -> crate::vector::VectorData { +fn generate_text<'a: 'input>(editor: &'a EditorApi<'a>, text: String, font_name: Font, font_size: f64) -> crate::vector::VectorData { let buzz_face = editor.font_cache.and_then(|cache| cache.get(&font_name)).map(|data| load_face(data)); crate::vector::VectorData::from_subpaths(to_path(&text, buzz_face, font_size, None)) } diff --git a/node-graph/gcore/src/value.rs b/node-graph/gcore/src/value.rs index 6c6bf74fb..2acd3b2af 100644 --- a/node-graph/gcore/src/value.rs +++ b/node-graph/gcore/src/value.rs @@ -158,6 +158,7 @@ mod test { assert_eq!(node.eval(()), 0); } #[test] + #[allow(clippy::unit_cmp)] fn test_unit_node() { let node = ForgetNode::new(); assert_eq!(node.eval(()), ()); diff --git a/node-graph/gpu-compiler/gpu-compiler-bin-wrapper/src/lib.rs b/node-graph/gpu-compiler/gpu-compiler-bin-wrapper/src/lib.rs index 9b33f88b8..95d1e2a81 100644 --- a/node-graph/gpu-compiler/gpu-compiler-bin-wrapper/src/lib.rs +++ b/node-graph/gpu-compiler/gpu-compiler-bin-wrapper/src/lib.rs @@ -10,6 +10,7 @@ pub fn compile_spirv(request: &CompileRequest, compile_dir: Option<&str>, manife io: request.shader_io.clone(), })?; + #[cfg(not(feature = "profiling"))] let features = ""; #[cfg(feature = "profiling")] let features = "profiling"; diff --git a/node-graph/gpu-executor/src/lib.rs b/node-graph/gpu-executor/src/lib.rs index ca94d4b5f..b13e07834 100644 --- a/node-graph/gpu-executor/src/lib.rs +++ b/node-graph/gpu-executor/src/lib.rs @@ -168,7 +168,7 @@ pub struct UniformNode { } #[node_macro::node_fn(UniformNode)] -fn uniform_node(data: T, executor: &'any_input E) -> ShaderInput { +fn uniform_node(data: T, executor: &'input E) -> ShaderInput { executor.create_uniform_buffer(data).unwrap() } @@ -177,7 +177,7 @@ pub struct StorageNode { } #[node_macro::node_fn(StorageNode)] -fn storage_node(data: T, executor: &'any_input E) -> ShaderInput { +fn storage_node(data: T, executor: &'input E) -> ShaderInput { executor .create_storage_buffer( data, @@ -205,7 +205,7 @@ pub struct CreateOutputBufferNode { } #[node_macro::node_fn(CreateOutputBufferNode)] -fn create_output_buffer_node(size: usize, executor: &'any_input E, ty: Type) -> ShaderInput { +fn create_output_buffer_node(size: usize, executor: &'input E, ty: Type) -> ShaderInput { executor.create_output_buffer(size, ty, true).unwrap() } @@ -216,7 +216,7 @@ pub struct CreateComputePassNode { } #[node_macro::node_fn(CreateComputePassNode)] -fn create_compute_pass_node(layout: PipelineLayout, executor: &'any_input E, output: ShaderInput, instances: u32) -> E::CommandBuffer { +fn create_compute_pass_node(layout: PipelineLayout, executor: &'input E, output: ShaderInput, instances: u32) -> E::CommandBuffer { executor.create_compute_pass(&layout, Some(output), instances).unwrap() } @@ -242,7 +242,7 @@ pub struct ExecuteComputePipelineNode { } #[node_macro::node_fn(ExecuteComputePipelineNode)] -fn execute_compute_pipeline_node(encoder: E::CommandBuffer, executor: &'any_input mut E) { +fn execute_compute_pipeline_node(encoder: E::CommandBuffer, executor: &'input mut E) { executor.execute_compute_pipeline(encoder).unwrap(); } @@ -251,6 +251,6 @@ fn execute_compute_pipeline_node(encoder: E::CommandBuffer, exec // executor: Executor, // } // #[node_macro::node_fn(ReadOutputBufferNode)] -// fn read_output_buffer_node(buffer: E::BufferHandle, executor: &'any_input mut E) -> Vec { +// fn read_output_buffer_node(buffer: E::BufferHandle, executor: &'input mut E) -> Vec { // executor.read_output_buffer(buffer).await.unwrap() // } diff --git a/node-graph/graph-craft/src/document.rs b/node-graph/graph-craft/src/document.rs index 00bb0d0e7..29cc652d6 100644 --- a/node-graph/graph-craft/src/document.rs +++ b/node-graph/graph-craft/src/document.rs @@ -295,7 +295,7 @@ impl NodeNetwork { self.previous_outputs.as_ref().unwrap_or(&self.outputs) } - pub fn input_types<'a>(&'a self) -> impl Iterator + 'a { + pub fn input_types(&self) -> impl Iterator + '_ { self.inputs.iter().map(move |id| self.nodes[id].inputs.get(0).map(|i| i.ty()).unwrap_or(concrete!(()))) } diff --git a/node-graph/graph-craft/src/document/value.rs b/node-graph/graph-craft/src/document/value.rs index 0126e9c0b..267fdae5d 100644 --- a/node-graph/graph-craft/src/document/value.rs +++ b/node-graph/graph-craft/src/document/value.rs @@ -1,6 +1,7 @@ use super::DocumentNode; use crate::executor::Any; pub use crate::imaginate_input::{ImaginateMaskStartingFill, ImaginateSamplingMethod, ImaginateStatus}; +use crate::proto::{Any as DAny, FutureAny}; use graphene_core::raster::{BlendMode, LuminanceCalculation}; use graphene_core::{Color, Node, Type}; @@ -305,11 +306,11 @@ impl<'a> TaggedValue { pub struct UpcastNode { value: TaggedValue, } -impl<'input> Node<'input, Box + 'input>> for UpcastNode { - type Output = Box + 'input>; +impl<'input> Node<'input, DAny<'input>> for UpcastNode { + type Output = FutureAny<'input>; - fn eval(&'input self, _: Box + 'input>) -> Self::Output { - self.value.clone().to_any() + fn eval(&'input self, _: DAny<'input>) -> Self::Output { + Box::pin(async move { self.value.clone().to_any() }) } } impl UpcastNode { diff --git a/node-graph/graph-craft/src/executor.rs b/node-graph/graph-craft/src/executor.rs index dbc1dd2e0..76f1a7f01 100644 --- a/node-graph/graph-craft/src/executor.rs +++ b/node-graph/graph-craft/src/executor.rs @@ -3,7 +3,7 @@ use std::error::Error; use dyn_any::DynAny; use crate::document::NodeNetwork; -use crate::proto::ProtoNetwork; +use crate::proto::{LocalFuture, ProtoNetwork}; pub struct Compiler {} @@ -37,5 +37,5 @@ impl Compiler { pub type Any<'a> = Box + 'a>; pub trait Executor { - fn execute<'a, 's: 'a>(&'s self, input: Any<'a>) -> Result, Box>; + fn execute<'a>(&'a self, input: Any<'a>) -> LocalFuture, Box>>; } diff --git a/node-graph/graph-craft/src/proto.rs b/node-graph/graph-craft/src/proto.rs index dbc29fd98..19e60e8b6 100644 --- a/node-graph/graph-craft/src/proto.rs +++ b/node-graph/graph-craft/src/proto.rs @@ -1,5 +1,6 @@ use std::borrow::Cow; use std::collections::{HashMap, HashSet}; + use std::hash::Hash; use xxhash_rust::xxh3::Xxh3; @@ -11,12 +12,15 @@ use graphene_core::*; use serde::{Deserialize, Serialize}; use std::pin::Pin; +pub type DynFuture<'n, T> = Pin + 'n>>; +pub type LocalFuture<'n, T> = Pin + 'n>>; pub type Any<'n> = Box + 'n>; -pub type TypeErasedNode<'n> = dyn for<'i> NodeIO<'i, Any<'i>, Output = Any<'i>> + 'n + Send + Sync; -pub type TypeErasedPinnedRef<'n> = Pin<&'n (dyn for<'i> NodeIO<'i, Any<'i>, Output = Any<'i>> + 'n + Send + Sync)>; -pub type TypeErasedPinned<'n> = Pin NodeIO<'i, Any<'i>, Output = Any<'i>> + 'n + Send + Sync>>; +pub type FutureAny<'n> = DynFuture<'n, Any<'n>>; +pub type TypeErasedNode<'n> = dyn for<'i> NodeIO<'i, Any<'i>, Output = FutureAny<'i>> + 'n; +pub type TypeErasedPinnedRef<'n> = Pin<&'n (dyn for<'i> NodeIO<'i, Any<'i>, Output = FutureAny<'i>> + 'n)>; +pub type TypeErasedPinned<'n> = Pin NodeIO<'i, Any<'i>, Output = FutureAny<'i>> + 'n>>; -pub type NodeConstructor = for<'a> fn(Vec>) -> TypeErasedPinned<'static>; +pub type NodeConstructor = for<'a> fn(Vec>) -> DynFuture<'static, TypeErasedPinned<'static>>; #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Debug, Default, PartialEq, Clone)] diff --git a/node-graph/gstd/Cargo.toml b/node-graph/gstd/Cargo.toml index 539fc5ddf..71f8a650b 100644 --- a/node-graph/gstd/Cargo.toml +++ b/node-graph/gstd/Cargo.toml @@ -59,6 +59,8 @@ node-macro = { path = "../node-macro" } boxcar = "0.1.0" xxhash-rust = { workspace = true } serde_json = "1.0.96" +reqwest = { version = "0.11.17", features = ["rustls", "rustls-tls"] } +futures = "0.3.28" [dependencies.serde] version = "1.0" diff --git a/node-graph/gstd/src/any.rs b/node-graph/gstd/src/any.rs index 1c11e739d..a908e4671 100644 --- a/node-graph/gstd/src/any.rs +++ b/node-graph/gstd/src/any.rs @@ -1,5 +1,6 @@ use dyn_any::StaticType; pub use graph_craft::proto::{Any, TypeErasedNode, TypeErasedPinned, TypeErasedPinnedRef}; +use graph_craft::proto::{DynFuture, FutureAny}; use graphene_core::NodeIO; pub use graphene_core::{generic, ops, Node}; use std::marker::PhantomData; @@ -12,18 +13,20 @@ pub struct DynAnyNode { impl<'input, _I: 'input + StaticType, _O: 'input + StaticType, N: 'input, S0: 'input> Node<'input, Any<'input>> for DynAnyNode<_I, _O, S0> where - N: for<'any_input> Node<'any_input, _I, Output = _O>, + N: for<'any_input> Node<'any_input, _I, Output = DynFuture<'any_input, _O>>, S0: for<'any_input> Node<'any_input, (), Output = &'any_input N>, { - type Output = Any<'input>; + type Output = FutureAny<'input>; #[inline] fn eval(&'input self, input: Any<'input>) -> Self::Output { let node = self.node.eval(()); - { - let node_name = core::any::type_name::(); - let input: Box<_I> = dyn_any::downcast(input).unwrap_or_else(|e| panic!("DynAnyNode Input, {0} in:\n{1}", e, node_name)); - Box::new(node.eval(*input)) - } + let node_name = core::any::type_name::(); + let input: Box<_I> = dyn_any::downcast(input).unwrap_or_else(|e| panic!("DynAnyNode Input, {0} in:\n{1}", e, node_name)); + let output = async move { + let result = node.eval(*input).await; + Box::new(result) as Any<'input> + }; + Box::pin(output) } fn reset(self: std::pin::Pin<&mut Self>) { @@ -56,11 +59,13 @@ impl<'input, _I: 'input + StaticType, _O: 'input + StaticType, N: 'input> Node<' where N: for<'any_input> Node<'any_input, _I, Output = &'any_input _O>, { - type Output = Any<'input>; + type Output = FutureAny<'input>; fn eval(&'input self, input: Any<'input>) -> Self::Output { let node_name = core::any::type_name::(); let input: Box<_I> = dyn_any::downcast(input).unwrap_or_else(|e| panic!("DynAnyRefNode Input, {e} in:\n{node_name}")); - Box::new(self.node.eval(*input)) + let result = self.node.eval(*input); + let output = async move { Box::new(result) as Any<'input> }; + Box::pin(output) } fn reset(self: std::pin::Pin<&mut Self>) { let wrapped_node = unsafe { self.map_unchecked_mut(|e| &mut e.node) }; @@ -79,14 +84,15 @@ pub struct DynAnyInRefNode { } impl<'input, _I: 'input + StaticType, _O: 'input + StaticType, N: 'input> Node<'input, Any<'input>> for DynAnyInRefNode<_I, _O, N> where - N: for<'any_input> Node<'any_input, &'any_input _I, Output = _O>, + N: for<'any_input> Node<'any_input, &'any_input _I, Output = DynFuture<'any_input, _O>>, { - type Output = Any<'input>; + type Output = FutureAny<'input>; fn eval(&'input self, input: Any<'input>) -> Self::Output { { let node_name = core::any::type_name::(); let input: Box<&_I> = dyn_any::downcast(input).unwrap_or_else(|e| panic!("DynAnyInRefNode Input, {e} in:\n{node_name}")); - Box::new(self.node.eval(*input)) + let result = self.node.eval(*input); + Box::pin(async move { Box::new(result.await) as Any<'_> }) } } } @@ -96,13 +102,37 @@ impl<_I, _O, S0> DynAnyInRefNode<_I, _O, S0> { } } +pub struct FutureWrapperNode { + node: Node, +} + +impl<'i, T: 'i, N: Node<'i, T>> Node<'i, T> for FutureWrapperNode +where + N: Node<'i, T>, +{ + type Output = DynFuture<'i, N::Output>; + fn eval(&'i self, input: T) -> Self::Output { + Box::pin(async move { self.node.eval(input) }) + } + fn reset(self: std::pin::Pin<&mut Self>) { + let wrapped_node = unsafe { self.map_unchecked_mut(|e| &mut e.node) }; + Node::reset(wrapped_node); + } +} + +impl<'i, N> FutureWrapperNode { + pub const fn new(node: N) -> Self { + Self { node } + } +} + pub trait IntoTypeErasedNode<'n> { fn into_type_erased(self) -> TypeErasedPinned<'n>; } impl<'n, N: 'n> IntoTypeErasedNode<'n> for N where - N: for<'i> NodeIO<'i, Any<'i>, Output = Any<'i>> + Send + Sync + 'n, + N: for<'i> NodeIO<'i, Any<'i>, Output = FutureAny<'i>> + 'n, { fn into_type_erased(self) -> TypeErasedPinned<'n> { Box::pin(self) @@ -139,13 +169,17 @@ pub struct DowncastBothNode<'a, I, O> { _o: PhantomData, } impl<'n: 'input, 'input, O: 'input + StaticType, I: 'input + StaticType> Node<'input, I> for DowncastBothNode<'n, I, O> { - type Output = O; + type Output = DynFuture<'input, O>; #[inline] fn eval(&'input self, input: I) -> Self::Output { { + let node_name = self.node.node_name(); let input = Box::new(input); - let out = dyn_any::downcast(self.node.eval(input)).unwrap_or_else(|e| panic!("DowncastBothNode Input {e}")); - *out + let future = self.node.eval(input); + Box::pin(async move { + let out = dyn_any::downcast(future.await).unwrap_or_else(|e| panic!("DowncastBothNode Input {e} in: \n{node_name}")); + *out + }) } } } @@ -166,13 +200,15 @@ pub struct DowncastBothRefNode<'a, I, O> { _i: PhantomData<(I, O)>, } impl<'n: 'input, 'input, O: 'input + StaticType, I: 'input + StaticType> Node<'input, I> for DowncastBothRefNode<'n, I, O> { - type Output = &'input O; + type Output = DynFuture<'input, &'input O>; #[inline] fn eval(&'input self, input: I) -> Self::Output { { let input = Box::new(input); - let out: Box<&_> = dyn_any::downcast::<&O>(self.node.eval(input)).unwrap_or_else(|e| panic!("DowncastBothRefNode Input {e}")); - *out + Box::pin(async move { + let out: Box<&_> = dyn_any::downcast::<&O>(self.node.eval(input).await).unwrap_or_else(|e| panic!("DowncastBothRefNode Input {e}")); + *out + }) } } } @@ -188,10 +224,12 @@ pub struct ComposeTypeErased<'a> { } impl<'i, 'a: 'i> Node<'i, Any<'i>> for ComposeTypeErased<'a> { - type Output = Any<'i>; + type Output = DynFuture<'i, Any<'i>>; fn eval(&'i self, input: Any<'i>) -> Self::Output { - let arg = self.first.eval(input); - self.second.eval(arg) + Box::pin(async move { + let arg = self.first.eval(input).await; + self.second.eval(arg).await + }) } } @@ -205,6 +243,21 @@ pub fn input_node(n: TypeErasedPinnedRef) -> DowncastBothNode<(), DowncastBothNode::new(n) } +pub struct PanicNode(PhantomData, PhantomData); + +impl<'i, I: 'i, O: 'i> Node<'i, I> for PanicNode { + type Output = O; + fn eval(&'i self, _: I) -> Self::Output { + unimplemented!("This node should never be evaluated") + } +} + +impl PanicNode { + pub const fn new() -> Self { + Self(PhantomData, PhantomData) + } +} + #[cfg(test)] mod test { use super::*; @@ -215,8 +268,8 @@ mod test { pub fn dyn_input_invalid_eval_panic() { //let add = DynAnyNode::new(AddNode::new()).into_type_erased(); //add.eval(Box::new(&("32", 32u32))); - let dyn_any = DynAnyNode::<(u32, u32), u32, _>::new(ValueNode::new(AddNode::new())); - let type_erased = dyn_any.into_type_erased(); + let dyn_any = DynAnyNode::<(u32, u32), u32, _>::new(ValueNode::new(FutureWrapperNode { node: AddNode::new() })); + let type_erased = Box::pin(dyn_any) as TypeErasedPinned; let _ref_type_erased = type_erased.as_ref(); //let type_erased = Box::pin(dyn_any) as TypeErasedPinned<'_>; type_erased.eval(Box::new(&("32", 32u32))); @@ -226,10 +279,10 @@ mod test { pub fn dyn_input_invalid_eval_panic_() { //let add = DynAnyNode::new(AddNode::new()).into_type_erased(); //add.eval(Box::new(&("32", 32u32))); - let dyn_any = DynAnyNode::<(u32, u32), u32, _>::new(ValueNode::new(AddNode::new())); + let dyn_any = DynAnyNode::<(u32, u32), u32, _>::new(ValueNode::new(FutureWrapperNode { node: AddNode::new() })); let type_erased = Box::pin(dyn_any) as TypeErasedPinned<'_>; type_erased.eval(Box::new((4u32, 2u32))); - let id_node = IdNode::new(); + let id_node = FutureWrapperNode::new(IdNode::new()); let type_erased_id = Box::pin(id_node) as TypeErasedPinned; let type_erased = ComposeTypeErased::new(type_erased.as_ref(), type_erased_id.as_ref()); type_erased.eval(Box::new((4u32, 2u32))); diff --git a/node-graph/gstd/src/brush.rs b/node-graph/gstd/src/brush.rs index ef3ec00a7..7b28d18f5 100644 --- a/node-graph/gstd/src/brush.rs +++ b/node-graph/gstd/src/brush.rs @@ -14,7 +14,7 @@ pub struct ReduceNode { } #[node_fn(ReduceNode)] -fn reduce(iter: I, initial: T, lambda: &'any_input Lambda) -> T +fn reduce(iter: I, initial: T, lambda: &'input Lambda) -> T where Lambda: for<'a> Node<'a, (T, I::Item), Output = T>, { diff --git a/node-graph/gstd/src/executor.rs b/node-graph/gstd/src/executor.rs index 390145a8c..f085973ee 100644 --- a/node-graph/gstd/src/executor.rs +++ b/node-graph/gstd/src/executor.rs @@ -17,7 +17,7 @@ pub struct GpuCompiler { // TODO: Move to graph-craft #[node_macro::node_fn(GpuCompiler)] -fn compile_gpu(node: &'input DocumentNode, mut typing_context: TypingContext, io: ShaderIO) -> compilation_client::Shader { +async fn compile_gpu(node: &'input DocumentNode, mut typing_context: TypingContext, io: ShaderIO) -> compilation_client::Shader { let compiler = graph_craft::executor::Compiler {}; let DocumentNodeImplementation::Network(network) = node.implementation; let proto_network = compiler.compile_single(network, true).unwrap(); @@ -25,7 +25,7 @@ fn compile_gpu(node: &'input DocumentNode, mut typing_context: TypingContext, io let input_types = proto_network.inputs.iter().map(|id| typing_context.get_type(*id).unwrap()).map(|node_io| node_io.output).collect(); let output_type = typing_context.get_type(proto_network.output).unwrap().output; - let bytes = compilation_client::compile_sync(proto_network, input_types, output_type, io).unwrap(); + let bytes = compilation_client::compile(proto_network, input_types, output_type, io).await.unwrap(); bytes } @@ -34,7 +34,7 @@ pub struct MapGpuNode { } #[node_macro::node_fn(MapGpuNode)] -fn map_gpu(inputs: Vec::BufferHandle>>, shader: &'any_input compilation_client::Shader) { +async fn map_gpu(inputs: Vec::BufferHandle>>, shader: &'any_input compilation_client::Shader) { use graph_craft::executor::Executor; let executor = NewExecutor::new().unwrap(); for input in shader.inputs.iter() { @@ -42,11 +42,13 @@ fn map_gpu(inputs: Vec::BufferHandle>>, executor.write_buffer(buffer, input.data).unwrap(); } todo!(); - let executor: GpuExecutor = GpuExecutor::new(Context::new_sync().unwrap(), shader.into(), "gpu::eval".into()).unwrap(); + /* + let executor: GpuExecutor = GpuExecutor::new(Context::new().await.unwrap(), shader.into(), "gpu::eval".into()).unwrap(); let data: Vec<_> = input.into_iter().collect(); let result = executor.execute(Box::new(data)).unwrap(); let result = dyn_any::downcast::>(result).unwrap(); *result + */ } pub struct MapGpuSingleImageNode { diff --git a/node-graph/gstd/src/http.rs b/node-graph/gstd/src/http.rs new file mode 100644 index 000000000..3df775869 --- /dev/null +++ b/node-graph/gstd/src/http.rs @@ -0,0 +1,32 @@ +use std::future::Future; + +use crate::Node; + +pub struct GetNode; + +#[node_macro::node_fn(GetNode)] +async fn get_node(url: String) -> reqwest::Response { + reqwest::get(url).await.unwrap() +} + +pub struct PostNode { + body: Body, +} + +#[node_macro::node_fn(PostNode)] +async fn post_node(url: String, body: String) -> reqwest::Response { + reqwest::Client::new().post(url).body(body).send().await.unwrap() +} + +#[derive(Clone, Copy, Debug)] +pub struct EvalSyncNode {} + +#[node_macro::node_fn(EvalSyncNode)] +fn eval_sync(future: F) -> F::Output { + let future = futures::future::maybe_done(future); + futures::pin_mut!(future); + match future.as_mut().take_output() { + Some(value) => value, + _ => panic!("Node construction future returned pending"), + } +} diff --git a/node-graph/gstd/src/lib.rs b/node-graph/gstd/src/lib.rs index 88b90947e..3a4c858cd 100644 --- a/node-graph/gstd/src/lib.rs +++ b/node-graph/gstd/src/lib.rs @@ -10,6 +10,8 @@ pub mod memo; pub mod raster; +pub mod http; + pub mod any; #[cfg(feature = "gpu")] diff --git a/node-graph/gstd/src/memo.rs b/node-graph/gstd/src/memo.rs index e7458f2d8..6c7fb092c 100644 --- a/node-graph/gstd/src/memo.rs +++ b/node-graph/gstd/src/memo.rs @@ -1,3 +1,5 @@ +use futures::Future; + use graphene_core::Node; use std::hash::{Hash, Hasher}; @@ -15,25 +17,30 @@ pub struct CacheNode { cache: boxcar::Vec<(u64, T, AtomicBool)>, node: CachedNode, } -impl<'i, T: 'i, I: 'i + Hash, CachedNode: 'i> Node<'i, I> for CacheNode +impl<'i, T: 'i + Clone, I: 'i + Hash, CachedNode: 'i> Node<'i, I> for CacheNode where - CachedNode: for<'any_input> Node<'any_input, I, Output = T>, + CachedNode: for<'any_input> Node<'any_input, I>, + for<'a> >::Output: core::future::Future + '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 + '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 { let_node: Let, _t: PhantomData, } + impl<'i, T: 'i, Let> Node<'i, ()> for RefNode where - Let: for<'a> Node<'a, Option, Output = &'a T>, + Let: for<'a> Node<'a, Option>, { - type Output = &'i T; + type Output = >>::Output; fn eval(&'i self, _: ()) -> Self::Output { self.let_node.eval(None) } diff --git a/node-graph/gstd/src/quantization.rs b/node-graph/gstd/src/quantization.rs index 45da56e08..99d693066 100644 --- a/node-graph/gstd/src/quantization.rs +++ b/node-graph/gstd/src/quantization.rs @@ -12,7 +12,7 @@ pub struct GenerateQuantizationNode { } #[node_macro::node_fn(GenerateQuantizationNode)] -fn generate_quantization_fn(image_frame: ImageFrame, samples: u32, function: u32) -> [Quantization; 4] { +fn generate_quantization_fn(image_frame: ImageFrame, samples: u32, function: u32) -> [Quantization; 4] { let image = image_frame.image; let len = image.data.len().min(10000); diff --git a/node-graph/gstd/src/raster.rs b/node-graph/gstd/src/raster.rs index 1ee9a9961..45be24ed0 100644 --- a/node-graph/gstd/src/raster.rs +++ b/node-graph/gstd/src/raster.rs @@ -85,7 +85,7 @@ pub struct MapImageNode { } #[node_macro::node_fn(MapImageNode<_P>)] -fn map_image>(image: Img, map_fn: &'any_input MapFn) -> Img +fn map_image>(image: Img, map_fn: &'input MapFn) -> Img where MapFn: for<'any_input> Node<'any_input, _P, Output = _P> + 'input, { @@ -282,7 +282,7 @@ pub struct BlendImageTupleNode { } #[node_macro::node_fn(BlendImageTupleNode<_P, _Fg>)] -fn blend_image_tuple<_P: Alpha + Pixel + Debug, MapFn, _Fg: Sample + Transform>(images: (ImageFrame<_P>, _Fg), map_fn: &'any_input MapFn) -> ImageFrame<_P> +fn blend_image_tuple<_P: Alpha + Pixel + Debug, MapFn, _Fg: Sample + Transform>(images: (ImageFrame<_P>, _Fg), map_fn: &'input MapFn) -> ImageFrame<_P> where MapFn: for<'any_input> Node<'any_input, (_P, _P), Output = _P> + 'input + Clone, { @@ -299,7 +299,7 @@ pub struct BlendImageNode { } #[node_macro::node_fn(BlendImageNode<_P>)] -fn blend_image_node<_P: Alpha + Pixel + Debug, MapFn, Forground: Sample + Transform>(foreground: Forground, background: ImageFrame<_P>, map_fn: &'any_input MapFn) -> ImageFrame<_P> +async fn blend_image_node<_P: Alpha + Pixel + Debug, MapFn, Forground: Sample + Transform>(foreground: Forground, background: ImageFrame<_P>, map_fn: &'input MapFn) -> ImageFrame<_P> where MapFn: for<'any_input> Node<'any_input, (_P, _P), Output = _P> + 'input, { @@ -314,7 +314,7 @@ pub struct BlendReverseImageNode { } #[node_macro::node_fn(BlendReverseImageNode<_P>)] -fn blend_image_node<_P: Alpha + Pixel + Debug, MapFn, Background: Transform + Sample>(foreground: ImageFrame<_P>, background: Background, map_fn: &'any_input MapFn) -> ImageFrame<_P> +fn blend_image_node<_P: Alpha + Pixel + Debug, MapFn, Background: Transform + Sample>(foreground: ImageFrame<_P>, background: Background, map_fn: &'input MapFn) -> ImageFrame<_P> where MapFn: for<'any_input> Node<'any_input, (_P, _P), Output = _P> + 'input, { diff --git a/node-graph/interpreted-executor/Cargo.toml b/node-graph/interpreted-executor/Cargo.toml index 27abad1b5..036cfd89a 100644 --- a/node-graph/interpreted-executor/Cargo.toml +++ b/node-graph/interpreted-executor/Cargo.toml @@ -24,3 +24,7 @@ log = "0.4" serde = { version = "1", features = ["derive"], optional = true } glam = { version = "0.22" } once_cell = "1.17.0" +futures = "0.3.28" + +[dev-dependencies] +tokio = { version = "1.12", features = ["rt", "macros"] } diff --git a/node-graph/interpreted-executor/src/executor.rs b/node-graph/interpreted-executor/src/executor.rs index 96b4c2d0c..6be76a2c0 100644 --- a/node-graph/interpreted-executor/src/executor.rs +++ b/node-graph/interpreted-executor/src/executor.rs @@ -6,7 +6,7 @@ use dyn_any::StaticType; use graph_craft::document::value::UpcastNode; use graph_craft::document::NodeId; use graph_craft::executor::Executor; -use graph_craft::proto::{ConstructionArgs, ProtoNetwork, ProtoNode, TypingContext}; +use graph_craft::proto::{ConstructionArgs, LocalFuture, ProtoNetwork, ProtoNode, TypingContext}; use graph_craft::Type; use graphene_std::any::{Any, TypeErasedPinned, TypeErasedPinnedRef}; @@ -33,11 +33,11 @@ impl Default for DynamicExecutor { } impl DynamicExecutor { - pub fn new(proto_network: ProtoNetwork) -> Result { + pub async fn new(proto_network: ProtoNetwork) -> Result { let mut typing_context = TypingContext::new(&node_registry::NODE_REGISTRY); typing_context.update(&proto_network)?; let output = proto_network.output; - let tree = BorrowTree::new(proto_network, &typing_context)?; + let tree = BorrowTree::new(proto_network, &typing_context).await?; Ok(Self { tree, @@ -47,11 +47,11 @@ impl DynamicExecutor { }) } - pub fn update(&mut self, proto_network: ProtoNetwork) -> Result<(), String> { + pub async fn update(&mut self, proto_network: ProtoNetwork) -> Result<(), String> { self.output = proto_network.output; self.typing_context.update(&proto_network)?; trace!("setting output to {}", self.output); - let mut orphans = self.tree.update(proto_network, &self.typing_context)?; + let mut orphans = self.tree.update(proto_network, &self.typing_context).await?; core::mem::swap(&mut self.orphaned_nodes, &mut orphans); for node_id in orphans { if self.orphaned_nodes.contains(&node_id) { @@ -75,8 +75,8 @@ impl DynamicExecutor { } impl Executor for DynamicExecutor { - fn execute<'a, 's: 'a>(&'s self, input: Any<'a>) -> Result, Box> { - self.tree.eval_any(self.output, input).ok_or_else(|| "Failed to execute".into()) + fn execute<'a>(&'a self, input: Any<'a>) -> LocalFuture, Box>> { + Box::pin(async move { self.tree.eval_any(self.output, input).await.ok_or_else(|| "Failed to execute".into()) }) } } @@ -118,20 +118,20 @@ pub struct BorrowTree { } impl BorrowTree { - pub fn new(proto_network: ProtoNetwork, typing_context: &TypingContext) -> Result { + pub async fn new(proto_network: ProtoNetwork, typing_context: &TypingContext) -> Result { let mut nodes = BorrowTree::default(); for (id, node) in proto_network.nodes { - nodes.push_node(id, node, typing_context)? + nodes.push_node(id, node, typing_context).await? } Ok(nodes) } /// Pushes new nodes into the tree and return orphaned nodes - pub fn update(&mut self, proto_network: ProtoNetwork, typing_context: &TypingContext) -> Result, String> { + pub async fn update(&mut self, proto_network: ProtoNetwork, typing_context: &TypingContext) -> Result, String> { let mut old_nodes: HashSet<_> = self.nodes.keys().copied().collect(); for (id, node) in proto_network.nodes { if !self.nodes.contains_key(&id) { - self.push_node(id, node, typing_context)?; + self.push_node(id, node, typing_context).await?; } else { let Some(node_container) = self.nodes.get_mut(&id) else { continue }; let mut node_container_writer = node_container.write().unwrap(); @@ -168,25 +168,25 @@ impl BorrowTree { self.nodes.get(&id).cloned() } - pub fn eval<'i, I: StaticType + 'i, O: StaticType + 'i>(&'i self, id: NodeId, input: I) -> Option { + pub async fn eval<'i, I: StaticType + 'i + Send + Sync, O: StaticType + Send + Sync + 'i>(&'i self, id: NodeId, input: I) -> Option { let node = self.nodes.get(&id).cloned()?; let reader = node.read().unwrap(); let output = reader.node.eval(Box::new(input)); - dyn_any::downcast::(output).ok().map(|o| *o) + dyn_any::downcast::(output.await).ok().map(|o| *o) } - pub fn eval_any<'i>(&'i self, id: NodeId, input: Any<'i>) -> Option> { + pub async fn eval_any<'i>(&'i self, id: NodeId, input: Any<'i>) -> Option> { let node = self.nodes.get(&id)?; // TODO: Comments by @TrueDoctor before this was merged: // TODO: Oof I dislike the evaluation being an unsafe operation but I guess its fine because it only is a lifetime extension // TODO: We should ideally let miri run on a test that evaluates the nodegraph multiple times to check if this contains any subtle UB but this looks fine for now - Some(unsafe { (*((&*node.read().unwrap()) as *const NodeContainer)).node.eval(input) }) + Some(unsafe { (*((&*node.read().unwrap()) as *const NodeContainer)).node.eval(input).await }) } pub fn free_node(&mut self, id: NodeId) { self.nodes.remove(&id); } - pub fn push_node(&mut self, id: NodeId, proto_node: ProtoNode, typing_context: &TypingContext) -> Result<(), String> { + pub async fn push_node(&mut self, id: NodeId, proto_node: ProtoNode, typing_context: &TypingContext) -> Result<(), String> { let ProtoNode { construction_args, identifier, @@ -207,7 +207,7 @@ impl BorrowTree { let ids: Vec<_> = ids.iter().map(|(id, _)| *id).collect(); let construction_nodes = self.node_refs(&ids); let constructor = typing_context.constructor(id).ok_or(format!("No constructor found for node {:?}", identifier))?; - let node = constructor(construction_nodes); + let node = constructor(construction_nodes).await; let node = NodeContainer { node, _dependencies: self.node_deps(&ids), @@ -226,12 +226,12 @@ mod test { use super::*; - #[test] - fn push_node() { + #[tokio::test] + async fn push_node() { let mut tree = BorrowTree::default(); let val_1_protonode = ProtoNode::value(ConstructionArgs::Value(TaggedValue::U32(2u32)), vec![]); - tree.push_node(0, val_1_protonode, &TypingContext::default()).unwrap(); + tree.push_node(0, val_1_protonode, &TypingContext::default()).await.unwrap(); let _node = tree.get(0).unwrap(); - assert_eq!(tree.eval(0, ()), Some(2u32)); + assert_eq!(tree.eval(0, ()).await, Some(2u32)); } } diff --git a/node-graph/interpreted-executor/src/lib.rs b/node-graph/interpreted-executor/src/lib.rs index 0a7772810..8a043220e 100644 --- a/node-graph/interpreted-executor/src/lib.rs +++ b/node-graph/interpreted-executor/src/lib.rs @@ -44,8 +44,8 @@ mod tests { assert_eq!(*downcast::(add).unwrap(), 6_u32); }*/ - #[test] - fn execute_add() { + #[tokio::test] + async fn execute_add() { use graph_craft::document::*; use graph_craft::*; @@ -109,15 +109,15 @@ mod tests { let compiler = Compiler {}; let protograph = compiler.compile_single(network, true).expect("Graph should be generated"); - let exec = DynamicExecutor::new(protograph).unwrap_or_else(|e| panic!("Failed to create executor: {}", e)); + let exec = DynamicExecutor::new(protograph).await.unwrap_or_else(|e| panic!("Failed to create executor: {}", e)); - let result = exec.execute(32_u32.into_dyn()).unwrap(); + let result = exec.execute(32_u32.into_dyn()).await.unwrap(); let val = *dyn_any::downcast::(result).unwrap(); assert_eq!(val, 33_u32); } - #[test] - fn double_number() { + #[tokio::test] + async fn double_number() { use graph_craft::document::*; use graph_craft::*; @@ -158,6 +158,6 @@ mod tests { let compiler = Compiler {}; let protograph = compiler.compile_single(network, true).expect("Graph should be generated"); - let _exec = DynamicExecutor::new(protograph).map(|e| panic!("The network should not type check: {:#?}", e)).unwrap_err(); + let _exec = DynamicExecutor::new(protograph).await.map(|e| panic!("The network should not type check: {:#?}", e)).unwrap_err(); } } diff --git a/node-graph/interpreted-executor/src/node_registry.rs b/node-graph/interpreted-executor/src/node_registry.rs index b7f01ddb0..e73ea1b84 100644 --- a/node-graph/interpreted-executor/src/node_registry.rs +++ b/node-graph/interpreted-executor/src/node_registry.rs @@ -1,43 +1,46 @@ use glam::{DAffine2, DVec2}; -use graph_craft::imaginate_input::{ImaginateMaskStartingFill, ImaginateSamplingMethod, ImaginateStatus}; -use graphene_core::ops::{CloneNode, IdNode, TypeNode}; + +use graphene_core::ops::IdNode; use graphene_core::vector::VectorData; use once_cell::sync::Lazy; use std::collections::HashMap; use graphene_core::raster::color::Color; -use graphene_core::raster::*; use graphene_core::structural::Then; -use graphene_core::value::{ClonedNode, CopiedNode, ForgetNode, ValueNode}; +use graphene_core::value::{ClonedNode, CopiedNode, ValueNode}; +use graphene_core::{fn_type, raster::*}; use graphene_core::{Node, NodeIO, NodeIOTypes}; use graphene_std::brush::*; use graphene_std::raster::*; -use graphene_std::any::{ComposeTypeErased, DowncastBothNode, DowncastBothRefNode, DynAnyInRefNode, DynAnyNode, DynAnyRefNode, IntoTypeErasedNode, TypeErasedPinnedRef}; +use graphene_std::any::{ComposeTypeErased, DowncastBothNode, DynAnyInRefNode, DynAnyNode, FutureWrapperNode, IntoTypeErasedNode, TypeErasedPinnedRef}; use graphene_core::{Cow, NodeIdentifier, Type, TypeDescriptor}; -use graph_craft::proto::NodeConstructor; +use graph_craft::proto::{NodeConstructor, TypeErasedPinned}; -use graphene_core::{concrete, fn_type, generic, value_fn}; +use graphene_core::{concrete, generic, value_fn}; use graphene_std::memo::{CacheNode, LetNode}; use graphene_std::raster::BlendImageTupleNode; -use crate::executor::NodeContainer; - use dyn_any::StaticType; use graphene_core::quantization::QuantizationChannels; macro_rules! construct_node { - ($args: ident, $path:ty, [$($type:tt),*]) => {{ + ($args: ident, $path:ty, [$($type:tt),*]) => { async move { let mut args: Vec> = $args.clone(); args.reverse(); - <$path>::new($(graphene_core::value::ClonedNode::new( - graphene_std::any::input_node::<$type>(args.pop() - .expect("Not enough arguments provided to construct node")).eval(())) + let node = <$path>::new($( + { + let node = graphene_std::any::input_node::<$type>(args.pop().expect("Not enough arguments provided to construct node")); + let value = node.eval(()).await; + graphene_core::value::ClonedNode::new(value) + } ),* - ) + ); + node + }} } @@ -47,16 +50,16 @@ macro_rules! register_node { ( NodeIdentifier::new(stringify!($path)), |args| { - let node = construct_node!(args, $path, [$($type),*]); + Box::pin(async move { + let node = construct_node!(args, $path, [$($type),*]).await; + let node = graphene_std::any::FutureWrapperNode::new(node); let any: DynAnyNode<$input, _, _> = graphene_std::any::DynAnyNode::new(graphene_core::value::ValueNode::new(node)); - Box::pin(any) + Box::pin(any) as TypeErasedPinned + }) }, { - let node = IdNode::new().into_type_erased(); - let node = NodeContainer::new(node, vec![]); - let _node = unsafe { node.erase_lifetime().static_ref() }; let node = <$path>::new($( - graphene_std::any::input_node::<$type>(_node) + graphene_std::any::PanicNode::<(), $type>::new() ),*); let params = vec![$(value_fn!($type)),*]; let mut node_io = <$path as NodeIO<'_, $input>>::to_node_io(&node, params); @@ -82,9 +85,12 @@ macro_rules! raster_node { ( NodeIdentifier::new(stringify!($path)), |args| { - let node = construct_node!(args, $path, [$($type),*]); - let any: DynAnyNode = graphene_std::any::DynAnyNode::new(graphene_core::value::ValueNode::new(node)); - Box::pin(any) + Box::pin(async move { + let node = construct_node!(args, $path, [$($type),*]).await; + let node = graphene_std::any::FutureWrapperNode::new(node); + let any: DynAnyNode = graphene_std::any::DynAnyNode::new(graphene_core::value::ValueNode::new(node)); + Box::pin(any) as TypeErasedPinned + }) }, { let params = vec![$(value_fn!($type)),*]; @@ -94,10 +100,13 @@ macro_rules! raster_node { ( NodeIdentifier::new(stringify!($path)), |args| { - let node = construct_node!(args, $path, [$($type),*]); - let map_node = graphene_std::raster::MapImageNode::new(graphene_core::value::ValueNode::new(node)); - let any: DynAnyNode, _, _> = graphene_std::any::DynAnyNode::new(graphene_core::value::ValueNode::new(map_node)); - Box::pin(any) + Box::pin(async move { + let node = construct_node!(args, $path, [$($type),*]).await; + let map_node = graphene_std::raster::MapImageNode::new(graphene_core::value::ValueNode::new(node)); + let map_node = graphene_std::any::FutureWrapperNode::new(map_node); + let any: DynAnyNode, _, _> = graphene_std::any::DynAnyNode::new(graphene_core::value::ValueNode::new(map_node)); + Box::pin(any) as TypeErasedPinned + }) }, { let params = vec![$(value_fn!($type)),*]; @@ -107,10 +116,13 @@ macro_rules! raster_node { ( NodeIdentifier::new(stringify!($path)), |args| { - let node = construct_node!(args, $path, [$($type),*]); - let map_node = graphene_std::raster::MapImageNode::new(graphene_core::value::ValueNode::new(node)); - let any: DynAnyNode, _, _> = graphene_std::any::DynAnyNode::new(graphene_core::value::ValueNode::new(map_node)); - Box::pin(any) + Box::pin(async move { + let node = construct_node!(args, $path, [$($type),*]).await; + let map_node = graphene_std::raster::MapImageNode::new(graphene_core::value::ValueNode::new(node)); + let map_node = graphene_std::any::FutureWrapperNode::new(map_node); + let any: DynAnyNode, _, _> = graphene_std::any::DynAnyNode::new(graphene_core::value::ValueNode::new(map_node)); + Box::pin(any) as TypeErasedPinned + }) }, { let params = vec![$(value_fn!($type)),*]; @@ -129,7 +141,7 @@ fn node_registry() -> HashMap, params: []), vec![( NodeIdentifier::new("graphene_core::ops::IdNode"), - |_| IdNode::new().into_type_erased(), + |_| Box::pin(async move { Box::pin(FutureWrapperNode::new(IdNode::new())) as TypeErasedPinned }), NodeIOTypes::new(generic!(I), generic!(I), vec![]), )], // TODO: create macro to impl for all types @@ -158,37 +170,39 @@ fn node_registry() -> HashMap> = DowncastBothNode::new(args[0]); - let channel_g: DowncastBothNode<(), ImageFrame> = DowncastBothNode::new(args[1]); - let channel_b: DowncastBothNode<(), ImageFrame> = DowncastBothNode::new(args[2]); - let channel_a: DowncastBothNode<(), ImageFrame> = DowncastBothNode::new(args[3]); + let channel_r: ImageFrame = DowncastBothNode::new(args[0]).eval(()).await; + let channel_g: ImageFrame = DowncastBothNode::new(args[1]).eval(()).await; + let channel_b: ImageFrame = DowncastBothNode::new(args[2]).eval(()).await; + let channel_a: ImageFrame = DowncastBothNode::new(args[3]).eval(()).await; - let insert_r = InsertChannelNode::new(channel_r.clone(), CopiedNode::new(RedGreenBlue::Red)); - let insert_g = InsertChannelNode::new(channel_g.clone(), CopiedNode::new(RedGreenBlue::Green)); - let insert_b = InsertChannelNode::new(channel_b.clone(), CopiedNode::new(RedGreenBlue::Blue)); - let complete_node = insert_r.then(insert_g).then(insert_b); - let complete_node = complete_node.then(MaskImageNode::new(channel_a.clone())); + let insert_r = InsertChannelNode::new(ClonedNode::new(channel_r.clone()), CopiedNode::new(RedGreenBlue::Red)); + let insert_g = InsertChannelNode::new(ClonedNode::new(channel_g.clone()), CopiedNode::new(RedGreenBlue::Green)); + let insert_b = InsertChannelNode::new(ClonedNode::new(channel_b.clone()), CopiedNode::new(RedGreenBlue::Blue)); + let complete_node = insert_r.then(insert_g).then(insert_b); + let complete_node = complete_node.then(MaskImageNode::new(ClonedNode::new(channel_a.clone()))); - // TODO: Move to FN Node for better performance - let (mut transform, mut bounds) = (DAffine2::ZERO, glam::UVec2::ZERO); - for imf in [channel_a, channel_r, channel_g, channel_b] { - let image = imf.eval(()); - if image.image.width() > bounds.x { - bounds = glam::UVec2::new(image.image.width(), image.image.height()); - transform = image.transform; + // TODO: Move to FN Node for better performance + let (mut transform, mut bounds) = (DAffine2::ZERO, glam::UVec2::ZERO); + for image in [channel_a, channel_r, channel_g, channel_b] { + if image.image.width() > bounds.x { + bounds = glam::UVec2::new(image.image.width(), image.image.height()); + transform = image.transform; + } } - } - let empty_image = ImageFrame { - image: Image::new(bounds.x, bounds.y, Color::BLACK), - transform, - }; - let final_image = ClonedNode::new(empty_image).then(complete_node); + let empty_image = ImageFrame { + image: Image::new(bounds.x, bounds.y, Color::BLACK), + transform, + }; + let final_image = ClonedNode::new(empty_image).then(complete_node); + let final_image = FutureWrapperNode::new(final_image); - let any: DynAnyNode<(), _, _> = graphene_std::any::DynAnyNode::new(ValueNode::new(final_image)); - Box::pin(any) + let any: DynAnyNode<(), _, _> = graphene_std::any::DynAnyNode::new(ValueNode::new(final_image)); + Box::pin(any) as TypeErasedPinned + }) }, NodeIOTypes::new( concrete!(()), @@ -200,12 +214,26 @@ fn node_registry() -> HashMap, input: ImageFrame, params: []), register_node!(graphene_std::memo::MonitorNode<_>, input: graphene_core::GraphicGroup, params: []), #[cfg(feature = "gpu")] - register_node!(graphene_std::executor::MapGpuSingleImageNode<_>, input: Image, params: [String]), + vec![( + NodeIdentifier::new("graphene_std::executor::MapGpuSingleImageNode<_>"), + |args| { + Box::pin(async move { + let document_node: DowncastBothNode<(), DocumentNode> = DowncastBothNode::new(args[0]); + let document_node = ClonedNode::new(document_node.eval(()).await); + let node = graphene_std::executor::MapGpuNode::new(document_node); + let any: DynAnyNode, _, _> = graphene_std::any::DynAnyNode::new(graphene_core::value::ValueNode::new(node)); + Box::pin(any) as TypeErasedPinned + }) + }, + NodeIOTypes::new(concrete!(ImageFrame), concrete!(ImageFrame), vec![value_fn!(DocumentNode)]), + )], vec![( NodeIdentifier::new("graphene_core::structural::ComposeNode<_, _, _>"), |args| { - let node = ComposeTypeErased::new(args[0], args[1]); - node.into_type_erased() + Box::pin(async move { + let node = ComposeTypeErased::new(args[0], args[1]); + node.into_type_erased() + }) }, NodeIOTypes::new( generic!(T), @@ -213,8 +241,6 @@ fn node_registry() -> HashMap, input: core::slice::Iter>, params: [ImageFrame, &ValueNode, ClonedNode>>>>]), - //register_node!(graphene_std::brush::ReduceNode<_, _>, input: core::slice::Iter>, params: [AxisAlignedBbox, &MergeBoundingBoxNode]), register_node!(graphene_std::brush::IntoIterNode<_>, input: &Vec, params: []), vec![( NodeIdentifier::new("graphene_std::brush::BrushNode"), @@ -223,42 +249,45 @@ fn node_registry() -> HashMap> = DowncastBothNode::new(args[0]); - let bounds: DowncastBothNode<(), ImageFrame> = DowncastBothNode::new(args[1]); - let trace: DowncastBothNode<(), Vec> = DowncastBothNode::new(args[2]); - let diameter: DowncastBothNode<(), f64> = DowncastBothNode::new(args[3]); - let hardness: DowncastBothNode<(), f64> = DowncastBothNode::new(args[4]); - let flow: DowncastBothNode<(), f64> = DowncastBothNode::new(args[5]); - let color: DowncastBothNode<(), Color> = DowncastBothNode::new(args[6]); + Box::pin(async move { + let image: DowncastBothNode<(), ImageFrame> = DowncastBothNode::new(args[0]); + let bounds: DowncastBothNode<(), ImageFrame> = DowncastBothNode::new(args[1]); + let trace: DowncastBothNode<(), Vec> = DowncastBothNode::new(args[2]); + let diameter: DowncastBothNode<(), f64> = DowncastBothNode::new(args[3]); + let hardness: DowncastBothNode<(), f64> = DowncastBothNode::new(args[4]); + let flow: DowncastBothNode<(), f64> = DowncastBothNode::new(args[5]); + let color: DowncastBothNode<(), Color> = DowncastBothNode::new(args[6]); - let stamp = BrushStampGeneratorNode::new(color, CopiedNode::new(hardness.eval(())), CopiedNode::new(flow.eval(()))); - let stamp = stamp.eval(diameter.eval(())); + let stamp = BrushStampGeneratorNode::new(CopiedNode::new(color.eval(()).await), CopiedNode::new(hardness.eval(()).await), CopiedNode::new(flow.eval(()).await)); + let stamp = stamp.eval(diameter.eval(()).await); - let frames = TranslateNode::new(CopiedNode::new(stamp)); - let frames = MapNode::new(ValueNode::new(frames)); - let frames = frames.eval(trace.eval(()).into_iter()).collect::>(); + let frames = TranslateNode::new(CopiedNode::new(stamp)); + let frames = MapNode::new(ValueNode::new(frames)); + let frames = frames.eval(trace.eval(()).await.into_iter()).collect::>(); - let background_bounds = ReduceNode::new(ClonedNode::new(None), ValueNode::new(MergeBoundingBoxNode::new())); - let background_bounds = background_bounds.eval(frames.clone().into_iter()); - let background_bounds = MergeBoundingBoxNode::new().eval((background_bounds, image.eval(()))); - let mut background_bounds = CopiedNode::new(background_bounds.unwrap().to_transform()); + let background_bounds = ReduceNode::new(ClonedNode::new(None), ValueNode::new(MergeBoundingBoxNode::new())); + let background_bounds = background_bounds.eval(frames.clone().into_iter()); + let background_bounds = MergeBoundingBoxNode::new().eval((background_bounds, image.eval(()).await)); + let mut background_bounds = CopiedNode::new(background_bounds.unwrap().to_transform()); - let bounds_transform = bounds.eval(()).transform; - if bounds_transform != DAffine2::ZERO { - background_bounds = CopiedNode::new(bounds_transform); - } + let bounds_transform = bounds.eval(()).await.transform; + if bounds_transform != DAffine2::ZERO { + background_bounds = CopiedNode::new(bounds_transform); + } - let background_image = background_bounds.then(EmptyImageNode::new(CopiedNode::new(Color::TRANSPARENT))); - let blend_node = graphene_core::raster::BlendNode::new(CopiedNode::new(BlendMode::Normal), CopiedNode::new(100.)); + let background_image = background_bounds.then(EmptyImageNode::new(CopiedNode::new(Color::TRANSPARENT))); + let blend_node = graphene_core::raster::BlendNode::new(CopiedNode::new(BlendMode::Normal), CopiedNode::new(100.)); - let background = ExtendImageNode::new(background_image); - let background_image = image.then(background); + let background = ExtendImageNode::new(background_image); + let background_image = image.and_then(background); - let final_image = ReduceNode::new(background_image, ValueNode::new(BlendImageTupleNode::new(ValueNode::new(blend_node)))); - let final_image = ClonedNode::new(frames.into_iter()).then(final_image); + let final_image = ReduceNode::new(ClonedNode::new(background_image.eval(()).await), ValueNode::new(BlendImageTupleNode::new(ValueNode::new(blend_node)))); + let final_image = ClonedNode::new(frames.into_iter()).then(final_image); - let any: DynAnyNode<(), _, _> = graphene_std::any::DynAnyNode::new(ValueNode::new(final_image)); - Box::pin(any) + let final_image = FutureWrapperNode::new(final_image); + let any: DynAnyNode<(), _, _> = graphene_std::any::DynAnyNode::new(ValueNode::new(final_image)); + any.into_type_erased() + }) }, NodeIOTypes::new( concrete!(()), @@ -277,14 +306,17 @@ fn node_registry() -> HashMap"), |args| { - let acc: DowncastBothNode<(), ImageFrame> = DowncastBothNode::new(args[0]); - let image = acc.eval(()); - let blend_node = graphene_core::raster::BlendNode::new(ClonedNode::new(BlendMode::Normal), ClonedNode::new(1.0)); - let _ = &blend_node as &dyn for<'i> Node<'i, (Color, Color), Output = Color>; - let node = ReduceNode::new(ClonedNode::new(image), ValueNode::new(BlendImageTupleNode::new(ValueNode::new(blend_node)))); - //let _ = &node as &dyn for<'i> Node<'i, core::slice::Iter>, Output = ImageFrame>; - let any: DynAnyNode> + Sync + Send>, _, _> = graphene_std::any::DynAnyNode::new(ValueNode::new(node)); - Box::pin(any) + Box::pin(async move { + let acc: DowncastBothNode<(), ImageFrame> = DowncastBothNode::new(args[0]); + let image = acc.eval(()).await; + let blend_node = graphene_core::raster::BlendNode::new(ClonedNode::new(BlendMode::Normal), ClonedNode::new(1.0)); + let _ = &blend_node as &dyn for<'i> Node<'i, (Color, Color), Output = Color>; + let node = ReduceNode::new(ClonedNode::new(image), ValueNode::new(BlendImageTupleNode::new(ValueNode::new(blend_node)))); + //let _ = &node as &dyn for<'i> Node<'i, core::slice::Iter>, Output = ImageFrame>; + let node = FutureWrapperNode::new(node); + let any: DynAnyNode> + Sync + Send>, _, _> = graphene_std::any::DynAnyNode::new(ValueNode::new(node)); + any.into_type_erased() + }) }, NodeIOTypes::new( concrete!(Box> + Sync + Send>), @@ -299,39 +331,25 @@ fn node_registry() -> HashMap, params: [f64, f64, f64, f64, f64]), register_node!(graphene_std::image_segmentation::ImageSegmentationNode<_>, input: ImageFrame, params: [ImageFrame]), register_node!(graphene_core::raster::IndexNode<_>, input: Vec>, params: [u32]), - vec![ - ( - NodeIdentifier::new("graphene_core::raster::BlendNode<_, _, _, _>"), - |args| { + vec![( + NodeIdentifier::new("graphene_core::raster::BlendNode<_, _, _, _>"), + |args| { + Box::pin(async move { let image: DowncastBothNode<(), ImageFrame> = DowncastBothNode::new(args[0]); let blend_mode: DowncastBothNode<(), BlendMode> = DowncastBothNode::new(args[1]); let opacity: DowncastBothNode<(), f64> = DowncastBothNode::new(args[2]); - let blend_node = graphene_core::raster::BlendNode::new(CopiedNode::new(blend_mode.eval(())), CopiedNode::new(opacity.eval(()))); - let node = graphene_std::raster::BlendImageNode::new(image, ValueNode::new(blend_node)); - let _ = &node as &dyn for<'i> Node<'i, ImageFrame, Output = ImageFrame>; + let blend_node = graphene_core::raster::BlendNode::new(CopiedNode::new(blend_mode.eval(()).await), CopiedNode::new(opacity.eval(()).await)); + let node = graphene_std::raster::BlendImageNode::new(image, FutureWrapperNode::new(ValueNode::new(blend_node))); let any: DynAnyNode, _, _> = graphene_std::any::DynAnyNode::new(graphene_core::value::ValueNode::new(node)); - any.into_type_erased() - }, - NodeIOTypes::new( - concrete!(ImageFrame), - concrete!(ImageFrame), - vec![value_fn!(ImageFrame), value_fn!(BlendMode), value_fn!(f64)], - ), + Box::pin(any) as TypeErasedPinned + }) + }, + NodeIOTypes::new( + concrete!(ImageFrame), + concrete!(ImageFrame), + vec![value_fn!(ImageFrame), value_fn!(BlendMode), value_fn!(f64)], ), - ( - NodeIdentifier::new("graphene_core::raster::EraseNode<_, _>"), - |args| { - let image: DowncastBothNode<(), ImageFrame> = DowncastBothNode::new(args[0]); - let opacity: DowncastBothNode<(), f64> = DowncastBothNode::new(args[1]); - let blend_node = graphene_std::brush::EraseNode::new(ClonedNode::new(opacity.eval(()))); - let node = graphene_std::raster::BlendImageNode::new(image, ValueNode::new(blend_node)); - let _ = &node as &dyn for<'i> Node<'i, ImageFrame, Output = ImageFrame>; - let any: DynAnyNode, _, _> = graphene_std::any::DynAnyNode::new(graphene_core::value::ValueNode::new(node)); - any.into_type_erased() - }, - NodeIOTypes::new(concrete!(ImageFrame), concrete!(ImageFrame), vec![value_fn!(ImageFrame), value_fn!(f64)]), - ), - ], + )], raster_node!(graphene_core::raster::GrayscaleNode<_, _, _, _, _, _, _>, params: [Color, f64, f64, f64, f64, f64, f64]), raster_node!(graphene_core::raster::HueSaturationNode<_, _, _>, params: [f64, f64, f64]), raster_node!(graphene_core::raster::InvertRGBNode, params: []), @@ -348,25 +366,29 @@ fn node_registry() -> HashMap"), |args| { - use graphene_core::raster::brightness_contrast::*; + Box::pin(async move { + use graphene_core::raster::brightness_contrast::*; - let brightness: DowncastBothNode<(), f64> = DowncastBothNode::new(args[0]); - let brightness = ClonedNode::new(brightness.eval(()) as f32); - let contrast: DowncastBothNode<(), f64> = DowncastBothNode::new(args[1]); - let contrast = ClonedNode::new(contrast.eval(()) as f32); - let use_legacy: DowncastBothNode<(), bool> = DowncastBothNode::new(args[2]); + let brightness: DowncastBothNode<(), f64> = DowncastBothNode::new(args[0]); + let brightness = ClonedNode::new(brightness.eval(()).await as f32); + let contrast: DowncastBothNode<(), f64> = DowncastBothNode::new(args[1]); + let contrast = ClonedNode::new(contrast.eval(()).await as f32); + let use_legacy: DowncastBothNode<(), bool> = DowncastBothNode::new(args[2]); - if use_legacy.eval(()) { - let generate_brightness_contrast_legacy_mapper_node = GenerateBrightnessContrastLegacyMapperNode::new(brightness, contrast); - let map_image_frame_node = graphene_std::raster::MapImageNode::new(ValueNode::new(generate_brightness_contrast_legacy_mapper_node.eval(()))); - let any: DynAnyNode, _, _> = graphene_std::any::DynAnyNode::new(ValueNode::new(map_image_frame_node)); - Box::pin(any) - } else { - let generate_brightness_contrast_mapper_node = GenerateBrightnessContrastMapperNode::new(brightness, contrast); - let map_image_frame_node = graphene_std::raster::MapImageNode::new(ValueNode::new(generate_brightness_contrast_mapper_node.eval(()))); - let any: DynAnyNode, _, _> = graphene_std::any::DynAnyNode::new(ValueNode::new(map_image_frame_node)); - Box::pin(any) - } + if use_legacy.eval(()).await { + let generate_brightness_contrast_legacy_mapper_node = GenerateBrightnessContrastLegacyMapperNode::new(brightness, contrast); + let map_image_frame_node = graphene_std::raster::MapImageNode::new(ValueNode::new(generate_brightness_contrast_legacy_mapper_node.eval(()))); + let map_image_frame_node = FutureWrapperNode::new(map_image_frame_node); + let any: DynAnyNode, _, _> = graphene_std::any::DynAnyNode::new(ValueNode::new(map_image_frame_node)); + Box::pin(any) as TypeErasedPinned + } else { + let generate_brightness_contrast_mapper_node = GenerateBrightnessContrastMapperNode::new(brightness, contrast); + let map_image_frame_node = graphene_std::raster::MapImageNode::new(ValueNode::new(generate_brightness_contrast_mapper_node.eval(()))); + let map_image_frame_node = FutureWrapperNode::new(map_image_frame_node); + let any: DynAnyNode, _, _> = graphene_std::any::DynAnyNode::new(ValueNode::new(map_image_frame_node)); + Box::pin(any) as TypeErasedPinned + } + }) }, NodeIOTypes::new(concrete!(ImageFrame), concrete!(ImageFrame), vec![value_fn!(f64), value_fn!(f64), value_fn!(bool)]), )], @@ -377,88 +399,102 @@ fn node_registry() -> HashMap"), |_| { - let node: LetNode> = graphene_std::memo::LetNode::new(); - let any = graphene_std::any::DynAnyRefNode::new(node); - any.into_type_erased() + Box::pin(async move { + let node: LetNode> = graphene_std::memo::LetNode::new(); + let any = graphene_std::any::DynAnyRefNode::new(node); + any.into_type_erased() + }) }, NodeIOTypes::new(concrete!(Option>), concrete!(&ImageFrame), vec![]), ), ( NodeIdentifier::new("graphene_std::memo::LetNode<_>"), |_| { - let node: LetNode = graphene_std::memo::LetNode::new(); - let any = graphene_std::any::DynAnyRefNode::new(node); - any.into_type_erased() + Box::pin(async move { + let node: LetNode = graphene_std::memo::LetNode::new(); + let any = graphene_std::any::DynAnyRefNode::new(node); + any.into_type_erased() + }) }, NodeIOTypes::new(concrete!(Option), concrete!(&graphene_core::EditorApi), vec![]), ), ( NodeIdentifier::new("graphene_std::memo::EndLetNode<_>"), |args| { - let input: DowncastBothNode<(), ImageFrame> = DowncastBothNode::new(args[0]); - let node = graphene_std::memo::EndLetNode::new(input); - let any: DynAnyInRefNode = graphene_std::any::DynAnyInRefNode::new(node); - any.into_type_erased() + Box::pin(async move { + let input: DowncastBothNode<(), ImageFrame> = DowncastBothNode::new(args[0]); + let node = graphene_std::memo::EndLetNode::new(input); + let any: DynAnyInRefNode = graphene_std::any::DynAnyInRefNode::new(node); + Box::pin(any) as TypeErasedPinned<'_> + }) }, - NodeIOTypes::new(generic!(T), concrete!(graphene_core::EditorApi), vec![value_fn!(ImageFrame)]), + NodeIOTypes::new(generic!(T), concrete!(ImageFrame), vec![value_fn!(ImageFrame)]), ), ( NodeIdentifier::new("graphene_std::memo::EndLetNode<_>"), |args| { - let input: DowncastBothNode<(), VectorData> = DowncastBothNode::new(args[0]); - let node = graphene_std::memo::EndLetNode::new(input); - let any: DynAnyInRefNode = graphene_std::any::DynAnyInRefNode::new(node); - any.into_type_erased() + Box::pin(async move { + let input: DowncastBothNode<(), VectorData> = DowncastBothNode::new(args[0]); + let node = graphene_std::memo::EndLetNode::new(input); + let any: DynAnyInRefNode = graphene_std::any::DynAnyInRefNode::new(node); + Box::pin(any) as TypeErasedPinned + }) }, - NodeIOTypes::new(generic!(T), concrete!(graphene_core::EditorApi), vec![value_fn!(VectorData)]), + NodeIOTypes::new(generic!(T), concrete!(VectorData), vec![value_fn!(VectorData)]), ), ( NodeIdentifier::new("graphene_std::memo::EndLetNode<_>"), |args| { - let input: DowncastBothNode<(), graphene_core::GraphicGroup> = DowncastBothNode::new(args[0]); - let node = graphene_std::memo::EndLetNode::new(input); - let any: DynAnyInRefNode = graphene_std::any::DynAnyInRefNode::new(node); - any.into_type_erased() + Box::pin(async move { + let input: DowncastBothNode<(), graphene_core::GraphicGroup> = DowncastBothNode::new(args[0]); + let node = graphene_std::memo::EndLetNode::new(input); + let any: DynAnyInRefNode = graphene_std::any::DynAnyInRefNode::new(node); + Box::pin(any) as TypeErasedPinned + }) }, - NodeIOTypes::new(generic!(T), concrete!(graphene_core::EditorApi), vec![value_fn!(graphene_core::GraphicGroup)]), + NodeIOTypes::new(generic!(T), concrete!(graphene_core::GraphicGroup), vec![value_fn!(graphene_core::GraphicGroup)]), ), ( NodeIdentifier::new("graphene_std::memo::EndLetNode<_>"), |args| { - let input: DowncastBothNode<(), graphene_core::Artboard> = DowncastBothNode::new(args[0]); - let node = graphene_std::memo::EndLetNode::new(input); - let any: DynAnyInRefNode = graphene_std::any::DynAnyInRefNode::new(node); - any.into_type_erased() + Box::pin(async move { + let input: DowncastBothNode<(), graphene_core::Artboard> = DowncastBothNode::new(args[0]); + let node = graphene_std::memo::EndLetNode::new(input); + let any: DynAnyInRefNode = graphene_std::any::DynAnyInRefNode::new(node); + Box::pin(any) as TypeErasedPinned + }) }, - NodeIOTypes::new(generic!(T), concrete!(graphene_core::EditorApi), vec![value_fn!(graphene_core::Artboard)]), + NodeIOTypes::new(generic!(T), concrete!(graphene_core::Artboard), vec![value_fn!(graphene_core::Artboard)]), ), ( NodeIdentifier::new("graphene_std::memo::RefNode<_, _>"), |args| { - let map_fn: DowncastBothRefNode, graphene_core::EditorApi> = DowncastBothRefNode::new(args[0]); - let node = graphene_std::memo::RefNode::new(map_fn); - let any = graphene_std::any::DynAnyRefNode::new(node); - any.into_type_erased() + Box::pin(async move { + let map_fn: DowncastBothNode, &graphene_core::EditorApi> = DowncastBothNode::new(args[0]); + //let map_fn = map_fn.then(EvalSyncNode::new()); + let node = graphene_std::memo::RefNode::new(map_fn); + let any = graphene_std::any::DynAnyNode::new(ValueNode::new(node)); + Box::pin(any) as TypeErasedPinned + }) }, - NodeIOTypes::new(concrete!(()), concrete!(&graphene_core::EditorApi), vec![]), - ), - ( - NodeIdentifier::new("graphene_core::structural::MapImageNode"), - |args| { - let map_fn: DowncastBothNode = DowncastBothNode::new(args[0]); - let node = graphene_std::raster::MapImageNode::new(ValueNode::new(map_fn)); - let any: DynAnyNode, _, _> = graphene_std::any::DynAnyNode::new(graphene_core::value::ValueNode::new(node)); - any.into_type_erased() - }, - NodeIOTypes::new(concrete!(Image), concrete!(Image), vec![]), + NodeIOTypes::new( + concrete!(()), + concrete!(&graphene_core::EditorApi), + vec![fn_type!(Option, &graphene_core::EditorApi)], + ), ), + /* ( NodeIdentifier::new("graphene_std::raster::ImaginateNode<_>"), |args| { - let cached = graphene_std::any::input_node::>>>(args[15]); - let node = graphene_std::raster::ImaginateNode::new(cached); - let any = DynAnyNode::new(ValueNode::new(node)); - any.into_type_erased() + Box::pin(async move { + let cached = graphene_std::any::input_node::>>>(args[15]); + let cached = cached.then(EvalSyncNode::new()); + let node = graphene_std::raster::ImaginateNode::new(cached); + let node = FutureWrapperNode::new(node); + let any = DynAnyNode::new(ValueNode::new(node)); + Box::pin(any) as TypeErasedPinned + }) }, NodeIOTypes::new( concrete!(ImageFrame), @@ -485,12 +521,15 @@ fn node_registry() -> HashMap::new(args[0]); let sigma = DowncastBothNode::<(), f64>::new(args[1]); let image = DowncastBothRefNode::, Image>::new(args[2]); + let image = image.then(EvalSyncNode::new()); let empty_image: ValueNode> = ValueNode::new(Image::empty()); let empty: TypeNode<_, (), Image> = TypeNode::new(empty_image.then(CloneNode::new())); use graphene_core::Node; @@ -517,61 +556,73 @@ fn node_registry() -> HashMap = TypeNode::new(dimensions); let new_image = dimensions.then(new_image); let new_image = ForgetNode::new().then(new_image); + let new_image = FutureWrapperNode::new(new_image); let node: DynAnyNode<&Image, _, _> = DynAnyNode::new(ValueNode::new(new_image)); - node.into_type_erased() + Box::pin(node) }, NodeIOTypes::new(concrete!(Image), concrete!(Image), vec![value_fn!(u32), value_fn!(f64)]), ), //register_node!(graphene_std::memo::CacheNode<_>, input: Image, params: []), + */ ( NodeIdentifier::new("graphene_std::memo::CacheNode"), |args| { - let input: DowncastBothNode<(), Image> = DowncastBothNode::new(args[0]); - let node: CacheNode, _> = graphene_std::memo::CacheNode::new(input); - let any = DynAnyRefNode::new(node); - any.into_type_erased() + Box::pin(async move { + let input: DowncastBothNode<(), Image> = DowncastBothNode::new(args[0]); + let node: CacheNode, _> = graphene_std::memo::CacheNode::new(input); + let any = DynAnyNode::new(ValueNode::new(node)); + Box::pin(any) as TypeErasedPinned + }) }, - NodeIOTypes::new(concrete!(()), concrete!(&Image), vec![value_fn!(Image)]), + NodeIOTypes::new(concrete!(()), concrete!(Image), vec![value_fn!(Image)]), ), ( NodeIdentifier::new("graphene_std::memo::CacheNode"), |args| { - let input: DowncastBothNode<(), ImageFrame> = DowncastBothNode::new(args[0]); - let node: CacheNode, _> = graphene_std::memo::CacheNode::new(input); - let any = DynAnyRefNode::new(node); - any.into_type_erased() + Box::pin(async move { + let input: DowncastBothNode<(), ImageFrame> = DowncastBothNode::new(args[0]); + let node: CacheNode, _> = graphene_std::memo::CacheNode::new(input); + let any = DynAnyNode::new(ValueNode::new(node)); + Box::pin(any) as TypeErasedPinned + }) }, - NodeIOTypes::new(concrete!(()), concrete!(&ImageFrame), vec![value_fn!(ImageFrame)]), + NodeIOTypes::new(concrete!(()), concrete!(ImageFrame), vec![value_fn!(ImageFrame)]), ), ( NodeIdentifier::new("graphene_std::memo::CacheNode"), |args| { - let input: DowncastBothNode, ImageFrame> = DowncastBothNode::new(args[0]); - let node: CacheNode, _> = graphene_std::memo::CacheNode::new(input); - let any = DynAnyRefNode::new(node); - any.into_type_erased() + Box::pin(async move { + let input: DowncastBothNode, ImageFrame> = DowncastBothNode::new(args[0]); + let node: CacheNode, _> = graphene_std::memo::CacheNode::new(input); + let any = DynAnyNode::new(ValueNode::new(node)); + Box::pin(any) as TypeErasedPinned + }) }, - NodeIOTypes::new(concrete!(ImageFrame), concrete!(&ImageFrame), vec![fn_type!(ImageFrame, ImageFrame)]), + NodeIOTypes::new(concrete!(ImageFrame), concrete!(ImageFrame), vec![fn_type!(ImageFrame, ImageFrame)]), ), ( NodeIdentifier::new("graphene_std::memo::CacheNode"), |args| { - let input: DowncastBothNode<(), QuantizationChannels> = DowncastBothNode::new(args[0]); - let node: CacheNode = graphene_std::memo::CacheNode::new(input); - let any = DynAnyRefNode::new(node); - any.into_type_erased() + Box::pin(async move { + let input: DowncastBothNode<(), QuantizationChannels> = DowncastBothNode::new(args[0]); + let node: CacheNode = graphene_std::memo::CacheNode::new(input); + let any = DynAnyNode::new(ValueNode::new(node)); + Box::pin(any) as TypeErasedPinned + }) }, - NodeIOTypes::new(concrete!(()), concrete!(&QuantizationChannels), vec![value_fn!(QuantizationChannels)]), + NodeIOTypes::new(concrete!(()), concrete!(QuantizationChannels), vec![value_fn!(QuantizationChannels)]), ), ( NodeIdentifier::new("graphene_std::memo::CacheNode"), |args| { - let input: DowncastBothNode<(), Vec> = DowncastBothNode::new(args[0]); - let node: CacheNode, _> = graphene_std::memo::CacheNode::new(input); - let any = DynAnyRefNode::new(node); - any.into_type_erased() + Box::pin(async move { + let input: DowncastBothNode<(), Vec> = DowncastBothNode::new(args[0]); + let node: CacheNode, _> = graphene_std::memo::CacheNode::new(input); + let any = DynAnyNode::new(ValueNode::new(node)); + Box::pin(any) as TypeErasedPinned + }) }, - NodeIOTypes::new(concrete!(()), concrete!(&Vec), vec![value_fn!(Vec)]), + NodeIOTypes::new(concrete!(()), concrete!(Vec), vec![value_fn!(Vec)]), ), ], register_node!(graphene_core::structural::ConsNode<_, _>, input: Image, params: [&str]), @@ -595,9 +646,9 @@ fn node_registry() -> HashMap>, params: [Vec] ), - register_node!(graphene_core::text::TextGenerator<_, _, _>, input: graphene_core::EditorApi, params: [String, graphene_core::text::Font, f64]), + register_node!(graphene_core::text::TextGenerator<_, _, _>, input: &graphene_core::EditorApi, params: [String, graphene_core::text::Font, f64]), register_node!(graphene_std::brush::VectorPointsNode, input: VectorData, params: []), - register_node!(graphene_core::ExtractImageFrame, input: graphene_core::EditorApi, params: []), + register_node!(graphene_core::ExtractImageFrame, input: &graphene_core::EditorApi, params: []), register_node!(graphene_core::ConstructLayerNode<_, _, _, _, _, _, _>, input: graphene_core::vector::VectorData, params: [String, BlendMode, f32, bool, bool, bool, graphene_core::GraphicGroup]), register_node!(graphene_core::ConstructLayerNode<_, _, _, _, _, _, _>, input: ImageFrame, params: [String, BlendMode, f32, bool, bool, bool, graphene_core::GraphicGroup]), register_node!(graphene_core::ConstructLayerNode<_, _, _, _, _, _, _>, input: graphene_core::GraphicGroup, params: [String, BlendMode, f32, bool, bool, bool, graphene_core::GraphicGroup]), diff --git a/node-graph/node-macro/src/lib.rs b/node-graph/node-macro/src/lib.rs index 67d594bee..0ba212e5f 100644 --- a/node-graph/node-macro/src/lib.rs +++ b/node-graph/node-macro/src/lib.rs @@ -1,6 +1,6 @@ use proc_macro::TokenStream; use proc_macro2::Span; -use quote::{format_ident, ToTokens}; +use quote::{format_ident, quote, ToTokens}; use syn::{ parse_macro_input, punctuated::Punctuated, token::Comma, FnArg, GenericParam, Ident, ItemFn, Lifetime, Pat, PatIdent, PathArguments, PredicateType, ReturnType, Token, TraitBound, Type, TypeParam, TypeParamBound, WhereClause, WherePredicate, @@ -8,14 +8,69 @@ use syn::{ #[proc_macro_attribute] pub fn node_fn(attr: TokenStream, item: TokenStream) -> TokenStream { - //let node_name = parse_macro_input!(attr as Ident); + let mut imp = node_impl_proxy(attr.clone(), item.clone()); + let new = node_new_impl(attr, item); + imp.extend(new); + imp +} +#[proc_macro_attribute] +pub fn node_new(attr: TokenStream, item: TokenStream) -> TokenStream { + node_new_impl(attr, item) +} + +#[proc_macro_attribute] +pub fn node_impl(attr: TokenStream, item: TokenStream) -> TokenStream { + node_impl_proxy(attr, item) +} + +fn node_new_impl(attr: TokenStream, item: TokenStream) -> TokenStream { let node = parse_macro_input!(attr as syn::PathSegment); let function = parse_macro_input!(item as ItemFn); let node = &node; let node_name = &node.ident; - let mut args = match node.arguments.clone() { + let mut args = args(node); + + let arg_idents = args + .iter() + .filter(|x| x.to_token_stream().to_string().starts_with('_')) + .map(|arg| Ident::new(arg.to_token_stream().to_string().to_lowercase().as_str(), Span::call_site())) + .collect::>(); + + let (_, _, parameter_pat_ident_patterns) = parse_inputs(&function); + let parameter_idents = parameter_pat_ident_patterns.iter().map(|pat_ident| &pat_ident.ident).collect::>(); + + // Extract the output type of the entire node - `()` by default + let struct_generics = (0..parameter_pat_ident_patterns.len()) + .map(|x| { + let ident = format_ident!("S{x}"); + ident + }) + .collect::>(); + + for ident in struct_generics.iter() { + args.push(Type::Verbatim(quote::quote!(#ident))); + } + + let struct_generics_iter = struct_generics.iter(); + quote::quote! { + #[automatically_derived] + impl <#(#args),*> #node_name<#(#args),*> + { + pub const fn new(#(#parameter_idents: #struct_generics_iter),*) -> Self{ + Self{ + #(#parameter_idents,)* + #(#arg_idents: core::marker::PhantomData,)* + } + } + } + } + .into() +} + +fn args(node: &syn::PathSegment) -> Vec { + match node.arguments.clone() { PathArguments::AngleBracketed(args) => args .args .into_iter() @@ -25,53 +80,59 @@ pub fn node_fn(attr: TokenStream, item: TokenStream) -> TokenStream { }) .collect::>(), _ => Default::default(), + } +} + +fn node_impl_proxy(attr: TokenStream, item: TokenStream) -> TokenStream { + let fn_item = item.clone(); + let function = parse_macro_input!(fn_item as ItemFn); + let sync_input = if function.sig.asyncness.is_some() { + node_impl_impl(attr, item, Asyncness::AllAsync) + } else { + node_impl_impl(attr, item, Asyncness::Sync) }; - let arg_idents = args - .iter() - .filter(|x| x.to_token_stream().to_string().starts_with('_')) - .map(|arg| Ident::new(arg.to_token_stream().to_string().to_lowercase().as_str(), Span::call_site())) - .collect::>(); + sync_input +} +enum Asyncness { + Sync, + AsyncOut, + AllAsync, +} - let mut function_inputs = function.sig.inputs.iter().filter_map(|arg| if let FnArg::Typed(typed_arg) = arg { Some(typed_arg) } else { None }); +fn node_impl_impl(attr: TokenStream, item: TokenStream, asyncness: Asyncness) -> TokenStream { + //let node_name = parse_macro_input!(attr as Ident); + let node = parse_macro_input!(attr as syn::PathSegment); + let function = parse_macro_input!(item as ItemFn); + + let node = &node; + let node_name = &node.ident; + let mut args = args(node); + + let async_out = match asyncness { + Asyncness::Sync => false, + Asyncness::AsyncOut | Asyncness::AllAsync => true, + }; + let async_in = matches!(asyncness, Asyncness::AllAsync); + + let body = &function.block; let mut type_generics = function.sig.generics.params.clone(); let mut where_clause = function.sig.generics.where_clause.clone().unwrap_or(WhereClause { where_token: Token![where](Span::call_site()), predicates: Default::default(), }); - // Extract primary input as first argument - let primary_input = function_inputs.next().expect("Primary input required - set to `()` if not needed."); + type_generics.iter_mut().for_each(|x| { + if let GenericParam::Type(t) = x { + t.bounds.insert(0, TypeParamBound::Lifetime(Lifetime::new("'input", Span::call_site()))); + } + }); + + let (primary_input, parameter_inputs, parameter_pat_ident_patterns) = parse_inputs(&function); + let primary_input_ty = &primary_input.ty; let Pat::Ident(PatIdent{ident: primary_input_ident, mutability: primary_input_mutability,..} ) =&*primary_input.pat else { panic!("Expected ident as primary input."); }; - let primary_input_ty = &primary_input.ty; - let aux_type_generics = type_generics - .iter() - .filter(|gen| { - if let GenericParam::Type(ty) = gen { - !function.sig.inputs.iter().take(1).any(|param_ty| match param_ty { - FnArg::Typed(pat_ty) => ty.ident == pat_ty.ty.to_token_stream().to_string(), - _ => false, - }) - } else { - false - } - }) - .cloned() - .collect::>(); - - let body = function.block; - - // Extract secondary inputs as all other arguments - let parameter_inputs = function_inputs.collect::>(); - let parameter_pat_ident_patterns = parameter_inputs - .iter() - .map(|input| { - let Pat::Ident(pat_ident) = &*input.pat else { panic!("Expected ident for secondary input."); }; - pat_ident - }) - .collect::>(); let parameter_idents = parameter_pat_ident_patterns.iter().map(|pat_ident| &pat_ident.ident).collect::>(); let parameter_mutability = parameter_pat_ident_patterns.iter().map(|pat_ident| &pat_ident.mutability); @@ -82,20 +143,94 @@ pub fn node_fn(attr: TokenStream, item: TokenStream) -> TokenStream { quote::quote!(()) }; - let struct_generics = (0..parameter_inputs.len()) - .map(|x| { - let ident = format_ident!("S{x}"); - ident - }) - .collect::>(); - let struct_generics_iter = struct_generics.iter(); + let struct_generics = (0..parameter_pat_ident_patterns.len()).map(|x| format_ident!("S{x}")).collect::>(); + let future_generics = (0..parameter_pat_ident_patterns.len()).map(|x| format_ident!("F{x}")).collect::>(); + let future_types = future_generics.iter().map(|x| Type::Verbatim(x.to_token_stream())).collect::>(); + let parameter_types = parameter_inputs.iter().map(|x| *x.ty.clone()).collect::>(); for ident in struct_generics.iter() { args.push(Type::Verbatim(quote::quote!(#ident))); } // Generics are simply `S0` through to `Sn-1` where n is the number of secondary inputs - let node_generics = struct_generics + let node_generics = construct_node_generics(&struct_generics); + let future_generic_params = construct_node_generics(&future_generics); + + let generics = if async_in { + type_generics + .into_iter() + .chain(node_generics.iter().cloned()) + .chain(future_generic_params.iter().cloned()) + .collect::>() + } else { + type_generics.into_iter().chain(node_generics.iter().cloned()).collect::>() + }; + + // Bindings for all of the above generics to a node with an input of `()` and an output of the type in the function + let node_bounds = if async_in { + let mut node_bounds = input_node_bounds(future_types, node_generics, |ty| quote! {Node<'input, (), Output = #ty>}); + let future_bounds = input_node_bounds(parameter_types, future_generic_params, |ty| quote! { core::future::Future}); + node_bounds.extend(future_bounds); + node_bounds + } else { + input_node_bounds(parameter_types, node_generics, |ty| quote! {Node<'input, (), Output = #ty>}) + }; + where_clause.predicates.extend(node_bounds); + + let output = if async_out { + quote::quote!(core::pin::Pin + 'input>>) + } else { + quote::quote!(#output) + }; + + let parameters = if matches!(asyncness, Asyncness::AllAsync) { + quote::quote!(#(let #parameter_mutability #parameter_idents = self.#parameter_idents.eval(()).await;)*) + } else { + quote::quote!(#(let #parameter_mutability #parameter_idents = self.#parameter_idents.eval(());)*) + }; + let mut body_with_inputs = quote::quote!( + #parameters + {#body} + ); + if async_out { + body_with_inputs = quote::quote!(Box::pin(async move { #body_with_inputs })); + } + + quote::quote! { + #[automatically_derived] + impl <'input, #generics> Node<'input, #primary_input_ty> for #node_name<#(#args),*> + #where_clause + { + type Output = #output; + #[inline] + fn eval(&'input self, #primary_input_mutability #primary_input_ident: #primary_input_ty) -> Self::Output { + #body_with_inputs + } + } + } + .into() +} + +fn parse_inputs(function: &ItemFn) -> (&syn::PatType, Vec<&syn::PatType>, Vec<&PatIdent>) { + let mut function_inputs = function.sig.inputs.iter().filter_map(|arg| if let FnArg::Typed(typed_arg) = arg { Some(typed_arg) } else { None }); + + // Extract primary input as first argument + let primary_input = function_inputs.next().expect("Primary input required - set to `()` if not needed."); + + // Extract secondary inputs as all other arguments + let parameter_inputs = function_inputs.collect::>(); + let parameter_pat_ident_patterns = parameter_inputs + .iter() + .map(|input| { + let Pat::Ident(pat_ident) = &*input.pat else { panic!("Expected ident for secondary input."); }; + pat_ident + }) + .collect::>(); + (primary_input, parameter_inputs, parameter_pat_ident_patterns) +} + +fn construct_node_generics(struct_generics: &[Ident]) -> Vec { + struct_generics .iter() .cloned() .map(|ident| { @@ -108,22 +243,17 @@ pub fn node_fn(attr: TokenStream, item: TokenStream) -> TokenStream { default: None, }) }) - .collect::>(); - type_generics.iter_mut().for_each(|x| { - if let GenericParam::Type(t) = x { - t.bounds.insert(0, TypeParamBound::Lifetime(Lifetime::new("'input", Span::call_site()))); - } - }); - let generics = type_generics.into_iter().chain(node_generics.iter().cloned()).collect::>(); - let new_fn_generics = aux_type_generics.into_iter().chain(node_generics.iter().cloned()).collect::>(); - // Bindings for all of the above generics to a node with an input of `()` and an output of the type in the function - let extra_where_clause = parameter_inputs + .collect() +} + +fn input_node_bounds(parameter_inputs: Vec, node_generics: Vec, trait_bound: impl Fn(Type) -> proc_macro2::TokenStream) -> Vec { + parameter_inputs .iter() .zip(&node_generics) .map(|(ty, name)| { - let ty = &ty.ty; let GenericParam::Type(generic_ty) = name else { panic!("Expected type generic."); }; let ident = &generic_ty.ident; + let bound = trait_bound(ty.clone()); WherePredicate::Type(PredicateType { lifetimes: None, bounded_ty: Type::Verbatim(ident.to_token_stream()), @@ -131,43 +261,10 @@ pub fn node_fn(attr: TokenStream, item: TokenStream) -> TokenStream { bounds: Punctuated::from_iter([TypeParamBound::Trait(TraitBound { paren_token: None, modifier: syn::TraitBoundModifier::None, - lifetimes: syn::parse_quote!(for<'any_input>), - path: syn::parse_quote!(Node<'any_input, (), Output = #ty>), + lifetimes: None, //syn::parse_quote!(for<'any_input>), + path: syn::parse_quote!(#bound), })]), }) }) - .collect::>(); - where_clause.predicates.extend(extra_where_clause.clone()); - - let input_lifetime = if generics.is_empty() { quote::quote!() } else { quote::quote!('input,) }; - - quote::quote! { - - impl <'input, #generics> Node<'input, #primary_input_ty> for #node_name<#(#args),*> - #where_clause - { - type Output = #output; - #[inline] - fn eval(&'input self, #primary_input_mutability #primary_input_ident: #primary_input_ty) -> Self::Output { - #( - let #parameter_mutability #parameter_idents = self.#parameter_idents.eval(()); - )* - - #body - } - } - - impl <#input_lifetime #new_fn_generics> #node_name<#(#args),*> - where #(#extra_where_clause),* - { - pub const fn new(#(#parameter_idents: #struct_generics_iter),*) -> Self{ - Self{ - #(#parameter_idents,)* - #(#arg_idents: core::marker::PhantomData,)* - } - } - } - - } - .into() + .collect() } diff --git a/node-graph/vulkan-executor/src/executor.rs b/node-graph/vulkan-executor/src/executor.rs index 2071a0ab3..473d46144 100644 --- a/node-graph/vulkan-executor/src/executor.rs +++ b/node-graph/vulkan-executor/src/executor.rs @@ -2,6 +2,7 @@ use super::context::Context; use graph_craft::executor::{Any, Executor}; +use graph_craft::proto::LocalFuture; use graphene_core::gpu::PushConstants; use bytemuck::Pod; @@ -38,7 +39,7 @@ impl GpuExecutor { } impl Executor for GpuExecutor { - fn execute<'i, 's: 'i>(&'s self, input: Any<'i>) -> Result, Box> { + fn execute<'i>(&'i self, input: Any<'i>) -> LocalFuture, Box>> { let input = dyn_any::downcast::>(input).expect("Wrong input type"); let context = &self.context; let result: Vec = execute_shader( @@ -49,10 +50,11 @@ impl( device: std::sync::Arc, queue: std::sync::Arc, diff --git a/node-graph/wgpu-executor/src/executor.rs b/node-graph/wgpu-executor/src/executor.rs index 1294ca0e4..7b7d4efbe 100644 --- a/node-graph/wgpu-executor/src/executor.rs +++ b/node-graph/wgpu-executor/src/executor.rs @@ -5,7 +5,10 @@ use wgpu::util::DeviceExt; use super::context::Context; use bytemuck::Pod; use dyn_any::StaticTypeSized; -use graph_craft::executor::{Any, Executor}; +use graph_craft::{ + executor::{Any, Executor}, + proto::LocalFuture, +}; #[derive(Debug)] pub struct GpuExecutor<'a, I: StaticTypeSized, O> { @@ -27,14 +30,16 @@ impl<'a, I: StaticTypeSized, O> GpuExecutor<'a, I, O> { } impl<'a, I: StaticTypeSized + Sync + Pod + Send, O: StaticTypeSized + Send + Sync + Pod> Executor for GpuExecutor<'a, I, O> { - fn execute<'i, 's: 'i>(&'s self, input: Any<'i>) -> Result, Box> { + fn execute<'i>(&'i self, input: Any<'i>) -> LocalFuture, Box>> { let input = dyn_any::downcast::>(input).expect("Wrong input type"); let context = &self.context; let future = execute_shader(context.device.clone(), context.queue.clone(), self.shader.to_vec(), *input, self.entry_point.clone()); - let result = future_executor::block_on(future); + Box::pin(async move { + let result = future.await; - let result: Vec = result.ok_or_else(|| String::from("Failed to execute shader"))?; - Ok(Box::new(result)) + let result: Vec = result.ok_or_else(|| String::from("Failed to execute shader"))?; + Ok(Box::new(result) as Any) + }) } }