diff --git a/node-graph/interpreted-executor/src/node_registry.rs b/node-graph/interpreted-executor/src/node_registry.rs index d9ee2e6ad2..46d4c3a3ef 100644 --- a/node-graph/interpreted-executor/src/node_registry.rs +++ b/node-graph/interpreted-executor/src/node_registry.rs @@ -1,10 +1,7 @@ -use dyn_any::StaticType; use glam::{DAffine2, DVec2, IVec2}; use graph_craft::application_io::PlatformEditorApi; use graph_craft::document::DocumentNode; use graph_craft::document::value::RenderOutput; -use graph_craft::proto::{NodeConstructor, TypeErasedBox}; -use graphene_std::any::DynAnyNode; use graphene_std::application_io::Texture; use graphene_std::brush::brush_stroke::BrushStroke; use graphene_std::gradient::GradientStops; @@ -20,15 +17,16 @@ use graphene_std::render_node::RenderIntermediate; use graphene_std::transform::Footprint; use graphene_std::uuid::NodeId; use graphene_std::vector::Vector; -use graphene_std::{Artboard, Context, Graphic, NodeIO, NodeIOTypes, ProtoNodeIdentifier, concrete, fn_type_fut, future}; +use graphene_std::registry::{ConstructionError, EdgeHandle, ErasedGNode, NodeIOTypes, RegistryEntry}; +use graphene_std::runtime::RuntimeHandle; +use graphene_std::{Artboard, Context, Graphic, ProtoNodeIdentifier, SourceId, concrete, fn_type}; use node_registry_macros::{async_node, convert_node, into_node}; use std::collections::HashMap; #[cfg(feature = "gpu")] use wgpu_executor::WgpuExecutor; -// TODO: turn into hashmap -fn node_registry() -> HashMap> { - let mut node_types: Vec<(ProtoNodeIdentifier, NodeConstructor, NodeIOTypes)> = vec![ +fn node_registry() -> HashMap> { + let mut node_types: Vec<(ProtoNodeIdentifier, RegistryEntry)> = vec![ // ========== // INTO NODES // ========== @@ -109,87 +107,87 @@ fn node_registry() -> HashMap>, to: List>, converter: &WgpuExecutor), #[cfg(feature = "gpu")] - convert_node!(from: List>, to: List>, converter: &WgpuExecutor), + convert_node!(from: List>, to: List>, converter: &WgpuExecutor, async), // ============= // MONITOR NODES // ============= - async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => ()]), - async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => List]), - async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => List]), - async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => List]), - async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => List>]), + async_node!(graphene_core::memo::MonitorNode<_, _>, input: Context, fn_params: [Context => ()]), + async_node!(graphene_core::memo::MonitorNode<_, _>, input: Context, fn_params: [Context => List]), + async_node!(graphene_core::memo::MonitorNode<_, _>, input: Context, fn_params: [Context => List]), + async_node!(graphene_core::memo::MonitorNode<_, _>, input: Context, fn_params: [Context => List]), + async_node!(graphene_core::memo::MonitorNode<_, _>, input: Context, fn_params: [Context => List>]), #[cfg(feature = "gpu")] - async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => List>]), - async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => List]), - async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => List]), - async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => Image]), - async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => String]), - async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => IVec2]), - async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => DVec2]), - async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => DAffine2]), - async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => Option]), - async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => bool]), - async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => f64]), - async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => u32]), - async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => u64]), - async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => BlendMode]), - async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => Texture]), - async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => graphene_std::application_io::resource::Resource]), - async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => graphene_std::transform::ReferencePoint]), - async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => graphene_std::vector::misc::BooleanOperation]), - async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => graphene_std::vector::style::StrokeCap]), - async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => graphene_std::vector::style::StrokeJoin]), - async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => graphene_std::vector::style::PaintOrder]), - async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => graphene_std::vector::style::StrokeAlign]), - async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => graphene_std::vector::style::Stroke]), - async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => Box]), - async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => graphene_std::vector::misc::CentroidType]), - async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => graphene_std::vector::misc::PointSpacingType]), - async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => Option]), - async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => List]), - async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => List]), - async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => List]), - async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => List]), - async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => List]), - async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => List]), - async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => List]), - async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => List]), - async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => List]), - async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => AttributeDyn]), - async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => AttributeValueDyn]), - async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => ListDyn]), - async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => Graphic]), - async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => graphene_std::text::Font]), - async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => List]), - async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => DocumentNode]), - async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => graphene_std::transform::Footprint]), - async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => graphene_std::blending::BlendMode]), - async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => graphene_std::raster::adjustments::LuminanceCalculation]), - async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => graphene_std::extract_xy::XY]), - async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => graphene_std::text_nodes::StringCapitalization]), - async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => graphene_std::raster::adjustments::RedGreenBlue]), - async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => graphene_std::raster::adjustments::RedGreenBlueAlpha]), - async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => graphene_std::animation::RealTimeMode]), - async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => graphene_std::raster::adjustments::NoiseType]), - async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => graphene_std::raster::adjustments::FractalType]), - async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => graphene_std::raster::adjustments::CellularDistanceFunction]), - async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => graphene_std::raster::adjustments::CellularReturnType]), - async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => graphene_std::raster::adjustments::DomainWarpType]), - async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => graphene_std::raster::adjustments::RelativeAbsolute]), - async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => graphene_std::raster::adjustments::SelectiveColorChoice]), - async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => graphene_std::vector::misc::GridType]), - async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => graphene_std::vector::misc::ArcType]), - async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => graphene_std::vector::misc::RowsOrColumns]), - async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => graphene_std::vector::misc::MergeByDistanceAlgorithm]), - async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => graphene_std::vector::misc::ExtrudeJoiningAlgorithm]), - async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => graphene_std::vector::misc::PointSpacingType]), - async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => graphene_std::vector::style::GradientType]), - async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => graphene_std::vector::style::GradientSpreadMethod]), - async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => graphene_std::transform::ReferencePoint]), - async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => graphene_std::vector::misc::CentroidType]), - async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => graphene_std::text::TextAlign]), - async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => graphene_std::transform::ScaleType]), - async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => graphene_std::vector::misc::InterpolationDistribution]), + async_node!(graphene_core::memo::MonitorNode<_, _>, input: Context, fn_params: [Context => List>]), + async_node!(graphene_core::memo::MonitorNode<_, _>, input: Context, fn_params: [Context => List]), + async_node!(graphene_core::memo::MonitorNode<_, _>, input: Context, fn_params: [Context => List]), + async_node!(graphene_core::memo::MonitorNode<_, _>, input: Context, fn_params: [Context => Image]), + async_node!(graphene_core::memo::MonitorNode<_, _>, input: Context, fn_params: [Context => String]), + async_node!(graphene_core::memo::MonitorNode<_, _>, input: Context, fn_params: [Context => IVec2]), + async_node!(graphene_core::memo::MonitorNode<_, _>, input: Context, fn_params: [Context => DVec2]), + async_node!(graphene_core::memo::MonitorNode<_, _>, input: Context, fn_params: [Context => DAffine2]), + async_node!(graphene_core::memo::MonitorNode<_, _>, input: Context, fn_params: [Context => Option]), + async_node!(graphene_core::memo::MonitorNode<_, _>, input: Context, fn_params: [Context => bool]), + async_node!(graphene_core::memo::MonitorNode<_, _>, input: Context, fn_params: [Context => f64]), + async_node!(graphene_core::memo::MonitorNode<_, _>, input: Context, fn_params: [Context => u32]), + async_node!(graphene_core::memo::MonitorNode<_, _>, input: Context, fn_params: [Context => u64]), + async_node!(graphene_core::memo::MonitorNode<_, _>, input: Context, fn_params: [Context => BlendMode]), + async_node!(graphene_core::memo::MonitorNode<_, _>, input: Context, fn_params: [Context => Texture]), + async_node!(graphene_core::memo::MonitorNode<_, _>, input: Context, fn_params: [Context => graphene_std::application_io::resource::Resource]), + async_node!(graphene_core::memo::MonitorNode<_, _>, input: Context, fn_params: [Context => graphene_std::transform::ReferencePoint]), + async_node!(graphene_core::memo::MonitorNode<_, _>, input: Context, fn_params: [Context => graphene_std::vector::misc::BooleanOperation]), + async_node!(graphene_core::memo::MonitorNode<_, _>, input: Context, fn_params: [Context => graphene_std::vector::style::StrokeCap]), + async_node!(graphene_core::memo::MonitorNode<_, _>, input: Context, fn_params: [Context => graphene_std::vector::style::StrokeJoin]), + async_node!(graphene_core::memo::MonitorNode<_, _>, input: Context, fn_params: [Context => graphene_std::vector::style::PaintOrder]), + async_node!(graphene_core::memo::MonitorNode<_, _>, input: Context, fn_params: [Context => graphene_std::vector::style::StrokeAlign]), + async_node!(graphene_core::memo::MonitorNode<_, _>, input: Context, fn_params: [Context => graphene_std::vector::style::Stroke]), + async_node!(graphene_core::memo::MonitorNode<_, _>, input: Context, fn_params: [Context => Box]), + async_node!(graphene_core::memo::MonitorNode<_, _>, input: Context, fn_params: [Context => graphene_std::vector::misc::CentroidType]), + async_node!(graphene_core::memo::MonitorNode<_, _>, input: Context, fn_params: [Context => graphene_std::vector::misc::PointSpacingType]), + async_node!(graphene_core::memo::MonitorNode<_, _>, input: Context, fn_params: [Context => Option]), + async_node!(graphene_core::memo::MonitorNode<_, _>, input: Context, fn_params: [Context => List]), + async_node!(graphene_core::memo::MonitorNode<_, _>, input: Context, fn_params: [Context => List]), + async_node!(graphene_core::memo::MonitorNode<_, _>, input: Context, fn_params: [Context => List]), + async_node!(graphene_core::memo::MonitorNode<_, _>, input: Context, fn_params: [Context => List]), + async_node!(graphene_core::memo::MonitorNode<_, _>, input: Context, fn_params: [Context => List]), + async_node!(graphene_core::memo::MonitorNode<_, _>, input: Context, fn_params: [Context => List]), + async_node!(graphene_core::memo::MonitorNode<_, _>, input: Context, fn_params: [Context => List]), + async_node!(graphene_core::memo::MonitorNode<_, _>, input: Context, fn_params: [Context => List]), + async_node!(graphene_core::memo::MonitorNode<_, _>, input: Context, fn_params: [Context => List]), + async_node!(graphene_core::memo::MonitorNode<_, _>, input: Context, fn_params: [Context => AttributeDyn]), + async_node!(graphene_core::memo::MonitorNode<_, _>, input: Context, fn_params: [Context => AttributeValueDyn]), + async_node!(graphene_core::memo::MonitorNode<_, _>, input: Context, fn_params: [Context => ListDyn]), + async_node!(graphene_core::memo::MonitorNode<_, _>, input: Context, fn_params: [Context => Graphic]), + async_node!(graphene_core::memo::MonitorNode<_, _>, input: Context, fn_params: [Context => graphene_std::text::Font]), + async_node!(graphene_core::memo::MonitorNode<_, _>, input: Context, fn_params: [Context => List]), + async_node!(graphene_core::memo::MonitorNode<_, _>, input: Context, fn_params: [Context => DocumentNode]), + async_node!(graphene_core::memo::MonitorNode<_, _>, input: Context, fn_params: [Context => graphene_std::transform::Footprint]), + async_node!(graphene_core::memo::MonitorNode<_, _>, input: Context, fn_params: [Context => graphene_std::blending::BlendMode]), + async_node!(graphene_core::memo::MonitorNode<_, _>, input: Context, fn_params: [Context => graphene_std::raster::adjustments::LuminanceCalculation]), + async_node!(graphene_core::memo::MonitorNode<_, _>, input: Context, fn_params: [Context => graphene_std::extract_xy::XY]), + async_node!(graphene_core::memo::MonitorNode<_, _>, input: Context, fn_params: [Context => graphene_std::text_nodes::StringCapitalization]), + async_node!(graphene_core::memo::MonitorNode<_, _>, input: Context, fn_params: [Context => graphene_std::raster::adjustments::RedGreenBlue]), + async_node!(graphene_core::memo::MonitorNode<_, _>, input: Context, fn_params: [Context => graphene_std::raster::adjustments::RedGreenBlueAlpha]), + async_node!(graphene_core::memo::MonitorNode<_, _>, input: Context, fn_params: [Context => graphene_std::animation::RealTimeMode]), + async_node!(graphene_core::memo::MonitorNode<_, _>, input: Context, fn_params: [Context => graphene_std::raster::adjustments::NoiseType]), + async_node!(graphene_core::memo::MonitorNode<_, _>, input: Context, fn_params: [Context => graphene_std::raster::adjustments::FractalType]), + async_node!(graphene_core::memo::MonitorNode<_, _>, input: Context, fn_params: [Context => graphene_std::raster::adjustments::CellularDistanceFunction]), + async_node!(graphene_core::memo::MonitorNode<_, _>, input: Context, fn_params: [Context => graphene_std::raster::adjustments::CellularReturnType]), + async_node!(graphene_core::memo::MonitorNode<_, _>, input: Context, fn_params: [Context => graphene_std::raster::adjustments::DomainWarpType]), + async_node!(graphene_core::memo::MonitorNode<_, _>, input: Context, fn_params: [Context => graphene_std::raster::adjustments::RelativeAbsolute]), + async_node!(graphene_core::memo::MonitorNode<_, _>, input: Context, fn_params: [Context => graphene_std::raster::adjustments::SelectiveColorChoice]), + async_node!(graphene_core::memo::MonitorNode<_, _>, input: Context, fn_params: [Context => graphene_std::vector::misc::GridType]), + async_node!(graphene_core::memo::MonitorNode<_, _>, input: Context, fn_params: [Context => graphene_std::vector::misc::ArcType]), + async_node!(graphene_core::memo::MonitorNode<_, _>, input: Context, fn_params: [Context => graphene_std::vector::misc::RowsOrColumns]), + async_node!(graphene_core::memo::MonitorNode<_, _>, input: Context, fn_params: [Context => graphene_std::vector::misc::MergeByDistanceAlgorithm]), + async_node!(graphene_core::memo::MonitorNode<_, _>, input: Context, fn_params: [Context => graphene_std::vector::misc::ExtrudeJoiningAlgorithm]), + async_node!(graphene_core::memo::MonitorNode<_, _>, input: Context, fn_params: [Context => graphene_std::vector::misc::PointSpacingType]), + async_node!(graphene_core::memo::MonitorNode<_, _>, input: Context, fn_params: [Context => graphene_std::vector::style::GradientType]), + async_node!(graphene_core::memo::MonitorNode<_, _>, input: Context, fn_params: [Context => graphene_std::vector::style::GradientSpreadMethod]), + async_node!(graphene_core::memo::MonitorNode<_, _>, input: Context, fn_params: [Context => graphene_std::transform::ReferencePoint]), + async_node!(graphene_core::memo::MonitorNode<_, _>, input: Context, fn_params: [Context => graphene_std::vector::misc::CentroidType]), + async_node!(graphene_core::memo::MonitorNode<_, _>, input: Context, fn_params: [Context => graphene_std::text::TextAlign]), + async_node!(graphene_core::memo::MonitorNode<_, _>, input: Context, fn_params: [Context => graphene_std::transform::ScaleType]), + async_node!(graphene_core::memo::MonitorNode<_, _>, input: Context, fn_params: [Context => graphene_std::vector::misc::InterpolationDistribution]), // Context nullification #[cfg(feature = "gpu")] async_node!(graphene_core::context_modification::ContextModificationNode<_, _>, input: Context, fn_params: [Context => &PlatformEditorApi, Context => graphene_std::ContextFeatures]), @@ -317,15 +315,21 @@ fn node_registry() -> HashMap> = HashMap::new(); + let mut map: HashMap> = HashMap::new(); + let insert = |map: &mut HashMap>, id: ProtoNodeIdentifier, entry: RegistryEntry| { + let rows = map.entry(id).or_default(); + if !rows.iter().any(|row| row.io == entry.io) { + rows.push(entry); + } + }; - for (id, entry) in graphene_std::registry::NODE_REGISTRY.lock().unwrap().iter() { - for (constructor, types) in entry.iter() { - map.entry(id.clone()).or_default().insert(types.clone(), *constructor); + for (id, entries) in graphene_std::registry::NODE_REGISTRY.lock().unwrap().iter() { + for entry in entries { + insert(&mut map, id.clone(), entry.clone()); } } - for (id, node_constructor, types) in node_types.into_iter() { + for (id, entry) in node_types.into_iter() { // TODO: this is a hack to remove the newline from the node new_name // This occurs for the ChannelMixerNode presumably because of the long name. // This might be caused by the stringify! macro @@ -338,43 +342,35 @@ fn node_registry() -> HashMap) or similar -pub static NODE_REGISTRY: once_cell::sync::Lazy>> = once_cell::sync::Lazy::new(|| node_registry()); +pub static NODE_REGISTRY: once_cell::sync::Lazy>> = once_cell::sync::Lazy::new(|| node_registry()); mod node_registry_macros { macro_rules! async_node { - // TODO: we currently need to annotate the type here because the compiler would otherwise (correctly) - // TODO: assign a Pin>> type to the node, which is not what we want for now. - // // This `params` variant of the macro wraps the normal `fn_params` variant and is used as a shorthand for writing `T` instead of `() => T` ($path:ty, input: $input:ty, params: [$($type:ty),*]) => { async_node!($path, input: $input, fn_params: [ $(() => $type),*]) }; - ($path:ty, input: $input:ty, fn_params: [$($arg:ty => $type:ty),*]) => { + ($path:ty, input: $input:ty, fn_params: [$first_arg:ty => $first:ty $(, $arg:ty => $type:ty)*]) => { ( ProtoNodeIdentifier::new(stringify!($path)), - |mut args| { - Box::pin(async move { - args.reverse(); - let node = <$path>::new($(graphene_std::any::downcast_node::<$arg, $type>(args.pop().expect("Not enough arguments provided to construct node"))),*); - let any: DynAnyNode<$input, _, _> = graphene_std::any::DynAnyNode::new(node); - Box::new(any) as TypeErasedBox - }) - }, - { - let node = <$path>::new($( - graphene_std::any::PanicNode::<$arg, core::pin::Pin + Send>>>::new() - ),*); - let params = vec![$(fn_type_fut!($arg, $type)),*]; - let mut node_io = NodeIO::<'_, $input>::to_async_node_io(&node, params); - node_io.call_argument = concrete!(<$input as StaticType>::Static); - node_io + RegistryEntry { + io: NodeIOTypes::new(concrete!($input), concrete!($first), vec![fn_type!($first_arg, $first) $(, fn_type!($arg, $type))*]), + constructor: |inputs| { + let expected = [stringify!($first) $(, stringify!($type))*].len(); + if inputs.len() != expected { + return Err(ConstructionError::Arity { expected, got: inputs.len() }); + } + let mut inputs = inputs.into_iter(); + let node = <$path>::new(inputs.next().unwrap().downcast::<$first>()? $(, inputs.next().unwrap().downcast::<$type>()?)*); + Ok(EdgeHandle::new(std::sync::Arc::new(node) as std::sync::Arc>)) + }, }, ) }; @@ -384,31 +380,23 @@ mod node_registry_macros { (from: $from:ty, to: $to:ty) => { ( ProtoNodeIdentifier::new(concat!["graphene_core::ops::IntoNode<", stringify!($to), ">"]), - |mut args| { - Box::pin(async move { - let node = graphene_std::ops::IntoNode::new( - graphene_std::any::downcast_node::(args.pop().unwrap()), - graphene_std::any::FutureWrapperNode::new(graphene_std::value::ClonedNode::new(std::marker::PhantomData::<$to>)), - ); - let any: DynAnyNode = graphene_std::any::DynAnyNode::new(node); - Box::new(any) as TypeErasedBox - }) - }, - { - let node = graphene_std::ops::IntoNode::new( - graphene_std::any::PanicNode:: + Send>>>::new(), - graphene_std::any::FutureWrapperNode::new(graphene_std::value::ClonedNode::new(std::marker::PhantomData::<$to>)), - ); - let params = vec![fn_type_fut!(Context, $from)]; - let node_io = NodeIO::<'_, Context>::to_async_node_io(&node, params); - node_io + RegistryEntry { + io: NodeIOTypes::new(concrete!(Context), concrete!($to), vec![fn_type!(Context, $from)]), + constructor: |inputs| { + if inputs.len() != 1 { + return Err(ConstructionError::Arity { expected: 1, got: inputs.len() }); + } + let mut inputs = inputs.into_iter(); + let node = graphene_std::ops::IntoNode::<$to, _>::new(inputs.next().unwrap().downcast::<$from>()?); + Ok(EdgeHandle::new(std::sync::Arc::new(node) as std::sync::Arc>)) + }, }, ) }; } macro_rules! convert_node { (from: $from:ty, to: numbers) => {{ - let x: Vec<(ProtoNodeIdentifier, NodeConstructor, NodeIOTypes)> = vec![ + let x: Vec<(ProtoNodeIdentifier, RegistryEntry)> = vec![ convert_node!(from: $from, to: f32), convert_node!(from: $from, to: f64), convert_node!(from: $from, to: i8), @@ -427,7 +415,7 @@ mod node_registry_macros { x }}; (from: numbers, to: $to:ty) => {{ - let x: Vec<(ProtoNodeIdentifier, NodeConstructor, NodeIOTypes)> = vec![ + let x: Vec<(ProtoNodeIdentifier, RegistryEntry)> = vec![ convert_node!(from: f32, to: $to), convert_node!(from: f64, to: $to), convert_node!(from: i8, to: $to), @@ -448,31 +436,44 @@ mod node_registry_macros { (from: $from:ty, to: $to:ty) => { convert_node!(from: $from, to: $to, converter: ()) }; + (from: $from:ty, to: $to:ty, converter: $convert:ty, async) => { + ( + ProtoNodeIdentifier::new(concat!["graphene_core::ops::ConvertNode<", stringify!($to), ">"]), + RegistryEntry { + io: NodeIOTypes::new( + concrete!(Context), + concrete!($to), + vec![fn_type!(Context, $from), fn_type!(Context, $convert), fn_type!(Context, RuntimeHandle), fn_type!(Context, SourceId)], + ), + constructor: |inputs| { + if inputs.len() != 4 { + return Err(ConstructionError::Arity { expected: 4, got: inputs.len() }); + } + let mut inputs = inputs.into_iter(); + let node = graphene_std::ops::ConvertAsyncNode::<$to, _, _, _, _>::new( + inputs.next().unwrap().downcast::<$from>()?, + inputs.next().unwrap().downcast::<$convert>()?, + inputs.next().unwrap().downcast::()?, + inputs.next().unwrap().downcast::()?, + ); + Ok(EdgeHandle::new(std::sync::Arc::new(node) as std::sync::Arc>)) + }, + }, + ) + }; (from: $from:ty, to: $to:ty, converter: $convert:ty) => { ( ProtoNodeIdentifier::new(concat!["graphene_core::ops::ConvertNode<", stringify!($to), ">"]), - |mut args| { - Box::pin(async move { - let mut args = args.drain(..); - let node = graphene_std::ops::ConvertNode::new( - graphene_std::any::downcast_node::(args.next().expect("Convert node did not get first argument")), - graphene_std::any::downcast_node::(args.next().expect("Convert node did not get converter argument")), - graphene_std::any::FutureWrapperNode::new(graphene_std::value::ClonedNode::new(std::marker::PhantomData::<$to>)) - ); - let any: DynAnyNode = graphene_std::any::DynAnyNode::new(node); - Box::new(any) as TypeErasedBox - }) - }, - { - let node = graphene_std::ops::ConvertNode::new( - - graphene_std::any::PanicNode:: + Send>>>::new(), - graphene_std::any::PanicNode:: + Send>>>::new(), - graphene_std::any::FutureWrapperNode::new(graphene_std::value::ClonedNode::new(std::marker::PhantomData::<$to>)) - ); - let params = vec![fn_type_fut!(Context, $from), fn_type_fut!(Context, $convert)]; - let node_io = NodeIO::<'_, Context>::to_async_node_io(&node, params); - node_io + RegistryEntry { + io: NodeIOTypes::new(concrete!(Context), concrete!($to), vec![fn_type!(Context, $from), fn_type!(Context, $convert)]), + constructor: |inputs| { + if inputs.len() != 2 { + return Err(ConstructionError::Arity { expected: 2, got: inputs.len() }); + } + let mut inputs = inputs.into_iter(); + let node = graphene_std::ops::ConvertNode::<$to, _, _>::new(inputs.next().unwrap().downcast::<$from>()?, inputs.next().unwrap().downcast::<$convert>()?); + Ok(EdgeHandle::new(std::sync::Arc::new(node) as std::sync::Arc>)) + }, }, ) }; diff --git a/node-graph/libraries/core-types/src/registry.rs b/node-graph/libraries/core-types/src/registry.rs index d4e3dd792d..2fdb0013d4 100644 --- a/node-graph/libraries/core-types/src/registry.rs +++ b/node-graph/libraries/core-types/src/registry.rs @@ -1,7 +1,7 @@ use crate::concrete; -use crate::context::ContextImpl; +use crate::context::{Context, ContextImpl}; use crate::gnode::GNode; -use crate::{ContextFeature, Node, NodeIO, NodeIOTypes, ProtoNodeIdentifier, Type, WasmNotSend}; +use crate::{ContextFeature, Node, NodeIO, ProtoNodeIdentifier, Type, WasmNotSend, WasmNotSync}; use dyn_any::{DynAny, StaticType}; use graphene_hash::CacheHash; pub use no_std_types::registry::types; @@ -62,15 +62,36 @@ pub enum RegistryValueSource { Scope(&'static str), } -type NodeRegistry = LazyLock>>>; +type NodeRegistry = LazyLock>>>; pub static NODE_REGISTRY: NodeRegistry = LazyLock::new(|| Mutex::new(HashMap::new())); pub static NODE_METADATA: LazyLock>> = LazyLock::new(|| Mutex::new(HashMap::new())); +pub use crate::NodeIOTypes; + +#[cfg(not(target_family = "wasm"))] +pub type ErasedGNode = dyn for<'c> GNode, Output = T> + Send + Sync; +#[cfg(target_family = "wasm")] pub type ErasedGNode = dyn for<'c> GNode, Output = T>; +#[cfg(not(target_family = "wasm"))] +pub type ErasedLendGNode = dyn for<'c> GNode, Output = &'c T> + Send + Sync; +#[cfg(target_family = "wasm")] pub type ErasedLendGNode = dyn for<'c> GNode, Output = &'c T>; +#[cfg(not(target_family = "wasm"))] +type DynEdge = dyn std::any::Any + Send + Sync; +#[cfg(target_family = "wasm")] +type DynEdge = dyn std::any::Any; + +pub fn edge_type() -> Type { + Type::Fn(Box::new(concrete!(Context)), Box::new(concrete!(T))) +} + +pub fn lend_edge_type() -> Type { + Type::Fn(Box::new(concrete!(Context)), Box::new(Type::Ref(Box::new(concrete!(T))))) +} + pub fn cache_key(ctx: &C) -> u64 { let mut hasher = std::hash::DefaultHasher::new(); ctx.cache_hash(&mut hasher); @@ -104,6 +125,12 @@ impl SharedEdge { } } +// SAFETY: `ptr` is derived from the owned Arc and never mutated through, so the edge is exactly as +// thread safe as the payload it shares. +unsafe impl Send for SharedEdge {} +// SAFETY: as in Send. +unsafe impl Sync for SharedEdge {} + impl GNode for SharedEdge where N: GNode + ?Sized, @@ -136,8 +163,8 @@ where } pub struct EdgeHandle { - node: Box, - share: fn(&dyn std::any::Any) -> Box, + node: Box, + share: fn(&DynEdge) -> Box, ty: Type, } @@ -149,14 +176,17 @@ impl std::fmt::Debug for EdgeHandle { impl EdgeHandle { pub fn new(node: std::sync::Arc>) -> Self { - Self::new_erased(node, concrete!(T)) + Self::new_erased(node, edge_type::()) } pub fn new_ref(node: std::sync::Arc>) -> Self { - Self::new_erased(node, Type::Ref(Box::new(concrete!(T)))) + Self::new_erased(node, lend_edge_type::()) } - pub fn new_erased(node: std::sync::Arc, ty: Type) -> Self { + pub fn new_erased(node: std::sync::Arc, ty: Type) -> Self + where + SharedEdge: WasmNotSend + WasmNotSync, + { Self { node: Box::new(SharedEdge::new(node)), share: |edge| Box::new(edge.downcast_ref::>().expect("share hook matches the stored edge type").share()), @@ -177,11 +207,11 @@ impl EdgeHandle { } pub fn downcast(self) -> Result>, ConstructionError> { - self.downcast_erased(concrete!(T)) + self.downcast_erased(edge_type::()) } pub fn downcast_lend(self) -> Result>, ConstructionError> { - self.downcast_erased(Type::Ref(Box::new(concrete!(T)))) + self.downcast_erased(lend_edge_type::()) } pub fn downcast_erased(self, expected: Type) -> Result, ConstructionError> { @@ -193,15 +223,11 @@ impl EdgeHandle { } } -pub struct NodeIoRecord { - pub inputs: Vec, - pub output: Type, -} - pub type NodeConstructor = fn(Vec) -> Result; +#[derive(Clone)] pub struct RegistryEntry { - pub io: NodeIoRecord, + pub io: NodeIOTypes, pub constructor: NodeConstructor, } @@ -519,7 +545,7 @@ mod tests { } } - type ErasedSplitEdge = dyn for<'c> GNode, Output = SplitBorrow<'c>>; + type ErasedSplitEdge = dyn for<'c> GNode, Output = SplitBorrow<'c>> + Send + Sync; let arena = Arena::new(4096); let generations = []; @@ -651,10 +677,7 @@ mod tests { Ok(EdgeHandle::new(Arc::new(ValueNode(0u32)) as Arc>)) } let entry = RegistryEntry { - io: NodeIoRecord { - inputs: vec![concrete!(String)], - output: concrete!(u32), - }, + io: NodeIOTypes::new(concrete!(Context), concrete!(u32), vec![edge_type::()]), constructor: construct_strlen, }; @@ -667,8 +690,8 @@ mod tests { assert_eq!( construct(&entry, vec![mistyped]).unwrap_err(), ConstructionError::Type { - expected: concrete!(String), - found: concrete!(f64), + expected: edge_type::(), + found: edge_type::(), } ); @@ -676,8 +699,8 @@ mod tests { assert_eq!( construct(&entry, vec![lent]).unwrap_err(), ConstructionError::Type { - expected: concrete!(String), - found: Type::Ref(Box::new(concrete!(String))), + expected: edge_type::(), + found: lend_edge_type::(), } ); } @@ -691,7 +714,7 @@ mod tests { let handle = EdgeHandle::new(Arc::new(CountingNode(AtomicU32::new(0))) as Arc>); let duplicate = handle.duplicate(); - assert_eq!(*duplicate.ty(), concrete!(u32)); + assert_eq!(*duplicate.ty(), edge_type::()); let first = handle.downcast::().unwrap(); let second = duplicate.downcast::().unwrap(); diff --git a/node-graph/node-macro/src/codegen.rs b/node-graph/node-macro/src/codegen.rs index 045e63d6f3..ccdff43798 100644 --- a/node-graph/node-macro/src/codegen.rs +++ b/node-graph/node-macro/src/codegen.rs @@ -255,6 +255,11 @@ pub(crate) fn generate_node_code(crate_ident: &CrateIdent, parsed: &ParsedNodeFn let gnode = crate::gcodegen::generate_gnode_code(crate_ident, parsed)?; let gnode_in_mod = gnode.in_mod; let gnode_top_level = gnode.top_level; + let entries_name = format_ident!("{}_entries", parsed.fn_name); + let register_entries = match gnode_in_mod.is_empty() { + true => quote!(), + false => quote!(gcore::registry::NODE_REGISTRY.lock().unwrap().entry(#identifier()).or_default().extend(#entries_name());), + }; let properties = &attributes.properties_string.as_ref().map(|value| quote!(Some(#value))).unwrap_or(quote!(None)); let memoize_flag = attributes.memoize; @@ -371,6 +376,7 @@ pub(crate) fn generate_node_code(crate_ident: &CrateIdent, parsed: &ParsedNodeFn ], }; NODE_METADATA.lock().unwrap().insert(#identifier(), metadata); + #register_entries } } diff --git a/node-graph/node-macro/src/gcodegen.rs b/node-graph/node-macro/src/gcodegen.rs index d1a6429604..f6bc80f91c 100644 --- a/node-graph/node-macro/src/gcodegen.rs +++ b/node-graph/node-macro/src/gcodegen.rs @@ -604,10 +604,11 @@ fn entries_tokens(parsed: &ParsedNodeFn, struct_name: &Ident, data_field_generic }); quote! { gcore::registry::RegistryEntry { - io: gcore::registry::NodeIoRecord { - inputs: vec![#(gcore::concrete!(#types)),*], - output: gcore::concrete!(#output), - }, + io: gcore::registry::NodeIOTypes::new( + gcore::concrete!(gcore::context::ContextImpl<'static>), + gcore::concrete!(#output), + vec![#(gcore::registry::edge_type::<#types>()),*], + ), constructor: |inputs| { if inputs.len() != #arity { return Err(gcore::registry::ConstructionError::Arity { expected: #arity, got: inputs.len() }); diff --git a/node-graph/nodes/gcore/src/memo.rs b/node-graph/nodes/gcore/src/memo.rs index 9064854d5c..472b06ecaa 100644 --- a/node-graph/nodes/gcore/src/memo.rs +++ b/node-graph/nodes/gcore/src/memo.rs @@ -1,5 +1,5 @@ use core_types::arena::{Arena, ArenaCell}; -use core_types::context::Ctx; +use core_types::context::{Ctx, CtxSnapshot, DeriveCtx, ExtractAll}; use core_types::frame_table::{FrameTable, Lookup}; use core_types::gnode::GNode; use core_types::gpoll::{Extent, Finality, GPoll, Interrupt}; @@ -102,26 +102,26 @@ pub fn park<'e, T>(arena: &'e Arena, result: GPoll) -> GPoll<&'e T> { } } -type MonitorValue = Arc>>>>; +type MonitorValue = Arc>>>>; /// The Monitor node is used by the editor to access the data flowing through it. #[node_macro::node(category(""), path(graphene_core::memo), serialize(serialize_monitor), properties("monitor_properties"), skip_impl)] -fn monitor( - input: I, +fn monitor( + ctx: impl Ctx + DeriveCtx + ExtractAll, #[allow(clippy::type_complexity)] #[data] - io: MonitorValue, - content: impl Node, + io: MonitorValue, + content: impl Node, Output = T>, ) -> Result { - let output = content.eval(input)?; + let output = content.eval(&ctx.derived())?; *io.lock().unwrap() = Some(Arc::new(IORecord { - input: input.clone(), + input: CtxSnapshot::capture(ctx), output: output.clone(), })); Ok(output) } -fn serialize_monitor(io: &MonitorValue) -> Option> { +fn serialize_monitor(io: &MonitorValue) -> Option> { let io = io.lock().unwrap(); io.as_ref().map(|output| output.clone() as Arc) } diff --git a/node-graph/nodes/math/src/lib.rs b/node-graph/nodes/math/src/lib.rs index 445789745a..69889a40bb 100644 --- a/node-graph/nodes/math/src/lib.rs +++ b/node-graph/nodes/math/src/lib.rs @@ -1136,6 +1136,16 @@ mod graphene_test { assert_eq!(GNode::eval(&wired, &ctx), GPoll::Final(true)); } + #[test] + fn ctor_registration_populates_the_node_registry() { + let registry = core_types::registry::NODE_REGISTRY.lock().unwrap(); + let rows = registry + .iter() + .find_map(|(id, rows)| id.as_str().ends_with("::AddNode").then_some(rows)) + .expect("AddNode rows registered at startup"); + assert_eq!(rows.len(), 6); + } + #[test] fn generic_add_registers_one_entry_per_implementation() { let arena = Arena::new(64);