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 8292d238..9bc972b8 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 @@ -125,6 +125,17 @@ impl Default for DocumentNodeBlueprint { // TODO: make document nodes not require a `'static` lifetime to avoid having to split the construction into const and non-const parts. static DOCUMENT_NODE_TYPES: once_cell::sync::Lazy> = once_cell::sync::Lazy::new(static_nodes); +fn monitor_node() -> DocumentNode { + DocumentNode { + name: "Monitor".to_string(), + inputs: Vec::new(), + implementation: DocumentNodeImplementation::proto("graphene_core::memo::MonitorNode<_, _, _>"), + manual_composition: Some(concrete!(Footprint)), + skip_deduplication: true, + ..Default::default() + } +} + // TODO: Dynamic node library /// Defines the "signature" or "header file"-like metadata for the document nodes, but not the implementation (which is defined in the node registry). /// The document node is the instance while these are the "class" (or "blueprint"). @@ -203,11 +214,8 @@ fn static_nodes() -> Vec { ( 1, DocumentNode { - name: "Monitor".to_string(), inputs: vec![NodeInput::node(0, 0)], - implementation: DocumentNodeImplementation::proto("graphene_core::memo::MonitorNode<_>"), - skip_deduplication: true, - ..Default::default() + ..monitor_node() }, ), ( @@ -2122,11 +2130,8 @@ fn static_nodes() -> Vec { outputs: vec![NodeOutput::new(1, 0)], nodes: [ DocumentNode { - name: "Monitor".to_string(), inputs: vec![NodeInput::Network(concrete!(VectorData))], - implementation: DocumentNodeImplementation::proto("graphene_core::memo::MonitorNode<_>"), - skip_deduplication: true, - ..Default::default() + ..monitor_node() }, DocumentNode { name: "Transform".to_string(), @@ -2329,11 +2334,8 @@ pub static IMAGINATE_NODE: Lazy = Lazy::new(|| DocumentNo ( 0, DocumentNode { - name: "Frame Monitor".into(), inputs: vec![NodeInput::Network(concrete!(ImageFrame))], - implementation: DocumentNodeImplementation::proto("graphene_core::memo::MonitorNode<_>"), - skip_deduplication: true, - ..Default::default() + ..monitor_node() }, ), ( diff --git a/editor/src/node_graph_executor.rs b/editor/src/node_graph_executor.rs index 34eac327..fbd66833 100644 --- a/editor/src/node_graph_executor.rs +++ b/editor/src/node_graph_executor.rs @@ -203,7 +203,7 @@ impl NodeRuntime { let monitor_nodes = scoped_network .recursive_nodes() - .filter(|(_, node)| node.implementation == DocumentNodeImplementation::proto("graphene_core::memo::MonitorNode<_>")) + .filter(|(_, node)| node.implementation == DocumentNodeImplementation::proto("graphene_core::memo::MonitorNode<_, _, _>")) .map(|(_, node)| node.path.clone().unwrap_or_default()) .collect::>(); @@ -259,10 +259,12 @@ impl NodeRuntime { warn!("Failed to introspect monitor node for thumbnail"); continue; }; - let Some(graphic_element_data) = value.downcast_ref::() else { + + let Some(io_data) = value.downcast_ref::>() else { warn!("Failed to downcast thumbnail to graphic element data"); continue; }; + let graphic_element_data = &io_data.output; use graphene_core::renderer::*; let bounds = graphic_element_data.bounding_box(DAffine2::IDENTITY); let render_params = RenderParams::new(ViewMode::Normal, ImageRenderMode::BlobUrl, bounds, true); diff --git a/node-graph/gcore/src/memo.rs b/node-graph/gcore/src/memo.rs index 94abedc8..747b81e1 100644 --- a/node-graph/gcore/src/memo.rs +++ b/node-graph/gcore/src/memo.rs @@ -45,32 +45,48 @@ impl MemoNode { } } +#[derive(Clone)] +pub struct IORecord { + pub input: I, + pub output: O, +} + #[cfg(feature = "alloc")] /// Caches the output of the last graph evaluation for introspection #[derive(Default)] -pub struct MonitorNode { - output: Cell>>, +pub struct MonitorNode { + io: Cell>>>, + node: N, } #[cfg(feature = "alloc")] -impl<'i, T: 'static + Clone> Node<'i, T> for MonitorNode { - type Output = T; - fn eval(&'i self, input: T) -> Self::Output { - self.output.set(Some(Arc::new(input.clone()))); - input +impl<'i, 'a: 'i, T, I, N> Node<'i, I> for MonitorNode +where + I: Clone + 'static, + >::Output: Future, + T: Clone + 'static, + N: Node<'i, I>, +{ + type Output = Pin + 'i>>; + fn eval(&'i self, input: I) -> Self::Output { + Box::pin(async move { + let output = self.node.eval(input.clone()).await; + self.io.set(Some(Arc::new(IORecord { input, output: output.clone() }))); + output + }) } fn serialize(&self) -> Option> { - let out = self.output.take(); - self.output.set(out.clone()); - (out).as_ref().map(|output| output.clone() as Arc) + let io = self.io.take(); + self.io.set(io.clone()); + (io).as_ref().map(|output| output.clone() as Arc) } } #[cfg(feature = "alloc")] -impl MonitorNode { - pub const fn new() -> MonitorNode { - MonitorNode { output: Cell::new(None) } +impl MonitorNode { + pub const fn new(node: N) -> MonitorNode { + MonitorNode { io: Cell::new(None), node } } } diff --git a/node-graph/interpreted-executor/src/lib.rs b/node-graph/interpreted-executor/src/lib.rs index ccc9bee3..7c0bca15 100644 --- a/node-graph/interpreted-executor/src/lib.rs +++ b/node-graph/interpreted-executor/src/lib.rs @@ -5,7 +5,6 @@ pub mod node_registry; mod tests { use graph_craft::document::value::TaggedValue; use graphene_core::*; - use std::borrow::Cow; use futures::executor::block_on; @@ -95,7 +94,7 @@ mod tests { 0, DocumentNode { name: "id".into(), - inputs: vec![NodeInput::ShortCircut(concrete!(u32))], + inputs: vec![NodeInput::Network(concrete!(u32))], implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::ops::IdNode")), ..Default::default() }, diff --git a/node-graph/interpreted-executor/src/node_registry.rs b/node-graph/interpreted-executor/src/node_registry.rs index d57e5ac0..d9945700 100644 --- a/node-graph/interpreted-executor/src/node_registry.rs +++ b/node-graph/interpreted-executor/src/node_registry.rs @@ -327,9 +327,9 @@ fn node_registry() -> HashMap, input: DAffine2, params: [Color]), - register_node!(graphene_core::memo::MonitorNode<_>, input: ImageFrame, params: []), - register_node!(graphene_core::memo::MonitorNode<_>, input: VectorData, params: []), - register_node!(graphene_core::memo::MonitorNode<_>, input: graphene_core::GraphicElementData, params: []), + async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Footprint, output: ImageFrame, fn_params: [Footprint => ImageFrame]), + async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Footprint, output: VectorData, fn_params: [Footprint => VectorData]), + async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Footprint, output: graphene_core::GraphicElementData, fn_params: [Footprint => graphene_core::GraphicElementData]), async_node!(graphene_std::wasm_application_io::LoadResourceNode<_>, input: WasmEditorApi, output: Arc<[u8]>, params: [String]), register_node!(graphene_std::wasm_application_io::DecodeImageNode, input: Arc<[u8]>, params: []), async_node!(graphene_std::wasm_application_io::CreateSurfaceNode, input: WasmEditorApi, output: Arc::Surface>>, params: []),