Add type checking to the node graph (#1025)

* Implement type inference

Add type hints to node trait

Add type annotation infrastructure

Refactor type ascription infrastructure

Run cargo fix

Insert infer types stub

Remove types from node identifier

* Implement covariance

* Disable rejection of generic inputs + parameters

* Fix lints

* Extend type checking to cover Network inputs

* Implement generic specialization

* Relax covariance rules

* Fix type annotations for TypErasedComposeNode

* Fix type checking errors

* Keep connection information during node resolution
* Fix TypeDescriptor PartialEq implementation

* Apply review suggestions

* Add documentation to type inference

* Add Imaginate node to document node types

* Fix whitespace in macros

* Add types to imaginate node

* Fix type declaration for imaginate node + add console logging

* Use fully qualified type names as fallback during comparison

---------

Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
Dennis Kobert 2023-02-15 23:31:30 +01:00 committed by Keavon Chambers
parent a64c856ec4
commit 5dab7de68d
25 changed files with 1365 additions and 1008 deletions

2
Cargo.lock generated
View file

@ -1646,6 +1646,7 @@ dependencies = [
"kurbo", "kurbo",
"log", "log",
"node-macro", "node-macro",
"once_cell",
"serde", "serde",
"specta", "specta",
"spirv-std", "spirv-std",
@ -2171,6 +2172,7 @@ dependencies = [
"graphene-std", "graphene-std",
"log", "log",
"num-traits", "num-traits",
"once_cell",
"rand_chacha 0.3.1", "rand_chacha 0.3.1",
"serde", "serde",
] ]

View file

@ -10,12 +10,13 @@ use document_legacy::layers::nodegraph_layer::NodeGraphFrameLayer;
use document_legacy::LayerId; use document_legacy::LayerId;
use graph_craft::document::value::TaggedValue; use graph_craft::document::value::TaggedValue;
use graph_craft::document::{DocumentNode, DocumentNodeImplementation, NodeId, NodeInput, NodeNetwork, NodeOutput}; use graph_craft::document::{DocumentNode, DocumentNodeImplementation, NodeId, NodeInput, NodeNetwork, NodeOutput};
use graphene_core::*;
mod document_node_types; mod document_node_types;
mod node_properties; mod node_properties;
use glam::IVec2; use glam::IVec2;
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)] #[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Hash, serde::Serialize, serde::Deserialize, specta::Type)]
pub enum FrontendGraphDataType { pub enum FrontendGraphDataType {
#[default] #[default]
#[serde(rename = "general")] #[serde(rename = "general")]
@ -310,7 +311,7 @@ impl NodeGraphMessageHandler {
exposed_inputs: node exposed_inputs: node
.inputs .inputs
.iter() .iter()
.zip(node_type.inputs) .zip(node_type.inputs.iter())
.skip(1) .skip(1)
.filter(|(input, _)| input.is_exposed()) .filter(|(input, _)| input.is_exposed())
.map(|(_, input_type)| NodeGraphInput { .map(|(_, input_type)| NodeGraphInput {
@ -769,7 +770,7 @@ impl MessageHandler<NodeGraphMessage, (&mut Document, &mut dyn Iterator<Item = &
if let Some(node) = network.nodes.get_mut(node_id) { if let Some(node) = network.nodes.get_mut(node_id) {
// Extend number of inputs if not already large enough // Extend number of inputs if not already large enough
if input_index >= node.inputs.len() { if input_index >= node.inputs.len() {
node.inputs.extend(((node.inputs.len() - 1)..input_index).map(|_| NodeInput::Network)); node.inputs.extend(((node.inputs.len() - 1)..input_index).map(|_| NodeInput::Network(generic!(T))));
} }
node.inputs[input_index] = NodeInput::Value { tagged_value: value, exposed: false }; node.inputs[input_index] = NodeInput::Value { tagged_value: value, exposed: false };
if network.connected_to_output(*node_id) { if network.connected_to_output(*node_id) {

View file

@ -1,16 +1,19 @@
use super::{node_properties, FrontendGraphDataType, FrontendNodeType}; use super::{node_properties, FrontendGraphDataType, FrontendNodeType};
use crate::messages::layout::utility_types::layout_widget::LayoutGroup; use crate::messages::layout::utility_types::layout_widget::LayoutGroup;
use crate::node_graph_executor::NodeGraphExecutor; use crate::node_graph_executor::NodeGraphExecutor;
use once_cell::sync::Lazy;
use graph_craft::document::value::*; use graph_craft::document::value::*;
use graph_craft::document::*; use graph_craft::document::*;
use graph_craft::imaginate_input::ImaginateSamplingMethod; use graph_craft::imaginate_input::ImaginateSamplingMethod;
use graph_craft::proto::{NodeIdentifier, Type};
use graph_craft::{concrete, generic}; use graph_craft::NodeIdentifier;
use graphene_core::raster::{Color, Image, LuminanceCalculation}; use graphene_core::raster::{Color, Image, LuminanceCalculation};
use graphene_core::*;
use std::collections::VecDeque; use std::collections::VecDeque;
#[derive(Debug, Clone, PartialEq, Hash)]
pub struct DocumentInputType { pub struct DocumentInputType {
pub name: &'static str, pub name: &'static str,
pub data_type: FrontendGraphDataType, pub data_type: FrontendGraphDataType,
@ -18,7 +21,11 @@ pub struct DocumentInputType {
} }
impl DocumentInputType { impl DocumentInputType {
pub const fn new(name: &'static str, tagged_value: TaggedValue, exposed: bool) -> Self { pub fn new(name: &'static str, data_type: FrontendGraphDataType, default: NodeInput) -> Self {
Self { name, data_type, default }
}
pub fn value(name: &'static str, tagged_value: TaggedValue, exposed: bool) -> Self {
let data_type = FrontendGraphDataType::with_tagged_value(&tagged_value); let data_type = FrontendGraphDataType::with_tagged_value(&tagged_value);
let default = NodeInput::value(tagged_value, exposed); let default = NodeInput::value(tagged_value, exposed);
Self { name, data_type, default } Self { name, data_type, default }
@ -33,6 +40,7 @@ impl DocumentInputType {
} }
} }
#[derive(Debug, Clone, PartialEq, Hash)]
pub struct DocumentOutputType { pub struct DocumentOutputType {
pub name: &'static str, pub name: &'static str,
pub data_type: FrontendGraphDataType, pub data_type: FrontendGraphDataType,
@ -61,8 +69,8 @@ pub enum NodeImplementation {
} }
impl NodeImplementation { impl NodeImplementation {
pub const fn proto(name: &'static str, types: &'static [Type]) -> Self { pub fn proto(name: &'static str) -> Self {
Self::ProtoNode(NodeIdentifier::new(name, types)) Self::ProtoNode(NodeIdentifier::new(name))
} }
} }
@ -71,486 +79,458 @@ pub struct DocumentNodeType {
pub name: &'static str, pub name: &'static str,
pub category: &'static str, pub category: &'static str,
pub identifier: NodeImplementation, pub identifier: NodeImplementation,
pub inputs: &'static [DocumentInputType], pub inputs: Vec<DocumentInputType>,
pub outputs: &'static [DocumentOutputType], pub outputs: Vec<DocumentOutputType>,
pub properties: fn(&DocumentNode, NodeId, &mut NodePropertiesContext) -> Vec<LayoutGroup>, pub properties: fn(&DocumentNode, NodeId, &mut NodePropertiesContext) -> Vec<LayoutGroup>,
} }
fn document_node_types() -> Vec<DocumentNodeType> {
let mut vec: Vec<_> = STATIC_NODES.to_vec();
const GAUSSIAN_BLUR_NODE_INPUTS: &[DocumentInputType] = &[
DocumentInputType::new("Image", TaggedValue::Image(Image::empty()), true),
DocumentInputType::new("Radius", TaggedValue::U32(3), false),
DocumentInputType::new("Sigma", TaggedValue::F64(1.), false),
];
let blur = DocumentNodeType {
name: "Gaussian Blur",
category: "Image Filters",
identifier: NodeImplementation::DocumentNode(NodeNetwork {
inputs: vec![0, 1, 1],
outputs: vec![NodeOutput::new(1, 0)],
nodes: vec![
(
0,
DocumentNode {
name: "CacheNode".to_string(),
inputs: vec![NodeInput::Network],
implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_std::memo::CacheNode", &[concrete!("Image")])),
metadata: Default::default(),
},
),
(
1,
DocumentNode {
name: "BlurNode".to_string(),
inputs: vec![NodeInput::node(0, 0), NodeInput::Network, NodeInput::Network, NodeInput::node(0, 0)],
implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::raster::BlurNode", &[concrete!("Image")])),
metadata: Default::default(),
},
),
]
.into_iter()
.collect(),
..Default::default()
}),
inputs: GAUSSIAN_BLUR_NODE_INPUTS,
outputs: &[DocumentOutputType {
name: "Image",
data_type: FrontendGraphDataType::Raster,
}],
properties: node_properties::blur_image_properties,
};
vec.push(blur);
const INPUT_NODE_INPUTS: &[DocumentInputType] = &[
DocumentInputType {
name: "In",
data_type: FrontendGraphDataType::General,
default: NodeInput::Network,
},
DocumentInputType::new("Transform", TaggedValue::DAffine2(DAffine2::IDENTITY), false),
];
let input = DocumentNodeType {
name: "Input",
category: "Ignore",
identifier: NodeImplementation::DocumentNode(NodeNetwork {
inputs: vec![0, 1],
outputs: vec![NodeOutput::new(0, 0), NodeOutput::new(1, 0)],
nodes: [
DocumentNode {
name: "Identity".to_string(),
inputs: vec![NodeInput::Network],
implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::ops::IdNode", &[generic!("T")])),
metadata: Default::default(),
},
DocumentNode {
name: "Identity".to_string(),
inputs: vec![NodeInput::Network],
implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::ops::IdNode", &[generic!("T")])),
metadata: Default::default(),
},
]
.into_iter()
.enumerate()
.map(|(id, node)| (id as NodeId, node))
.collect(),
..Default::default()
}),
inputs: INPUT_NODE_INPUTS,
outputs: &[
DocumentOutputType {
name: "Image",
data_type: FrontendGraphDataType::Raster,
},
DocumentOutputType {
name: "Transform",
data_type: FrontendGraphDataType::Number,
},
],
properties: node_properties::input_properties,
};
vec.push(input);
vec
}
// We use the once cell for lazy initialization to avoid the overhead of reconstructing the node list every time. // We use the once cell for lazy initialization to avoid the overhead of reconstructing the node list every time.
// TODO: make document nodes not require a `'static` lifetime to avoid having to split the construction into const and non-const parts. // 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<Vec<DocumentNodeType>> = once_cell::sync::Lazy::new(document_node_types); static DOCUMENT_NODE_TYPES: once_cell::sync::Lazy<Vec<DocumentNodeType>> = once_cell::sync::Lazy::new(static_nodes);
// TODO: Dynamic node library // TODO: Dynamic node library
static STATIC_NODES: &[DocumentNodeType] = &[ fn static_nodes() -> Vec<DocumentNodeType> {
DocumentNodeType { vec![
name: "Identity", DocumentNodeType {
category: "General", name: "Identity",
identifier: NodeImplementation::proto("graphene_core::ops::IdNode", &[generic!("T")]), category: "General",
inputs: &[DocumentInputType { identifier: NodeImplementation::proto("graphene_core::ops::IdNode"),
name: "In", inputs: vec![DocumentInputType {
data_type: FrontendGraphDataType::General, name: "In",
default: NodeInput::node(0, 0), data_type: FrontendGraphDataType::General,
}], default: NodeInput::node(0, 0),
outputs: &[DocumentOutputType::new("Out", FrontendGraphDataType::General)], }],
properties: |_document_node, _node_id, _context| node_properties::string_properties("The identity node simply returns the input"), outputs: vec![DocumentOutputType::new("Out", FrontendGraphDataType::General)],
}, properties: |_document_node, _node_id, _context| node_properties::string_properties("The identity node simply returns the input"),
DocumentNodeType { },
name: "Image", DocumentNodeType {
category: "Ignore", name: "Image",
identifier: NodeImplementation::proto("graphene_core::ops::IdNode", &[generic!("T")]), category: "Ignore",
inputs: &[DocumentInputType::new("Image", TaggedValue::Image(Image::empty()), false)], identifier: NodeImplementation::proto("graphene_core::ops::IdNode"),
outputs: &[DocumentOutputType::new("Image", FrontendGraphDataType::Raster)], inputs: vec![DocumentInputType::value("Image", TaggedValue::Image(Image::empty()), false)],
properties: |_document_node, _node_id, _context| node_properties::string_properties("A bitmap image embedded in this node"), outputs: vec![DocumentOutputType::new("Image", FrontendGraphDataType::Raster)],
}, properties: |_document_node, _node_id, _context| node_properties::string_properties("A bitmap image embedded in this node"),
// DocumentNodeType { },
// name: "Input", // DocumentNodeType {
// category: "Ignore", // name: "Input",
// identifier: NodeImplementation::proto("graphene_core::ops::IdNode", &[generic!("T")]), // category: "Ignore",
// inputs: &[DocumentInputType { // identifier: NodeImplementation::proto("graphene_core::ops::IdNode"),
// name: "In", // inputs: vec![DocumentInputType {
// data_type: FrontendGraphDataType::Raster, // name: "In",
// default: NodeInput::Network, // data_type: FrontendGraphDataType::Raster,
// }], // default: NodeInput::Network,
// outputs: &[DocumentOutputType::new("Out", FrontendGraphDataType::Raster)], // }],
// properties: node_properties::input_properties, // outputs: vec![DocumentOutputType::new("Out", FrontendGraphDataType::Raster)],
// }, // properties: node_properties::input_properties,
DocumentNodeType { // },
name: "Output", DocumentNodeType {
category: "Ignore", name: "Input",
identifier: NodeImplementation::proto("graphene_core::ops::IdNode", &[generic!("T")]), category: "Ignore",
inputs: &[DocumentInputType { identifier: NodeImplementation::DocumentNode(NodeNetwork {
name: "In", inputs: vec![0, 1],
data_type: FrontendGraphDataType::Raster, outputs: vec![NodeOutput::new(0, 0), NodeOutput::new(1, 0)],
default: NodeInput::value(TaggedValue::Image(Image::empty()), true), nodes: [
}], DocumentNode {
outputs: &[], name: "Identity".to_string(),
properties: |_document_node, _node_id, _context| node_properties::string_properties("The graph's output is rendered into the frame"), inputs: vec![NodeInput::Network(concrete!(Image))],
}, implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::ops::IdNode")),
DocumentNodeType { metadata: Default::default(),
name: "Image Frame", },
category: "General", DocumentNode {
identifier: NodeImplementation::proto("graphene_std::raster::ImageFrameNode<_>", &[concrete!("Image"), concrete!("DAffine2")]), name: "Identity".to_string(),
inputs: &[ inputs: vec![NodeInput::Network(concrete!(DAffine2))],
DocumentInputType::new("Image", TaggedValue::Image(Image::empty()), true), implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::ops::IdNode")),
DocumentInputType::new("Transform", TaggedValue::DAffine2(DAffine2::IDENTITY), true), metadata: Default::default(),
], },
outputs: &[DocumentOutputType::new("Image", FrontendGraphDataType::Raster)], ]
properties: |_document_node, _node_id, _context| node_properties::string_properties("Creates an embedded image with the given transform"), .into_iter()
}, .enumerate()
DocumentNodeType { .map(|(id, node)| (id as NodeId, node))
name: "Grayscale", .collect(),
category: "Image Adjustments", ..Default::default()
identifier: NodeImplementation::proto( }),
"graphene_core::raster::GrayscaleNode<_, _, _, _, _, _, _>", inputs: vec![
&[ DocumentInputType {
concrete!("Image"), name: "In",
concrete!("Color"), data_type: FrontendGraphDataType::General,
concrete!("f64"), default: NodeInput::Network(concrete!(Image)),
concrete!("f64"), },
concrete!("f64"), DocumentInputType::value("Transform", TaggedValue::DAffine2(DAffine2::IDENTITY), false),
concrete!("f64"),
concrete!("f64"),
concrete!("f64"),
], ],
), outputs: vec![
inputs: &[ DocumentOutputType {
DocumentInputType { name: "Image",
name: "Image", data_type: FrontendGraphDataType::Raster,
},
DocumentOutputType {
name: "Transform",
data_type: FrontendGraphDataType::Number,
},
],
properties: node_properties::input_properties,
},
DocumentNodeType {
name: "Output",
category: "Ignore",
identifier: NodeImplementation::proto("graphene_core::ops::IdNode"),
inputs: vec![DocumentInputType {
name: "In",
data_type: FrontendGraphDataType::Raster, data_type: FrontendGraphDataType::Raster,
default: NodeInput::value(TaggedValue::Image(Image::empty()), true), default: NodeInput::value(TaggedValue::Image(Image::empty()), true),
}, }],
DocumentInputType { outputs: vec![],
name: "Tint", properties: |_document_node, _node_id, _context| node_properties::string_properties("The graph's output is rendered into the frame"),
data_type: FrontendGraphDataType::Number, },
default: NodeInput::value(TaggedValue::Color(Color::BLACK), false), DocumentNodeType {
}, name: "Image Frame",
DocumentInputType { category: "General",
name: "Reds", identifier: NodeImplementation::proto("graphene_std::raster::ImageFrameNode<_>"),
data_type: FrontendGraphDataType::Number, inputs: vec![
default: NodeInput::value(TaggedValue::F64(50.), false), DocumentInputType::value("Image", TaggedValue::Image(Image::empty()), true),
}, DocumentInputType::value("Transform", TaggedValue::DAffine2(DAffine2::IDENTITY), true),
DocumentInputType { ],
name: "Yellows", outputs: vec![DocumentOutputType::new("Image", FrontendGraphDataType::Raster)],
data_type: FrontendGraphDataType::Number, properties: |_document_node, _node_id, _context| node_properties::string_properties("Creates an embedded image with the given transform"),
default: NodeInput::value(TaggedValue::F64(50.), false), },
}, DocumentNodeType {
DocumentInputType { name: "Grayscale",
name: "Greens", category: "Image Adjustments",
data_type: FrontendGraphDataType::Number, identifier: NodeImplementation::proto("graphene_core::raster::GrayscaleNode<_, _, _, _, _, _, _>"),
default: NodeInput::value(TaggedValue::F64(50.), false), inputs: vec![
}, DocumentInputType {
DocumentInputType { name: "Image",
name: "Cyans", data_type: FrontendGraphDataType::Raster,
data_type: FrontendGraphDataType::Number, default: NodeInput::value(TaggedValue::Image(Image::empty()), true),
default: NodeInput::value(TaggedValue::F64(50.), false), },
}, DocumentInputType {
DocumentInputType { name: "Tint",
name: "Blues", data_type: FrontendGraphDataType::Number,
data_type: FrontendGraphDataType::Number, default: NodeInput::value(TaggedValue::Color(Color::BLACK), false),
default: NodeInput::value(TaggedValue::F64(50.), false), },
}, DocumentInputType {
DocumentInputType { name: "Reds",
name: "Magentas", data_type: FrontendGraphDataType::Number,
data_type: FrontendGraphDataType::Number, default: NodeInput::value(TaggedValue::F64(50.), false),
default: NodeInput::value(TaggedValue::F64(50.), false), },
}, DocumentInputType {
], name: "Yellows",
outputs: &[DocumentOutputType::new("Image", FrontendGraphDataType::Raster)], data_type: FrontendGraphDataType::Number,
properties: node_properties::grayscale_properties, default: NodeInput::value(TaggedValue::F64(50.), false),
}, },
DocumentNodeType { DocumentInputType {
name: "Luminance", name: "Greens",
category: "Image Adjustments", data_type: FrontendGraphDataType::Number,
identifier: NodeImplementation::proto("graphene_core::raster::LuminanceNode<_>", &[concrete!("Image"), concrete!("LuminanceCalculation")]), default: NodeInput::value(TaggedValue::F64(50.), false),
inputs: &[ },
DocumentInputType::new("Image", TaggedValue::Image(Image::empty()), true), DocumentInputType {
DocumentInputType::new("Luma Calculation", TaggedValue::LuminanceCalculation(LuminanceCalculation::SRGB), false), name: "Cyans",
], data_type: FrontendGraphDataType::Number,
outputs: &[DocumentOutputType::new("Image", FrontendGraphDataType::Raster)], default: NodeInput::value(TaggedValue::F64(50.), false),
properties: node_properties::luminance_properties, },
}, DocumentInputType {
#[cfg(feature = "gpu")] name: "Blues",
DocumentNodeType { data_type: FrontendGraphDataType::Number,
name: "GpuImage", default: NodeInput::value(TaggedValue::F64(50.), false),
category: "Image Adjustments", },
identifier: NodeImplementation::proto("graphene_std::executor::MapGpuSingleImageNode", &[concrete!("Image")]), DocumentInputType {
inputs: &[ name: "Magentas",
DocumentInputType::new("Image", TaggedValue::Image(Image::empty()), true), data_type: FrontendGraphDataType::Number,
DocumentInputType { default: NodeInput::value(TaggedValue::F64(50.), false),
name: "Path", },
data_type: FrontendGraphDataType::Text, ],
default: NodeInput::value(TaggedValue::String(String::new()), false), outputs: vec![DocumentOutputType::new("Image", FrontendGraphDataType::Raster)],
}, properties: node_properties::grayscale_properties,
], },
outputs: &[DocumentOutputType::new("Image", FrontendGraphDataType::Raster)], DocumentNodeType {
properties: node_properties::gpu_map_properties, name: "Luminance",
}, category: "Image Adjustments",
#[cfg(feature = "quantization")] identifier: NodeImplementation::proto("graphene_core::raster::LuminanceNode<_>"),
DocumentNodeType { inputs: vec![
name: "QuantizeImage", DocumentInputType::value("Image", TaggedValue::Image(Image::empty()), true),
category: "Image Adjustments", DocumentInputType::value("Luma Calculation", TaggedValue::LuminanceCalculation(LuminanceCalculation::SRGB), false),
identifier: NodeImplementation::proto("graphene_std::quantization::GenerateQuantizationNode", &[concrete!("Image")]), ],
inputs: &[ outputs: vec![DocumentOutputType::new("Image", FrontendGraphDataType::Raster)],
DocumentInputType { properties: node_properties::luminance_properties,
},
DocumentNodeType {
name: "Gaussian Blur",
category: "Image Filters",
identifier: NodeImplementation::DocumentNode(NodeNetwork {
inputs: vec![0, 1, 1],
outputs: vec![NodeOutput::new(1, 0)],
nodes: vec![
(
0,
DocumentNode {
name: "CacheNode".to_string(),
inputs: vec![NodeInput::Network(concrete!(Image))],
implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_std::memo::CacheNode")),
metadata: Default::default(),
},
),
(
1,
DocumentNode {
name: "BlurNode".to_string(),
inputs: vec![NodeInput::node(0, 0), NodeInput::Network(concrete!(u32)), NodeInput::Network(concrete!(f64)), NodeInput::node(0, 0)],
implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::raster::BlurNode")),
metadata: Default::default(),
},
),
]
.into_iter()
.collect(),
..Default::default()
}),
inputs: vec![
DocumentInputType::value("Image", TaggedValue::Image(Image::empty()), true),
DocumentInputType::value("Radius", TaggedValue::U32(3), false),
DocumentInputType::value("Sigma", TaggedValue::F64(1.), false),
],
outputs: vec![DocumentOutputType {
name: "Image", name: "Image",
data_type: FrontendGraphDataType::Raster, data_type: FrontendGraphDataType::Raster,
default: NodeInput::value(TaggedValue::Image(Image::empty()), true), }],
}, properties: node_properties::blur_image_properties,
DocumentInputType { },
name: "samples", #[cfg(feature = "gpu")]
data_type: FrontendGraphDataType::Number, DocumentNodeType {
default: NodeInput::value(TaggedValue::U32(100), false), name: "GpuImage",
}, category: "Image Adjustments",
DocumentInputType { identifier: NodeImplementation::proto("graphene_std::executor::MapGpuSingleImageNode"),
name: "Fn index", inputs: vec![
data_type: FrontendGraphDataType::Number, DocumentInputType::new("Image", TaggedValue::Image(Image::empty()), true),
default: NodeInput::value(TaggedValue::U32(0), false), DocumentInputType {
}, name: "Path",
], data_type: FrontendGraphDataType::Text,
outputs: &[DocumentOutputType::new("Image", FrontendGraphDataType::Raster)], default: NodeInput::value(TaggedValue::String(String::new()), false),
properties: node_properties::quantize_properties, },
}, ],
DocumentNodeType { outputs: vec![DocumentOutputType::new("Image", FrontendGraphDataType::Raster)],
name: "Cache", properties: node_properties::gpu_map_properties,
category: "Structural", },
identifier: NodeImplementation::proto("graphene_std::memo::CacheNode", &[concrete!("Image")]), #[cfg(feature = "quantization")]
inputs: &[DocumentInputType::new("Image", TaggedValue::Image(Image::empty()), true)], DocumentNodeType {
outputs: &[DocumentOutputType::new("Image", FrontendGraphDataType::Raster)], name: "QuantizeImage",
properties: node_properties::no_properties, category: "Image Adjustments",
}, identifier: NodeImplementation::proto("graphene_std::quantization::GenerateQuantizationNode"),
DocumentNodeType { inputs: vec![
name: "Invert RGB", DocumentInputType {
category: "Image Adjustments", name: "Image",
identifier: NodeImplementation::proto("graphene_core::raster::InvertRGBNode", &[concrete!("Image")]), data_type: FrontendGraphDataType::Raster,
inputs: &[DocumentInputType::new("Image", TaggedValue::Image(Image::empty()), true)], default: NodeInput::value(TaggedValue::Image(Image::empty()), true),
outputs: &[DocumentOutputType::new("Image", FrontendGraphDataType::Raster)], },
properties: node_properties::no_properties, DocumentInputType {
}, name: "samples",
DocumentNodeType { data_type: FrontendGraphDataType::Number,
name: "Hue/Saturation", default: NodeInput::value(TaggedValue::U32(100), false),
category: "Image Adjustments", },
identifier: NodeImplementation::proto( DocumentInputType {
"graphene_core::raster::HueSaturationNode<_, _, _>", name: "Fn index",
&[concrete!("Image"), concrete!("f64"), concrete!("f64"), concrete!("f64")], data_type: FrontendGraphDataType::Number,
), default: NodeInput::value(TaggedValue::U32(0), false),
inputs: &[ },
DocumentInputType::new("Image", TaggedValue::Image(Image::empty()), true), ],
DocumentInputType::new("Hue Shift", TaggedValue::F64(0.), false), outputs: vec![DocumentOutputType::new("Image", FrontendGraphDataType::Raster)],
DocumentInputType::new("Saturation Shift", TaggedValue::F64(0.), false), properties: node_properties::quantize_properties,
DocumentInputType::new("Lightness Shift", TaggedValue::F64(0.), false), },
], DocumentNodeType {
outputs: &[DocumentOutputType::new("Image", FrontendGraphDataType::Raster)], name: "Cache",
properties: node_properties::adjust_hsl_properties, category: "Structural",
}, identifier: NodeImplementation::proto("graphene_std::memo::CacheNode"),
DocumentNodeType { inputs: vec![DocumentInputType::value("Image", TaggedValue::Image(Image::empty()), true)],
name: "Brightness/Contrast", outputs: vec![DocumentOutputType::new("Image", FrontendGraphDataType::Raster)],
category: "Image Adjustments", properties: node_properties::no_properties,
identifier: NodeImplementation::proto("graphene_core::raster::BrightnessContrastNode<_, _>", &[concrete!("Image"), concrete!("f64"), concrete!("f64")]), },
inputs: &[ DocumentNodeType {
DocumentInputType::new("Image", TaggedValue::Image(Image::empty()), true), name: "Invert RGB",
DocumentInputType::new("Brightness", TaggedValue::F64(0.), false), category: "Image Adjustments",
DocumentInputType::new("Contrast", TaggedValue::F64(0.), false), identifier: NodeImplementation::proto("graphene_core::raster::InvertRGBNode"),
], inputs: vec![DocumentInputType::value("Image", TaggedValue::Image(Image::empty()), true)],
outputs: &[DocumentOutputType::new("Image", FrontendGraphDataType::Raster)], outputs: vec![DocumentOutputType::new("Image", FrontendGraphDataType::Raster)],
properties: node_properties::brighten_image_properties, properties: node_properties::no_properties,
}, },
DocumentNodeType { DocumentNodeType {
name: "Threshold", name: "Hue/Saturation",
category: "Image Adjustments", category: "Image Adjustments",
identifier: NodeImplementation::proto("graphene_core::raster::ThresholdNode<_, _>", &[concrete!("Image"), concrete!("LuminanceCalculation"), concrete!("f64")]), identifier: NodeImplementation::proto("graphene_core::raster::HueSaturationNode<_, _, _>"),
inputs: &[ inputs: vec![
DocumentInputType::new("Image", TaggedValue::Image(Image::empty()), true), DocumentInputType::value("Image", TaggedValue::Image(Image::empty()), true),
DocumentInputType::new("Luma Calculation", TaggedValue::LuminanceCalculation(LuminanceCalculation::SRGB), false), DocumentInputType::value("Hue Shift", TaggedValue::F64(0.), false),
DocumentInputType::new("Threshold", TaggedValue::F64(50.), false), DocumentInputType::value("Saturation Shift", TaggedValue::F64(0.), false),
], DocumentInputType::value("Lightness Shift", TaggedValue::F64(0.), false),
outputs: &[DocumentOutputType::new("Image", FrontendGraphDataType::Raster)], ],
properties: node_properties::adjust_threshold_properties, outputs: vec![DocumentOutputType::new("Image", FrontendGraphDataType::Raster)],
}, properties: node_properties::adjust_hsl_properties,
DocumentNodeType { },
name: "Vibrance", DocumentNodeType {
category: "Image Adjustments", name: "Brightness/Contrast",
identifier: NodeImplementation::proto("graphene_core::raster::VibranceNode<_>", &[concrete!("Image"), concrete!("f64")]), category: "Image Adjustments",
inputs: &[ identifier: NodeImplementation::proto("graphene_core::raster::BrightnessContrastNode<_, _>"),
DocumentInputType::new("Image", TaggedValue::Image(Image::empty()), true), inputs: vec![
DocumentInputType::new("Vibrance", TaggedValue::F64(0.), false), DocumentInputType::value("Image", TaggedValue::Image(Image::empty()), true),
], DocumentInputType::value("Brightness", TaggedValue::F64(0.), false),
outputs: &[DocumentOutputType::new("Image", FrontendGraphDataType::Raster)], DocumentInputType::value("Contrast", TaggedValue::F64(0.), false),
properties: node_properties::adjust_vibrance_properties, ],
}, outputs: vec![DocumentOutputType::new("Image", FrontendGraphDataType::Raster)],
DocumentNodeType { properties: node_properties::brighten_image_properties,
name: "Opacity", },
category: "Image Adjustments", DocumentNodeType {
identifier: NodeImplementation::proto("graphene_core::raster::OpacityNode<_>", &[concrete!("Image"), concrete!("f64")]), name: "Threshold",
inputs: &[ category: "Image Adjustments",
DocumentInputType::new("Image", TaggedValue::Image(Image::empty()), true), identifier: NodeImplementation::proto("graphene_core::raster::ThresholdNode<_, _>"),
DocumentInputType::new("Factor", TaggedValue::F64(100.), false), inputs: vec![
], DocumentInputType::value("Image", TaggedValue::Image(Image::empty()), true),
outputs: &[DocumentOutputType::new("Image", FrontendGraphDataType::Raster)], DocumentInputType::value("Luma Calculation", TaggedValue::LuminanceCalculation(LuminanceCalculation::SRGB), false),
properties: node_properties::multiply_opacity, DocumentInputType::value("Threshold", TaggedValue::F64(50.), false),
}, ],
DocumentNodeType { outputs: vec![DocumentOutputType::new("Image", FrontendGraphDataType::Raster)],
name: "Posterize", properties: node_properties::adjust_threshold_properties,
category: "Image Adjustments", },
identifier: NodeImplementation::proto("graphene_core::raster::PosterizeNode<_>", &[concrete!("Image"), concrete!("f64")]), DocumentNodeType {
inputs: &[ name: "Vibrance",
DocumentInputType::new("Image", TaggedValue::Image(Image::empty()), true), category: "Image Adjustments",
DocumentInputType::new("Value", TaggedValue::F64(4.), false), identifier: NodeImplementation::proto("graphene_core::raster::VibranceNode<_>"),
], inputs: vec![
outputs: &[DocumentOutputType::new("Image", FrontendGraphDataType::Raster)], DocumentInputType::value("Image", TaggedValue::Image(Image::empty()), true),
properties: node_properties::posterize_properties, DocumentInputType::value("Vibrance", TaggedValue::F64(0.), false),
}, ],
DocumentNodeType { outputs: vec![DocumentOutputType::new("Image", FrontendGraphDataType::Raster)],
name: "Exposure", properties: node_properties::adjust_vibrance_properties,
category: "Image Adjustments", },
identifier: NodeImplementation::proto( DocumentNodeType {
"graphene_core::raster::ExposureNode<_, _, _>", name: "Opacity",
&[concrete!("Image"), concrete!("f64"), concrete!("f64"), concrete!("f64")], category: "Image Adjustments",
), identifier: NodeImplementation::proto("graphene_core::raster::OpacityNode<_>"),
inputs: &[ inputs: vec![
DocumentInputType::new("Image", TaggedValue::Image(Image::empty()), true), DocumentInputType::value("Image", TaggedValue::Image(Image::empty()), true),
DocumentInputType::new("Exposure", TaggedValue::F64(0.), false), DocumentInputType::value("Factor", TaggedValue::F64(100.), false),
DocumentInputType::new("Offset", TaggedValue::F64(0.), false), ],
DocumentInputType::new("Gamma Correction", TaggedValue::F64(1.), false), outputs: vec![DocumentOutputType::new("Image", FrontendGraphDataType::Raster)],
], properties: node_properties::multiply_opacity,
outputs: &[DocumentOutputType::new("Image", FrontendGraphDataType::Raster)], },
properties: node_properties::exposure_properties, DocumentNodeType {
}, name: "Posterize",
IMAGINATE_NODE, category: "Image Adjustments",
DocumentNodeType { identifier: NodeImplementation::proto("graphene_core::raster::PosterizeNode<_>"),
name: "Add", inputs: vec![
category: "Math", DocumentInputType::value("Image", TaggedValue::Image(Image::empty()), true),
identifier: NodeImplementation::proto("graphene_core::ops::AddParameterNode<_>", &[concrete!("f64"), concrete!("f64")]), DocumentInputType::value("Value", TaggedValue::F64(4.), false),
inputs: &[ ],
DocumentInputType::new("Input", TaggedValue::F64(0.), true), outputs: vec![DocumentOutputType::new("Image", FrontendGraphDataType::Raster)],
DocumentInputType::new("Addend", TaggedValue::F64(0.), true), properties: node_properties::posterize_properties,
], },
outputs: &[DocumentOutputType::new("Output", FrontendGraphDataType::Number)], DocumentNodeType {
properties: node_properties::add_properties, name: "Exposure",
}, category: "Image Adjustments",
/*DocumentNodeType { identifier: NodeImplementation::proto("graphene_core::raster::ExposureNode<_, _, _>"),
name: "Unit Circle Generator", inputs: vec![
category: "Vector", DocumentInputType::value("Image", TaggedValue::Image(Image::empty()), true),
identifier: NodeImplementation::proto("graphene_std::vector::generator_nodes::UnitCircleGenerator", &[]), DocumentInputType::value("Exposure", TaggedValue::F64(0.), false),
inputs: &[DocumentInputType::none()], DocumentInputType::value("Offset", TaggedValue::F64(0.), false),
outputs: &[DocumentOutputType::new("Vector", FrontendGraphDataType::Subpath)], DocumentInputType::value("Gamma Correction", TaggedValue::F64(1.), false),
properties: node_properties::no_properties, ],
}, outputs: vec![DocumentOutputType::new("Image", FrontendGraphDataType::Raster)],
DocumentNodeType { properties: node_properties::exposure_properties,
name: "Unit Square Generator", },
category: "Vector", DocumentNodeType {
identifier: NodeImplementation::proto("graphene_std::vector::generator_nodes::UnitSquareGenerator", &[]), name: "Add",
inputs: &[DocumentInputType::none()], category: "Math",
outputs: &[DocumentOutputType::new("Vector", FrontendGraphDataType::Subpath)], identifier: NodeImplementation::proto("graphene_core::ops::AddParameterNode<_>"),
properties: node_properties::no_properties, inputs: vec![
}, DocumentInputType::value("Input", TaggedValue::F64(0.), true),
DocumentNodeType { DocumentInputType::value("Addend", TaggedValue::F64(0.), true),
name: "Path Generator", ],
category: "Vector", outputs: vec![DocumentOutputType::new("Output", FrontendGraphDataType::Number)],
identifier: NodeImplementation::proto("graphene_core::ops::IdNode", &[generic!("T")]), properties: node_properties::add_properties,
inputs: &[DocumentInputType { },
name: "Path Data", (*IMAGINATE_NODE).clone(),
data_type: FrontendGraphDataType::Subpath, /*DocumentNodeType {
default: NodeInput::value(TaggedValue::Subpath(Subpath::new()), false), name: "Unit Circle Generator",
}], category: "Vector",
outputs: &[DocumentOutputType::new("Vector", FrontendGraphDataType::Subpath)], identifier: NodeImplementation::proto("graphene_std::vector::generator_nodes::UnitCircleGenerator", &[]),
properties: node_properties::no_properties, inputs: vec![DocumentInputType::none()],
}, outputs: vec![DocumentOutputType::new("Vector", FrontendGraphDataType::Subpath)],
DocumentNodeType { properties: node_properties::no_properties,
name: "Transform Subpath", },
category: "Vector", DocumentNodeType {
identifier: NodeImplementation::proto("graphene_std::vector::generator_nodes::TransformSubpathNode", &[]), name: "Unit Square Generator",
inputs: &[ category: "Vector",
DocumentInputType::new("Subpath", TaggedValue::Subpath(Subpath::empty()), true), identifier: NodeImplementation::proto("graphene_std::vector::generator_nodes::UnitSquareGenerator", &[]),
DocumentInputType::new("Translation", TaggedValue::DVec2(DVec2::ZERO), false), inputs: vec![DocumentInputType::none()],
DocumentInputType::new("Rotation", TaggedValue::F64(0.), false), outputs: vec![DocumentOutputType::new("Vector", FrontendGraphDataType::Subpath)],
DocumentInputType::new("Scale", TaggedValue::DVec2(DVec2::ONE), false), properties: node_properties::no_properties,
DocumentInputType::new("Skew", TaggedValue::DVec2(DVec2::ZERO), false), },
], DocumentNodeType {
outputs: &[DocumentOutputType::new("Vector", FrontendGraphDataType::Subpath)], name: "Path Generator",
properties: node_properties::transform_properties, category: "Vector",
}, identifier: NodeImplementation::proto("graphene_core::ops::IdNode"),
DocumentNodeType { inputs: vec![DocumentInputType {
name: "Blit Subpath", name: "Path Data",
category: "Vector", data_type: FrontendGraphDataType::Subpath,
identifier: NodeImplementation::proto("graphene_std::vector::generator_nodes::BlitSubpath", &[]), default: NodeInput::value(TaggedValue::Subpath(Subpath::new()), false),
inputs: &[ }],
DocumentInputType::new("Image", TaggedValue::Image(Image::empty()), true), outputs: vec![DocumentOutputType::new("Vector", FrontendGraphDataType::Subpath)],
DocumentInputType::new("Subpath", TaggedValue::Subpath(Subpath::empty()), true), properties: node_properties::no_properties,
], },
outputs: &[DocumentOutputType::new("Vector", FrontendGraphDataType::Raster)], DocumentNodeType {
properties: node_properties::no_properties, name: "Transform Subpath",
},*/ category: "Vector",
]; identifier: NodeImplementation::proto("graphene_std::vector::generator_nodes::TransformSubpathNode", &[]),
inputs: vec![
DocumentInputType::new("Subpath", TaggedValue::Subpath(Subpath::empty()), true),
DocumentInputType::new("Translation", TaggedValue::DVec2(DVec2::ZERO), false),
DocumentInputType::new("Rotation", TaggedValue::F64(0.), false),
DocumentInputType::new("Scale", TaggedValue::DVec2(DVec2::ONE), false),
DocumentInputType::new("Skew", TaggedValue::DVec2(DVec2::ZERO), false),
],
outputs: vec![DocumentOutputType::new("Vector", FrontendGraphDataType::Subpath)],
properties: node_properties::transform_properties,
},
DocumentNodeType {
name: "Blit Subpath",
category: "Vector",
identifier: NodeImplementation::proto("graphene_std::vector::generator_nodes::BlitSubpath", &[]),
inputs: vec![
DocumentInputType::new("Image", TaggedValue::Image(Image::empty()), true),
DocumentInputType::new("Subpath", TaggedValue::Subpath(Subpath::empty()), true),
],
outputs: vec![DocumentOutputType::new("Vector", FrontendGraphDataType::Raster)],
properties: node_properties::no_properties,
},*/
]
}
pub const IMAGINATE_NODE: DocumentNodeType = DocumentNodeType { pub static IMAGINATE_NODE: Lazy<DocumentNodeType> = Lazy::new(|| DocumentNodeType {
name: "Imaginate", name: "Imaginate",
category: "Image Synthesis", category: "Image Synthesis",
identifier: NodeImplementation::proto("graphene_std::raster::ImaginateNode<_>", &[concrete!("Image"), concrete!("Option<std::sync::Arc<Image>>")]), identifier: NodeImplementation::proto("graphene_std::raster::ImaginateNode<_>"),
inputs: &[ inputs: vec![
DocumentInputType::new("Input Image", TaggedValue::Image(Image::empty()), true), DocumentInputType::value("Input Image", TaggedValue::Image(Image::empty()), true),
DocumentInputType::new("Transform", TaggedValue::DAffine2(DAffine2::IDENTITY), true), DocumentInputType::value("Transform", TaggedValue::DAffine2(DAffine2::IDENTITY), true),
DocumentInputType::new("Seed", TaggedValue::F64(0.), false), // Remember to keep index used in `NodeGraphFrameImaginateRandom` updated with this entry's index DocumentInputType::value("Seed", TaggedValue::F64(0.), false), // Remember to keep index used in `NodeGraphFrameImaginateRandom` updated with this entry's index
DocumentInputType::new("Resolution", TaggedValue::OptionalDVec2(None), false), DocumentInputType::value("Resolution", TaggedValue::OptionalDVec2(None), false),
DocumentInputType::new("Samples", TaggedValue::F64(30.), false), DocumentInputType::value("Samples", TaggedValue::F64(30.), false),
DocumentInputType::new("Sampling Method", TaggedValue::ImaginateSamplingMethod(ImaginateSamplingMethod::EulerA), false), DocumentInputType::value("Sampling Method", TaggedValue::ImaginateSamplingMethod(ImaginateSamplingMethod::EulerA), false),
DocumentInputType::new("Prompt Guidance", TaggedValue::F64(7.5), false), DocumentInputType::value("Prompt Guidance", TaggedValue::F64(7.5), false),
DocumentInputType::new("Prompt", TaggedValue::String(String::new()), false), DocumentInputType::value("Prompt", TaggedValue::String(String::new()), false),
DocumentInputType::new("Negative Prompt", TaggedValue::String(String::new()), false), DocumentInputType::value("Negative Prompt", TaggedValue::String(String::new()), false),
DocumentInputType::new("Adapt Input Image", TaggedValue::Bool(false), false), DocumentInputType::value("Adapt Input Image", TaggedValue::Bool(false), false),
DocumentInputType::new("Image Creativity", TaggedValue::F64(66.), false), DocumentInputType::value("Image Creativity", TaggedValue::F64(66.), false),
DocumentInputType::new("Masking Layer", TaggedValue::LayerPath(None), false), DocumentInputType::value("Masking Layer", TaggedValue::LayerPath(None), false),
DocumentInputType::new("Inpaint", TaggedValue::Bool(true), false), DocumentInputType::value("Inpaint", TaggedValue::Bool(true), false),
DocumentInputType::new("Mask Blur", TaggedValue::F64(4.), false), DocumentInputType::value("Mask Blur", TaggedValue::F64(4.), false),
DocumentInputType::new("Mask Starting Fill", TaggedValue::ImaginateMaskStartingFill(ImaginateMaskStartingFill::Fill), false), DocumentInputType::value("Mask Starting Fill", TaggedValue::ImaginateMaskStartingFill(ImaginateMaskStartingFill::Fill), false),
DocumentInputType::new("Improve Faces", TaggedValue::Bool(false), false), DocumentInputType::value("Improve Faces", TaggedValue::Bool(false), false),
DocumentInputType::new("Tiling", TaggedValue::Bool(false), false), DocumentInputType::value("Tiling", TaggedValue::Bool(false), false),
// Non-user status (is document input the right way to do this?) // Non-user status (is document input the right way to do this?)
DocumentInputType::new("Cached Data", TaggedValue::RcImage(None), false), DocumentInputType::value("Cached Data", TaggedValue::RcImage(None), false),
DocumentInputType::new("Percent Complete", TaggedValue::F64(0.), false), DocumentInputType::value("Percent Complete", TaggedValue::F64(0.), false),
DocumentInputType::new("Status", TaggedValue::ImaginateStatus(ImaginateStatus::Idle), false), DocumentInputType::value("Status", TaggedValue::ImaginateStatus(ImaginateStatus::Idle), false),
], ],
outputs: &[DocumentOutputType::new("Image", FrontendGraphDataType::Raster)], outputs: vec![DocumentOutputType::new("Image", FrontendGraphDataType::Raster)],
properties: node_properties::imaginate_properties, properties: node_properties::imaginate_properties,
}; });
pub fn resolve_document_node_type(name: &str) -> Option<&DocumentNodeType> { pub fn resolve_document_node_type(name: &str) -> Option<&DocumentNodeType> {
DOCUMENT_NODE_TYPES.iter().find(|node| node.name == name) DOCUMENT_NODE_TYPES.iter().find(|node| node.name == name)
@ -580,7 +560,7 @@ impl DocumentNodeType {
name: format!("{}_impl", self.name), name: format!("{}_impl", self.name),
// TODO: Allow inserting nodes that contain other nodes. // TODO: Allow inserting nodes that contain other nodes.
implementation: DocumentNodeImplementation::Unresolved(ident.clone()), implementation: DocumentNodeImplementation::Unresolved(ident.clone()),
inputs: (0..num_inputs).map(|_| NodeInput::Network).collect(), inputs: self.inputs.iter().map(|i| NodeInput::Network(i.default.ty())).collect(),
metadata: DocumentNodeMetadata::default(), metadata: DocumentNodeMetadata::default(),
}, },
)] )]
@ -610,7 +590,7 @@ pub fn new_image_network(output_offset: i32, output_node_id: NodeId) -> NodeNetw
outputs: vec![NodeOutput::new(1, 0)], outputs: vec![NodeOutput::new(1, 0)],
nodes: [ nodes: [
resolve_document_node_type("Input").expect("Input node does not exist").to_document_node( resolve_document_node_type("Input").expect("Input node does not exist").to_document_node(
[NodeInput::Network, NodeInput::value(TaggedValue::DAffine2(DAffine2::IDENTITY), false)], [NodeInput::Network(concrete!(Image)), NodeInput::value(TaggedValue::DAffine2(DAffine2::IDENTITY), false)],
DocumentNodeMetadata::position((8, 4)), DocumentNodeMetadata::position((8, 4)),
), ),
resolve_document_node_type("Output") resolve_document_node_type("Output")

View file

@ -231,6 +231,7 @@ impl MessageHandler<PortfolioMessage, (&InputPreprocessorMessageHandler, &Prefer
percent, percent,
status, status,
} => { } => {
debug!("ImaginateSetGeneratingStatus: {:?} {:?} {:?} {:?}", document_id, layer_path, node_path, percent);
let get = |name: &str| IMAGINATE_NODE.inputs.iter().position(|input| input.name == name).unwrap_or_else(|| panic!("Input {name} not found")); let get = |name: &str| IMAGINATE_NODE.inputs.iter().position(|input| input.name == name).unwrap_or_else(|| panic!("Input {name} not found"));
if let Some(percentage) = percent { if let Some(percentage) = percent {
responses.push_back( responses.push_back(

View file

@ -120,7 +120,7 @@ impl Fsm for ImaginateToolFsmState {
use graph_craft::document::*; use graph_craft::document::*;
let imaginate_node_type = IMAGINATE_NODE; let imaginate_node_type = &*IMAGINATE_NODE;
let mut imaginate_inputs: Vec<NodeInput> = imaginate_node_type.inputs.iter().map(|input| input.default.clone()).collect(); let mut imaginate_inputs: Vec<NodeInput> = imaginate_node_type.inputs.iter().map(|input| input.default.clone()).collect();
imaginate_inputs[0] = NodeInput::node(0, 0); imaginate_inputs[0] = NodeInput::node(0, 0);

View file

@ -39,7 +39,10 @@ impl NodeGraphExecutor {
let proto_network = c.compile_single(network, true)?; let proto_network = c.compile_single(network, true)?;
assert_ne!(proto_network.nodes.len(), 0, "No protonodes exist?"); assert_ne!(proto_network.nodes.len(), 0, "No protonodes exist?");
self.executor.update(proto_network); if let Err(e) = self.executor.update(proto_network) {
error!("Failed to update executor:\n{}", e);
return Err(e);
}
use dyn_any::IntoDynAny; use dyn_any::IntoDynAny;
use graph_craft::executor::Executor; use graph_craft::executor::Executor;
@ -65,7 +68,7 @@ impl NodeGraphExecutor {
} }
match &inner_network.nodes.get(&node_path[end]).unwrap().inputs[input_index] { match &inner_network.nodes.get(&node_path[end]).unwrap().inputs[input_index] {
// If the input is from a parent network then adjust the input index and continue iteration // If the input is from a parent network then adjust the input index and continue iteration
NodeInput::Network => { NodeInput::Network(_) => {
input_index = inner_network input_index = inner_network
.inputs .inputs
.iter() .iter()

View file

@ -137,13 +137,13 @@ where
} }
macro_rules! impl_type { macro_rules! impl_type {
($($id:ident$(<$($(($l:lifetime, $s:lifetime)),*|)?$($T:ident),*>)?),*) => { ($($id:ident$(<$($(($l:lifetime, $s:lifetime)),*|)?$($T:ident),*>)?),*) => {
$( $(
impl< $($($T: $crate::StaticTypeSized ,)*)?> $crate::StaticType for $id $(<$($($l,)*)?$($T, )*>)?{ impl< $($($T: $crate::StaticTypeSized ,)*)?> $crate::StaticType for $id $(<$($($l,)*)?$($T, )*>)?{
type Static = $id$(<$($($s,)*)?$(<$T as $crate::StaticTypeSized>::Static,)*>)?; type Static = $id$(<$($($s,)*)?$(<$T as $crate::StaticTypeSized>::Static,)*>)?;
} }
)* )*
}; };
} }
#[cfg(feature = "alloc")] #[cfg(feature = "alloc")]
@ -206,12 +206,13 @@ use core::{
marker::{PhantomData, PhantomPinned}, marker::{PhantomData, PhantomPinned},
mem::{ManuallyDrop, MaybeUninit}, mem::{ManuallyDrop, MaybeUninit},
num::Wrapping, num::Wrapping,
ops::Range,
time::Duration, time::Duration,
}; };
impl_type!( impl_type!(
Option<T>, Result<T, E>, Cell<T>, UnsafeCell<T>, RefCell<T>, MaybeUninit<T>, Option<T>, Result<T, E>, Cell<T>, UnsafeCell<T>, RefCell<T>, MaybeUninit<T>,
ManuallyDrop<T>, PhantomData<T>, PhantomPinned, Empty<T>, ManuallyDrop<T>, PhantomData<T>, PhantomPinned, Empty<T>, Range<T>,
Wrapping<T>, Duration, bool, f32, f64, char, Wrapping<T>, Duration, bool, f32, f64, char,
u8, AtomicU8, u16, AtomicU16, u32, AtomicU32, u64, usize, AtomicUsize, u8, AtomicU8, u16, AtomicU16, u32, AtomicU32, u64, usize, AtomicUsize,
i8, AtomicI8, i16, AtomicI16, i32, AtomicI32, i64, isize, AtomicIsize, i8, AtomicI8, i16, AtomicI16, i32, AtomicI32, i64, isize, AtomicIsize,
@ -265,19 +266,19 @@ fn test_tuple_of_boxes() {
} }
macro_rules! impl_tuple { macro_rules! impl_tuple {
(@rec $t:ident) => { }; (@rec $t:ident) => { };
(@rec $_:ident $($t:ident)+) => { (@rec $_:ident $($t:ident)+) => {
impl_tuple! { @impl $($t)* } impl_tuple! { @impl $($t)* }
impl_tuple! { @rec $($t)* } impl_tuple! { @rec $($t)* }
}; };
(@impl $($t:ident)*) => { (@impl $($t:ident)*) => {
impl< $($t: StaticTypeSized,)*> StaticType for ($($t,)*) { impl< $($t: StaticTypeSized,)*> StaticType for ($($t,)*) {
type Static = ($(<$t as $crate::StaticTypeSized>::Static,)*); type Static = ($(<$t as $crate::StaticTypeSized>::Static,)*);
} }
}; };
($($t:ident)*) => { ($($t:ident)*) => {
impl_tuple! { @rec _t $($t)* } impl_tuple! { @rec _t $($t)* }
}; };
} }
impl_tuple! { impl_tuple! {

View file

@ -1,8 +1,10 @@
use gpu_compiler_bin_wrapper::CompileRequest; use gpu_compiler_bin_wrapper::CompileRequest;
use graph_craft::concrete;
use graph_craft::document::value::TaggedValue; use graph_craft::document::value::TaggedValue;
use graph_craft::document::*; use graph_craft::document::*;
use graph_craft::proto::*;
use graph_craft::{concrete, generic}; use graph_craft::*;
use std::borrow::Cow;
fn main() { fn main() {
let client = reqwest::blocking::Client::new(); let client = reqwest::blocking::Client::new();
@ -17,7 +19,7 @@ fn main() {
DocumentNode { DocumentNode {
name: "Inc Node".into(), name: "Inc Node".into(),
inputs: vec![ inputs: vec![
NodeInput::Network, NodeInput::Network(concrete!(u32)),
NodeInput::Value { NodeInput::Value {
tagged_value: TaggedValue::U32(1), tagged_value: TaggedValue::U32(1),
exposed: false, exposed: false,
@ -47,9 +49,9 @@ fn add_network() -> NodeNetwork {
0, 0,
DocumentNode { DocumentNode {
name: "Cons".into(), name: "Cons".into(),
inputs: vec![NodeInput::Network, NodeInput::Network], inputs: vec![NodeInput::Network(concrete!(u32)), NodeInput::Network(concrete!(u32))],
metadata: DocumentNodeMetadata::default(), metadata: DocumentNodeMetadata::default(),
implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::structural::ConsNode", &[generic!("T"), concrete!("u32")])), implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::structural::ConsNode")),
}, },
), ),
( (
@ -58,7 +60,7 @@ fn add_network() -> NodeNetwork {
name: "Add".into(), name: "Add".into(),
inputs: vec![NodeInput::node(0, 0)], inputs: vec![NodeInput::node(0, 0)],
metadata: DocumentNodeMetadata::default(), metadata: DocumentNodeMetadata::default(),
implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::ops::AddNode", &[generic!("T"), generic!("U")])), implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::ops::AddNode")),
}, },
), ),
] ]

View file

@ -9,7 +9,7 @@ license = "MIT OR Apache-2.0"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[features] [features]
std = ["dyn-any", "dyn-any/std"] std = ["dyn-any", "dyn-any/std", "alloc"]
default = ["async", "serde", "kurbo", "log", "std"] default = ["async", "serde", "kurbo", "log", "std"]
log = ["dep:log"] log = ["dep:log"]
serde = ["dep:serde", "glam/serde"] serde = ["dep:serde", "glam/serde"]
@ -17,9 +17,10 @@ gpu = ["spirv-std", "bytemuck", "glam/bytemuck", "dyn-any"]
async = ["async-trait", "alloc"] async = ["async-trait", "alloc"]
nightly = [] nightly = []
alloc = ["dyn-any", "bezier-rs"] alloc = ["dyn-any", "bezier-rs"]
type_id_logging = []
[dependencies] [dependencies]
dyn-any = {path = "../../libraries/dyn-any", features = ["derive"], optional = true, default-features = false } dyn-any = {path = "../../libraries/dyn-any", features = ["derive", "glam"], optional = true, default-features = false }
spirv-std = { git = "https://github.com/EmbarkStudios/rust-gpu", features = ["glam"] , optional = true} spirv-std = { git = "https://github.com/EmbarkStudios/rust-gpu", features = ["glam"] , optional = true}
bytemuck = {version = "1.8", features = ["derive"], optional = true} bytemuck = {version = "1.8", features = ["derive"], optional = true}
@ -34,4 +35,5 @@ kurbo = { git = "https://github.com/linebender/kurbo.git", features = [
glam = { version = "^0.22", default-features = false, features = ["scalar-math", "libm"]} glam = { version = "^0.22", default-features = false, features = ["scalar-math", "libm"]}
node-macro = {path = "../node-macro"} node-macro = {path = "../node-macro"}
specta.workspace = true specta.workspace = true
once_cell = { version = "1.17.0", default-features = false }
# forma = { version = "0.1.0", package = "forma-render" } # forma = { version = "0.1.0", package = "forma-render" }

View file

@ -21,12 +21,52 @@ pub mod raster;
#[cfg(feature = "alloc")] #[cfg(feature = "alloc")]
pub mod vector; pub mod vector;
use core::any::TypeId;
// pub trait Node: for<'n> NodeIO<'n> { // pub trait Node: for<'n> NodeIO<'n> {
pub trait Node<'i, Input: 'i>: 'i { pub trait Node<'i, Input: 'i>: 'i {
type Output: 'i; type Output: 'i;
fn eval<'s: 'i>(&'s self, input: Input) -> Self::Output; fn eval<'s: 'i>(&'s self, input: Input) -> Self::Output;
} }
#[cfg(feature = "alloc")]
mod types;
pub use types::*;
pub trait NodeIO<'i, Input: 'i>: 'i + Node<'i, Input>
where
Self::Output: 'i + StaticType,
Input: 'i + StaticType,
{
fn input_type(&self) -> TypeId {
TypeId::of::<Input::Static>()
}
fn input_type_name(&self) -> &'static str {
core::any::type_name::<Input>()
}
fn output_type(&self) -> core::any::TypeId {
TypeId::of::<<Self::Output as StaticType>::Static>()
}
fn output_type_name(&self) -> &'static str {
core::any::type_name::<Self::Output>()
}
#[cfg(feature = "alloc")]
fn to_node_io(&self, parameters: Vec<Type>) -> NodeIOTypes {
NodeIOTypes {
input: concrete!(<Input as StaticType>::Static),
output: concrete!(<Self::Output as StaticType>::Static),
parameters,
}
}
}
impl<'i, N: Node<'i, I>, I> NodeIO<'i, I> for N
where
N::Output: 'i + StaticType,
I: 'i + StaticType,
{
}
/*impl<'i, I: 'i, O: 'i> Node<'i, I> for &'i dyn for<'n> Node<'n, I, Output = O> { /*impl<'i, I: 'i, O: 'i> Node<'i, I> for &'i dyn for<'n> Node<'n, I, Output = O> {
type Output = O; type Output = O;
@ -42,6 +82,8 @@ impl<'i, 'n: 'i, I: 'i, O: 'i> Node<'i, I> for &'n dyn for<'a> Node<'a, I, Outpu
} }
} }
use core::pin::Pin; use core::pin::Pin;
use dyn_any::StaticType;
#[cfg(feature = "alloc")] #[cfg(feature = "alloc")]
impl<'i, I: 'i, O: 'i> Node<'i, I> for Pin<Box<dyn for<'a> Node<'a, I, Output = O> + 'i>> { impl<'i, I: 'i, O: 'i> Node<'i, I> for Pin<Box<dyn for<'a> Node<'a, I, Output = O> + 'i>> {
type Output = O; type Output = O;

View file

@ -0,0 +1,108 @@
use core::any::TypeId;
#[cfg(not(feature = "std"))]
pub use alloc::borrow::Cow;
#[cfg(feature = "std")]
pub use std::borrow::Cow;
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct NodeIOTypes {
pub input: Type,
pub output: Type,
pub parameters: Vec<Type>,
}
impl NodeIOTypes {
pub fn new(input: Type, output: Type, parameters: Vec<Type>) -> Self {
Self { input, output, parameters }
}
}
#[macro_export]
macro_rules! concrete {
($type:ty) => {
Type::Concrete(TypeDescriptor {
id: Some(core::any::TypeId::of::<$type>()),
name: Cow::Borrowed(core::any::type_name::<$type>()),
})
};
}
#[macro_export]
macro_rules! generic {
($type:ty) => {{
Type::Generic(Cow::Borrowed(stringify!($type)))
}};
}
#[derive(Clone, Debug, PartialEq, Eq, Hash, specta::Type)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct NodeIdentifier {
pub name: Cow<'static, str>,
}
#[derive(Clone, Debug, Eq, specta::Type)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct TypeDescriptor {
#[cfg_attr(feature = "serde", serde(skip))]
#[specta(skip)]
pub id: Option<TypeId>,
pub name: Cow<'static, str>,
}
impl core::hash::Hash for TypeDescriptor {
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
self.id.hash(state);
}
}
impl PartialEq for TypeDescriptor {
fn eq(&self, other: &Self) -> bool {
match (self.id, other.id) {
(Some(id), Some(other_id)) => id == other_id,
_ => {
warn!("TypeDescriptor::eq: comparing types without ids based on name");
self.name == other.name
}
}
}
}
#[derive(Clone, PartialEq, Eq, Hash, specta::Type)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum Type {
Generic(Cow<'static, str>),
Concrete(TypeDescriptor),
}
impl core::fmt::Debug for Type {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
Self::Generic(arg0) => f.write_fmt(format_args!("Generic({})", arg0)),
#[cfg(feature = "type_id_logging")]
Self::Concrete(arg0) => f.write_fmt(format_args!("Concrete({}, {:?}))", arg0.name, arg0.id)),
#[cfg(not(feature = "type_id_logging"))]
Self::Concrete(arg0) => f.write_fmt(format_args!("Concrete({})", arg0.name)),
}
}
}
impl std::fmt::Display for Type {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Type::Generic(name) => write!(f, "{}", name),
Type::Concrete(ty) => write!(f, "{}", ty.name),
}
}
}
impl From<&'static str> for NodeIdentifier {
fn from(s: &'static str) -> Self {
NodeIdentifier { name: Cow::Borrowed(s) }
}
}
impl NodeIdentifier {
pub const fn new(name: &'static str) -> Self {
NodeIdentifier { name: Cow::Borrowed(name) }
}
}

View file

@ -14,19 +14,19 @@ pub fn compile_spirv(network: &graph_craft::document::NodeNetwork, input_type: &
.arg("--release") .arg("--release")
.arg("--manifest-path") .arg("--manifest-path")
.arg(manifest_path) .arg(manifest_path)
.current_dir(manifest_path.replace("Cargo.toml", "")) .current_dir(manifest_path.replace("Cargo.toml", ""))
.env_clear() .env_clear()
.envs(non_cargo_env_vars) .envs(non_cargo_env_vars)
.arg("--features") .arg("--features")
.arg(features) .arg(features)
.arg("--") .arg("--")
.arg(input_type) .arg(input_type)
.arg(output_type) .arg(output_type)
// TODO: handle None case properly // TODO: handle None case properly
.arg(compile_dir.unwrap()) .arg(compile_dir.unwrap())
.stdin(std::process::Stdio::piped()) .stdin(std::process::Stdio::piped())
.stdout(std::process::Stdio::piped()) .stdout(std::process::Stdio::piped())
.spawn()?; .spawn()?;
cargo_command.stdin.as_mut().unwrap().write_all(serialized_graph.as_bytes())?; cargo_command.stdin.as_mut().unwrap().write_all(serialized_graph.as_bytes())?;
let output = cargo_command.wait_with_output()?; let output = cargo_command.wait_with_output()?;

View file

@ -88,16 +88,16 @@ pub fn serialize_gpu(network: &ProtoNetwork, input_type: &str, output_type: &str
use spirv_builder::{MetadataPrintout, SpirvBuilder, SpirvMetadata}; use spirv_builder::{MetadataPrintout, SpirvBuilder, SpirvMetadata};
pub fn compile(dir: &Path) -> Result<spirv_builder::CompileResult, spirv_builder::SpirvBuilderError> { pub fn compile(dir: &Path) -> Result<spirv_builder::CompileResult, spirv_builder::SpirvBuilderError> {
dbg!(&dir); dbg!(&dir);
let result = SpirvBuilder::new(dir, "spirv-unknown-spv1.5") let result = SpirvBuilder::new(dir, "spirv-unknown-spv1.5")
.print_metadata(MetadataPrintout::DependencyOnly) .print_metadata(MetadataPrintout::DependencyOnly)
.multimodule(false) .multimodule(false)
.preserve_bindings(true) .preserve_bindings(true)
.release(true) .release(true)
//.relax_struct_store(true) //.relax_struct_store(true)
//.relax_block_layout(true) //.relax_block_layout(true)
.spirv_metadata(SpirvMetadata::Full) .spirv_metadata(SpirvMetadata::Full)
.build()?; .build()?;
Ok(result) Ok(result)
} }

View file

@ -1,18 +1,18 @@
use graph_craft::document::NodeNetwork;
use gpu_compiler as compiler; use gpu_compiler as compiler;
use graph_craft::document::NodeNetwork;
use std::io::Write; use std::io::Write;
fn main() -> anyhow::Result<()> { fn main() -> anyhow::Result<()> {
println!("Starting Gpu Compiler!"); println!("Starting GPU Compiler!");
let mut stdin = std::io::stdin(); let mut stdin = std::io::stdin();
let mut stdout = std::io::stdout(); let mut stdout = std::io::stdout();
let input_type = std::env::args().nth(1).expect("input type arg missing"); let input_type = std::env::args().nth(1).expect("input type arg missing");
let output_type = std::env::args().nth(2).expect("output type arg missing"); let output_type = std::env::args().nth(2).expect("output type arg missing");
let compile_dir = std::env::args().nth(3).map(|x| std::path::PathBuf::from(&x)).unwrap_or(tempfile::tempdir()?.into_path()); let compile_dir = std::env::args().nth(3).map(|x| std::path::PathBuf::from(&x)).unwrap_or(tempfile::tempdir()?.into_path());
let network: NodeNetwork = serde_json::from_reader(&mut stdin)?; let network: NodeNetwork = serde_json::from_reader(&mut stdin)?;
let compiler = graph_craft::executor::Compiler{}; let compiler = graph_craft::executor::Compiler {};
let proto_network = compiler.compile(network, true); let proto_network = compiler.compile(network, true);
dbg!(&compile_dir); dbg!(&compile_dir);
let metadata = compiler::Metadata::new("project".to_owned(), vec!["test@example.com".to_owned()]); let metadata = compiler::Metadata::new("project".to_owned(), vec!["test@example.com".to_owned()]);

View file

@ -7,32 +7,32 @@ extern crate spirv_std;
#[cfg(target_arch = "spirv")] #[cfg(target_arch = "spirv")]
pub mod gpu { pub mod gpu {
use super::*; use super::*;
use spirv_std::spirv; use spirv_std::spirv;
use spirv_std::glam::UVec3; use spirv_std::glam::UVec3;
#[allow(unused)] #[allow(unused)]
#[spirv(compute(threads({{compute_threads}})))] #[spirv(compute(threads({{compute_threads}})))]
pub fn eval ( pub fn eval (
#[spirv(global_invocation_id)] global_id: UVec3, #[spirv(global_invocation_id)] global_id: UVec3,
#[spirv(storage_buffer, descriptor_set = 0, binding = 0)] a: &[{{input_type}}], #[spirv(storage_buffer, descriptor_set = 0, binding = 0)] a: &[{{input_type}}],
#[spirv(storage_buffer, descriptor_set = 0, binding = 1)] y: &mut [{{output_type}}], #[spirv(storage_buffer, descriptor_set = 0, binding = 1)] y: &mut [{{output_type}}],
#[spirv(push_constant)] push_consts: &graphene_core::gpu::PushConstants, #[spirv(push_constant)] push_consts: &graphene_core::gpu::PushConstants,
) { ) {
let gid = global_id.x as usize; let gid = global_id.x as usize;
// Only process up to n, which is the length of the buffers. // Only process up to n, which is the length of the buffers.
if global_id.x < push_consts.n { if global_id.x < push_consts.n {
y[gid] = node_graph(a[gid]); y[gid] = node_graph(a[gid]);
} }
} }
fn node_graph(input: {{input_type}}) -> {{output_type}} { fn node_graph(input: {{input_type}}) -> {{output_type}} {
use graphene_core::Node; use graphene_core::Node;
{% for node in nodes %} {% for node in nodes %}
let {{node.id}} = {{node.fqn}}::new({% for arg in node.args %}{{arg}}, {% endfor %}); let {{node.id}} = {{node.fqn}}::new({% for arg in node.args %}{{arg}}, {% endfor %});
{% endfor %} {% endfor %}
{{last_node}}.eval(input) {{last_node}}.eval(input)
} }
} }

View file

@ -11,7 +11,7 @@ serde = ["dep:serde", "graphene-core/serde", "glam/serde"]
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
graphene-core = { path = "../gcore", features = ["alloc"] } graphene-core = { path = "../gcore", features = ["std"] }
dyn-any = { path = "../../libraries/dyn-any", features = ["log-bad-types", "rc", "glam"] } dyn-any = { path = "../../libraries/dyn-any", features = ["log-bad-types", "rc", "glam"] }
num-traits = "0.2" num-traits = "0.2"
dyn-clone = "1.0" dyn-clone = "1.0"

View file

@ -1,13 +1,15 @@
use crate::document::value::TaggedValue; use crate::document::value::TaggedValue;
use crate::generic; use crate::proto::{ConstructionArgs, ProtoNetwork, ProtoNode, ProtoNodeInput};
use crate::proto::{ConstructionArgs, NodeIdentifier, ProtoNetwork, ProtoNode, ProtoNodeInput, Type}; use graphene_core::{NodeIdentifier, Type};
use dyn_any::{DynAny, StaticType}; use dyn_any::{DynAny, StaticType};
use glam::IVec2; use glam::IVec2;
use graphene_core::TypeDescriptor;
use rand_chacha::{ use rand_chacha::{
rand_core::{RngCore, SeedableRng}, rand_core::{RngCore, SeedableRng},
ChaCha20Rng, ChaCha20Rng,
}; };
use std::borrow::Cow;
use std::collections::{HashMap, HashSet}; use std::collections::{HashMap, HashSet};
use std::sync::Mutex; use std::sync::Mutex;
@ -59,7 +61,7 @@ impl DocumentNode {
.inputs .inputs
.iter() .iter()
.enumerate() .enumerate()
.filter(|(_, input)| matches!(input, NodeInput::Network)) .filter(|(_, input)| matches!(input, NodeInput::Network(_)))
.nth(offset) .nth(offset)
.expect("no network input"); .expect("no network input");
@ -80,9 +82,9 @@ impl DocumentNode {
assert_eq!(output_index, 0, "Outputs should be flattened before converting to protonode."); assert_eq!(output_index, 0, "Outputs should be flattened before converting to protonode.");
(ProtoNodeInput::Node(node_id), ConstructionArgs::Nodes(vec![])) (ProtoNodeInput::Node(node_id), ConstructionArgs::Nodes(vec![]))
} }
NodeInput::Network => (ProtoNodeInput::Network, ConstructionArgs::Nodes(vec![])), NodeInput::Network(ty) => (ProtoNodeInput::Network(ty), ConstructionArgs::Nodes(vec![])),
}; };
assert!(!self.inputs.iter().any(|input| matches!(input, NodeInput::Network)), "recieved non resolved parameter"); assert!(!self.inputs.iter().any(|input| matches!(input, NodeInput::Network(_))), "recieved non resolved parameter");
assert!( assert!(
!self.inputs.iter().any(|input| matches!(input, NodeInput::Value { .. })), !self.inputs.iter().any(|input| matches!(input, NodeInput::Value { .. })),
"recieved value as parameter. inupts: {:#?}, construction_args: {:#?}", "recieved value as parameter. inupts: {:#?}, construction_args: {:#?}",
@ -129,12 +131,12 @@ impl DocumentNode {
} }
} }
#[derive(Clone, Debug, specta::Type)] #[derive(Debug, Clone, PartialEq, Hash, specta::Type)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum NodeInput { pub enum NodeInput {
Node { node_id: NodeId, output_index: usize }, Node { node_id: NodeId, output_index: usize },
Value { tagged_value: value::TaggedValue, exposed: bool }, Value { tagged_value: value::TaggedValue, exposed: bool },
Network, Network(Type),
} }
impl NodeInput { impl NodeInput {
@ -153,17 +155,14 @@ impl NodeInput {
match self { match self {
NodeInput::Node { .. } => true, NodeInput::Node { .. } => true,
NodeInput::Value { exposed, .. } => *exposed, NodeInput::Value { exposed, .. } => *exposed,
NodeInput::Network => false, NodeInput::Network(_) => false,
} }
} }
} pub fn ty(&self) -> Type {
match self {
impl PartialEq for NodeInput { NodeInput::Node { .. } => unreachable!("ty() called on NodeInput::Node"),
fn eq(&self, other: &Self) -> bool { NodeInput::Value { tagged_value, .. } => tagged_value.ty(),
match (&self, &other) { NodeInput::Network(ty) => ty.clone(),
(Self::Node { node_id: n0, output_index: o0 }, Self::Node { node_id: n1, output_index: o1 }) => n0 == n1 && o0 == o1,
(Self::Value { tagged_value: v1, .. }, Self::Value { tagged_value: v2, .. }) => v1 == v2,
_ => core::mem::discriminant(self) == core::mem::discriminant(other),
} }
} }
} }
@ -357,7 +356,7 @@ impl NodeNetwork {
.unwrap_or_else(|| panic!("The node which was supposed to be flattened does not exist in the network, id {} network {:#?}", node, self)); .unwrap_or_else(|| panic!("The node which was supposed to be flattened does not exist in the network, id {} network {:#?}", node, self));
if self.disabled.contains(&id) { if self.disabled.contains(&id) {
node.implementation = DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::ops::IdNode", &[generic!("T")])); node.implementation = DocumentNodeImplementation::Unresolved("graphene_core::ops::IdNode".into());
node.inputs.drain(1..); node.inputs.drain(1..);
self.nodes.insert(id, node); self.nodes.insert(id, node);
return; return;
@ -394,7 +393,7 @@ impl NodeNetwork {
let value_node = DocumentNode { let value_node = DocumentNode {
name, name,
inputs: vec![NodeInput::Value { tagged_value, exposed }], inputs: vec![NodeInput::Value { tagged_value, exposed }],
implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::value::ValueNode", &[generic!("T")])), implementation: DocumentNodeImplementation::Unresolved("graphene_core::value::ValueNode".into()),
metadata: DocumentNodeMetadata::default(), metadata: DocumentNodeMetadata::default(),
}; };
assert!(!self.nodes.contains_key(&new_id)); assert!(!self.nodes.contains_key(&new_id));
@ -402,7 +401,7 @@ impl NodeNetwork {
let network_input = self.nodes.get_mut(network_input).unwrap(); let network_input = self.nodes.get_mut(network_input).unwrap();
network_input.populate_first_network_input(new_id, 0, *offset); network_input.populate_first_network_input(new_id, 0, *offset);
} }
NodeInput::Network => { NodeInput::Network(_) => {
*network_offsets.get_mut(network_input).unwrap() += 1; *network_offsets.get_mut(network_input).unwrap() += 1;
if let Some(index) = self.inputs.iter().position(|i| *i == id) { if let Some(index) = self.inputs.iter().position(|i| *i == id) {
self.inputs[index] = *network_input; self.inputs[index] = *network_input;
@ -410,7 +409,7 @@ impl NodeNetwork {
} }
} }
} }
node.implementation = DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::ops::IdNode", &[generic!("T")])); node.implementation = DocumentNodeImplementation::Unresolved("graphene_core::ops::IdNode".into());
node.inputs = inner_network node.inputs = inner_network
.outputs .outputs
.iter() .iter()
@ -419,6 +418,7 @@ impl NodeNetwork {
output_index: node_output_index, output_index: node_output_index,
}) })
.collect(); .collect();
for node_id in new_nodes { for node_id in new_nodes {
self.flatten_with_fns(node_id, map_ids, gen_id); self.flatten_with_fns(node_id, map_ids, gen_id);
} }
@ -456,8 +456,8 @@ impl NodeNetwork {
0, 0,
DocumentNode { DocumentNode {
name: "Input".into(), name: "Input".into(),
inputs: vec![NodeInput::Network], inputs: vec![NodeInput::Network(concrete!(u32))],
implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::ops::IdNode", &[generic!("T")])), implementation: DocumentNodeImplementation::Unresolved("graphene_core::ops::IdNode".into()),
metadata: DocumentNodeMetadata { position: (8, 4).into() }, metadata: DocumentNodeMetadata { position: (8, 4).into() },
}, },
), ),
@ -466,7 +466,7 @@ impl NodeNetwork {
DocumentNode { DocumentNode {
name: "Output".into(), name: "Output".into(),
inputs: vec![NodeInput::node(output_node_id, 0)], inputs: vec![NodeInput::node(output_node_id, 0)],
implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::ops::IdNode", &[generic!("T")])), implementation: DocumentNodeImplementation::Unresolved("graphene_core::ops::IdNode".into()),
metadata: DocumentNodeMetadata { position: (output_offset, 4).into() }, metadata: DocumentNodeMetadata { position: (output_offset, 4).into() },
}, },
), ),
@ -553,7 +553,8 @@ impl NodeNetwork {
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use super::*; use super::*;
use crate::proto::{ConstructionArgs, NodeIdentifier, ProtoNetwork, ProtoNode, ProtoNodeInput}; use crate::proto::{ConstructionArgs, ProtoNetwork, ProtoNode, ProtoNodeInput};
use graphene_core::NodeIdentifier;
fn gen_node_id() -> NodeId { fn gen_node_id() -> NodeId {
static mut NODE_ID: NodeId = 3; static mut NODE_ID: NodeId = 3;
@ -572,9 +573,9 @@ mod test {
0, 0,
DocumentNode { DocumentNode {
name: "Cons".into(), name: "Cons".into(),
inputs: vec![NodeInput::Network, NodeInput::Network], inputs: vec![NodeInput::Network(concrete!(u32)), NodeInput::Network(concrete!(u32))],
metadata: DocumentNodeMetadata::default(), metadata: DocumentNodeMetadata::default(),
implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::structural::ConsNode", &[generic!("T"), generic!("U")])), implementation: DocumentNodeImplementation::Unresolved("graphene_core::structural::ConsNode".into()),
}, },
), ),
( (
@ -583,7 +584,7 @@ mod test {
name: "Add".into(), name: "Add".into(),
inputs: vec![NodeInput::node(0, 0)], inputs: vec![NodeInput::node(0, 0)],
metadata: DocumentNodeMetadata::default(), metadata: DocumentNodeMetadata::default(),
implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::ops::AddNode", &[generic!("T"), generic!("U")])), implementation: DocumentNodeImplementation::Unresolved("graphene_core::ops::AddNode".into()),
}, },
), ),
] ]
@ -605,9 +606,9 @@ mod test {
1, 1,
DocumentNode { DocumentNode {
name: "Cons".into(), name: "Cons".into(),
inputs: vec![NodeInput::Network, NodeInput::Network], inputs: vec![NodeInput::Network(concrete!(u32)), NodeInput::Network(concrete!(u32))],
metadata: DocumentNodeMetadata::default(), metadata: DocumentNodeMetadata::default(),
implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::structural::ConsNode", &[generic!("T"), generic!("U")])), implementation: DocumentNodeImplementation::Unresolved("graphene_core::structural::ConsNode".into()),
}, },
), ),
( (
@ -616,7 +617,7 @@ mod test {
name: "Add".into(), name: "Add".into(),
inputs: vec![NodeInput::node(1, 0)], inputs: vec![NodeInput::node(1, 0)],
metadata: DocumentNodeMetadata::default(), metadata: DocumentNodeMetadata::default(),
implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::ops::AddNode", &[generic!("T"), generic!("U")])), implementation: DocumentNodeImplementation::Unresolved("graphene_core::ops::AddNode".into()),
}, },
), ),
] ]
@ -637,7 +638,7 @@ mod test {
DocumentNode { DocumentNode {
name: "Inc".into(), name: "Inc".into(),
inputs: vec![ inputs: vec![
NodeInput::Network, NodeInput::Network(concrete!(u32)),
NodeInput::Value { NodeInput::Value {
tagged_value: value::TaggedValue::U32(2), tagged_value: value::TaggedValue::U32(2),
exposed: false, exposed: false,
@ -663,15 +664,15 @@ mod test {
fn resolve_proto_node_add() { fn resolve_proto_node_add() {
let document_node = DocumentNode { let document_node = DocumentNode {
name: "Cons".into(), name: "Cons".into(),
inputs: vec![NodeInput::Network, NodeInput::node(0, 0)], inputs: vec![NodeInput::Network(concrete!(u32)), NodeInput::node(0, 0)],
metadata: DocumentNodeMetadata::default(), metadata: DocumentNodeMetadata::default(),
implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::structural::ConsNode", &[generic!("T"), generic!("U")])), implementation: DocumentNodeImplementation::Unresolved("graphene_core::structural::ConsNode".into()),
}; };
let proto_node = document_node.resolve_proto_node(); let proto_node = document_node.resolve_proto_node();
let reference = ProtoNode { let reference = ProtoNode {
identifier: NodeIdentifier::new("graphene_core::structural::ConsNode", &[generic!("T"), generic!("U")]), identifier: "graphene_core::structural::ConsNode".into(),
input: ProtoNodeInput::Network, input: ProtoNodeInput::Network(concrete!(u32)),
construction_args: ConstructionArgs::Nodes(vec![0]), construction_args: ConstructionArgs::Nodes(vec![0]),
}; };
assert_eq!(proto_node, reference); assert_eq!(proto_node, reference);
@ -686,7 +687,7 @@ mod test {
( (
1, 1,
ProtoNode { ProtoNode {
identifier: NodeIdentifier::new("graphene_core::ops::IdNode", &[generic!("T")]), identifier: "graphene_core::ops::IdNode".into(),
input: ProtoNodeInput::Node(11), input: ProtoNodeInput::Node(11),
construction_args: ConstructionArgs::Nodes(vec![]), construction_args: ConstructionArgs::Nodes(vec![]),
}, },
@ -694,15 +695,15 @@ mod test {
( (
10, 10,
ProtoNode { ProtoNode {
identifier: NodeIdentifier::new("graphene_core::structural::ConsNode", &[generic!("T"), generic!("U")]), identifier: "graphene_core::structural::ConsNode".into(),
input: ProtoNodeInput::Network, input: ProtoNodeInput::Network(concrete!(u32)),
construction_args: ConstructionArgs::Nodes(vec![14]), construction_args: ConstructionArgs::Nodes(vec![14]),
}, },
), ),
( (
11, 11,
ProtoNode { ProtoNode {
identifier: NodeIdentifier::new("graphene_core::ops::AddNode", &[generic!("T"), generic!("U")]), identifier: "graphene_core::ops::AddNode".into(),
input: ProtoNodeInput::Node(10), input: ProtoNodeInput::Node(10),
construction_args: ConstructionArgs::Nodes(vec![]), construction_args: ConstructionArgs::Nodes(vec![]),
}, },
@ -731,16 +732,16 @@ mod test {
name: "Inc".into(), name: "Inc".into(),
inputs: vec![NodeInput::node(11, 0)], inputs: vec![NodeInput::node(11, 0)],
metadata: DocumentNodeMetadata::default(), metadata: DocumentNodeMetadata::default(),
implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::ops::IdNode", &[generic!("T")])), implementation: DocumentNodeImplementation::Unresolved("graphene_core::ops::IdNode".into()),
}, },
), ),
( (
10, 10,
DocumentNode { DocumentNode {
name: "Cons".into(), name: "Cons".into(),
inputs: vec![NodeInput::Network, NodeInput::node(14, 0)], inputs: vec![NodeInput::Network(concrete!(u32)), NodeInput::node(14, 0)],
metadata: DocumentNodeMetadata::default(), metadata: DocumentNodeMetadata::default(),
implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::structural::ConsNode", &[generic!("T"), generic!("U")])), implementation: DocumentNodeImplementation::Unresolved("graphene_core::structural::ConsNode".into()),
}, },
), ),
( (
@ -752,7 +753,7 @@ mod test {
exposed: false, exposed: false,
}], }],
metadata: DocumentNodeMetadata::default(), metadata: DocumentNodeMetadata::default(),
implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::value::ValueNode", &[generic!("T")])), implementation: DocumentNodeImplementation::Unresolved("graphene_core::value::ValueNode".into()),
}, },
), ),
( (
@ -761,7 +762,7 @@ mod test {
name: "Add".into(), name: "Add".into(),
inputs: vec![NodeInput::node(10, 0)], inputs: vec![NodeInput::node(10, 0)],
metadata: DocumentNodeMetadata::default(), metadata: DocumentNodeMetadata::default(),
implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::ops::AddNode", &[generic!("T"), generic!("U")])), implementation: DocumentNodeImplementation::Unresolved("graphene_core::ops::AddNode".into()),
}, },
), ),
] ]
@ -780,18 +781,18 @@ mod test {
1, 1,
DocumentNode { DocumentNode {
name: "Identity 1".into(), name: "Identity 1".into(),
inputs: vec![NodeInput::Network], inputs: vec![NodeInput::Network(concrete!(u32))],
metadata: DocumentNodeMetadata::default(), metadata: DocumentNodeMetadata::default(),
implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::ops::IdNode", &[generic!("T")])), implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::ops::IdNode")),
}, },
), ),
( (
2, 2,
DocumentNode { DocumentNode {
name: "Identity 2".into(), name: "Identity 2".into(),
inputs: vec![NodeInput::Network], inputs: vec![NodeInput::Network(concrete!(u32))],
metadata: DocumentNodeMetadata::default(), metadata: DocumentNodeMetadata::default(),
implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::ops::IdNode", &[generic!("T")])), implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::ops::IdNode")),
}, },
), ),
] ]
@ -821,7 +822,7 @@ mod test {
name: "Result".into(), name: "Result".into(),
inputs: vec![result_node_input], inputs: vec![result_node_input],
metadata: DocumentNodeMetadata::default(), metadata: DocumentNodeMetadata::default(),
implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::ops::IdNode", &[generic!("T")])), implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::ops::IdNode")),
}, },
), ),
] ]

View file

@ -3,7 +3,7 @@ use dyn_any::{DynAny, Upcast};
use dyn_clone::DynClone; use dyn_clone::DynClone;
pub use glam::{DAffine2, DVec2}; pub use glam::{DAffine2, DVec2};
use graphene_core::raster::LuminanceCalculation; use graphene_core::raster::LuminanceCalculation;
use graphene_core::Node; use graphene_core::{Node, Type};
use std::hash::Hash; use std::hash::Hash;
pub use std::sync::Arc; pub use std::sync::Arc;
@ -142,6 +142,32 @@ impl<'a> TaggedValue {
TaggedValue::LayerPath(x) => Box::new(x), TaggedValue::LayerPath(x) => Box::new(x),
} }
} }
pub fn ty(&self) -> Type {
use graphene_core::TypeDescriptor;
use std::borrow::Cow;
match self {
TaggedValue::None => concrete!(()),
TaggedValue::String(_) => concrete!(String),
TaggedValue::U32(_) => concrete!(u32),
TaggedValue::F32(_) => concrete!(f32),
TaggedValue::F64(_) => concrete!(f64),
TaggedValue::Bool(_) => concrete!(bool),
TaggedValue::DVec2(_) => concrete!(DVec2),
TaggedValue::OptionalDVec2(_) => concrete!(Option<DVec2>),
TaggedValue::Image(_) => concrete!(graphene_core::raster::Image),
TaggedValue::RcImage(_) => concrete!(Option<Arc<graphene_core::raster::Image>>),
TaggedValue::Color(_) => concrete!(graphene_core::raster::Color),
TaggedValue::Subpath(_) => concrete!(graphene_core::vector::subpath::Subpath),
TaggedValue::RcSubpath(_) => concrete!(Arc<graphene_core::vector::subpath::Subpath>),
TaggedValue::ImaginateSamplingMethod(_) => concrete!(ImaginateSamplingMethod),
TaggedValue::ImaginateMaskStartingFill(_) => concrete!(ImaginateMaskStartingFill),
TaggedValue::ImaginateStatus(_) => concrete!(ImaginateStatus),
TaggedValue::LayerPath(_) => concrete!(Option<Vec<u64>>),
TaggedValue::DAffine2(_) => concrete!(DAffine2),
TaggedValue::LuminanceCalculation(_) => concrete!(LuminanceCalculation),
}
}
} }
pub struct UpcastNode { pub struct UpcastNode {

View file

@ -1,6 +1,10 @@
#[macro_use] #[macro_use]
extern crate log; extern crate log;
#[macro_use]
extern crate graphene_core;
pub use graphene_core::{concrete, generic, NodeIdentifier, Type, TypeDescriptor};
pub mod document; pub mod document;
pub mod proto; pub mod proto;

View file

@ -4,87 +4,16 @@ use std::hash::Hash;
use crate::document::value; use crate::document::value;
use crate::document::NodeId; use crate::document::NodeId;
use dyn_any::DynAny;
use graphene_core::*;
use std::pin::Pin;
#[macro_export] pub type Any<'n> = Box<dyn DynAny<'n> + 'n>;
macro_rules! concrete { pub type TypeErasedNode<'n> = dyn for<'i> NodeIO<'i, Any<'i>, Output = Any<'i>> + 'n + Send + Sync;
($type:expr) => { pub type TypeErasedPinnedRef<'n> = Pin<&'n (dyn for<'i> NodeIO<'i, Any<'i>, Output = Any<'i>> + 'n + Send + Sync)>;
Type::Concrete(std::borrow::Cow::Borrowed($type)) pub type TypeErasedPinned<'n> = Pin<Box<dyn for<'i> NodeIO<'i, Any<'i>, Output = Any<'i>> + 'n + Send + Sync>>;
};
}
#[macro_export]
macro_rules! generic {
($type:expr) => {
Type::Generic(std::borrow::Cow::Borrowed($type))
};
}
#[derive(Clone, Debug, PartialEq, specta::Type)] pub type NodeConstructor = for<'a> fn(Vec<TypeErasedPinnedRef<'static>>) -> TypeErasedPinned<'static>;
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct NodeIdentifier {
pub name: std::borrow::Cow<'static, str>,
pub types: std::borrow::Cow<'static, [Type]>,
}
impl NodeIdentifier {
pub fn fully_qualified_name(&self) -> String {
let mut name = String::new();
name.push_str(self.name.as_ref());
name.push('<');
for t in self.types.as_ref() {
name.push_str(t.to_string().as_str());
name.push_str(", ");
}
name.pop();
name.pop();
name.push('>');
name
}
}
#[derive(Clone, Debug, PartialEq, specta::Type)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum Type {
Generic(std::borrow::Cow<'static, str>),
Concrete(std::borrow::Cow<'static, str>),
}
impl From<&'static str> for Type {
fn from(s: &'static str) -> Self {
Type::Concrete(std::borrow::Cow::Borrowed(s))
}
}
impl std::fmt::Display for Type {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Type::Generic(name) => write!(f, "{}", name),
Type::Concrete(name) => write!(f, "{}", name),
}
}
}
impl Type {
pub const fn from_str(concrete: &'static str) -> Self {
Type::Concrete(std::borrow::Cow::Borrowed(concrete))
}
}
impl From<&'static str> for NodeIdentifier {
fn from(s: &'static str) -> Self {
NodeIdentifier {
name: std::borrow::Cow::Borrowed(s),
types: std::borrow::Cow::Borrowed(&[]),
}
}
}
impl NodeIdentifier {
pub const fn new(name: &'static str, types: &'static [Type]) -> Self {
NodeIdentifier {
name: std::borrow::Cow::Borrowed(name),
types: std::borrow::Cow::Borrowed(types),
}
}
}
#[derive(Debug, Default, PartialEq)] #[derive(Debug, Default, PartialEq)]
pub struct ProtoNetwork { pub struct ProtoNetwork {
@ -140,11 +69,10 @@ pub struct ProtoNode {
pub identifier: NodeIdentifier, pub identifier: NodeIdentifier,
} }
#[derive(Debug, Default, PartialEq, Eq, Clone, Copy)] #[derive(Debug, PartialEq, Eq, Clone)]
pub enum ProtoNodeInput { pub enum ProtoNodeInput {
None, None,
#[default] Network(Type),
Network,
Node(NodeId), Node(NodeId),
} }
@ -161,11 +89,14 @@ impl ProtoNode {
pub fn stable_node_id(&self) -> Option<NodeId> { pub fn stable_node_id(&self) -> Option<NodeId> {
use std::hash::Hasher; use std::hash::Hasher;
let mut hasher = std::collections::hash_map::DefaultHasher::new(); let mut hasher = std::collections::hash_map::DefaultHasher::new();
self.identifier.fully_qualified_name().hash(&mut hasher); self.identifier.name.hash(&mut hasher);
self.construction_args.hash(&mut hasher); self.construction_args.hash(&mut hasher);
match self.input { match self.input {
ProtoNodeInput::None => "none".hash(&mut hasher), ProtoNodeInput::None => "none".hash(&mut hasher),
ProtoNodeInput::Network => "network".hash(&mut hasher), ProtoNodeInput::Network(ref ty) => {
"network".hash(&mut hasher);
ty.hash(&mut hasher);
}
ProtoNodeInput::Node(id) => id.hash(&mut hasher), ProtoNodeInput::Node(id) => id.hash(&mut hasher),
}; };
Some(hasher.finish() as NodeId) Some(hasher.finish() as NodeId)
@ -173,7 +104,7 @@ impl ProtoNode {
pub fn value(value: ConstructionArgs) -> Self { pub fn value(value: ConstructionArgs) -> Self {
Self { Self {
identifier: NodeIdentifier::new("graphene_core::value::ValueNode", &[Type::Generic(Cow::Borrowed("T"))]), identifier: NodeIdentifier::new("graphene_core::value::ValueNode"),
construction_args: value, construction_args: value,
input: ProtoNodeInput::None, input: ProtoNodeInput::None,
} }
@ -260,20 +191,22 @@ impl ProtoNetwork {
} }
pub fn resolve_inputs(&mut self) { pub fn resolve_inputs(&mut self) {
while !self.resolve_inputs_impl() {} let mut resolved = HashSet::new();
while !self.resolve_inputs_impl(&mut resolved) {}
} }
fn resolve_inputs_impl(&mut self) -> bool { fn resolve_inputs_impl(&mut self, resolved: &mut HashSet<NodeId>) -> bool {
self.reorder_ids(); self.reorder_ids();
let mut lookup = self.nodes.iter().map(|(id, _)| (*id, *id)).collect::<HashMap<_, _>>(); let mut lookup = self.nodes.iter().map(|(id, _)| (*id, *id)).collect::<HashMap<_, _>>();
let compose_node_id = self.nodes.len() as NodeId; let compose_node_id = self.nodes.len() as NodeId;
let inputs = self.nodes.iter().map(|(_, node)| node.input).collect::<Vec<_>>(); let inputs = self.nodes.iter().map(|(_, node)| node.input.clone()).collect::<Vec<_>>();
if let Some((input_node, id, input)) = self.nodes.iter_mut().find_map(|(id, node)| { let resolved_lookup = resolved.clone();
if let Some((input_node, id, input)) = self.nodes.iter_mut().filter(|(id, _)| !resolved_lookup.contains(id)).find_map(|(id, node)| {
if let ProtoNodeInput::Node(input_node) = node.input { if let ProtoNodeInput::Node(input_node) = node.input {
node.input = ProtoNodeInput::None; resolved.insert(*id);
let pre_node_input = inputs.get(input_node as usize).expect("input node should exist"); let pre_node_input = inputs.get(input_node as usize).expect("input node should exist");
Some((input_node, *id, *pre_node_input)) Some((input_node, *id, pre_node_input.clone()))
} else { } else {
None None
} }
@ -283,7 +216,7 @@ impl ProtoNetwork {
self.nodes.push(( self.nodes.push((
compose_node_id, compose_node_id,
ProtoNode { ProtoNode {
identifier: NodeIdentifier::new("graphene_core::structural::ComposeNode<_, _, _>", &[generic!("T"), generic!("U")]), identifier: NodeIdentifier::new("graphene_core::structural::ComposeNode<_, _, _>"),
construction_args: ConstructionArgs::Nodes(vec![input_node, id]), construction_args: ConstructionArgs::Nodes(vec![input_node, id]),
input, input,
}, },
@ -373,6 +306,172 @@ impl ProtoNetwork {
} }
} }
/// The `TypingContext` is used to store the types of the nodes indexed by their stable node id.
#[derive(Debug, Default, Clone, PartialEq, Eq)]
pub struct TypingContext {
lookup: Cow<'static, HashMap<NodeIdentifier, HashMap<NodeIOTypes, NodeConstructor>>>,
inferred: HashMap<NodeId, NodeIOTypes>,
constructor: HashMap<NodeId, NodeConstructor>,
}
impl TypingContext {
/// Creates a new `TypingContext` with the given lookup table.
pub fn new(lookup: &'static HashMap<NodeIdentifier, HashMap<NodeIOTypes, NodeConstructor>>) -> Self {
Self {
lookup: Cow::Borrowed(lookup),
..Default::default()
}
}
/// Updates the `TypingContext` wtih a given proto network. This will infer the types of the nodes
/// and store them in the `inferred` field. The proto network has to be topologically sorted
/// and contain fully resolved stable node ids.
pub fn update(&mut self, network: &ProtoNetwork) -> Result<(), String> {
for (id, node) in network.nodes.iter() {
self.infer(*id, node)?;
}
Ok(())
}
/// Returns the node constructor for a given node id.
pub fn constructor(&self, node_id: NodeId) -> Option<NodeConstructor> {
self.constructor.get(&node_id).copied()
}
/// Returns the inferred types for a given node id.
pub fn infer(&mut self, node_id: NodeId, node: &ProtoNode) -> Result<NodeIOTypes, String> {
let identifier = node.identifier.name.clone();
// Return the inferred type if it is already known
if let Some(infered) = self.inferred.get(&node_id) {
return Ok(infered.clone());
}
let parameters = match node.construction_args {
// If the node has a value parameter we can infer the return type from it
ConstructionArgs::Value(ref v) => {
assert!(matches!(node.input, ProtoNodeInput::None));
let types = NodeIOTypes::new(concrete!(()), v.ty(), vec![]);
self.inferred.insert(node_id, types.clone());
return Ok(types);
}
// If the node has nodes as parameters we can infer the types from the node outputs
ConstructionArgs::Nodes(ref nodes) => nodes
.iter()
.map(|id| {
self.inferred
.get(id)
.ok_or(format!("Inferring type of {node_id} depends on {id} which is not present in the typing context"))
.map(|node| node.output.clone())
})
.collect::<Result<Vec<Type>, String>>()?,
};
// Get the node input type from the proto node declaration
let input = match node.input {
ProtoNodeInput::None => concrete!(()),
ProtoNodeInput::Network(ref ty) => ty.clone(),
ProtoNodeInput::Node(id) => {
let input = self
.inferred
.get(&id)
.ok_or(format!("Inferring type of {node_id} depends on {id} which is not present in the typing context"))?;
input.output.clone()
}
};
let impls = self.lookup.get(&node.identifier).ok_or(format!("No implementations found for {:?}", node.identifier))?;
if matches!(input, Type::Generic(_)) {
return Err(format!("Generic types are not supported as inputs yet {:?} occured in {:?}", &input, node.identifier));
}
if parameters.iter().any(|p| matches!(p, Type::Generic(_))) {
return Err(format!("Generic types are not supported in parameters: {:?} occured in {:?}", parameters, node.identifier));
}
let covariant = |output, input| match (&output, &input) {
(Type::Concrete(t1), Type::Concrete(t2)) => t1 == t2,
(Type::Concrete(_), Type::Generic(_)) => true,
// TODO: relax this requirement when allowing generic types as inputs
(Type::Generic(_), _) => false,
};
// List of all implementations that match the input and parameter types
let valid_output_types = impls
.keys()
.filter(|node_io| covariant(input.clone(), node_io.input.clone()) && parameters.iter().zip(node_io.parameters.iter()).all(|(p1, p2)| covariant(p1.clone(), p2.clone())))
.collect::<Vec<_>>();
// Attempt to substitute generic types with concrete types and save the list of results
let substitution_results = valid_output_types
.iter()
.map(|node_io| {
collect_generics(node_io)
.iter()
.try_for_each(|generic| check_generic(node_io, &input, &parameters, generic).map(|_| ()))
.map(|_| {
if let Type::Generic(out) = &node_io.output {
((*node_io).clone(), check_generic(node_io, &input, &parameters, out).unwrap())
} else {
((*node_io).clone(), node_io.output.clone())
}
})
})
.collect::<Vec<_>>();
// Collect all substitutions that are valid
let valid_impls = substitution_results.iter().filter_map(|result| result.as_ref().ok()).collect::<Vec<_>>();
match valid_impls.as_slice() {
[] => {
dbg!(&self.inferred);
Err(format!(
"No implementations found for {identifier} with \ninput: {input:?} and \nparameters: {parameters:?}.\nOther Implementations found: {:?}",
impls,
))
}
[(org_nio, output)] => {
let node_io = NodeIOTypes::new(input, (*output).clone(), parameters);
// Save the inferred type
self.inferred.insert(node_id, node_io.clone());
self.constructor.insert(node_id, impls[org_nio]);
Ok(node_io)
}
_ => Err(format!(
"Multiple implementations found for {identifier} with input {input:?} and parameters {parameters:?} (valid types: {valid_output_types:?}"
)),
}
}
}
/// Returns a list of all generic types used in the node
fn collect_generics(types: &NodeIOTypes) -> Vec<Cow<'static, str>> {
let inputs = [&types.input].into_iter().chain(types.parameters.iter());
let mut generics = inputs
.filter_map(|t| match t {
Type::Generic(out) => Some(out.clone()),
_ => None,
})
.collect::<Vec<_>>();
if let Type::Generic(out) = &types.output {
generics.push(out.clone());
}
generics.dedup();
generics
}
/// Checks if a generic type can be substituted with a concrete type and returns the concrete type
fn check_generic(types: &NodeIOTypes, input: &Type, parameters: &[Type], generic: &str) -> Result<Type, String> {
let inputs = [(&types.input, input)].into_iter().chain(types.parameters.iter().zip(parameters.iter()));
let mut concrete_inputs = inputs.filter(|(ni, _)| matches!(ni, Type::Generic(input) if generic == input));
let (_, out_ty) = concrete_inputs
.next()
.ok_or_else(|| format!("Generic output type {generic} is not dependent on input {input:?} or parameters {parameters:?}",))?;
if concrete_inputs.any(|(_, ty)| ty != out_ty) {
return Err(format!("Generic output type {generic} is dependent on multiple inputs or parameters",));
}
Ok(out_ty.clone())
}
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use super::*; use super::*;
@ -436,12 +535,12 @@ mod test {
assert_eq!( assert_eq!(
ids, ids,
vec![ vec![
17495035641492238530, 15907139529964845467,
14931179783740213471, 17186311536944112733,
2268573767208263092, 1674503539363691855,
14616574692620381527, 10408773954839245246,
12110007198416821768, 1677533587730447846,
11185814750012198757 6826908746727711035
] ]
); );
} }
@ -471,7 +570,7 @@ mod test {
10, 10,
ProtoNode { ProtoNode {
identifier: "cons".into(), identifier: "cons".into(),
input: ProtoNodeInput::Network, input: ProtoNodeInput::Network(concrete!(u32)),
construction_args: ConstructionArgs::Nodes(vec![14]), construction_args: ConstructionArgs::Nodes(vec![14]),
}, },
), ),

View file

@ -1,6 +1,8 @@
use dyn_any::{DynAny, StaticType}; use dyn_any::StaticType;
pub use graph_craft::proto::{Any, TypeErasedNode, TypeErasedPinned, TypeErasedPinnedRef};
use graphene_core::NodeIO;
pub use graphene_core::{generic, ops, Node}; pub use graphene_core::{generic, ops, Node};
use std::{marker::PhantomData, pin::Pin}; use std::marker::PhantomData;
pub struct DynAnyNode<I, O, Node> { pub struct DynAnyNode<I, O, Node> {
node: Node, node: Node,
@ -39,17 +41,13 @@ impl<_I, _O, S0> DynAnyRefNode<_I, _O, S0> {
} }
} }
pub type TypeErasedNode<'n> = dyn for<'i> Node<'i, Any<'i>, Output = Any<'i>> + 'n + Send + Sync;
pub type TypeErasedPinnedRef<'n> = Pin<&'n (dyn for<'i> Node<'i, Any<'i>, Output = Any<'i>> + 'n + Send + Sync)>;
pub type TypeErasedPinned<'n> = Pin<Box<dyn for<'i> Node<'i, Any<'i>, Output = Any<'i>> + 'n + Send + Sync>>;
pub trait IntoTypeErasedNode<'n> { pub trait IntoTypeErasedNode<'n> {
fn into_type_erased(self) -> TypeErasedPinned<'n>; fn into_type_erased(self) -> TypeErasedPinned<'n>;
} }
impl<'n, N: 'n> IntoTypeErasedNode<'n> for N impl<'n, N: 'n> IntoTypeErasedNode<'n> for N
where where
N: for<'i> Node<'i, Any<'i>, Output = Any<'i>> + Send + Sync + 'n, N: for<'i> NodeIO<'i, Any<'i>, Output = Any<'i>> + Send + Sync + 'n,
{ {
fn into_type_erased(self) -> TypeErasedPinned<'n> { fn into_type_erased(self) -> TypeErasedPinned<'n> {
Box::pin(self) Box::pin(self)
@ -70,7 +68,7 @@ impl<N: Copy, O: StaticType> Copy for DowncastNode<O, N> {}
#[node_macro::node_fn(DowncastNode<_O>)] #[node_macro::node_fn(DowncastNode<_O>)]
fn downcast<N, _O: StaticType>(input: Any<'input>, node: &'input N) -> _O fn downcast<N, _O: StaticType>(input: Any<'input>, node: &'input N) -> _O
where where
N: Node<'input, Any<'input>, Output = Any<'input>>, N: for<'any_input> Node<'any_input, Any<'any_input>, Output = Any<'any_input>> + 'input,
{ {
let node_name = core::any::type_name::<N>(); let node_name = core::any::type_name::<N>();
let out = dyn_any::downcast(node.eval(input)).unwrap_or_else(|e| panic!("DowncastNode Input {e} in:\n{node_name}")); let out = dyn_any::downcast(node.eval(input)).unwrap_or_else(|e| panic!("DowncastNode Input {e} in:\n{node_name}"));
@ -148,8 +146,6 @@ impl<'a> ComposeTypeErased<'a> {
} }
} }
pub type Any<'n> = Box<dyn DynAny<'n> + 'n>;
pub fn input_node<O: StaticType>(n: TypeErasedPinnedRef) -> DowncastBothNode<(), O> { pub fn input_node<O: StaticType>(n: TypeErasedPinnedRef) -> DowncastBothNode<(), O> {
DowncastBothNode::new(n) DowncastBothNode::new(n)
} }

View file

@ -24,3 +24,4 @@ rand_chacha = "0.3.1"
log = "0.4" log = "0.4"
serde = { version = "1", features = ["derive"], optional = true } serde = { version = "1", features = ["derive"], optional = true }
glam = { version = "0.22" } glam = { version = "0.22" }
once_cell = "1.17.0"

View file

@ -6,28 +6,44 @@ use dyn_any::StaticType;
use graph_craft::document::value::UpcastNode; use graph_craft::document::value::UpcastNode;
use graph_craft::document::NodeId; use graph_craft::document::NodeId;
use graph_craft::executor::Executor; use graph_craft::executor::Executor;
use graph_craft::proto::{ConstructionArgs, ProtoNetwork, ProtoNode, ProtoNodeInput}; use graph_craft::proto::{ConstructionArgs, ProtoNetwork, ProtoNode, TypingContext};
use graphene_std::any::{Any, TypeErasedPinned, TypeErasedPinnedRef}; use graphene_std::any::{Any, TypeErasedPinned, TypeErasedPinnedRef};
use crate::node_registry::constrcut_node; use crate::node_registry;
#[derive(Default, Debug, Clone)] #[derive(Debug, Clone)]
pub struct DynamicExecutor { pub struct DynamicExecutor {
output: NodeId, output: NodeId,
tree: BorrowTree, tree: BorrowTree,
typing_context: TypingContext,
}
impl Default for DynamicExecutor {
fn default() -> Self {
Self {
output: Default::default(),
tree: Default::default(),
typing_context: TypingContext::new(&node_registry::NODE_REGISTRY),
}
}
} }
impl DynamicExecutor { impl DynamicExecutor {
pub fn new(proto_network: ProtoNetwork) -> Self { pub fn new(proto_network: ProtoNetwork) -> Result<Self, String> {
let mut typing_context = TypingContext::new(&node_registry::NODE_REGISTRY);
typing_context.update(&proto_network)?;
let output = proto_network.output; let output = proto_network.output;
let tree = BorrowTree::new(proto_network); let tree = BorrowTree::new(proto_network, &typing_context)?;
Self { tree, output }
Ok(Self { tree, output, typing_context })
} }
pub fn update(&mut self, proto_network: ProtoNetwork) { pub fn update(&mut self, proto_network: ProtoNetwork) -> Result<(), String> {
self.output = proto_network.output; self.output = proto_network.output;
self.typing_context.update(&proto_network)?;
trace!("setting output to {}", self.output); trace!("setting output to {}", self.output);
self.tree.update(proto_network); self.tree.update(proto_network, &self.typing_context)?;
Ok(())
} }
} }
@ -38,7 +54,7 @@ impl Executor for DynamicExecutor {
} }
pub struct NodeContainer<'n> { pub struct NodeContainer<'n> {
node: TypeErasedPinned<'n>, pub node: TypeErasedPinned<'n>,
// the dependencies are only kept to ensure that the nodes are not dropped while still in use // the dependencies are only kept to ensure that the nodes are not dropped while still in use
_dependencies: Vec<Arc<NodeContainer<'static>>>, _dependencies: Vec<Arc<NodeContainer<'static>>>,
} }
@ -50,6 +66,10 @@ impl<'a> core::fmt::Debug for NodeContainer<'a> {
} }
impl<'a> NodeContainer<'a> { impl<'a> NodeContainer<'a> {
pub fn new(node: TypeErasedPinned<'a>, _dependencies: Vec<Arc<NodeContainer<'static>>>) -> Self {
Self { node, _dependencies }
}
/// Return a static reference to the TypeErasedNode /// Return a static reference to the TypeErasedNode
/// # Safety /// # Safety
/// This is unsafe because the returned reference is only valid as long as the NodeContainer is alive /// This is unsafe because the returned reference is only valid as long as the NodeContainer is alive
@ -58,7 +78,7 @@ impl<'a> NodeContainer<'a> {
} }
} }
impl NodeContainer<'static> { impl NodeContainer<'static> {
unsafe fn static_ref(&self) -> TypeErasedPinnedRef<'static> { pub unsafe fn static_ref(&self) -> TypeErasedPinnedRef<'static> {
let s = &*(self as *const Self); let s = &*(self as *const Self);
*(&s.node.as_ref() as *const TypeErasedPinnedRef<'static>) *(&s.node.as_ref() as *const TypeErasedPinnedRef<'static>)
} }
@ -70,24 +90,24 @@ pub struct BorrowTree {
} }
impl BorrowTree { impl BorrowTree {
pub fn new(proto_network: ProtoNetwork) -> Self { pub fn new(proto_network: ProtoNetwork, typing_context: &TypingContext) -> Result<Self, String> {
let mut nodes = BorrowTree::default(); let mut nodes = BorrowTree::default();
for (id, node) in proto_network.nodes { for (id, node) in proto_network.nodes {
nodes.push_node(id, node) nodes.push_node(id, node, typing_context)?
} }
nodes Ok(nodes)
} }
/// Pushes new nodes into the tree and return orphaned nodes /// Pushes new nodes into the tree and return orphaned nodes
pub fn update(&mut self, proto_network: ProtoNetwork) -> Vec<NodeId> { pub fn update(&mut self, proto_network: ProtoNetwork, typing_context: &TypingContext) -> Result<Vec<NodeId>, String> {
let mut old_nodes: HashSet<_> = self.nodes.keys().copied().collect(); let mut old_nodes: HashSet<_> = self.nodes.keys().copied().collect();
for (id, node) in proto_network.nodes { for (id, node) in proto_network.nodes {
if !self.nodes.contains_key(&id) { if !self.nodes.contains_key(&id) {
self.push_node(id, node); self.push_node(id, node, typing_context)?;
old_nodes.remove(&id); old_nodes.remove(&id);
} }
} }
old_nodes.into_iter().collect() Ok(old_nodes.into_iter().collect())
} }
fn node_refs(&self, nodes: &[NodeId]) -> Vec<TypeErasedPinnedRef<'static>> { fn node_refs(&self, nodes: &[NodeId]) -> Vec<TypeErasedPinnedRef<'static>> {
@ -120,16 +140,8 @@ impl BorrowTree {
self.nodes.remove(&id); self.nodes.remove(&id);
} }
pub fn push_node(&mut self, id: NodeId, proto_node: ProtoNode) { pub fn push_node(&mut self, id: NodeId, proto_node: ProtoNode, typing_context: &TypingContext) -> Result<(), String> {
let ProtoNode { input, construction_args, identifier } = proto_node; let ProtoNode { construction_args, identifier, .. } = proto_node;
assert!(
!matches!(&input, &ProtoNodeInput::Node(_)),
"Only nodes without inputs are supported. Any inputs should already be resolved by placing ComposeNodes {:?}, {:?}, {:?}",
identifier,
construction_args,
input,
);
match construction_args { match construction_args {
ConstructionArgs::Value(value) => { ConstructionArgs::Value(value) => {
@ -141,7 +153,8 @@ impl BorrowTree {
} }
ConstructionArgs::Nodes(ids) => { ConstructionArgs::Nodes(ids) => {
let construction_nodes = self.node_refs(&ids); let construction_nodes = self.node_refs(&ids);
let node = constrcut_node(identifier, construction_nodes); let constructor = typing_context.constructor(id).ok_or(format!("No constructor found for node {:?}", identifier))?;
let node = constructor(construction_nodes);
let node = NodeContainer { let node = NodeContainer {
node, node,
_dependencies: self.node_deps(&ids), _dependencies: self.node_deps(&ids),
@ -149,7 +162,8 @@ impl BorrowTree {
let node = unsafe { node.erase_lifetime() }; let node = unsafe { node.erase_lifetime() };
self.store_node(Arc::new(node), id); self.store_node(Arc::new(node), id);
} }
} };
Ok(())
} }
} }
@ -163,7 +177,7 @@ mod test {
fn push_node() { fn push_node() {
let mut tree = BorrowTree::default(); let mut tree = BorrowTree::default();
let val_1_protonode = ProtoNode::value(ConstructionArgs::Value(TaggedValue::U32(2u32))); let val_1_protonode = ProtoNode::value(ConstructionArgs::Value(TaggedValue::U32(2u32)));
tree.push_node(0, val_1_protonode); tree.push_node(0, val_1_protonode, &TypingContext::default()).unwrap();
let _node = tree.get(0).unwrap(); let _node = tree.get(0).unwrap();
assert_eq!(tree.eval(0, ()), Some(2u32)); assert_eq!(tree.eval(0, ()), Some(2u32));
} }

View file

@ -6,8 +6,9 @@ pub mod node_registry;
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use dyn_any::IntoDynAny; use dyn_any::IntoDynAny;
use graphene_core::*;
use std::borrow::Cow;
/* /*
#[test] #[test]
@ -46,7 +47,7 @@ mod tests {
#[test] #[test]
fn execute_add() { fn execute_add() {
use graph_craft::document::*; use graph_craft::document::*;
use graph_craft::proto::*;
use graph_craft::*; use graph_craft::*;
fn add_network() -> NodeNetwork { fn add_network() -> NodeNetwork {
@ -58,8 +59,8 @@ mod tests {
0, 0,
DocumentNode { DocumentNode {
name: "Cons".into(), name: "Cons".into(),
inputs: vec![NodeInput::Network, NodeInput::Network], inputs: vec![NodeInput::Network(concrete!(u32)), NodeInput::Network(concrete!(&u32))],
implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::structural::ConsNode<_, _>", &[concrete!("u32"), concrete!("u32")])), implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::structural::ConsNode<_, _>")),
metadata: DocumentNodeMetadata::default(), metadata: DocumentNodeMetadata::default(),
}, },
), ),
@ -68,7 +69,7 @@ mod tests {
DocumentNode { DocumentNode {
name: "Add".into(), name: "Add".into(),
inputs: vec![NodeInput::node(0, 0)], inputs: vec![NodeInput::node(0, 0)],
implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::ops::AddNode", &[concrete!("(u32, u32)")])), implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::ops::AddNode")),
metadata: DocumentNodeMetadata::default(), metadata: DocumentNodeMetadata::default(),
}, },
), ),
@ -87,9 +88,9 @@ mod tests {
DocumentNode { DocumentNode {
name: "Inc".into(), name: "Inc".into(),
inputs: vec![ inputs: vec![
NodeInput::Network, NodeInput::Network(concrete!(u32)),
NodeInput::Value { NodeInput::Value {
tagged_value: value::TaggedValue::U32(1), tagged_value: graph_craft::document::value::TaggedValue::U32(1u32),
exposed: false, exposed: false,
}, },
], ],
@ -108,7 +109,7 @@ mod tests {
let compiler = Compiler {}; let compiler = Compiler {};
let protograph = compiler.compile_single(network, true).expect("Graph should be generated"); let protograph = compiler.compile_single(network, true).expect("Graph should be generated");
let exec = DynamicExecutor::new(protograph); let exec = DynamicExecutor::new(protograph).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()).unwrap();
let val = *dyn_any::downcast::<u32>(result).unwrap(); let val = *dyn_any::downcast::<u32>(result).unwrap();

View file

@ -1,296 +1,369 @@
use glam::DAffine2; use glam::{DAffine2, DVec2};
use graph_craft::imaginate_input::{ImaginateMaskStartingFill, ImaginateSamplingMethod, ImaginateStatus};
use graphene_core::ops::{CloneNode, IdNode, TypeNode}; use graphene_core::ops::{CloneNode, IdNode, TypeNode};
use once_cell::sync::Lazy;
use std::collections::HashMap;
use graphene_core::raster::color::Color; use graphene_core::raster::color::Color;
use graphene_core::raster::*; use graphene_core::raster::*;
use graphene_core::structural::Then; use graphene_core::structural::Then;
use graphene_core::value::{ForgetNode, ValueNode}; use graphene_core::value::{ForgetNode, ValueNode};
use graphene_core::{Node, NodeIO, NodeIOTypes};
use graphene_std::any::{ComposeTypeErased, DowncastBothNode, DowncastBothRefNode, DynAnyNode, IntoTypeErasedNode, TypeErasedPinned, TypeErasedPinnedRef}; use graphene_std::any::{ComposeTypeErased, DowncastBothNode, DowncastBothRefNode, DynAnyNode, IntoTypeErasedNode};
use graph_craft::proto::NodeIdentifier; use graphene_core::{Cow, NodeIdentifier, Type, TypeDescriptor};
use graph_craft::proto::Type;
type NodeConstructor = for<'a> fn(Vec<TypeErasedPinnedRef<'static>>) -> TypeErasedPinned<'static>; use graph_craft::proto::NodeConstructor;
use graph_craft::{concrete, generic}; use graphene_core::{concrete, generic};
use graphene_std::memo::CacheNode; use graphene_std::memo::CacheNode;
use crate::executor::NodeContainer;
use dyn_any::StaticType;
macro_rules! register_node { macro_rules! register_node {
($path:ty, input: $input:ty, params: [$($type:ty),*]) => { ($path:ty, input: $input:ty, params: [$($type:ty),*]) => {
( {NodeIdentifier::new(stringify!($path), &[concrete!(stringify!($input)), $(concrete!(stringify!($type))),*])}, (
|args| { NodeIdentifier::new(stringify!($path)),
let mut args = args.clone(); |args| {
args.reverse(); let mut args = args.clone();
let node = <$path>::new($(graphene_std::any::input_node::<$type>(args.pop().expect("not enough arguments provided to construct node"))),*); args.reverse();
let any: DynAnyNode<$input, _, _> = graphene_std::any::DynAnyNode::new(graphene_core::value::ValueNode::new(node)); let node = <$path>::new($(graphene_std::any::input_node::<$type>(args.pop().expect("not enough arguments provided to construct node"))),*);
Box::pin(any) as TypeErasedPinned let any: DynAnyNode<$input, _, _> = graphene_std::any::DynAnyNode::new(graphene_core::value::ValueNode::new(node));
}) Box::pin(any)
},{
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)),*);
let params = vec![$(concrete!($type)),*];
let mut node_io = <$path as NodeIO<'_, $input>>::to_node_io(&node, params);
node_io.input = concrete!(<$input as StaticType>::Static);
node_io
}
)
}; };
} }
macro_rules! raster_node { macro_rules! raster_node {
($path:ty, params: [$($type:ty),*]) => { ($path:ty, params: [$($type:ty),*]) => {
( {NodeIdentifier::new(stringify!($path), &[concrete!("Image"), $(concrete!(stringify!($type))),*])}, (
|args| { NodeIdentifier::new(stringify!($path)),
let mut args = args.clone(); |args| {
args.reverse(); let mut args = args.clone();
let node = <$path>::new($(graphene_std::any::input_node::<$type>(args.pop().expect("Not enough arguments provided to construct node"))),*); args.reverse();
let map_node = graphene_std::raster::MapImageNode::new(graphene_core::value::ValueNode::new(node)); let node = <$path>::new($(
let any: DynAnyNode<Image, _, _> = graphene_std::any::DynAnyNode::new(graphene_core::value::ValueNode::new(map_node)); graphene_core::value::ClonedNode::new(
Box::pin(any) as TypeErasedPinned graphene_std::any::input_node::<$type>(args.pop().expect("Not enough arguments provided to construct node"))
}) .eval(()))
),*);
let map_node = graphene_std::raster::MapImageNode::new(graphene_core::value::ValueNode::new(node));
let any: DynAnyNode<Image, _, _> = graphene_std::any::DynAnyNode::new(graphene_core::value::ValueNode::new(map_node));
Box::pin(any)
}, {
let params = vec![$(concrete!($type)),*];
NodeIOTypes::new(concrete!(Image), concrete!(Image), params)
}
)
}; };
} }
//TODO: turn into hashmap //TODO: turn into hashmap
static NODE_REGISTRY: &[(NodeIdentifier, NodeConstructor)] = &[ fn node_registry() -> HashMap<NodeIdentifier, HashMap<NodeIOTypes, NodeConstructor>> {
//register_node!(graphene_core::ops::IdNode, input: Any<'_>, params: []), let node_types: Vec<(NodeIdentifier, NodeConstructor, NodeIOTypes)> = vec![
(NodeIdentifier::new("graphene_core::ops::IdNode", &[generic!("T")]), |_| IdNode::new().into_type_erased()), //register_node!(graphene_core::ops::IdNode, input: Any<'_>, params: []),
// TODO: create macro to impl for all types
register_node!(graphene_core::structural::ConsNode<_, _>, input: u32, params: [u32]),
register_node!(graphene_core::structural::ConsNode<_, _>, input: u32, params: [&u32]),
register_node!(graphene_core::structural::ConsNode<_, _>, input: &u32, params: [u32]),
register_node!(graphene_core::structural::ConsNode<_, _>, input: &u32, params: [&u32]),
register_node!(graphene_core::ops::AddNode, input: (u32, u32), params: []),
register_node!(graphene_core::ops::AddParameterNode<_>, input: u32, params: [u32]),
register_node!(graphene_core::ops::AddParameterNode<_>, input: &u32, params: [u32]),
register_node!(graphene_core::ops::AddParameterNode<_>, input: u32, params: [&u32]),
register_node!(graphene_core::ops::AddParameterNode<_>, input: &u32, params: [&u32]),
register_node!(graphene_core::ops::AddParameterNode<_>, input: f64, params: [f64]),
register_node!(graphene_core::ops::AddParameterNode<_>, input: &f64, params: [f64]),
register_node!(graphene_core::ops::AddParameterNode<_>, input: f64, params: [&f64]),
register_node!(graphene_core::ops::AddParameterNode<_>, input: &f64, params: [&f64]),
(NodeIdentifier::new("graphene_core::structural::ComposeNode<_, _, _>", &[generic!("T"), generic!("U")]), |args| {
let node = ComposeTypeErased::new(args[0], args[1]);
node.into_type_erased()
}),
(NodeIdentifier::new("graphene_core::ops::IdNode", &[generic!("T")]), |_| IdNode::new().into_type_erased()),
// Filters
raster_node!(graphene_core::raster::LuminanceNode<_>, params: [LuminanceCalculation]),
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: []),
raster_node!(graphene_core::raster::ThresholdNode<_, _>, params: [LuminanceCalculation, f64]),
raster_node!(graphene_core::raster::VibranceNode<_>, params: [f64]),
raster_node!(graphene_core::raster::BrightnessContrastNode< _, _>, params: [f64, f64]),
raster_node!(graphene_core::raster::OpacityNode<_>, params: [f64]),
raster_node!(graphene_core::raster::PosterizeNode<_>, params: [f64]),
raster_node!(graphene_core::raster::ExposureNode<_, _, _>, params: [f64, f64, f64]),
(NodeIdentifier::new("graphene_core::structural::MapImageNode", &[]), |args| {
let map_fn: DowncastBothNode<Color, Color> = DowncastBothNode::new(args[0]);
let node = graphene_std::raster::MapImageNode::new(ValueNode::new(map_fn));
let any: DynAnyNode<Image, _, _> = graphene_std::any::DynAnyNode::new(graphene_core::value::ValueNode::new(node));
any.into_type_erased()
}),
(
NodeIdentifier::new("graphene_std::raster::ImaginateNode<_>", &[concrete!("Image"), concrete!("Option<std::sync::Arc<Image>>")]),
|args| {
let cached = graphene_std::any::input_node::<Option<std::sync::Arc<Image>>>(args[16]);
let node = graphene_std::raster::ImaginateNode::new(cached);
let any = DynAnyNode::new(ValueNode::new(node));
any.into_type_erased()
},
),
(NodeIdentifier::new("graphene_core::raster::BlurNode", &[concrete!("Image")]), |args| {
let radius = DowncastBothNode::<(), u32>::new(args[0]);
let sigma = DowncastBothNode::<(), f64>::new(args[1]);
let image = DowncastBothRefNode::<Image, Image>::new(args[2]);
let empty_image: ValueNode<Image> = ValueNode::new(Image::empty());
let empty: TypeNode<_, (), Image> = TypeNode::new(empty_image.then(CloneNode::new()));
//let image = &image as &dyn for<'a> Node<'a, (), Output = &'a Image>;
// dirty hack: we abuse that the cache node will ignore the input if it is evaluated a second time
let image = empty.then(image).then(ImageRefNode::new());
let window = WindowNode::new(radius, image.clone());
let map_gaussian = MapSndNode::new(ValueNode::new(DistanceNode.then(GaussianNode::new(sigma))));
let map_distances = MapNode::new(ValueNode::new(map_gaussian));
let gaussian_iter = window.then(map_distances);
let avg = gaussian_iter.then(WeightedAvgNode::new());
let avg: TypeNode<_, u32, Color> = TypeNode::new(avg);
let blur_iter = MapNode::new(ValueNode::new(avg));
let pixel_iter = image.clone().then(ImageIndexIterNode::new());
let blur = pixel_iter.then(blur_iter);
let collect = CollectNode {};
let vec = blur.then(collect);
let new_image = MapImageSliceNode::new(vec);
let dimensions = image.then(ImageDimensionsNode::new());
let dimensions: TypeNode<_, (), (u32, u32)> = TypeNode::new(dimensions);
let new_image = dimensions.then(new_image);
let new_image = ForgetNode::new().then(new_image);
let node: DynAnyNode<&Image, _, _> = DynAnyNode::new(ValueNode::new(new_image));
node.into_type_erased()
}),
//register_node!(graphene_std::memo::CacheNode<_>, input: Image, params: []),
(NodeIdentifier::new("graphene_std::memo::CacheNode", &[concrete!("Image")]), |_| {
let node: CacheNode<Image> = graphene_std::memo::CacheNode::new();
let any = graphene_std::any::DynAnyRefNode::new(node);
any.into_type_erased()
}),
register_node!(graphene_core::structural::ConsNode<_, _>, input: Image, params: [&str]),
register_node!(graphene_std::raster::ImageFrameNode<_>, input: Image, params: [DAffine2]),
/*
(NodeIdentifier::new("graphene_std::raster::ImageNode", &[concrete!("&str")]), |_proto_node, stack| {
stack.push_fn(|_nodes| {
let image = FnNode::new(|s: &str| graphene_std::raster::image_node::<&str>().eval(s).unwrap());
let node: DynAnyNode<_, &str, _, _> = DynAnyNode::new(image);
node.into_type_erased()
})
}),
(NodeIdentifier::new("graphene_std::raster::ExportImageNode", &[concrete!("&str")]), |proto_node, stack| {
stack.push_fn(|nodes| {
let pre_node = nodes.get(proto_node.input.unwrap_node() as usize).unwrap();
let image = FnNode::new(|input: (Image, &str)| graphene_std::raster::export_image_node().eval(input).unwrap());
let node: DynAnyNode<_, (Image, &str), _, _> = DynAnyNode::new(image);
let node = (pre_node).then(node);
node.into_type_erased()
})
}),
( (
NodeIdentifier::new("graphene_core::structural::ConsNode", &[concrete!("Image"), concrete!("&str")]), NodeIdentifier::new("graphene_core::ops::IdNode"),
|proto_node, stack| { |_| IdNode::new().into_type_erased(),
let node_id = proto_node.input.unwrap_node() as usize; NodeIOTypes::new(generic!(I), generic!(I), vec![]),
if let ConstructionArgs::Nodes(cons_node_arg) = proto_node.construction_args { ),
stack.push_fn(move |nodes| { // TODO: create macro to impl for all types
let pre_node = nodes.get(node_id).unwrap(); register_node!(graphene_core::structural::ConsNode<_, _>, input: u32, params: [u32]),
let cons_node_arg = nodes.get(cons_node_arg[0] as usize).unwrap(); register_node!(graphene_core::structural::ConsNode<_, _>, input: u32, params: [&u32]),
register_node!(graphene_core::structural::ConsNode<_, _>, input: &u32, params: [u32]),
register_node!(graphene_core::structural::ConsNode<_, _>, input: &u32, params: [&u32]),
register_node!(graphene_core::ops::AddNode, input: (u32, u32), params: []),
register_node!(graphene_core::ops::AddNode, input: (u32, &u32), params: []),
register_node!(graphene_core::ops::AddParameterNode<_>, input: u32, params: [u32]),
register_node!(graphene_core::ops::AddParameterNode<_>, input: &u32, params: [u32]),
register_node!(graphene_core::ops::AddParameterNode<_>, input: u32, params: [&u32]),
register_node!(graphene_core::ops::AddParameterNode<_>, input: &u32, params: [&u32]),
register_node!(graphene_core::ops::AddParameterNode<_>, input: f64, params: [f64]),
register_node!(graphene_core::ops::AddParameterNode<_>, input: &f64, params: [f64]),
register_node!(graphene_core::ops::AddParameterNode<_>, input: f64, params: [&f64]),
register_node!(graphene_core::ops::AddParameterNode<_>, input: &f64, params: [&f64]),
(
NodeIdentifier::new("graphene_core::structural::ComposeNode<_, _, _>"),
|args| {
let node = ComposeTypeErased::new(args[0], args[1]);
node.into_type_erased()
},
NodeIOTypes::new(generic!(T), generic!(U), vec![generic!(V), generic!(U)]),
),
// Filters
raster_node!(graphene_core::raster::LuminanceNode<_>, params: [LuminanceCalculation]),
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: []),
raster_node!(graphene_core::raster::ThresholdNode<_, _>, params: [LuminanceCalculation, f64]),
raster_node!(graphene_core::raster::VibranceNode<_>, params: [f64]),
raster_node!(graphene_core::raster::BrightnessContrastNode< _, _>, params: [f64, f64]),
raster_node!(graphene_core::raster::OpacityNode<_>, params: [f64]),
raster_node!(graphene_core::raster::PosterizeNode<_>, params: [f64]),
raster_node!(graphene_core::raster::ExposureNode<_, _, _>, params: [f64, f64, f64]),
(
NodeIdentifier::new("graphene_core::structural::MapImageNode"),
|args| {
let map_fn: DowncastBothNode<Color, Color> = DowncastBothNode::new(args[0]);
let node = graphene_std::raster::MapImageNode::new(ValueNode::new(map_fn));
let any: DynAnyNode<Image, _, _> = graphene_std::any::DynAnyNode::new(graphene_core::value::ValueNode::new(node));
any.into_type_erased()
},
NodeIOTypes::new(concrete!(Image), concrete!(Image), vec![]),
),
(
NodeIdentifier::new("graphene_std::raster::ImaginateNode<_>"),
|args| {
let cached = graphene_std::any::input_node::<Option<std::sync::Arc<Image>>>(args[16]);
let node = graphene_std::raster::ImaginateNode::new(cached);
let any = DynAnyNode::new(ValueNode::new(node));
any.into_type_erased()
},
NodeIOTypes::new(
concrete!(Image),
concrete!(Image),
vec![
concrete!(DAffine2),
concrete!(f64),
concrete!(Option<DVec2>),
concrete!(f64),
concrete!(ImaginateSamplingMethod),
concrete!(f64),
concrete!(String),
concrete!(String),
concrete!(bool),
concrete!(f64),
concrete!(Option<Vec<u64>>),
concrete!(bool),
concrete!(f64),
concrete!(ImaginateMaskStartingFill),
concrete!(bool),
concrete!(bool),
concrete!(Option<std::sync::Arc<Image>>),
concrete!(f64),
concrete!(ImaginateStatus),
],
),
),
(
NodeIdentifier::new("graphene_core::raster::BlurNode"),
|args| {
let radius = DowncastBothNode::<(), u32>::new(args[0]);
let sigma = DowncastBothNode::<(), f64>::new(args[1]);
let image = DowncastBothRefNode::<Image, Image>::new(args[2]);
let empty_image: ValueNode<Image> = ValueNode::new(Image::empty());
let empty: TypeNode<_, (), Image> = TypeNode::new(empty_image.then(CloneNode::new()));
let cons_node = ConsNode::new(DowncastNode::<_, &str>::new(cons_node_arg)); //let image = &image as &dyn for<'a> Node<'a, (), Output = &'a Image>;
let node: DynAnyNode<_, Image, _, _> = DynAnyNode::new(cons_node); // dirty hack: we abuse that the cache node will ignore the input if it is evaluated a second time
let node = (pre_node).then(node); let image = empty.then(image).then(ImageRefNode::new());
let window = WindowNode::new(radius, image.clone());
let map_gaussian = MapSndNode::new(ValueNode::new(DistanceNode.then(GaussianNode::new(sigma))));
let map_distances = MapNode::new(ValueNode::new(map_gaussian));
let gaussian_iter = window.then(map_distances);
let avg = gaussian_iter.then(WeightedAvgNode::new());
let avg: TypeNode<_, u32, Color> = TypeNode::new(avg);
let blur_iter = MapNode::new(ValueNode::new(avg));
let pixel_iter = image.clone().then(ImageIndexIterNode::new());
let blur = pixel_iter.then(blur_iter);
let collect = CollectNode {};
let vec = blur.then(collect);
let new_image = MapImageSliceNode::new(vec);
let dimensions = image.then(ImageDimensionsNode::new());
let dimensions: TypeNode<_, (), (u32, u32)> = TypeNode::new(dimensions);
let new_image = dimensions.then(new_image);
let new_image = ForgetNode::new().then(new_image);
let node: DynAnyNode<&Image, _, _> = DynAnyNode::new(ValueNode::new(new_image));
node.into_type_erased()
},
NodeIOTypes::new(concrete!(Image), concrete!(Image), vec![concrete!(u32), concrete!(f64)]),
),
//register_node!(graphene_std::memo::CacheNode<_>, input: Image, params: []),
(
NodeIdentifier::new("graphene_std::memo::CacheNode"),
|_| {
let node: CacheNode<Image> = graphene_std::memo::CacheNode::new();
let any = graphene_std::any::DynAnyRefNode::new(node);
any.into_type_erased()
},
NodeIOTypes::new(concrete!(Image), concrete!(&Image), vec![]),
),
register_node!(graphene_core::structural::ConsNode<_, _>, input: Image, params: [&str]),
register_node!(graphene_std::raster::ImageFrameNode<_>, input: Image, params: [DAffine2]),
/*
(NodeIdentifier::new("graphene_std::raster::ImageNode", &[concrete!("&str")]), |_proto_node, stack| {
stack.push_fn(|_nodes| {
let image = FnNode::new(|s: &str| graphene_std::raster::image_node::<&str>().eval(s).unwrap());
let node: DynAnyNode<_, &str, _, _> = DynAnyNode::new(image);
node.into_type_erased()
})
}),
(NodeIdentifier::new("graphene_std::raster::ExportImageNode", &[concrete!("&str")]), |proto_node, stack| {
stack.push_fn(|nodes| {
let pre_node = nodes.get(proto_node.input.unwrap_node() as usize).unwrap();
let image = FnNode::new(|input: (Image, &str)| graphene_std::raster::export_image_node().eval(input).unwrap());
let node: DynAnyNode<_, (Image, &str), _, _> = DynAnyNode::new(image);
let node = (pre_node).then(node);
node.into_type_erased()
})
}),
(
NodeIdentifier::new("graphene_core::structural::ConsNode", &[concrete!("Image"), concrete!("&str")]),
|proto_node, stack| {
let node_id = proto_node.input.unwrap_node() as usize;
if let ConstructionArgs::Nodes(cons_node_arg) = proto_node.construction_args {
stack.push_fn(move |nodes| {
let pre_node = nodes.get(node_id).unwrap();
let cons_node_arg = nodes.get(cons_node_arg[0] as usize).unwrap();
let cons_node = ConsNode::new(DowncastNode::<_, &str>::new(cons_node_arg));
let node: DynAnyNode<_, Image, _, _> = DynAnyNode::new(cons_node);
let node = (pre_node).then(node);
node.into_type_erased()
})
} else {
unimplemented!()
}
},
),
(NodeIdentifier::new("graphene_std::vector::generator_nodes::UnitCircleGenerator", &[]), |_proto_node, stack| {
stack.push_fn(|_nodes| DynAnyNode::new(graphene_std::vector::generator_nodes::UnitCircleGenerator).into_type_erased())
}),
(NodeIdentifier::new("graphene_std::vector::generator_nodes::UnitSquareGenerator", &[]), |_proto_node, stack| {
stack.push_fn(|_nodes| DynAnyNode::new(graphene_std::vector::generator_nodes::UnitSquareGenerator).into_type_erased())
}),
(NodeIdentifier::new("graphene_std::vector::generator_nodes::BlitSubpath", &[]), |proto_node, stack| {
stack.push_fn(move |nodes| {
let ConstructionArgs::Nodes(construction_nodes) = proto_node.construction_args else { unreachable!("BlitSubpath without subpath input") };
let value_node = nodes.get(construction_nodes[0] as usize).unwrap();
let input_node: DowncastBothNode<_, (), Subpath> = DowncastBothNode::new(value_node);
let node = DynAnyNode::new(graphene_std::vector::generator_nodes::BlitSubpath::new(input_node));
if let ProtoNodeInput::Node(node_id) = proto_node.input {
let pre_node = nodes.get(node_id as usize).unwrap();
(pre_node).then(node).into_type_erased()
} else {
node.into_type_erased() node.into_type_erased()
}) }
} else { })
unimplemented!() }),
} (NodeIdentifier::new("graphene_std::vector::generator_nodes::TransformSubpathNode", &[]), |proto_node, stack| {
}, stack.push_fn(move |nodes| {
), let ConstructionArgs::Nodes(construction_nodes) = proto_node.construction_args else { unreachable!("TransformSubpathNode without subpath input") };
(NodeIdentifier::new("graphene_std::vector::generator_nodes::UnitCircleGenerator", &[]), |_proto_node, stack| { let translate_node: DowncastBothNode<_, (), DVec2> = DowncastBothNode::new(nodes.get(construction_nodes[0] as usize).unwrap());
stack.push_fn(|_nodes| DynAnyNode::new(graphene_std::vector::generator_nodes::UnitCircleGenerator).into_type_erased()) let rotate_node: DowncastBothNode<_, (), f64> = DowncastBothNode::new(nodes.get(construction_nodes[1] as usize).unwrap());
}), let scale_node: DowncastBothNode<_, (), DVec2> = DowncastBothNode::new(nodes.get(construction_nodes[2] as usize).unwrap());
(NodeIdentifier::new("graphene_std::vector::generator_nodes::UnitSquareGenerator", &[]), |_proto_node, stack| { let shear_node: DowncastBothNode<_, (), DVec2> = DowncastBothNode::new(nodes.get(construction_nodes[3] as usize).unwrap());
stack.push_fn(|_nodes| DynAnyNode::new(graphene_std::vector::generator_nodes::UnitSquareGenerator).into_type_erased())
}),
(NodeIdentifier::new("graphene_std::vector::generator_nodes::BlitSubpath", &[]), |proto_node, stack| {
stack.push_fn(move |nodes| {
let ConstructionArgs::Nodes(construction_nodes) = proto_node.construction_args else { unreachable!("BlitSubpath without subpath input") };
let value_node = nodes.get(construction_nodes[0] as usize).unwrap();
let input_node: DowncastBothNode<_, (), Subpath> = DowncastBothNode::new(value_node);
let node = DynAnyNode::new(graphene_std::vector::generator_nodes::BlitSubpath::new(input_node));
if let ProtoNodeInput::Node(node_id) = proto_node.input { let node = DynAnyNode::new(graphene_std::vector::generator_nodes::TransformSubpathNode::new(translate_node, rotate_node, scale_node, shear_node));
let pre_node = nodes.get(node_id as usize).unwrap();
(pre_node).then(node).into_type_erased()
} else {
node.into_type_erased()
}
})
}),
(NodeIdentifier::new("graphene_std::vector::generator_nodes::TransformSubpathNode", &[]), |proto_node, stack| {
stack.push_fn(move |nodes| {
let ConstructionArgs::Nodes(construction_nodes) = proto_node.construction_args else { unreachable!("TransformSubpathNode without subpath input") };
let translate_node: DowncastBothNode<_, (), DVec2> = DowncastBothNode::new(nodes.get(construction_nodes[0] as usize).unwrap());
let rotate_node: DowncastBothNode<_, (), f64> = DowncastBothNode::new(nodes.get(construction_nodes[1] as usize).unwrap());
let scale_node: DowncastBothNode<_, (), DVec2> = DowncastBothNode::new(nodes.get(construction_nodes[2] as usize).unwrap());
let shear_node: DowncastBothNode<_, (), DVec2> = DowncastBothNode::new(nodes.get(construction_nodes[3] as usize).unwrap());
let node = DynAnyNode::new(graphene_std::vector::generator_nodes::TransformSubpathNode::new(translate_node, rotate_node, scale_node, shear_node)); if let ProtoNodeInput::Node(node_id) = proto_node.input {
let pre_node = nodes.get(node_id as usize).unwrap();
(pre_node).then(node).into_type_erased()
} else {
node.into_type_erased()
}
})
}),
#[cfg(feature = "gpu")]
(
NodeIdentifier::new("graphene_std::executor::MapGpuNode", &[concrete!("&TypeErasedNode"), concrete!("Color"), concrete!("Color")]),
|proto_node, stack| {
if let ConstructionArgs::Nodes(operation_node_id) = proto_node.construction_args {
stack.push_fn(move |nodes| {
info!("Map image Depending upon id {:?}", operation_node_id);
let operation_node = nodes.get(operation_node_id[0] as usize).unwrap();
let input_node: DowncastBothNode<_, (), &graph_craft::document::NodeNetwork> = DowncastBothNode::new(operation_node);
let map_node: graphene_std::executor::MapGpuNode<_, Vec<u32>, u32, u32> = graphene_std::executor::MapGpuNode::new(input_node);
let map_node = DynAnyNode::new(map_node);
if let ProtoNodeInput::Node(node_id) = proto_node.input { if let ProtoNodeInput::Node(node_id) = proto_node.input {
let pre_node = nodes.get(node_id as usize).unwrap(); let pre_node = nodes.get(node_id as usize).unwrap();
(pre_node).then(node).into_type_erased() (pre_node).then(map_node).into_type_erased()
} else { } else {
node.into_type_erased() map_node.into_type_erased()
} }
}) })
}), } else {
#[cfg(feature = "gpu")] unimplemented!()
( }
NodeIdentifier::new("graphene_std::executor::MapGpuNode", &[concrete!("&TypeErasedNode"), concrete!("Color"), concrete!("Color")]), },
|proto_node, stack| { ),
if let ConstructionArgs::Nodes(operation_node_id) = proto_node.construction_args { #[cfg(feature = "gpu")]
stack.push_fn(move |nodes| { (
info!("Map image Depending upon id {:?}", operation_node_id); NodeIdentifier::new("graphene_std::executor::MapGpuSingleImageNode", &[concrete!("&TypeErasedNode")]),
let operation_node = nodes.get(operation_node_id[0] as usize).unwrap(); |proto_node, stack| {
let input_node: DowncastBothNode<_, (), &graph_craft::document::NodeNetwork> = DowncastBothNode::new(operation_node); if let ConstructionArgs::Nodes(operation_node_id) = proto_node.construction_args {
let map_node: graphene_std::executor::MapGpuNode<_, Vec<u32>, u32, u32> = graphene_std::executor::MapGpuNode::new(input_node); stack.push_fn(move |nodes| {
let map_node = DynAnyNode::new(map_node); info!("Map image Depending upon id {:?}", operation_node_id);
let operation_node = nodes.get(operation_node_id[0] as usize).unwrap();
let input_node: DowncastBothNode<_, (), String> = DowncastBothNode::new(operation_node);
let map_node = graphene_std::executor::MapGpuSingleImageNode(input_node);
let map_node = DynAnyNode::new(map_node);
if let ProtoNodeInput::Node(node_id) = proto_node.input { if let ProtoNodeInput::Node(node_id) = proto_node.input {
let pre_node = nodes.get(node_id as usize).unwrap(); let pre_node = nodes.get(node_id as usize).unwrap();
(pre_node).then(map_node).into_type_erased() (pre_node).then(map_node).into_type_erased()
} else { } else {
map_node.into_type_erased() map_node.into_type_erased()
} }
}) })
} else { } else {
unimplemented!() unimplemented!()
} }
}, },
), ),
#[cfg(feature = "gpu")] #[cfg(feature = "quantization")]
( (
NodeIdentifier::new("graphene_std::executor::MapGpuSingleImageNode", &[concrete!("&TypeErasedNode")]), NodeIdentifier::new("graphene_std::quantization::GenerateQuantizationNode", &[concrete!("&TypeErasedNode")]),
|proto_node, stack| { |proto_node, stack| {
if let ConstructionArgs::Nodes(operation_node_id) = proto_node.construction_args { if let ConstructionArgs::Nodes(operation_node_id) = proto_node.construction_args {
stack.push_fn(move |nodes| { stack.push_fn(move |nodes| {
info!("Map image Depending upon id {:?}", operation_node_id); info!("Quantization Depending upon id {:?}", operation_node_id);
let operation_node = nodes.get(operation_node_id[0] as usize).unwrap(); let samples_node = nodes.get(operation_node_id[0] as usize).unwrap();
let input_node: DowncastBothNode<_, (), String> = DowncastBothNode::new(operation_node); let index_node = nodes.get(operation_node_id[1] as usize).unwrap();
let map_node = graphene_std::executor::MapGpuSingleImageNode(input_node); let samples_node: DowncastBothNode<_, (), u32> = DowncastBothNode::new(samples_node);
let map_node = DynAnyNode::new(map_node); let index_node: DowncastBothNode<_, (), u32> = DowncastBothNode::new(index_node);
let map_node = graphene_std::quantization::GenerateQuantizationNode::new(samples_node, index_node);
let map_node = DynAnyNode::new(map_node);
if let ProtoNodeInput::Node(node_id) = proto_node.input { if let ProtoNodeInput::Node(node_id) = proto_node.input {
let pre_node = nodes.get(node_id as usize).unwrap(); let pre_node = nodes.get(node_id as usize).unwrap();
(pre_node).then(map_node).into_type_erased() (pre_node).then(map_node).into_type_erased()
} else { } else {
map_node.into_type_erased() map_node.into_type_erased()
} }
}) })
} else { } else {
unimplemented!() unimplemented!()
} }
}, },
), ),
#[cfg(feature = "quantization")] <<<<<<< HEAD
( */
NodeIdentifier::new("graphene_std::quantization::GenerateQuantizationNode", &[concrete!("&TypeErasedNode")]), ];
|proto_node, stack| { let mut map: HashMap<NodeIdentifier, HashMap<NodeIOTypes, NodeConstructor>> = HashMap::new();
if let ConstructionArgs::Nodes(operation_node_id) = proto_node.construction_args { for (id, c, types) in node_types {
stack.push_fn(move |nodes| { map.entry(id).or_default().insert(types.clone(), c);
info!("Quantization Depending upon id {:?}", operation_node_id);
let samples_node = nodes.get(operation_node_id[0] as usize).unwrap();
let index_node = nodes.get(operation_node_id[1] as usize).unwrap();
let samples_node: DowncastBothNode<_, (), u32> = DowncastBothNode::new(samples_node);
let index_node: DowncastBothNode<_, (), u32> = DowncastBothNode::new(index_node);
let map_node = graphene_std::quantization::GenerateQuantizationNode::new(samples_node, index_node);
let map_node = DynAnyNode::new(map_node);
if let ProtoNodeInput::Node(node_id) = proto_node.input {
let pre_node = nodes.get(node_id as usize).unwrap();
(pre_node).then(map_node).into_type_erased()
} else {
map_node.into_type_erased()
}
})
} else {
unimplemented!()
}
},
),
<<<<<<< HEAD
*/
];
pub fn constrcut_node<'a>(ident: NodeIdentifier, construction_args: Vec<TypeErasedPinnedRef<'static>>) -> TypeErasedPinned<'a> {
if let Some((_id, f)) = NODE_REGISTRY.iter().find(|(id, _)| *id == ident) {
f(construction_args)
} else {
let other_types = NODE_REGISTRY.iter().map(|(id, _)| id).filter(|id| id.name.as_ref() == ident.name.as_ref()).collect::<Vec<_>>();
panic!("NodeImplementation: {:?} not found in Registry. Types for which the node is implemented:\n {:#?}", ident, other_types);
} }
map
} }
pub static NODE_REGISTRY: Lazy<HashMap<NodeIdentifier, HashMap<NodeIOTypes, NodeConstructor>>> = Lazy::new(|| node_registry());
/* /*
#[cfg(test)] #[cfg(test)]
mod protograph_testing { mod protograph_testing {