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 1c3d538b..1abf870c 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 @@ -93,6 +93,7 @@ impl NodeImplementation { } } +/// Acts as a description for a [DocumentNode] before it gets instantiated as one. #[derive(Clone)] pub struct DocumentNodeType { pub name: &'static str, @@ -125,6 +126,8 @@ impl Default for DocumentNodeType { static DOCUMENT_NODE_TYPES: once_cell::sync::Lazy> = once_cell::sync::Lazy::new(static_nodes); // 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"). fn static_nodes() -> Vec { vec![ DocumentNodeType { @@ -498,7 +501,7 @@ fn static_nodes() -> Vec { name: "Frame", data_type: FrontendGraphDataType::Raster, }], - properties: |_document_node, _node_id, _context| node_properties::string_properties("The graph's output is drawn in the layer"), + properties: |_document_node, _node_id, _context| node_properties::string_properties("Consumes the scope opened by the Begin Scope node and evaluates the contained node network"), ..Default::default() }, DocumentNodeType { @@ -798,8 +801,11 @@ fn static_nodes() -> Vec { category: "Image Adjustments", identifier: NodeImplementation::DocumentNode(NodeNetwork { inputs: vec![0], - outputs: vec![NodeOutput::new(4, 0), NodeOutput::new(1, 0), NodeOutput::new(2, 0), NodeOutput::new(3, 0), NodeOutput::new(4, 0)], + outputs: vec![NodeOutput::new(1, 0), NodeOutput::new(2, 0), NodeOutput::new(3, 0), NodeOutput::new(4, 0)], nodes: [ + // The input image feeds into the identity, then we take its passed-through value when the other channels are reading from it instead of the original input. + // We do this for technical restrictions imposed by Graphene which doesn't allow an input to feed into multiple interior nodes in the subgraph. + // Diagram: DocumentNode { name: "Identity".to_string(), inputs: vec![NodeInput::Network(concrete!(ImageFrame))], diff --git a/node-graph/gcore/src/structural.rs b/node-graph/gcore/src/structural.rs index ddaea936..a86af76b 100644 --- a/node-graph/gcore/src/structural.rs +++ b/node-graph/gcore/src/structural.rs @@ -2,6 +2,25 @@ use core::marker::PhantomData; use crate::{Node, NodeMut}; +// This is how we can generically define composition of two nodes. +// This is done generically as shown: +// A concrete example: +// And showing the direction of data flow: +/// ┌────────────────┐ +/// T │ │ U +/// ───────────►│ Compose Node ├───────────► +/// │ │ +/// └────┬───────────┤ +/// ┌──────────┐ │ │ +/// │ │ T -> V │ │ +/// │ First ├─────────────►│ │ +/// │ │ │ │ +/// └──────────┘ │ │ +/// ┌──────────┐ │ │ +/// │ │ V -> U │ │ +/// │ Second ├─────────────►│ │ +/// │ │ └───────────┘ +/// └──────────┘ #[derive(Clone, Copy)] pub struct ComposeNode { first: First, diff --git a/node-graph/gcore/src/types.rs b/node-graph/gcore/src/types.rs index 6947a3de..428d1dd0 100644 --- a/node-graph/gcore/src/types.rs +++ b/node-graph/gcore/src/types.rs @@ -102,12 +102,18 @@ impl PartialEq for TypeDescriptor { } } +/// Graph runtime type information used for type inference. #[derive(Clone, PartialEq, Eq, Hash, specta::Type)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub enum Type { + /// A wrapper for some type variable used within the inference system. Resolved at inference time and replaced with a concrete type. Generic(Cow<'static, str>), + /// A wrapper around the Rust type id for any concrete Rust type. Allows us to do equality comparisons, like checking if a String == a String. Concrete(TypeDescriptor), + /// Runtime type information for a function. Given some input, gives some output. + /// See the example and explanation in the `ComposeNode` implementation within the node registry for more info. Fn(Box, Box), + /// Not used at the moment. Future(Box), } diff --git a/node-graph/graph-craft/src/document.rs b/node-graph/graph-craft/src/document.rs index 7832515e..967c3be4 100644 --- a/node-graph/graph-craft/src/document.rs +++ b/node-graph/graph-craft/src/document.rs @@ -141,55 +141,55 @@ impl DocumentNode { } /// Represents the possible inputs to a node. -/// -/// # More about short circuting -/// -/// In Graphite nodes are functions and by default, these are composed into a single function -/// by inserting Compose nodes. -/// -/// ```text -/// ┌─────────────────┐ ┌──────────────────┐ ┌──────────────────┐ -/// │ │◄──────────────┤ │◄───────────────┤ │ -/// │ A │ │ B │ │ C │ -/// │ ├──────────────►│ ├───────────────►│ │ -/// └─────────────────┘ └──────────────────┘ └──────────────────┘ -/// ``` -/// -/// This is equivalent to calling c(b(a(input))) when evaluating c with input ( `c.eval(input)`). -/// But sometimes we might want to have a little more control over the order of execution. -/// This is why we allow nodes to opt out of the input forwarding by consuming the input directly. -/// -/// ```text -/// ┌─────────────────────┐ ┌─────────────┐ -/// │ │◄───────────────┤ │ -/// │ Cache Node │ │ C │ -/// │ ├───────────────►│ │ -/// ┌──────────────────┐ ├─────────────────────┤ └─────────────┘ -/// │ │◄──────────────┤ │ -/// │ A │ │ * Cached Node │ -/// │ ├──────────────►│ │ -/// └──────────────────┘ └─────────────────────┘ -/// ``` -/// -/// In this case the Cache node actually consumes its input and then manually forwards it to its parameter Node. -/// This is necessary because the Cache Node needs to short-circut the actual node evaluation. #[derive(Debug, Clone, PartialEq, Hash, DynAny)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub enum NodeInput { - Node { - node_id: NodeId, - output_index: usize, - lambda: bool, - }, - Value { - tagged_value: TaggedValue, - exposed: bool, - }, + /// A reference to another node in the same network from which this node can receive its input. + Node { node_id: NodeId, output_index: usize, lambda: bool }, + + /// A hardcoded value that can't change after the graph is compiled. Gets converted into a value node during graph compilation. + Value { tagged_value: TaggedValue, exposed: bool }, + + /// Input that is provided by the parent network to this document node, instead of from a hardcoded value or another node within the same network. Network(Type), + /// A short circuting input represents an input that is not resolved through function composition - /// but actually consuming the provided input instead of passing it to its predecessor. - /// See [NodeInput] docs for more explanation. + /// but rather by actually consuming the provided input instead of passing it to its predecessor. + /// + /// In Graphite nodes are functions, and by default these are composed into a single function + /// by automatic insertion of inserting Compose nodes. + /// + /// ```text + /// ┌───────────────┐ ┌───────────────┐ ┌───────────────┐ + /// │ │◄───┤ │◄───┤ │ + /// │ A │ │ B │ │ C │ + /// │ ├───►│ ├───►│ │ + /// └───────────────┘ └───────────────┘ └───────────────┘ + /// ``` + /// + /// This is equivalent to calling c(b(a(input))) when evaluating c with input ( `c.eval(input)`). + /// But sometimes we might want to have a little more control over the order of execution. + /// This is why we allow nodes to opt out of the input forwarding by consuming the input directly. + /// + /// ```text + /// ┌───────────────┐ ┌───────────────┐ + /// │ │◄───┤ │ + /// │ Cache Node │ │ C │ + /// │ ├───►│ │ + /// ┌───────────────┐ ├───────────────┤ └───────────────┘ + /// │ │◄───┤ │ + /// │ A │ │ * Cached Node │ + /// │ ├───►│ │ + /// └───────────────┘ └───────────────┘ + /// ``` + /// + /// In this case the Cache node actually consumes its input and then manually forwards it to its parameter Node. + /// This is necessary because the Cache Node needs to short-circut the actual node evaluation. // TODO: Update + // ShortCircut(Type), + + /// A Rust source code string. Allows us to insert literal Rust code. Only used for GPU compilation. + /// We can use this whenever we spin up Rustc. Sort of like inline assembly, but because our language is Rust, it acts as inline Rust. Inline(InlineRust), } diff --git a/node-graph/interpreted-executor/src/node_registry.rs b/node-graph/interpreted-executor/src/node_registry.rs index 9009c5ea..5476011f 100644 --- a/node-graph/interpreted-executor/src/node_registry.rs +++ b/node-graph/interpreted-executor/src/node_registry.rs @@ -423,6 +423,8 @@ fn node_registry() -> HashMap { ... }` struct. NodeIOTypes::new( generic!(T), generic!(U),