Add comments to help explain Graphene concepts

This commit is contained in:
Keavon Chambers 2023-10-24 21:32:42 -07:00
parent ee08938bb0
commit bafde43145
5 changed files with 78 additions and 45 deletions

View File

@ -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<Vec<DocumentNodeType>> = 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<DocumentNodeType> {
vec![
DocumentNodeType {
@ -498,7 +501,7 @@ fn static_nodes() -> Vec<DocumentNodeType> {
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<DocumentNodeType> {
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: <https://files.keavon.com/-/AchingSecondHypsilophodon/capture.png>
DocumentNode {
name: "Identity".to_string(),
inputs: vec![NodeInput::Network(concrete!(ImageFrame<Color>))],

View File

@ -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: <https://files.keavon.com/-/SurprisedGaseousAnhinga/capture.png>
// A concrete example: <https://files.keavon.com/-/ExcitableGoldRay/capture.png>
// And showing the direction of data flow: <https://files.keavon.com/-/SoreShimmeringElephantseal/capture.png>
/// ┌────────────────┐
/// T │ │ U
/// ───────────►│ Compose Node ├───────────►
/// │ │
/// └────┬───────────┤
/// ┌──────────┐ │ │
/// │ │ T -> V │ │
/// │ First ├─────────────►│ │
/// │ │ │ │
/// └──────────┘ │ │
/// ┌──────────┐ │ │
/// │ │ V -> U │ │
/// │ Second ├─────────────►│ │
/// │ │ └───────────┘
/// └──────────┘
#[derive(Clone, Copy)]
pub struct ComposeNode<First, Second, I> {
first: First,

View File

@ -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<Type>, Box<Type>),
/// Not used at the moment.
Future(Box<Type>),
}

View File

@ -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),
}

View File

@ -423,6 +423,8 @@ fn node_registry() -> HashMap<NodeIdentifier, HashMap<NodeIOTypes, NodeConstruct
node.into_type_erased()
})
},
// This is how we can generically define composition of two nodes.
// See further details in the code definition for the `struct ComposeNode<First, Second, I> { ... }` struct.
NodeIOTypes::new(
generic!(T),
generic!(U),