mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-16 06:38:03 +08:00
merge onto master
This commit is contained in:
@@ -234,6 +234,7 @@ impl CloneVarArgs for Arc<OwnedContextImpl> {
|
||||
}
|
||||
}
|
||||
|
||||
// Lifetime isnt necessary?
|
||||
pub type Context<'a> = Option<Arc<OwnedContextImpl>>;
|
||||
type DynRef<'a> = &'a (dyn Any + Send + Sync);
|
||||
type DynBox = Box<dyn Any + Send + Sync>;
|
||||
|
||||
@@ -37,6 +37,7 @@ pub use context::*;
|
||||
pub use ctor;
|
||||
pub use dyn_any::{StaticTypeSized, WasmNotSend, WasmNotSync};
|
||||
pub use graphic_element::{Artboard, ArtboardGroupTable, GraphicElement, GraphicGroupTable};
|
||||
pub use memo::IntrospectMode;
|
||||
pub use memo::MemoHash;
|
||||
pub use num_traits;
|
||||
pub use raster::Color;
|
||||
@@ -58,11 +59,18 @@ pub trait Node<'i, Input> {
|
||||
fn node_name(&self) -> &'static str {
|
||||
std::any::type_name::<Self>()
|
||||
}
|
||||
/// Serialize the node which is used for the `introspect` function which can retrieve values from monitor nodes.
|
||||
fn serialize(&self) -> Option<std::sync::Arc<dyn std::any::Any + Send + Sync>> {
|
||||
log::warn!("Node::serialize not implemented for {}", std::any::type_name::<Self>());
|
||||
|
||||
/// Get the call argument or output data for the monitor node on the next evaluation after set_introspect_input
|
||||
/// Also returns a boolean of whether the node was evaluated
|
||||
fn introspect(&self, _introspect_mode: IntrospectMode) -> Option<Box<dyn std::any::Any + Send + Sync>> {
|
||||
log::warn!("Node::introspect not implemented for {}", std::any::type_name::<Self>());
|
||||
None
|
||||
}
|
||||
|
||||
// The introspect mode is set before the graph evaluation, and tells the monitor node what data to store
|
||||
fn set_introspect(&self, _introspect_mode: IntrospectMode) {
|
||||
log::warn!("Node::set_introspect not implemented for {}", std::any::type_name::<Self>());
|
||||
}
|
||||
}
|
||||
|
||||
mod types;
|
||||
|
||||
@@ -107,47 +107,73 @@ pub mod impure_memo {
|
||||
pub const IDENTIFIER: crate::ProtoNodeIdentifier = crate::ProtoNodeIdentifier::new("graphene_core::memo::ImpureMemoNode");
|
||||
}
|
||||
|
||||
/// Stores both what a node was called with and what it returned.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct IORecord<I, O> {
|
||||
pub input: I,
|
||||
pub output: O,
|
||||
#[derive(Copy, Clone, Debug, serde::Serialize, serde::Deserialize)]
|
||||
pub enum IntrospectMode {
|
||||
Input,
|
||||
Data,
|
||||
}
|
||||
|
||||
/// Caches the output of the last graph evaluation for introspection
|
||||
#[derive(Default)]
|
||||
pub struct MonitorNode<I, T, N> {
|
||||
pub struct MonitorNode<I, O, N> {
|
||||
#[allow(clippy::type_complexity)]
|
||||
io: Arc<Mutex<Option<Arc<IORecord<I, T>>>>>,
|
||||
input: Arc<Mutex<Option<Box<I>>>>,
|
||||
output: Arc<Mutex<Option<Box<O>>>>,
|
||||
// Gets set to true by the editor when before evaluating the network, then reset when the monitor node is evaluated
|
||||
introspect_input: Arc<Mutex<bool>>,
|
||||
introspect_output: Arc<Mutex<bool>>,
|
||||
node: N,
|
||||
}
|
||||
|
||||
impl<'i, T, I, N> Node<'i, I> for MonitorNode<I, T, N>
|
||||
impl<'i, I, O, N> Node<'i, I> for MonitorNode<I, O, N>
|
||||
where
|
||||
I: Clone + 'static + Send + Sync,
|
||||
T: Clone + 'static + Send + Sync,
|
||||
for<'a> N: Node<'a, I, Output: Future<Output = T> + WasmNotSend> + 'i,
|
||||
O: Clone + 'static + Send + Sync,
|
||||
for<'a> N: Node<'a, I, Output: Future<Output = O> + WasmNotSend> + Send + Sync + 'i,
|
||||
{
|
||||
type Output = DynFuture<'i, T>;
|
||||
type Output = DynFuture<'i, O>;
|
||||
fn eval(&'i self, input: I) -> Self::Output {
|
||||
let io = self.io.clone();
|
||||
let output_fut = self.node.eval(input.clone());
|
||||
Box::pin(async move {
|
||||
let output = output_fut.await;
|
||||
*io.lock().unwrap() = Some(Arc::new(IORecord { input, output: output.clone() }));
|
||||
let output = self.node.eval(input.clone()).await;
|
||||
let mut introspect_input = self.introspect_input.lock().unwrap();
|
||||
if *introspect_input {
|
||||
*self.input.lock().unwrap() = Some(Box::new(input));
|
||||
*introspect_input = false;
|
||||
}
|
||||
let mut introspect_output = self.introspect_output.lock().unwrap();
|
||||
if *introspect_output {
|
||||
*self.output.lock().unwrap() = Some(Box::new(output.clone()));
|
||||
*introspect_output = false;
|
||||
}
|
||||
output
|
||||
})
|
||||
}
|
||||
|
||||
fn serialize(&self) -> Option<Arc<dyn std::any::Any + Send + Sync>> {
|
||||
let io = self.io.lock().unwrap();
|
||||
(io).as_ref().map(|output| output.clone() as Arc<dyn std::any::Any + Send + Sync>)
|
||||
// After introspecting, the input/output get set to None because the Arc is moved to the editor where it can be directly accessed.
|
||||
fn introspect(&self, introspect_mode: IntrospectMode) -> Option<Box<dyn std::any::Any + Send + Sync>> {
|
||||
match introspect_mode {
|
||||
IntrospectMode::Input => self.input.lock().unwrap().take().map(|input| input as Box<dyn std::any::Any + Send + Sync>),
|
||||
IntrospectMode::Data => self.output.lock().unwrap().take().map(|output| output as Box<dyn std::any::Any + Send + Sync>),
|
||||
}
|
||||
}
|
||||
|
||||
fn set_introspect(&self, introspect_mode: IntrospectMode) {
|
||||
match introspect_mode {
|
||||
IntrospectMode::Input => *self.introspect_input.lock().unwrap() = true,
|
||||
IntrospectMode::Data => *self.introspect_output.lock().unwrap() = true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<I, T, N> MonitorNode<I, T, N> {
|
||||
pub fn new(node: N) -> MonitorNode<I, T, N> {
|
||||
MonitorNode { io: Arc::new(Mutex::new(None)), node }
|
||||
impl<I, O, N> MonitorNode<I, O, N> {
|
||||
pub fn new(node: N) -> MonitorNode<I, O, N> {
|
||||
MonitorNode {
|
||||
input: Arc::new(Mutex::new(None)),
|
||||
output: Arc::new(Mutex::new(None)),
|
||||
introspect_input: Arc::new(Mutex::new(false)),
|
||||
introspect_output: Arc::new(Mutex::new(false)),
|
||||
node,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -24,10 +24,6 @@ where
|
||||
fn reset(&self) {
|
||||
self.0.reset();
|
||||
}
|
||||
|
||||
fn serialize(&self) -> Option<std::sync::Arc<dyn std::any::Any + Send + Sync>> {
|
||||
self.0.serialize()
|
||||
}
|
||||
}
|
||||
impl<'i, N: for<'a> Node<'a, I>, I: 'i> TypeNode<N, I, <N as Node<'i, I>>::Output> {
|
||||
pub fn new(node: N) -> Self {
|
||||
|
||||
@@ -132,6 +132,7 @@ pub type TypeErasedPinned<'n> = Pin<Box<TypeErasedNode<'n>>>;
|
||||
pub type SharedNodeContainer = std::sync::Arc<NodeContainer>;
|
||||
|
||||
pub type NodeConstructor = fn(Vec<SharedNodeContainer>) -> DynFuture<'static, TypeErasedBox<'static>>;
|
||||
pub type MonitorConstructor = fn(SharedNodeContainer) -> TypeErasedBox<'static>;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct NodeContainer {
|
||||
@@ -208,11 +209,10 @@ where
|
||||
#[inline]
|
||||
fn eval(&'input self, input: I) -> Self::Output {
|
||||
{
|
||||
let node_name = self.node.node_name();
|
||||
let input = Box::new(input);
|
||||
let future = self.node.eval(input);
|
||||
Box::pin(async move {
|
||||
let out = dyn_any::downcast(future.await).unwrap_or_else(|e| panic!("DowncastBothNode Input {e} in: \n{node_name}"));
|
||||
let out = dyn_any::downcast(future.await).unwrap_or_else(|e| panic!("DowncastBothNode Input {e} in: \n{:?}", self.node.node_name()));
|
||||
*out
|
||||
})
|
||||
}
|
||||
@@ -220,11 +220,8 @@ where
|
||||
fn reset(&self) {
|
||||
self.node.reset();
|
||||
}
|
||||
|
||||
fn serialize(&self) -> Option<std::sync::Arc<dyn std::any::Any + Send + Sync>> {
|
||||
self.node.serialize()
|
||||
}
|
||||
}
|
||||
|
||||
impl<I, O> DowncastBothNode<I, O> {
|
||||
pub const fn new(node: SharedNodeContainer) -> Self {
|
||||
Self {
|
||||
@@ -234,6 +231,11 @@ impl<I, O> DowncastBothNode<I, O> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn downcast_node<I: StaticType, O: StaticType>(n: SharedNodeContainer) -> DowncastBothNode<I, O> {
|
||||
DowncastBothNode::new(n)
|
||||
}
|
||||
|
||||
pub struct FutureWrapperNode<Node> {
|
||||
node: Node,
|
||||
}
|
||||
@@ -252,11 +254,6 @@ where
|
||||
fn reset(&self) {
|
||||
self.node.reset();
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn serialize(&self) -> Option<std::sync::Arc<dyn std::any::Any + Send + Sync>> {
|
||||
self.node.serialize()
|
||||
}
|
||||
}
|
||||
|
||||
impl<N> FutureWrapperNode<N> {
|
||||
@@ -294,10 +291,6 @@ where
|
||||
fn reset(&self) {
|
||||
self.node.reset();
|
||||
}
|
||||
|
||||
fn serialize(&self) -> Option<std::sync::Arc<dyn std::any::Any + Send + Sync>> {
|
||||
self.node.serialize()
|
||||
}
|
||||
}
|
||||
impl<'input, I, O, N> DynAnyNode<I, O, N>
|
||||
where
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::Node;
|
||||
use crate::registry::Node;
|
||||
use std::marker::PhantomData;
|
||||
|
||||
/// This is how we can generically define composition of two nodes.
|
||||
|
||||
@@ -84,3 +84,12 @@ impl std::fmt::Display for NodeId {
|
||||
write!(f, "{}", self.0)
|
||||
}
|
||||
}
|
||||
|
||||
// Stable Node Id of a protonode, generated during compilation based on the input values
|
||||
pub type SNI = NodeId;
|
||||
|
||||
// An input of a compiled protonode, used to reference thumbnails, which are stored on a per input basis
|
||||
pub type CompiledProtonodeInput = (NodeId, usize);
|
||||
|
||||
// Path to the protonode in the document network
|
||||
pub type ProtonodePath = Box<[NodeId]>;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -108,6 +108,16 @@ macro_rules! tagged_value {
|
||||
_ => Err(format!("Cannot convert {:?} to TaggedValue",std::any::type_name_of_val(input))),
|
||||
}
|
||||
}
|
||||
// Check for equality between a dynamic type and a tagged value without cloning
|
||||
pub fn compare_value_to_dyn_any(&self, any: Box<dyn std::any::Any + Send + Sync>) -> bool {
|
||||
match self {
|
||||
TaggedValue::None => any.downcast_ref::<()>().is_some(),
|
||||
$(TaggedValue::$identifier(value) => {any.downcast_ref::<$ty>().map_or(false, |v| v==value)}, )*
|
||||
TaggedValue::RenderOutput(value) => any.downcast_ref::<RenderOutput>().map_or(false, |v| v==value),
|
||||
TaggedValue::SurfaceFrame(value) => any.downcast_ref::<SurfaceFrame>().map_or(false, |v| v==value),
|
||||
TaggedValue::EditorApi(value) => any.downcast_ref::<Arc<WasmEditorApi>>().map_or(false, |v| v==value),
|
||||
}
|
||||
}
|
||||
pub fn from_type(input: &Type) -> Option<Self> {
|
||||
match input {
|
||||
Type::Generic(_) => None,
|
||||
@@ -372,6 +382,18 @@ impl TaggedValue {
|
||||
_ => panic!("Passed value is not of type u32"),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_renderable<'a>(value: &'a TaggedValue) -> Option<&'a dyn graphene_svg_renderer::GraphicElementRendered> {
|
||||
match value {
|
||||
TaggedValue::VectorData(v) => Some(v),
|
||||
TaggedValue::RasterData(r) => Some(r),
|
||||
TaggedValue::GraphicElement(e) => Some(e),
|
||||
TaggedValue::GraphicGroup(g) => Some(g),
|
||||
TaggedValue::ArtboardGroup(a) => Some(a),
|
||||
TaggedValue::Artboard(a) => Some(a),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for TaggedValue {
|
||||
|
||||
@@ -1,36 +1 @@
|
||||
use crate::document::NodeNetwork;
|
||||
use crate::proto::{LocalFuture, ProtoNetwork};
|
||||
use std::error::Error;
|
||||
|
||||
pub struct Compiler {}
|
||||
|
||||
impl Compiler {
|
||||
pub fn compile(&self, mut network: NodeNetwork) -> impl Iterator<Item = Result<ProtoNetwork, String>> {
|
||||
let node_ids = network.nodes.keys().copied().collect::<Vec<_>>();
|
||||
network.populate_dependants();
|
||||
for id in node_ids {
|
||||
network.flatten(id);
|
||||
}
|
||||
network.resolve_scope_inputs();
|
||||
network.remove_redundant_id_nodes();
|
||||
// network.remove_dead_nodes(0);
|
||||
let proto_networks = network.into_proto_networks();
|
||||
|
||||
proto_networks.map(move |mut proto_network| {
|
||||
proto_network.resolve_inputs()?;
|
||||
proto_network.generate_stable_node_ids();
|
||||
Ok(proto_network)
|
||||
})
|
||||
}
|
||||
pub fn compile_single(&self, network: NodeNetwork) -> Result<ProtoNetwork, String> {
|
||||
assert_eq!(network.exports.len(), 1, "Graph with multiple outputs not yet handled");
|
||||
let Some(proto_network) = self.compile(network).next() else {
|
||||
return Err("Failed to convert graph into proto graph".to_string());
|
||||
};
|
||||
proto_network
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Executor<I, O> {
|
||||
fn execute(&self, input: I) -> LocalFuture<'_, Result<O, Box<dyn Error>>>;
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,5 @@
|
||||
use crate::document::NodeNetwork;
|
||||
use crate::graphene_compiler::Compiler;
|
||||
use crate::proto::ProtoNetwork;
|
||||
|
||||
pub fn load_network(document_string: &str) -> NodeNetwork {
|
||||
let document: serde_json::Value = serde_json::from_str(document_string).expect("Failed to parse document");
|
||||
@@ -8,11 +7,6 @@ pub fn load_network(document_string: &str) -> NodeNetwork {
|
||||
serde_json::from_str::<NodeNetwork>(&document).expect("Failed to parse document")
|
||||
}
|
||||
|
||||
pub fn compile(network: NodeNetwork) -> ProtoNetwork {
|
||||
let compiler = Compiler {};
|
||||
compiler.compile_single(network).unwrap()
|
||||
}
|
||||
|
||||
pub fn load_from_name(name: &str) -> NodeNetwork {
|
||||
let content = std::fs::read(format!("../../demo-artwork/{name}.graphite")).expect("failed to read file");
|
||||
let content = std::str::from_utf8(&content).unwrap();
|
||||
|
||||
@@ -3,7 +3,7 @@ use fern::colors::{Color, ColoredLevelConfig};
|
||||
use futures::executor::block_on;
|
||||
use graph_craft::document::*;
|
||||
use graph_craft::graphene_compiler::{Compiler, Executor};
|
||||
use graph_craft::proto::ProtoNetwork;
|
||||
use graph_craft::proto::{ProtoNetwork, ProtoNode};
|
||||
use graph_craft::util::load_network;
|
||||
use graph_craft::wasm_application_io::EditorPreferences;
|
||||
use graphene_core::text::FontCache;
|
||||
@@ -180,17 +180,17 @@ fn fix_nodes(network: &mut NodeNetwork) {
|
||||
}
|
||||
}
|
||||
}
|
||||
fn compile_graph(document_string: String, editor_api: Arc<WasmEditorApi>) -> Result<ProtoNetwork, Box<dyn Error>> {
|
||||
fn compile_graph(document_string: String, editor_api: Arc<WasmEditorApi>) -> Result<Vec<ProtoNode>, Box<dyn Error>> {
|
||||
let mut network = load_network(&document_string);
|
||||
fix_nodes(&mut network);
|
||||
|
||||
let substitutions = preprocessor::generate_node_substitutions();
|
||||
preprocessor::expand_network(&mut network, &substitutions);
|
||||
|
||||
let wrapped_network = wrap_network_in_scope(network.clone(), editor_api);
|
||||
let mut wrapped_network = wrap_network_in_scope(network.clone(), editor_api);
|
||||
|
||||
let compiler = Compiler {};
|
||||
compiler.compile_single(wrapped_network).map_err(|x| x.into())
|
||||
wrapped_network.flatten().map(|result|result.0).map_err(|x| x.into())
|
||||
}
|
||||
|
||||
fn create_executor(proto_network: ProtoNetwork) -> Result<DynamicExecutor, Box<dyn Error>> {
|
||||
|
||||
@@ -2,13 +2,13 @@ use criterion::BenchmarkGroup;
|
||||
use criterion::measurement::Measurement;
|
||||
use futures::executor::block_on;
|
||||
use graph_craft::proto::ProtoNetwork;
|
||||
use graph_craft::util::{DEMO_ART, compile, load_from_name};
|
||||
use graph_craft::util::{DEMO_ART, load_from_name};
|
||||
use interpreted_executor::dynamic_executor::DynamicExecutor;
|
||||
|
||||
pub fn setup_network(name: &str) -> (DynamicExecutor, ProtoNetwork) {
|
||||
let network = load_from_name(name);
|
||||
let proto_network = compile(network);
|
||||
let executor = block_on(DynamicExecutor::new(proto_network.clone())).unwrap();
|
||||
let mut network = load_from_name(name);
|
||||
let proto_network = network.flatten().unwrap();
|
||||
let executor = block_on(DynamicExecutor::new(proto_network.0)).unwrap();
|
||||
(executor, proto_network)
|
||||
}
|
||||
|
||||
|
||||
@@ -7,8 +7,8 @@ use graphene_std::transform::Footprint;
|
||||
use interpreted_executor::dynamic_executor::DynamicExecutor;
|
||||
|
||||
fn update_executor<M: Measurement>(name: &str, c: &mut BenchmarkGroup<M>) {
|
||||
let network = load_from_name(name);
|
||||
let proto_network = compile(network);
|
||||
let mut network = load_from_name(name);
|
||||
let proto_network = network.flatten().unwrap().0;
|
||||
let empty = ProtoNetwork::default();
|
||||
|
||||
let executor = futures::executor::block_on(DynamicExecutor::new(empty)).unwrap();
|
||||
@@ -30,8 +30,8 @@ fn update_executor_demo(c: &mut Criterion) {
|
||||
}
|
||||
|
||||
fn run_once<M: Measurement>(name: &str, c: &mut BenchmarkGroup<M>) {
|
||||
let network = load_from_name(name);
|
||||
let proto_network = compile(network);
|
||||
let mut network = load_from_name(name);
|
||||
let proto_network = network.flatten().unwrap().0;
|
||||
|
||||
let executor = futures::executor::block_on(DynamicExecutor::new(proto_network)).unwrap();
|
||||
let footprint = Footprint::default();
|
||||
|
||||
@@ -1,11 +1,15 @@
|
||||
use crate::node_registry;
|
||||
use crate::node_registry::{MONITOR_NODES, NODE_REGISTRY};
|
||||
use dyn_any::StaticType;
|
||||
use graph_craft::Type;
|
||||
use graph_craft::document::NodeId;
|
||||
use glam::DAffine2;
|
||||
use graph_craft::document::value::{TaggedValue, UpcastAsRefNode, UpcastNode};
|
||||
use graph_craft::graphene_compiler::Executor;
|
||||
use graph_craft::proto::{ConstructionArgs, GraphError, LocalFuture, NodeContainer, ProtoNetwork, ProtoNode, SharedNodeContainer, TypeErasedBox, TypingContext};
|
||||
use graph_craft::proto::{ConstructionArgs, GraphError, LocalFuture, NodeContainer, ProtoNode, SharedNodeContainer, TypeErasedBox, TypingContext, downcast_node};
|
||||
use graph_craft::proto::{GraphErrorType, GraphErrors};
|
||||
use graph_craft::{Type, concrete};
|
||||
use graphene_std::application_io::{ExportFormat, RenderConfig, TimingInformation};
|
||||
use graphene_std::memo::{IntrospectMode, MonitorNode};
|
||||
use graphene_std::transform::Footprint;
|
||||
use graphene_std::uuid::{CompiledProtonodeInput, NodeId, SNI};
|
||||
use graphene_std::{NodeIOTypes, OwnedContextImpl};
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::error::Error;
|
||||
use std::sync::Arc;
|
||||
@@ -13,118 +17,116 @@ use std::sync::Arc;
|
||||
/// An executor of a node graph that does not require an online compilation server, and instead uses `Box<dyn ...>`.
|
||||
#[derive(Clone)]
|
||||
pub struct DynamicExecutor {
|
||||
output: NodeId,
|
||||
output: Option<SNI>,
|
||||
/// Stores all of the dynamic node structs.
|
||||
tree: BorrowTree,
|
||||
/// Stores the types of the proto nodes.
|
||||
typing_context: TypingContext,
|
||||
// This allows us to keep the nodes around for one more frame which is used for introspection
|
||||
orphaned_nodes: HashSet<NodeId>,
|
||||
// TODO: Add lifetime for removed nodes so that if a SNI changes, then changes back to its previous SNI, the node does
|
||||
// not have to be reinserted
|
||||
// lifetime: HashSet<(SNI, usize)>,
|
||||
}
|
||||
|
||||
impl Default for DynamicExecutor {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
output: Default::default(),
|
||||
output: None,
|
||||
tree: Default::default(),
|
||||
typing_context: TypingContext::new(&node_registry::NODE_REGISTRY),
|
||||
orphaned_nodes: HashSet::new(),
|
||||
typing_context: TypingContext::new(&NODE_REGISTRY, &MONITOR_NODES),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Clone, Debug, Default, serde::Serialize, serde::Deserialize)]
|
||||
pub struct NodeTypes {
|
||||
pub inputs: Vec<Type>,
|
||||
pub output: Type,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Clone, Debug, Default, serde::Serialize, serde::Deserialize)]
|
||||
pub struct ResolvedDocumentNodeTypes {
|
||||
pub types: HashMap<Vec<NodeId>, NodeTypes>,
|
||||
}
|
||||
|
||||
type Path = Box<[NodeId]>;
|
||||
|
||||
#[derive(PartialEq, Clone, Debug, Default, serde::Serialize, serde::Deserialize)]
|
||||
pub struct ResolvedDocumentNodeTypesDelta {
|
||||
pub add: Vec<(Path, NodeTypes)>,
|
||||
pub remove: Vec<Path>,
|
||||
}
|
||||
|
||||
impl DynamicExecutor {
|
||||
pub async fn new(proto_network: ProtoNetwork) -> Result<Self, GraphErrors> {
|
||||
let mut typing_context = TypingContext::new(&node_registry::NODE_REGISTRY);
|
||||
pub async fn new(proto_network: Vec<ProtoNode>) -> Result<Self, GraphErrors> {
|
||||
let mut typing_context = TypingContext::default();
|
||||
typing_context.update(&proto_network)?;
|
||||
let output = proto_network.output;
|
||||
let output = proto_network.get(0).map(|protonode| protonode.stable_node_id);
|
||||
let tree = BorrowTree::new(proto_network, &typing_context).await?;
|
||||
|
||||
Ok(Self {
|
||||
tree,
|
||||
output,
|
||||
typing_context,
|
||||
orphaned_nodes: HashSet::new(),
|
||||
})
|
||||
Ok(Self { tree, output, typing_context })
|
||||
}
|
||||
|
||||
/// Updates the existing [`BorrowTree`] to reflect the new [`ProtoNetwork`], reusing nodes where possible.
|
||||
#[cfg_attr(debug_assertions, inline(never))]
|
||||
pub async fn update(&mut self, proto_network: ProtoNetwork) -> Result<ResolvedDocumentNodeTypesDelta, GraphErrors> {
|
||||
self.output = proto_network.output;
|
||||
pub async fn update(mut self, proto_network: Vec<ProtoNode>) -> Result<(Vec<(SNI, Vec<Type>)>, Vec<(SNI, usize)>), GraphErrors> {
|
||||
self.output = proto_network.get(0).map(|protonode| protonode.stable_node_id);
|
||||
self.typing_context.update(&proto_network)?;
|
||||
let (add, orphaned) = self.tree.update(proto_network, &self.typing_context).await?;
|
||||
let old_to_remove = core::mem::replace(&mut self.orphaned_nodes, orphaned);
|
||||
let mut remove = Vec::with_capacity(old_to_remove.len() - self.orphaned_nodes.len().min(old_to_remove.len()));
|
||||
for node_id in old_to_remove {
|
||||
if self.orphaned_nodes.contains(&node_id) {
|
||||
let path = self.tree.free_node(node_id);
|
||||
self.typing_context.remove_inference(node_id);
|
||||
if let Some(path) = path {
|
||||
remove.push(path);
|
||||
}
|
||||
}
|
||||
// A protonode id can change while having the same document path, and the path can change while having the same stable node id.
|
||||
// Either way, the mapping of paths to ids and ids to paths has to be kept in sync.
|
||||
// The mapping of monitor node paths has to kept in sync as well.
|
||||
let (add, orphaned_proto_nodes) = self.tree.update(proto_network, &self.typing_context).await?;
|
||||
let mut remove = Vec::new();
|
||||
for sni in orphaned_proto_nodes {
|
||||
let Some(types) = self.typing_context.type_of(sni) else {
|
||||
log::error!("Could not get type for protonode {sni} when removing");
|
||||
continue;
|
||||
};
|
||||
remove.push((sni, types.inputs.len()));
|
||||
self.tree.free_node(&sni, types.inputs.len());
|
||||
self.typing_context.remove_inference(&sni);
|
||||
}
|
||||
let add = self.document_node_types(add.into_iter()).collect();
|
||||
Ok(ResolvedDocumentNodeTypesDelta { add, remove })
|
||||
|
||||
let add_with_types = add
|
||||
.into_iter()
|
||||
.filter_map(|sni| {
|
||||
let Some(types) = self.typing_context.type_of(sni) else {
|
||||
log::debug!("Could not get type for added node: {sni}");
|
||||
return None;
|
||||
};
|
||||
Some((sni, types.inputs.clone()))
|
||||
})
|
||||
.collect();
|
||||
|
||||
Ok((add_with_types, remove))
|
||||
}
|
||||
|
||||
/// Calls the `Node::serialize` for that specific node, returning for example the cached value for a monitor node. The node path must match the document node path.
|
||||
pub fn introspect(&self, node_path: &[NodeId]) -> Result<Arc<dyn std::any::Any + Send + Sync + 'static>, IntrospectError> {
|
||||
self.tree.introspect(node_path)
|
||||
/// Intospect the value for that specific protonode input, returning for example the cached value for a monitor node.
|
||||
pub fn introspect(&self, protonode_input: CompiledProtonodeInput, introspect_mode: IntrospectMode) -> Result<Box<dyn std::any::Any + Send + Sync>, IntrospectError> {
|
||||
let node = self.get_monitor_node_container(protonode_input)?;
|
||||
node.introspect(introspect_mode).ok_or(IntrospectError::IntrospectNotImplemented)
|
||||
}
|
||||
|
||||
pub fn set_introspect(&self, protonode_input: CompiledProtonodeInput, introspect_mode: IntrospectMode) {
|
||||
let Ok(node) = self.get_monitor_node_container(protonode_input) else {
|
||||
log::error!("Could not get monitor node for input: {:?}", protonode_input);
|
||||
return;
|
||||
};
|
||||
node.set_introspect(introspect_mode);
|
||||
}
|
||||
|
||||
pub fn get_monitor_node_container(&self, protonode_input: CompiledProtonodeInput) -> Result<SharedNodeContainer, IntrospectError> {
|
||||
// The SNI of the monitor nodes are the ids of the protonode + input index
|
||||
let monitor_node_id = NodeId(protonode_input.0.0 + protonode_input.1 as u64 + 1);
|
||||
let inserted_node = self.tree.nodes.get(&monitor_node_id).ok_or(IntrospectError::ProtoNodeNotFound(monitor_node_id))?;
|
||||
Ok(inserted_node.clone())
|
||||
}
|
||||
|
||||
pub fn input_type(&self) -> Option<Type> {
|
||||
self.typing_context.type_of(self.output).map(|node_io| node_io.call_argument.clone())
|
||||
self.output.and_then(|output| self.typing_context.type_of(output).map(|node_io| node_io.call_argument.clone()))
|
||||
}
|
||||
|
||||
pub fn tree(&self) -> &BorrowTree {
|
||||
&self.tree
|
||||
}
|
||||
|
||||
pub fn output(&self) -> NodeId {
|
||||
pub fn output(&self) -> Option<SNI> {
|
||||
self.output
|
||||
}
|
||||
|
||||
pub fn output_type(&self) -> Option<Type> {
|
||||
self.typing_context.type_of(self.output).map(|node_io| node_io.return_value.clone())
|
||||
self.output.and_then(|output| self.typing_context.type_of(output).map(|node_io| node_io.return_value.clone()))
|
||||
}
|
||||
|
||||
pub fn document_node_types<'a>(&'a self, nodes: impl Iterator<Item = Path> + 'a) -> impl Iterator<Item = (Path, NodeTypes)> + 'a {
|
||||
nodes.flat_map(|id| self.tree.source_map().get(&id).map(|(_, b)| (id, b.clone())))
|
||||
// TODO: https://github.com/GraphiteEditor/Graphite/issues/1767
|
||||
// TODO: Non exposed inputs are not added to the inputs_source_map, so they are not included in the resolved_document_node_types. The type is still available in the typing_context. This only affects the UI-only "Import" node.
|
||||
}
|
||||
}
|
||||
|
||||
impl<I> Executor<I, TaggedValue> for &DynamicExecutor
|
||||
where
|
||||
I: StaticType + 'static + Send + Sync + std::panic::UnwindSafe,
|
||||
{
|
||||
fn execute(&self, input: I) -> LocalFuture<'_, Result<TaggedValue, Box<dyn Error>>> {
|
||||
pub fn execute<I>(&self, input: I) -> LocalFuture<'_, Result<TaggedValue, Box<dyn Error>>>
|
||||
where
|
||||
I: dyn_any::StaticType + 'static + Send + Sync + std::panic::UnwindSafe,
|
||||
{
|
||||
Box::pin(async move {
|
||||
use futures::FutureExt;
|
||||
let output_node = self.output.ok_or("Could not execute network before compilation")?;
|
||||
|
||||
let result = self.tree.eval_tagged_value(self.output, input);
|
||||
let result = self.tree.eval_tagged_value(output_node, input);
|
||||
let wrapped_result = std::panic::AssertUnwindSafe(result).catch_unwind().await;
|
||||
|
||||
match wrapped_result {
|
||||
@@ -136,15 +138,98 @@ where
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// If node to evaluate is None then the most downstream node is used
|
||||
// pub async fn evaluate_from_node(&self, editor_context: EditorContext, node_to_evaluate: Option<SNI>) -> Result<TaggedValue, String> {
|
||||
// let node_to_evaluate: NodeId = node_to_evaluate
|
||||
// .or_else(|| self.output)
|
||||
// .ok_or("Could not find output node when evaluating network. Has the network been compiled?")?;
|
||||
// let input_type = self
|
||||
// .typing_context
|
||||
// .type_of(node_to_evaluate)
|
||||
// .map(|node_io| node_io.call_argument.clone())
|
||||
// .ok_or("Could not get input type of network to execute".to_string())?;
|
||||
// let result = match input_type {
|
||||
// t if t == concrete!(EditorContext) => self.execute(editor_context, node_to_evaluate).await.map_err(|e| e.to_string()),
|
||||
// t if t == concrete!(()) => (&self).execute((), node_to_evaluate).await.map_err(|e| e.to_string()),
|
||||
// t => Err(format!("Invalid input type {t:?}")),
|
||||
// };
|
||||
// let result = match result {
|
||||
// Ok(value) => value,
|
||||
// Err(e) => return Err(e),
|
||||
// };
|
||||
|
||||
// Ok(result)
|
||||
// }
|
||||
}
|
||||
pub struct InputMapping {}
|
||||
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct EditorContext {
|
||||
// pub footprint: Option<Footprint>,
|
||||
// pub downstream_transform: Option<DAffine2>,
|
||||
// pub real_time: Option<f64>,
|
||||
// pub animation_time: Option<f64>,
|
||||
// pub index: Option<usize>,
|
||||
// pub editor_var_args: Option<(Vec<String>, Vec<Arc<Box<[dyn std::any::Any + 'static + std::panic::UnwindSafe]>>>)>,
|
||||
|
||||
// TODO: Temporarily used to execute with RenderConfig as call argument, will be removed once these fields can be passed
|
||||
// As a scope input to the reworked render node. This will allow the Editor Context to be used to evaluate any node
|
||||
pub render_config: RenderConfig,
|
||||
}
|
||||
|
||||
unsafe impl StaticType for EditorContext {
|
||||
type Static = EditorContext;
|
||||
}
|
||||
|
||||
// impl Default for EditorContext {
|
||||
// fn default() -> Self {
|
||||
// EditorContext {
|
||||
// footprint: None,
|
||||
// downstream_transform: None,
|
||||
// real_time: None,
|
||||
// animation_time: None,
|
||||
// index: None,
|
||||
// // editor_var_args: None,
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// impl EditorContext {
|
||||
// pub fn to_context(&self) -> graphene_std::Context {
|
||||
// let mut context = OwnedContextImpl::default();
|
||||
// if let Some(footprint) = self.footprint {
|
||||
// context.set_footprint(footprint);
|
||||
// }
|
||||
// if let Some(footprint) = self.footprint {
|
||||
// context.set_footprint(footprint);
|
||||
// }
|
||||
// if let Some(downstream_transform) = self.downstream_transform {
|
||||
// context.set_downstream_transform(downstream_transform);
|
||||
// }
|
||||
// if let Some(real_time) = self.real_time {
|
||||
// context.set_real_time(real_time);
|
||||
// }
|
||||
// if let Some(animation_time) = self.animation_time {
|
||||
// context.set_animation_time(animation_time);
|
||||
// }
|
||||
// if let Some(index) = self.index {
|
||||
// context.set_index(index);
|
||||
// }
|
||||
// // if let Some(editor_var_args) = self.editor_var_args {
|
||||
// // let (variable_names, values)
|
||||
// // context.set_varargs((variable_names, values))
|
||||
// // }
|
||||
// context.into_context()
|
||||
// }
|
||||
// }
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub enum IntrospectError {
|
||||
PathNotFound(Vec<NodeId>),
|
||||
ProtoNodeNotFound(NodeId),
|
||||
ProtoNodeNotFound(SNI),
|
||||
NoData,
|
||||
RuntimeNotReady,
|
||||
IntrospectNotImplemented,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for IntrospectError {
|
||||
@@ -154,6 +239,7 @@ impl std::fmt::Display for IntrospectError {
|
||||
IntrospectError::ProtoNodeNotFound(id) => write!(f, "ProtoNode not found: {:?}", id),
|
||||
IntrospectError::NoData => write!(f, "No data found for this node"),
|
||||
IntrospectError::RuntimeNotReady => write!(f, "Node runtime is not ready"),
|
||||
IntrospectError::IntrospectNotImplemented => write!(f, "Intospect not implemented"),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -178,55 +264,41 @@ impl std::fmt::Display for IntrospectError {
|
||||
/// A store of the dynamically typed nodes and also the source map.
|
||||
#[derive(Default, Clone)]
|
||||
pub struct BorrowTree {
|
||||
/// A hashmap of node IDs and dynamically typed nodes.
|
||||
nodes: HashMap<NodeId, (SharedNodeContainer, Path)>,
|
||||
/// A hashmap from the document path to the proto node ID.
|
||||
source_map: HashMap<Path, (NodeId, NodeTypes)>,
|
||||
// A hashmap of node IDs and dynamically typed nodes, as well as the number of inserted monitor nodes
|
||||
nodes: HashMap<SNI, SharedNodeContainer>,
|
||||
}
|
||||
|
||||
impl BorrowTree {
|
||||
pub async fn new(proto_network: ProtoNetwork, typing_context: &TypingContext) -> Result<BorrowTree, GraphErrors> {
|
||||
pub async fn new(proto_network: Vec<ProtoNode>, typing_context: &TypingContext) -> Result<BorrowTree, GraphErrors> {
|
||||
let mut nodes = BorrowTree::default();
|
||||
for (id, node) in proto_network.nodes {
|
||||
nodes.push_node(id, node, typing_context).await?
|
||||
for node in proto_network {
|
||||
nodes.push_node(node, typing_context).await?
|
||||
}
|
||||
Ok(nodes)
|
||||
}
|
||||
|
||||
/// Pushes new nodes into the tree and return orphaned nodes
|
||||
pub async fn update(&mut self, proto_network: ProtoNetwork, typing_context: &TypingContext) -> Result<(Vec<Path>, HashSet<NodeId>), GraphErrors> {
|
||||
let mut old_nodes: HashSet<_> = self.nodes.keys().copied().collect();
|
||||
let mut new_nodes: Vec<_> = Vec::new();
|
||||
// TODO: Problem: When an identity node is connected directly to an export the first input to identity node is not added to the proto network, while the second input is. This means the primary input does not have a type.
|
||||
for (id, node) in proto_network.nodes {
|
||||
if !self.nodes.contains_key(&id) {
|
||||
new_nodes.push(node.original_location.path.clone().unwrap_or_default().into());
|
||||
self.push_node(id, node, typing_context).await?;
|
||||
} else if self.update_source_map(id, typing_context, &node) {
|
||||
new_nodes.push(node.original_location.path.clone().unwrap_or_default().into());
|
||||
/// Pushes new nodes into the tree and returns a vec of document nodes that had their types changed, and a vec of all nodes that were removed (including auto inserted value nodes)
|
||||
pub async fn update(&mut self, proto_network: Vec<ProtoNode>, typing_context: &TypingContext) -> Result<(Vec<SNI>, HashSet<SNI>), GraphErrors> {
|
||||
let mut old_nodes = self.nodes.keys().copied().into_iter().collect::<HashSet<_>>();
|
||||
// List of all document node paths that need to be updated, which occurs if their path changes or type changes
|
||||
let mut nodes_with_new_type = Vec::new();
|
||||
for node in proto_network {
|
||||
let sni = node.stable_node_id;
|
||||
old_nodes.remove(&sni);
|
||||
let sni = node.stable_node_id;
|
||||
if !self.nodes.contains_key(&sni) {
|
||||
if node.original_location.send_types_to_editor {
|
||||
nodes_with_new_type.push(sni)
|
||||
}
|
||||
self.push_node(node, typing_context);
|
||||
}
|
||||
old_nodes.remove(&id);
|
||||
}
|
||||
Ok((new_nodes, old_nodes))
|
||||
|
||||
Ok((nodes_with_new_type, old_nodes))
|
||||
}
|
||||
|
||||
fn node_deps(&self, nodes: &[NodeId]) -> Vec<SharedNodeContainer> {
|
||||
nodes.iter().map(|node| self.nodes.get(node).unwrap().0.clone()).collect()
|
||||
}
|
||||
|
||||
fn store_node(&mut self, node: SharedNodeContainer, id: NodeId, path: Path) {
|
||||
self.nodes.insert(id, (node, path));
|
||||
}
|
||||
|
||||
/// Calls the `Node::serialize` for that specific node, returning for example the cached value for a monitor node. The node path must match the document node path.
|
||||
pub fn introspect(&self, node_path: &[NodeId]) -> Result<Arc<dyn std::any::Any + Send + Sync + 'static>, IntrospectError> {
|
||||
let (id, _) = self.source_map.get(node_path).ok_or_else(|| IntrospectError::PathNotFound(node_path.to_vec()))?;
|
||||
let (node, _path) = self.nodes.get(id).ok_or(IntrospectError::ProtoNodeNotFound(*id))?;
|
||||
node.serialize().ok_or(IntrospectError::NoData)
|
||||
}
|
||||
|
||||
pub fn get(&self, id: NodeId) -> Option<SharedNodeContainer> {
|
||||
self.nodes.get(&id).map(|(node, _)| node.clone())
|
||||
fn node_deps(&self, nodes: &[SNI]) -> Vec<SharedNodeContainer> {
|
||||
nodes.iter().map(|node| self.nodes.get(node).unwrap().clone()).collect()
|
||||
}
|
||||
|
||||
/// Evaluate the output node of the [`BorrowTree`].
|
||||
@@ -235,18 +307,18 @@ impl BorrowTree {
|
||||
I: StaticType + 'i + Send + Sync,
|
||||
O: StaticType + 'i,
|
||||
{
|
||||
let (node, _path) = self.nodes.get(&id).cloned()?;
|
||||
let node = self.nodes.get(&id).cloned()?;
|
||||
let output = node.eval(Box::new(input));
|
||||
dyn_any::downcast::<O>(output.await).ok().map(|o| *o)
|
||||
}
|
||||
/// Evaluate the output node of the [`BorrowTree`] and cast it to a tagged value.
|
||||
/// This ensures that no borrowed data can escape the node graph.
|
||||
pub async fn eval_tagged_value<I>(&self, id: NodeId, input: I) -> Result<TaggedValue, String>
|
||||
pub async fn eval_tagged_value<I>(&self, id: SNI, input: I) -> Result<TaggedValue, String>
|
||||
where
|
||||
I: StaticType + 'static + Send + Sync,
|
||||
{
|
||||
let (node, _path) = self.nodes.get(&id).cloned().ok_or("Output node not found in executor")?;
|
||||
let output = node.eval(Box::new(input));
|
||||
let inserted_node = self.nodes.get(&id).cloned().ok_or("Output node not found in executor")?;
|
||||
let output = inserted_node.eval(Box::new(input));
|
||||
TaggedValue::try_from_any(output.await)
|
||||
}
|
||||
|
||||
@@ -305,58 +377,12 @@ impl BorrowTree {
|
||||
/// - Removes the node from `nodes` HashMap.
|
||||
/// - If the node is the primary node for its path in the `source_map`, it's also removed from there.
|
||||
/// - Returns `None` if the node is not found in the `nodes` HashMap.
|
||||
pub fn free_node(&mut self, id: NodeId) -> Option<Path> {
|
||||
let (_, path) = self.nodes.remove(&id)?;
|
||||
if self.source_map.get(&path)?.0 == id {
|
||||
self.source_map.remove(&path);
|
||||
return Some(path);
|
||||
pub fn free_node(&mut self, id: &SNI, inputs: usize) {
|
||||
self.nodes.remove(&id);
|
||||
// Also remove all corresponding monitor nodes
|
||||
for monitor_index in 1..=inputs {
|
||||
self.nodes.remove(&NodeId(id.0 + monitor_index as u64));
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
/// Updates the source map for a given node in the [`BorrowTree`].
|
||||
///
|
||||
/// This method updates or inserts an entry in the `source_map` HashMap for the specified node,
|
||||
/// using type information from the provided [`TypingContext`] and [`ProtoNode`].
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `self` - Mutable reference to the [`BorrowTree`].
|
||||
/// * `id` - The `NodeId` of the node to update in the source map.
|
||||
/// * `typing_context` - A reference to the [`TypingContext`] containing type information.
|
||||
/// * `proto_node` - A reference to the [`ProtoNode`] containing original location information.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// `bool` - `true` if a new entry was inserted, `false` if an existing entry was updated.
|
||||
///
|
||||
/// # Notes
|
||||
///
|
||||
/// - Updates or inserts an entry in the `source_map` HashMap.
|
||||
/// - Uses the `ProtoNode`'s original location path as the key for the source map.
|
||||
/// - Collects input types from both the main input and parameters.
|
||||
/// - Returns `false` and logs a warning if the node's type information is not found in the typing context.
|
||||
fn update_source_map(&mut self, id: NodeId, typing_context: &TypingContext, proto_node: &ProtoNode) -> bool {
|
||||
let Some(node_io) = typing_context.type_of(id) else {
|
||||
log::warn!("did not find type");
|
||||
return false;
|
||||
};
|
||||
let inputs = [&node_io.call_argument].into_iter().chain(&node_io.inputs).cloned().collect();
|
||||
|
||||
let node_path = &proto_node.original_location.path.as_ref().unwrap_or(const { &vec![] });
|
||||
|
||||
let entry = self.source_map.entry(node_path.to_vec().into()).or_default();
|
||||
|
||||
let update = (
|
||||
id,
|
||||
NodeTypes {
|
||||
inputs,
|
||||
output: node_io.return_value.clone(),
|
||||
},
|
||||
);
|
||||
let modified = *entry != update;
|
||||
*entry = update;
|
||||
modified
|
||||
}
|
||||
|
||||
/// Inserts a new node into the [`BorrowTree`], calling the constructor function from `node_registry.rs`.
|
||||
@@ -374,53 +400,58 @@ impl BorrowTree {
|
||||
/// - `Nodes`: Constructs a node using other nodes as dependencies.
|
||||
/// - Uses the constructor function from the `typing_context` for `Nodes` construction arguments.
|
||||
/// - Returns an error if no constructor is found for the given node ID.
|
||||
async fn push_node(&mut self, id: NodeId, proto_node: ProtoNode, typing_context: &TypingContext) -> Result<(), GraphErrors> {
|
||||
self.update_source_map(id, typing_context, &proto_node);
|
||||
let path = proto_node.original_location.path.clone().unwrap_or_default();
|
||||
|
||||
match &proto_node.construction_args {
|
||||
async fn push_node(&mut self, proto_node: ProtoNode, typing_context: &TypingContext) -> Result<(), GraphErrors> {
|
||||
let sni = proto_node.stable_node_id;
|
||||
// Move the value into the upcast node instead of cloning it
|
||||
match proto_node.construction_args {
|
||||
ConstructionArgs::Value(value) => {
|
||||
let node = if let TaggedValue::EditorApi(api) = &**value {
|
||||
let editor_api = UpcastAsRefNode::new(api.clone());
|
||||
let node = Box::new(editor_api) as TypeErasedBox<'_>;
|
||||
NodeContainer::new(node)
|
||||
} else {
|
||||
let upcasted = UpcastNode::new(value.to_owned());
|
||||
let node = Box::new(upcasted) as TypeErasedBox<'_>;
|
||||
NodeContainer::new(node)
|
||||
};
|
||||
self.store_node(node, id, path.into());
|
||||
// The constructor for nodes with value construction args (value nodes) is not called.
|
||||
// It is not necessary to clone the Arc for the wasm editor api, since the value node is deduplicated and only called once.
|
||||
// It is cloned whenever it is evaluated
|
||||
let upcasted = UpcastNode::new(value);
|
||||
let node = Box::new(upcasted) as TypeErasedBox<'_>;
|
||||
self.nodes.insert(sni, NodeContainer::new(node));
|
||||
}
|
||||
ConstructionArgs::Inline(_) => unimplemented!("Inline nodes are not supported yet"),
|
||||
ConstructionArgs::Nodes(ids) => {
|
||||
let ids: Vec<_> = ids.iter().map(|(id, _)| *id).collect();
|
||||
let construction_nodes = self.node_deps(&ids);
|
||||
let constructor = typing_context.constructor(id).ok_or_else(|| vec![GraphError::new(&proto_node, GraphErrorType::NoConstructor)])?;
|
||||
let node = constructor(construction_nodes).await;
|
||||
ConstructionArgs::Nodes(ref node_construction_args) => {
|
||||
let construction_nodes = self.node_deps(&node_construction_args.inputs);
|
||||
|
||||
let types = typing_context.type_of(sni).ok_or_else(|| vec![GraphError::new(&proto_node, GraphErrorType::NoConstructor)])?;
|
||||
let monitor_nodes = construction_nodes
|
||||
.into_iter()
|
||||
.enumerate()
|
||||
.map(|(input_index, construction_node)| {
|
||||
let input_type = types.inputs.get(input_index).unwrap(); //.ok_or_else(|| vec![GraphError::new(&proto_node, GraphErrorType::NoConstructor)])?;
|
||||
let monitor_constructor = typing_context.monitor_constructor(input_type).unwrap(); // .ok_or_else(|| vec![GraphError::new(&proto_node, GraphErrorType::NoConstructor)])?;
|
||||
let monitor = monitor_constructor(construction_node);
|
||||
let monitor_node_container = NodeContainer::new(monitor);
|
||||
self.nodes.insert(NodeId(sni.0 + input_index as u64 + 1), monitor_node_container.clone());
|
||||
monitor_node_container
|
||||
})
|
||||
.collect();
|
||||
|
||||
let constructor = typing_context.constructor(sni).ok_or_else(|| vec![GraphError::new(&proto_node, GraphErrorType::NoConstructor)])?;
|
||||
let node = constructor(monitor_nodes).await;
|
||||
let node = NodeContainer::new(node);
|
||||
self.store_node(node, id, path.into());
|
||||
self.nodes.insert(sni, node);
|
||||
}
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Returns the source map of the borrow tree
|
||||
pub fn source_map(&self) -> &HashMap<Path, (NodeId, NodeTypes)> {
|
||||
&self.source_map
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use graph_craft::document::value::TaggedValue;
|
||||
use graphene_std::uuid::NodeId;
|
||||
|
||||
#[test]
|
||||
fn push_node_sync() {
|
||||
let mut tree = BorrowTree::default();
|
||||
let val_1_protonode = ProtoNode::value(ConstructionArgs::Value(TaggedValue::U32(2u32).into()), vec![]);
|
||||
let val_1_protonode = ProtoNode::value(ConstructionArgs::Value(TaggedValue::U32(2u32).into()), vec![], NodeId(0));
|
||||
let context = TypingContext::default();
|
||||
let future = tree.push_node(NodeId(0), val_1_protonode, &context);
|
||||
let future = tree.push_node(val_1_protonode, &context);
|
||||
futures::executor::block_on(future).unwrap();
|
||||
let _node = tree.get(NodeId(0)).unwrap();
|
||||
let result = futures::executor::block_on(tree.eval(NodeId(0), ()));
|
||||
|
||||
@@ -43,8 +43,8 @@ mod tests {
|
||||
use graph_craft::graphene_compiler::Compiler;
|
||||
|
||||
let compiler = Compiler {};
|
||||
let protograph = compiler.compile_single(network).expect("Graph should be generated");
|
||||
let protonetwork = network.flatten().map(|result| result.0).expect("Graph should be generated");
|
||||
|
||||
let _exec = block_on(DynamicExecutor::new(protograph)).map(|_e| panic!("The network should not type check ")).unwrap_err();
|
||||
let _exec = block_on(DynamicExecutor::new(protonetwork)).map(|_e| panic!("The network should not type check ")).unwrap_err();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use dyn_any::StaticType;
|
||||
use glam::{DVec2, IVec2, UVec2};
|
||||
use graph_craft::document::value::RenderOutput;
|
||||
use graph_craft::proto::{NodeConstructor, TypeErasedBox};
|
||||
use graph_craft::proto::{MonitorConstructor, NodeConstructor, TypeErasedBox};
|
||||
use graphene_core::raster::color::Color;
|
||||
use graphene_core::raster::*;
|
||||
use graphene_core::raster_types::{CPU, GPU, RasterDataTable};
|
||||
@@ -18,7 +18,7 @@ use graphene_std::any::{ComposeTypeErased, DynAnyNode, IntoTypeErasedNode};
|
||||
use graphene_std::application_io::{ImageTexture, SurfaceFrame};
|
||||
#[cfg(feature = "gpu")]
|
||||
use graphene_std::wasm_application_io::{WasmEditorApi, WasmSurfaceHandle};
|
||||
use node_registry_macros::{async_node, convert_node, into_node};
|
||||
use node_registry_macros::{async_node, convert_node, into_node, monitor_node};
|
||||
use once_cell::sync::Lazy;
|
||||
use std::collections::HashMap;
|
||||
#[cfg(feature = "gpu")]
|
||||
@@ -192,6 +192,52 @@ fn node_registry() -> HashMap<ProtoNodeIdentifier, HashMap<NodeIOTypes, NodeCons
|
||||
|
||||
pub static NODE_REGISTRY: Lazy<HashMap<ProtoNodeIdentifier, HashMap<NodeIOTypes, NodeConstructor>>> = Lazy::new(|| node_registry());
|
||||
|
||||
fn monitor_nodes() -> HashMap<Type, MonitorConstructor> {
|
||||
let nodes: Vec<(Type, MonitorConstructor)> = vec![
|
||||
monitor_node!(ImageTexture),
|
||||
monitor_node!(VectorDataTable),
|
||||
monitor_node!(GraphicGroupTable),
|
||||
monitor_node!(GraphicElement),
|
||||
monitor_node!(Artboard),
|
||||
monitor_node!(RasterDataTable<CPU>),
|
||||
monitor_node!(RasterDataTable<GPU>),
|
||||
monitor_node!(graphene_core::instances::Instances<Artboard>),
|
||||
monitor_node!(String),
|
||||
monitor_node!(IVec2),
|
||||
monitor_node!(DVec2),
|
||||
monitor_node!(bool),
|
||||
monitor_node!(f64),
|
||||
monitor_node!(u32),
|
||||
monitor_node!(u64),
|
||||
monitor_node!(()),
|
||||
monitor_node!(Vec<f64>),
|
||||
monitor_node!(BlendMode),
|
||||
monitor_node!(graphene_std::transform::ReferencePoint),
|
||||
monitor_node!(graphene_path_bool::BooleanOperation),
|
||||
monitor_node!(Option<Color>),
|
||||
monitor_node!(graphene_core::vector::style::Fill),
|
||||
monitor_node!(graphene_core::vector::style::StrokeCap),
|
||||
monitor_node!(graphene_core::vector::style::StrokeJoin),
|
||||
monitor_node!(graphene_core::vector::style::PaintOrder),
|
||||
monitor_node!(graphene_core::vector::style::StrokeAlign),
|
||||
monitor_node!(graphene_core::vector::style::Stroke),
|
||||
monitor_node!(graphene_core::vector::style::Gradient),
|
||||
monitor_node!(graphene_core::vector::style::GradientStops),
|
||||
monitor_node!(Vec<graphene_core::uuid::NodeId>),
|
||||
monitor_node!(Color),
|
||||
monitor_node!(Box<graphene_core::vector::VectorModification>),
|
||||
monitor_node!(graphene_std::vector::misc::CentroidType),
|
||||
monitor_node!(graphene_std::vector::misc::PointSpacingType),
|
||||
];
|
||||
let mut monitor_nodes = HashMap::new();
|
||||
for (monitor_type, constructor) in nodes {
|
||||
monitor_nodes.insert(monitor_type, constructor);
|
||||
}
|
||||
monitor_nodes
|
||||
}
|
||||
|
||||
pub static MONITOR_NODES: Lazy<HashMap<Type, MonitorConstructor>> = Lazy::new(|| monitor_nodes());
|
||||
|
||||
mod node_registry_macros {
|
||||
macro_rules! async_node {
|
||||
// TODO: we currently need to annotate the type here because the compiler would otherwise (correctly)
|
||||
@@ -207,7 +253,7 @@ mod node_registry_macros {
|
||||
|mut args| {
|
||||
Box::pin(async move {
|
||||
args.reverse();
|
||||
let node = <$path>::new($(graphene_std::any::downcast_node::<$arg, $type>(args.pop().expect("Not enough arguments provided to construct node"))),*);
|
||||
let node = <$path>::new($(graphene_std::registry::downcast_node::<$arg, $type>(args.pop().expect("Not enough arguments provided to construct node"))),*);
|
||||
let any: DynAnyNode<$input, _, _> = graphene_std::any::DynAnyNode::new(node);
|
||||
Box::new(any) as TypeErasedBox
|
||||
})
|
||||
@@ -285,7 +331,18 @@ mod node_registry_macros {
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! monitor_node {
|
||||
($type:ty) => {
|
||||
(concrete!($type), |arg| {
|
||||
let node = <graphene_core::memo::MonitorNode<graphene_std::Context, _, _>>::new(graphene_std::registry::downcast_node::<graphene_std::Context, $type>(arg));
|
||||
let any: DynAnyNode<_, _, _> = graphene_std::any::DynAnyNode::new(node);
|
||||
Box::new(any) as TypeErasedBox
|
||||
})
|
||||
};
|
||||
}
|
||||
|
||||
pub(crate) use async_node;
|
||||
pub(crate) use convert_node;
|
||||
pub(crate) use into_node;
|
||||
pub(crate) use monitor_node;
|
||||
}
|
||||
|
||||
@@ -8,22 +8,13 @@ use graphene_std::Context;
|
||||
use graphene_std::uuid::NodeId;
|
||||
use std::sync::Arc;
|
||||
|
||||
// TODO: this is copy pasta from the editor (and does get out of sync)
|
||||
pub fn wrap_network_in_scope(mut network: NodeNetwork, editor_api: Arc<WasmEditorApi>) -> NodeNetwork {
|
||||
network.generate_node_paths(&[]);
|
||||
|
||||
let inner_network = DocumentNode {
|
||||
implementation: DocumentNodeImplementation::Network(network),
|
||||
inputs: vec![],
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
// TODO: Replace with "Output" definition?
|
||||
// let render_node = resolve_document_node_type("Output")
|
||||
// .expect("Output node type not found")
|
||||
// .node_template_input_override(vec![Some(NodeInput::node(NodeId(1), 0)), Some(NodeInput::node(NodeId(0), 1))])
|
||||
// .document_node;
|
||||
|
||||
let render_node = DocumentNode {
|
||||
inputs: vec![NodeInput::node(NodeId(0), 0), NodeInput::node(NodeId(2), 0)],
|
||||
implementation: DocumentNodeImplementation::Network(NodeNetwork {
|
||||
@@ -64,20 +55,12 @@ pub fn wrap_network_in_scope(mut network: NodeNetwork, editor_api: Arc<WasmEdito
|
||||
};
|
||||
|
||||
// wrap the inner network in a scope
|
||||
let nodes = vec![
|
||||
inner_network,
|
||||
render_node,
|
||||
DocumentNode {
|
||||
implementation: DocumentNodeImplementation::ProtoNode(graphene_std::ops::identity::IDENTIFIER),
|
||||
inputs: vec![NodeInput::value(TaggedValue::EditorApi(editor_api), false)],
|
||||
..Default::default()
|
||||
},
|
||||
];
|
||||
let nodes = vec![inner_network, render_node];
|
||||
|
||||
NodeNetwork {
|
||||
exports: vec![NodeInput::node(NodeId(1), 0)],
|
||||
nodes: nodes.into_iter().enumerate().map(|(id, node)| (NodeId(id as u64), node)).collect(),
|
||||
scope_injections: [("editor-api".to_string(), (NodeId(2), concrete!(&WasmEditorApi)))].into_iter().collect(),
|
||||
scope_injections: [("editor-api".to_string(), TaggedValue::EditorApi(editor_api))].into_iter().collect(),
|
||||
// TODO(TrueDoctor): check if it makes sense to set `generated` to `true`
|
||||
generated: false,
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ use graph_craft::document::*;
|
||||
use graph_craft::proto::RegistryValueSource;
|
||||
use graph_craft::{ProtoNodeIdentifier, concrete};
|
||||
use graphene_std::registry::*;
|
||||
use graphene_std::uuid::NodeId;
|
||||
use graphene_std::*;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user