From 4e1bfddcd8d88d574829f47b13f7713bd3d84e55 Mon Sep 17 00:00:00 2001 From: Dennis Kobert Date: Fri, 2 Jun 2023 11:05:32 +0200 Subject: [PATCH] Remove unsafe code and clean up the code base in general (#1263) * Remove unsafe code * Make node graph test syncronous * Add miri step to ci * Remove unsafe from node graph evaluation * Replace operation pseudo_hash with hash based on discriminant * Fix test * Move memo module to core and make it safe * Fix formatting * Remove unused stuff from gstd * Use safe casting for creating key variants * Fix memo node types * Fix ref node * "fix" ub * Use correct input types for ExtractImageFrame * Fix types for async nodes * Fix missing implementation * Manually override output type for async nodes * Fix types for EditorApi * Fix output type for WasmSurfaceHandle * Remove unused miri.yml * Fix incorrect type for cache node --- .github/workflows/ci.yml | 10 + Cargo.lock | 48 +- Cargo.toml | 1 - document-legacy/src/operation.rs | 15 +- editor/Cargo.toml | 7 +- editor/src/dispatcher.rs | 3 + .../input_mapper_message_handler.rs | 6 +- .../utility_types/input_keyboard.rs | 3 +- editor/src/messages/message.rs | 22 - .../document/document_message_handler.rs | 4 + .../document_node_types.rs | 88 +--- .../tool/tool_messages/select_tool.rs | 4 +- editor/src/node_graph_executor.rs | 47 +- frontend/wasm/src/editor_api.rs | 42 +- node-graph/borrow_stack/Cargo.toml | 10 - node-graph/borrow_stack/src/lib.rs | 80 ---- node-graph/compilation-client/src/main.rs | 2 +- node-graph/gcore/src/application_io.rs | 34 +- .../src/application_io/wasm_application_io.rs | 22 +- node-graph/gcore/src/lib.rs | 3 +- node-graph/gcore/src/memo.rs | 158 +++++++ node-graph/gcore/src/storage.rs | 14 +- node-graph/gcore/src/structural.rs | 1 + node-graph/gcore/src/text.rs | 2 +- node-graph/gcore/src/types.rs | 17 +- node-graph/gcore/src/value.rs | 22 - node-graph/graph-craft/src/document.rs | 10 +- node-graph/graph-craft/src/document/value.rs | 178 +++----- node-graph/graph-craft/src/executor.rs | 4 +- node-graph/graph-craft/src/proto.rs | 1 - node-graph/gstd/Cargo.toml | 15 +- node-graph/gstd/src/any.rs | 17 +- node-graph/gstd/src/executor.rs | 6 +- node-graph/gstd/src/lib.rs | 3 - node-graph/gstd/src/main.rs | 148 ------- node-graph/gstd/src/memo.rs | 177 -------- node-graph/interpreted-executor/Cargo.toml | 11 +- .../interpreted-executor/src/executor.rs | 34 +- node-graph/interpreted-executor/src/lib.rs | 52 +-- .../interpreted-executor/src/node_registry.rs | 414 +++++------------- node-graph/node-macro/src/lib.rs | 8 +- node-graph/vulkan-executor/src/executor.rs | 13 +- node-graph/wgpu-executor/src/executor.rs | 16 +- 43 files changed, 520 insertions(+), 1252 deletions(-) delete mode 100644 node-graph/borrow_stack/Cargo.toml delete mode 100644 node-graph/borrow_stack/src/lib.rs create mode 100644 node-graph/gcore/src/memo.rs delete mode 100644 node-graph/gstd/src/main.rs delete mode 100644 node-graph/gstd/src/memo.rs diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 128c1ebf4..49f5d9048 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -107,6 +107,16 @@ jobs: run: | mold -run cargo nextest run + miri: + runs-on: self-hosted + + steps: + - uses: actions/checkout@v2 + + - name: 🧪 Run Rust miri + run: | + mold -run cargo +nightly miri nextest run -j32 + cargo-deny: runs-on: ubuntu-latest diff --git a/Cargo.lock b/Cargo.lock index f9aa7ce35..cf95b9337 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -390,19 +390,6 @@ dependencies = [ "generic-array", ] -[[package]] -name = "borrow_stack" -version = "0.1.0" -dependencies = [ - "dyn-any", -] - -[[package]] -name = "boxcar" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38c99613cb3cd7429889a08dfcf651721ca971c86afa30798461f8eee994de47" - [[package]] name = "brotli" version = "3.3.4" @@ -1707,8 +1694,6 @@ version = "0.1.0" dependencies = [ "autoquant", "bezier-rs", - "borrow_stack", - "boxcar", "bytemuck", "compilation-client", "dyn-any", @@ -1723,13 +1708,9 @@ dependencies = [ "kurbo", "log", "node-macro", - "once_cell", - "proc-macro2", - "quote", "reqwest", "serde", "serde_json", - "syn 1.0.109", "tempfile", "vulkan-executor", "wgpu-executor", @@ -1778,7 +1759,6 @@ version = "0.0.0" dependencies = [ "bezier-rs", "bitflags 1.3.2", - "borrow_stack", "derivative", "dyn-any", "env_logger", @@ -1792,6 +1772,7 @@ dependencies = [ "interpreted-executor", "kurbo", "log", + "num_enum 0.6.1", "once_cell", "remain", "serde", @@ -2214,7 +2195,6 @@ dependencies = [ name = "interpreted-executor" version = "0.1.0" dependencies = [ - "borrow_stack", "dyn-any", "dyn-clone", "futures", @@ -2226,7 +2206,6 @@ dependencies = [ "num-traits", "once_cell", "serde", - "tokio", ] [[package]] @@ -2706,7 +2685,7 @@ dependencies = [ "bitflags 1.3.2", "jni-sys", "ndk-sys", - "num_enum", + "num_enum 0.5.11", "thiserror", ] @@ -2873,7 +2852,16 @@ version = "0.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1f646caf906c20226733ed5b1374287eb97e3c2a5c227ce668c1f2ce20ae57c9" dependencies = [ - "num_enum_derive", + "num_enum_derive 0.5.11", +] + +[[package]] +name = "num_enum" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a015b430d3c108a207fd776d2e2196aaf8b1cf8cf93253e3a097ff3085076a1" +dependencies = [ + "num_enum_derive 0.6.1", ] [[package]] @@ -2888,6 +2876,18 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "num_enum_derive" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96667db765a921f7b295ffee8b60472b686a51d4f21c2ee4ffdb94c7013b65a6" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 2.0.15", +] + [[package]] name = "num_threads" version = "0.1.6" diff --git a/Cargo.toml b/Cargo.toml index 093df2dc9..96347157d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,7 +9,6 @@ members = [ "node-graph/gstd", "node-graph/graph-craft", "node-graph/interpreted-executor", - "node-graph/borrow_stack", "node-graph/node-macro", "node-graph/compilation-server", "node-graph/compilation-client", diff --git a/document-legacy/src/operation.rs b/document-legacy/src/operation.rs index 9d9c589e3..ef1c708fa 100644 --- a/document-legacy/src/operation.rs +++ b/document-legacy/src/operation.rs @@ -167,22 +167,9 @@ pub enum Operation { } impl Operation { - /// Returns the byte representation of the message. - /// - /// # Safety - /// This function reads from uninitialized memory!!! - /// Only use if you know what you are doing - unsafe fn as_slice(&self) -> &[u8] { - core::slice::from_raw_parts(self as *const Operation as *const u8, std::mem::size_of::()) - } - /// Returns a pseudo hash that should uniquely identify the operation. - /// This is needed because `Hash` is not implemented for f64s - /// - /// # Safety - /// This function reads from uninitialized memory but the generated value should be fine. pub fn pseudo_hash(&self) -> u64 { let mut s = DefaultHasher::new(); - unsafe { self.as_slice() }.hash(&mut s); + std::mem::discriminant(self).hash(&mut s); s.finish() } } diff --git a/editor/Cargo.toml b/editor/Cargo.toml index 3360c9874..f8008d94d 100644 --- a/editor/Cargo.toml +++ b/editor/Cargo.toml @@ -12,10 +12,7 @@ license = "Apache-2.0" [features] gpu = ["interpreted-executor/gpu", "graphene-std/gpu", "graphene-core/gpu"] -quantization = [ - "graphene-std/quantization", - "interpreted-executor/quantization", -] +quantization = ["graphene-std/quantization", "interpreted-executor/quantization"] [dependencies] log = "0.4" @@ -41,10 +38,10 @@ image = { version = "0.24", default-features = false, features = [ ] } graph-craft = { path = "../node-graph/graph-craft" } interpreted-executor = { path = "../node-graph/interpreted-executor" } -borrow_stack = { path = "../node-graph/borrow_stack" } dyn-any = { path = "../libraries/dyn-any" } graphene-core = { path = "../node-graph/gcore" } graphene-std = { path = "../node-graph/gstd" } +num_enum = "0.6.1" [dependencies.document-legacy] path = "../document-legacy" diff --git a/editor/src/dispatcher.rs b/editor/src/dispatcher.rs index 93c021abf..2bd5bbaee 100644 --- a/editor/src/dispatcher.rs +++ b/editor/src/dispatcher.rs @@ -323,6 +323,7 @@ mod test { } #[test] + #[cfg_attr(miri, ignore)] /// - create rect, shape and ellipse /// - select shape /// - copy @@ -362,6 +363,7 @@ mod test { } #[test] + #[cfg_attr(miri, ignore)] fn copy_paste_folder() { let mut editor = create_editor_with_three_layers(); @@ -450,6 +452,7 @@ mod test { } #[test] + #[cfg_attr(miri, ignore)] /// - create rect, shape and ellipse /// - select ellipse and rect /// - copy diff --git a/editor/src/messages/input_mapper/input_mapper_message_handler.rs b/editor/src/messages/input_mapper/input_mapper_message_handler.rs index f3af500c1..528258b8e 100644 --- a/editor/src/messages/input_mapper/input_mapper_message_handler.rs +++ b/editor/src/messages/input_mapper/input_mapper_message_handler.rs @@ -37,9 +37,9 @@ impl InputMapperMessageHandler { .filter_map(|(i, m)| { let ma = m.0.iter().find_map(|m| actions.find_map(|a| (a == m.action.to_discriminant()).then(|| m.action.to_discriminant()))); - ma.map(|a| unsafe { (std::mem::transmute_copy::(&i), a) }) + ma.map(|a| ((i as u8).try_into().unwrap(), a)) }) - .for_each(|(k, a)| { + .for_each(|(k, a): (Key, _)| { let _ = write!(output, "{}: {}, ", k.to_discriminant().local_name(), a.local_name().split('.').last().unwrap()); }); output.replace("Key", "") @@ -72,7 +72,7 @@ impl InputMapperMessageHandler { "Attempting to convert a Key with enum index {}, which is larger than the number of Key enums", i ); - unsafe { std::mem::transmute_copy::(&i) } + (i as u8).try_into().unwrap() }) .collect::>(); diff --git a/editor/src/messages/input_mapper/utility_types/input_keyboard.rs b/editor/src/messages/input_mapper/utility_types/input_keyboard.rs index e946de3c9..8e65c319d 100644 --- a/editor/src/messages/input_mapper/utility_types/input_keyboard.rs +++ b/editor/src/messages/input_mapper/utility_types/input_keyboard.rs @@ -50,7 +50,8 @@ bitflags! { // (although we ignore the shift key, so the user doesn't have to press `Ctrl Shift +` on a US keyboard), even if the keyboard layout // is for a different locale where the `+` key is somewhere entirely different, shifted or not. This would then also work for numpad `+`. #[impl_message(Message, InputMapperMessage, KeyDown)] -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Deserialize, Serialize, specta::Type)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Deserialize, Serialize, specta::Type, num_enum::TryFromPrimitive)] +#[repr(u8)] pub enum Key { // Writing system keys Digit0, diff --git a/editor/src/messages/message.rs b/editor/src/messages/message.rs index bee7fccfa..c66fee415 100644 --- a/editor/src/messages/message.rs +++ b/editor/src/messages/message.rs @@ -41,28 +41,6 @@ pub enum Message { Workspace(WorkspaceMessage), } -impl Message { - /// Returns the byte representation of the message. - /// - /// # Safety - /// This function reads from uninitialized memory!!! - /// Only use if you know what you are doing. - unsafe fn as_slice(&self) -> &[u8] { - core::slice::from_raw_parts(self as *const Message as *const u8, std::mem::size_of::()) - } - - /// Returns a pseudo hash that should uniquely identify the message. - /// This is needed because `Hash` is not implemented for `f64`s - /// - /// # Safety - /// This function reads from uninitialized memory but the generated value should be fine. - pub fn pseudo_hash(&self) -> u64 { - let mut s = DefaultHasher::new(); - unsafe { self.as_slice() }.hash(&mut s); - s.finish() - } -} - /// Provides an impl of `specta::Type` for `MessageDiscriminant`, the struct created by `impl_message`. /// Specta isn't integrated with `impl_message`, so a remote impl must be provided using this /// struct. diff --git a/editor/src/messages/portfolio/document/document_message_handler.rs b/editor/src/messages/portfolio/document/document_message_handler.rs index 5a4d0218b..29d628b1f 100644 --- a/editor/src/messages/portfolio/document/document_message_handler.rs +++ b/editor/src/messages/portfolio/document/document_message_handler.rs @@ -1001,6 +1001,10 @@ impl DocumentMessageHandler { // Calculate the size of the region to be exported and generate an SVG of the artwork below this layer within that region let transform = self.document_legacy.multiply_transforms(&layer_path).unwrap(); let size = DVec2::new(transform.transform_vector2(DVec2::new(1., 0.)).length(), transform.transform_vector2(DVec2::new(0., 1.)).length()); + // TODO: Fix this hack + // This is a hack to prevent the compiler from optimizing out the size calculation which likely is due + // to undefined behavior. THIS IS NOT A FIX. + log::trace!("size: {:?}", size); let svg = self.render_document(size, transform.inverse(), persistent_data, DocumentRenderMode::OnlyBelowLayerInFolder(&layer_path)); self.restore_document_transform(old_transforms); diff --git a/editor/src/messages/portfolio/document/node_graph/node_graph_message_handler/document_node_types.rs b/editor/src/messages/portfolio/document/node_graph/node_graph_message_handler/document_node_types.rs index 8e6c206e5..e2a7c98e5 100644 --- a/editor/src/messages/portfolio/document/node_graph/node_graph_message_handler/document_node_types.rs +++ b/editor/src/messages/portfolio/document/node_graph/node_graph_message_handler/document_node_types.rs @@ -148,7 +148,7 @@ fn static_nodes() -> Vec { 1, DocumentNode { inputs: vec![NodeInput::node(0, 0)], - implementation: DocumentNodeImplementation::proto("graphene_std::memo::MonitorNode<_>"), + implementation: DocumentNodeImplementation::proto("graphene_core::memo::MonitorNode<_>"), ..Default::default() }, ), @@ -198,7 +198,7 @@ fn static_nodes() -> Vec { DocumentNode { name: "Cache".to_string(), inputs: vec![NodeInput::ShortCircut(concrete!(())), NodeInput::node(0, 0)], - implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_std::memo::CacheNode")), + implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::memo::MemoNode<_, _>")), ..Default::default() }, // We currently just clone by default @@ -262,7 +262,7 @@ fn static_nodes() -> Vec { DocumentNode { name: "Cache".to_string(), inputs: vec![NodeInput::ShortCircut(concrete!(())), NodeInput::node(0, 0)], - implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_std::memo::CacheNode")), + implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::memo::MemoNode<_, _>")), ..Default::default() }, ] @@ -305,7 +305,7 @@ fn static_nodes() -> Vec { DocumentNode { name: "Cache".to_string(), inputs: vec![NodeInput::ShortCircut(concrete!(())), NodeInput::node(1, 0)], - implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_std::memo::CacheNode")), + implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::memo::MemoNode<_, _>")), ..Default::default() }, DocumentNode { @@ -355,13 +355,13 @@ fn static_nodes() -> Vec { DocumentNode { name: "LetNode".to_string(), inputs: vec![NodeInput::node(0, 0)], - implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_std::memo::LetNode<_>")), + implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::memo::LetNode<_>")), ..Default::default() }, DocumentNode { name: "RefNode".to_string(), inputs: vec![NodeInput::ShortCircut(concrete!(())), NodeInput::lambda(1, 0)], - implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_std::memo::RefNode<_, _>")), + implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::memo::RefNode<_, _>")), ..Default::default() }, ] @@ -392,7 +392,7 @@ fn static_nodes() -> Vec { DocumentNodeType { name: "End Scope", category: "Ignore", - identifier: NodeImplementation::proto("graphene_std::memo::EndLetNode<_>"), + identifier: NodeImplementation::proto("graphene_core::memo::EndLetNode<_>"), inputs: vec![ DocumentInputType { name: "Scope", @@ -666,47 +666,6 @@ fn static_nodes() -> Vec { ], properties: node_properties::no_properties, }, - DocumentNodeType { - name: "Gaussian Blur", - category: "Ignore", - 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")), - ..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")), - ..Default::default() - }, - ), - ] - .into_iter() - .collect(), - ..Default::default() - }), - inputs: vec![ - DocumentInputType::value("Image", TaggedValue::ImageFrame(ImageFrame::empty()), true), - DocumentInputType::value("Radius", TaggedValue::U32(3), false), - DocumentInputType::value("Sigma", TaggedValue::F64(1.), false), - ], - outputs: vec![DocumentOutputType { - name: "Image", - data_type: FrontendGraphDataType::Raster, - }], - properties: node_properties::blur_image_properties, - }, DocumentNodeType { name: "Brush", category: "Brush", @@ -735,34 +694,9 @@ fn static_nodes() -> Vec { properties: node_properties::no_properties, }, DocumentNodeType { - name: "Cache", + name: "Memoize", category: "Structural", - identifier: NodeImplementation::DocumentNode(NodeNetwork { - inputs: vec![0], - outputs: vec![NodeOutput::new(1, 0)], - nodes: [ - ( - 0, - DocumentNode { - name: "CacheNode".to_string(), - inputs: vec![NodeInput::ShortCircut(concrete!(())), NodeInput::Network(concrete!(ImageFrame))], - implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_std::memo::CacheNode")), - ..Default::default() - }, - ), - ( - 1, - DocumentNode { - name: "CloneNode".to_string(), - inputs: vec![NodeInput::node(0, 0)], - implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::ops::CloneNode<_>")), - ..Default::default() - }, - ), - ] - .into(), - ..Default::default() - }), + identifier: NodeImplementation::proto("graphene_core::memo::MemoNode<_, _>"), inputs: vec![DocumentInputType::value("Image", TaggedValue::ImageFrame(ImageFrame::empty()), true)], outputs: vec![DocumentOutputType::new("Image", FrontendGraphDataType::Raster)], properties: node_properties::no_properties, @@ -778,7 +712,7 @@ fn static_nodes() -> Vec { DocumentNodeType { name: "Ref", category: "Structural", - identifier: NodeImplementation::proto("graphene_std::memo::CacheNode"), + identifier: NodeImplementation::proto("graphene_core::memo::MemoNode<_, _>"), inputs: vec![DocumentInputType::value("Image", TaggedValue::ImageFrame(ImageFrame::empty()), true)], outputs: vec![DocumentOutputType::new("Image", FrontendGraphDataType::Raster)], properties: node_properties::no_properties, @@ -1330,7 +1264,7 @@ pub fn wrap_network_in_scope(mut network: NodeNetwork) -> NodeNetwork { let mut network_inputs = Vec::new(); let mut input_type = None; for (id, node) in network.nodes.iter() { - for (index, input) in node.inputs.iter().enumerate() { + for input in node.inputs.iter() { if let NodeInput::Network(_) = input { if input_type.is_none() { input_type = Some(input.clone()); diff --git a/editor/src/messages/tool/tool_messages/select_tool.rs b/editor/src/messages/tool/tool_messages/select_tool.rs index 5ba583c45..27c750e08 100644 --- a/editor/src/messages/tool/tool_messages/select_tool.rs +++ b/editor/src/messages/tool/tool_messages/select_tool.rs @@ -1222,9 +1222,7 @@ fn edit_layer_deepest_manipulation(intersect: &Layer, responses: &mut VecDeque, incoming_layer_path_vector: &Vec) -> bool { let layer_paths = document.document_legacy.folder_children_paths(layer_path); for path in layer_paths { - if path == *incoming_layer_path_vector { - return true; - } else if document.document_legacy.is_folder(path.clone()) && recursive_search(document, &path, incoming_layer_path_vector) { + if path == *incoming_layer_path_vector || document.document_legacy.is_folder(path.clone()) && recursive_search(document, &path, incoming_layer_path_vector) { return true; } } diff --git a/editor/src/node_graph_executor.rs b/editor/src/node_graph_executor.rs index 639b91838..601290717 100644 --- a/editor/src/node_graph_executor.rs +++ b/editor/src/node_graph_executor.rs @@ -7,7 +7,6 @@ use crate::messages::prelude::*; use document_legacy::layers::layer_info::LayerDataType; use document_legacy::{LayerId, Operation}; -use dyn_any::DynAny; use graph_craft::document::value::TaggedValue; use graph_craft::document::{generate_uuid, DocumentNodeImplementation, NodeId, NodeNetwork}; use graph_craft::executor::Compiler; @@ -18,7 +17,7 @@ use graphene_core::renderer::{SvgSegment, SvgSegmentList}; use graphene_core::text::FontCache; use graphene_core::vector::style::ViewMode; -use graphene_core::wasm_application_io::{WasmApplicationIo, WasmSurfaceHandleFrame}; +use graphene_core::wasm_application_io::WasmApplicationIo; use graphene_core::{Color, EditorApi, SurfaceFrame, SurfaceId}; use interpreted_executor::executor::DynamicExecutor; @@ -110,7 +109,7 @@ impl NodeRuntime { updates: responses, new_thumbnails: self.thumbnails.clone(), }; - self.sender.send(response); + self.sender.send(response).expect("Failed to send response"); } } } @@ -118,7 +117,7 @@ impl NodeRuntime { /// Wraps a network in a scope and returns the new network and the paths to the monitor nodes. fn wrap_network(network: NodeNetwork) -> (NodeNetwork, Vec>) { - let mut scoped_network = wrap_network_in_scope(network); + let scoped_network = wrap_network_in_scope(network); //scoped_network.generate_node_paths(&[]); let monitor_nodes = scoped_network @@ -126,7 +125,6 @@ impl NodeRuntime { .filter(|(node, _, _)| node.implementation == DocumentNodeImplementation::proto("graphene_std::memo::MonitorNode<_>")) .map(|(_, _, path)| path) .collect(); - //scoped_network.remove_dead_nodes(); (scoped_network, monitor_nodes) } @@ -149,40 +147,23 @@ impl NodeRuntime { return Err(e); } - use dyn_any::IntoDynAny; use graph_craft::executor::Executor; let result = match self.executor.input_type() { - Some(t) if t == concrete!(EditorApi) => self.executor.execute(editor_api.into_dyn()).await.map_err(|e| e.to_string()), - Some(t) if t == concrete!(()) => self.executor.execute(().into_dyn()).await.map_err(|e| e.to_string()), + Some(t) if t == concrete!(EditorApi) => (&self.executor).execute(editor_api).await.map_err(|e| e.to_string()), + Some(t) if t == concrete!(()) => (&self.executor).execute(()).await.map_err(|e| e.to_string()), _ => Err("Invalid input type".to_string()), - }; + }?; - match result { - Ok(result) => { - if DynAny::type_id(result.as_ref()) == core::any::TypeId::of::() { - let Ok(value) = dyn_any::downcast::(result) else { unreachable!()}; - let new_id = value.surface_handle.surface_id; - let old_id = self.canvas_cache.insert(path.to_vec(), new_id); - if let Some(old_id) = old_id { - if old_id != new_id { - self.wasm_io.destroy_surface(old_id); - } - } - return Ok(TaggedValue::SurfaceFrame(SurfaceFrame { - surface_id: new_id, - transform: value.transform, - })); - } - - let type_name = DynAny::type_name(result.as_ref()); - match TaggedValue::try_from_any(result) { - Some(x) => Ok(x), - None => Err(format!("Invalid output type: {}", type_name)), + if let TaggedValue::SurfaceFrame(SurfaceFrame { surface_id, transform }) = result { + let old_id = self.canvas_cache.insert(path.to_vec(), surface_id); + if let Some(old_id) = old_id { + if old_id != surface_id { + self.wasm_io.destroy_surface(old_id); } } - Err(e) => Err(e), } + Ok(result) } /// Recomputes the thumbnails for the layers in the graph, modifying the state and updating the UI. @@ -289,13 +270,13 @@ impl NodeGraphExecutor { image_frame, generation_id, }; - self.sender.send(NodeRuntimeMessage::GenerationRequest(request)); + self.sender.send(NodeRuntimeMessage::GenerationRequest(request)).expect("Failed to send generation request"); generation_id } pub fn update_font_cache(&self, font_cache: FontCache) { - self.sender.send(NodeRuntimeMessage::FontCacheUpdate(font_cache)); + self.sender.send(NodeRuntimeMessage::FontCacheUpdate(font_cache)).expect("Failed to send font cache update"); } pub fn introspect_node(&self, path: &[NodeId]) -> Option> { diff --git a/frontend/wasm/src/editor_api.rs b/frontend/wasm/src/editor_api.rs index de8882bd0..a3d56ca6a 100644 --- a/frontend/wasm/src/editor_api.rs +++ b/frontend/wasm/src/editor_api.rs @@ -72,31 +72,33 @@ async fn poll_node_graph_evaluation() { editor::node_graph_executor::run_node_graph().await; // Get the editor instances, dispatch the message, and store the `FrontendMessage` queue response - EDITOR_INSTANCES.with(|instances| { - JS_EDITOR_HANDLES.with(|handles| { - // Mutably borrow the editors, and if successful, we can access them in the closure - instances.try_borrow_mut().map(|mut editors| { - // Get the editor instance for this editor ID, then dispatch the message to the backend, and return its response `FrontendMessage` queue - for (id, editor) in editors.iter_mut() { - let handles = handles.borrow_mut(); - let handle = handles.get(id).unwrap(); - let mut messages = VecDeque::new(); - editor.poll_node_graph_evaluation(&mut messages); - // Send each `FrontendMessage` to the JavaScript frontend + EDITOR_INSTANCES + .with(|instances| { + JS_EDITOR_HANDLES.with(|handles| { + // Mutably borrow the editors, and if successful, we can access them in the closure + instances.try_borrow_mut().map(|mut editors| { + // Get the editor instance for this editor ID, then dispatch the message to the backend, and return its response `FrontendMessage` queue + for (id, editor) in editors.iter_mut() { + let handles = handles.borrow_mut(); + let handle = handles.get(id).unwrap(); + let mut messages = VecDeque::new(); + editor.poll_node_graph_evaluation(&mut messages); + // Send each `FrontendMessage` to the JavaScript frontend - let mut responses = Vec::new(); - for message in messages.into_iter() { - responses.extend(editor.handle_message(message)); - } + let mut responses = Vec::new(); + for message in messages.into_iter() { + responses.extend(editor.handle_message(message)); + } - for response in responses.into_iter() { - handle.send_frontend_message_to_js(response); + for response in responses.into_iter() { + handle.send_frontend_message_to_js(response); + } + // If the editor cannot be borrowed then it has encountered a panic - we should just ignore new dispatches } - // If the editor cannot be borrowed then it has encountered a panic - we should just ignore new dispatches - } + }) }) }) - }); + .unwrap_or_else(|_| log::error!("Failed to borrow editor instances")); } #[wasm_bindgen] diff --git a/node-graph/borrow_stack/Cargo.toml b/node-graph/borrow_stack/Cargo.toml deleted file mode 100644 index ee86bc38a..000000000 --- a/node-graph/borrow_stack/Cargo.toml +++ /dev/null @@ -1,10 +0,0 @@ -[package] -name = "borrow_stack" -version = "0.1.0" -edition = "2021" -license = "MIT OR Apache-2.0" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -dyn-any = {path = "../../libraries/dyn-any"} diff --git a/node-graph/borrow_stack/src/lib.rs b/node-graph/borrow_stack/src/lib.rs deleted file mode 100644 index 321544ae4..000000000 --- a/node-graph/borrow_stack/src/lib.rs +++ /dev/null @@ -1,80 +0,0 @@ -use std::{ - mem::MaybeUninit, - pin::Pin, - sync::atomic::{AtomicUsize, Ordering}, -}; - -use dyn_any::StaticTypeSized; - -pub trait BorrowStack { - /// # Safety - unsafe fn push(&self, value: T); - /// # Safety - unsafe fn pop(&self); - /// # Safety - unsafe fn get<'a>(&self) -> &'a [::Static]; -} - -#[derive(Debug)] -pub struct FixedSizeStack { - data: Pin]>>, - capacity: usize, - len: AtomicUsize, -} - -impl<'n, T: 'n + dyn_any::StaticTypeSized> FixedSizeStack { - pub fn new(capacity: usize) -> Self { - let layout = std::alloc::Layout::array::>(capacity).unwrap(); - let array = unsafe { std::alloc::alloc(layout) }; - let array = Box::into_pin(unsafe { Box::from_raw(core::ptr::slice_from_raw_parts_mut(array as *mut MaybeUninit, capacity)) }); - - Self { - data: array, - capacity, - len: AtomicUsize::new(0), - } - } - - pub fn len(&self) -> usize { - self.len.load(Ordering::SeqCst) - } - - pub fn is_empty(&self) -> bool { - self.len.load(Ordering::SeqCst) == 0 - } - pub fn push_fn<'a>(&self, f: impl FnOnce(&'a [::Static]) -> T) { - assert_eq!(std::mem::size_of::(), std::mem::size_of::()); - unsafe { self.push(f(self.get())) } - } -} - -impl BorrowStack for FixedSizeStack { - unsafe fn push(&self, value: T) { - let len = self.len.load(Ordering::SeqCst); - assert!(len < self.capacity); - let ptr = self.data[len].as_ptr(); - let static_value = std::mem::transmute_copy(&value); - (ptr as *mut T::Static).write(static_value); - std::mem::forget(value); - self.len.fetch_add(1, Ordering::SeqCst); - } - - unsafe fn pop(&self) { - let ptr = self.data[self.len.load(Ordering::SeqCst)].as_ptr(); - let _ = Box::from_raw(ptr as *mut T); - self.len.fetch_sub(1, Ordering::SeqCst); - } - - unsafe fn get<'a>(&self) -> &'a [T::Static] { - std::slice::from_raw_parts(self.data.as_ptr() as *const T::Static, self.len.load(Ordering::SeqCst)) - } -} - -#[cfg(test)] -mod tests { - #[test] - fn it_works() { - let result = 2 + 2; - assert_eq!(result, 4); - } -} diff --git a/node-graph/compilation-client/src/main.rs b/node-graph/compilation-client/src/main.rs index 8fa7c0d7c..0080d60d3 100644 --- a/node-graph/compilation-client/src/main.rs +++ b/node-graph/compilation-client/src/main.rs @@ -4,7 +4,7 @@ use graph_craft::concrete; use graph_craft::document::value::TaggedValue; use graph_craft::document::*; use graph_craft::*; -use graphene_core::raster::adjustments::{BlendMode, BlendNode}; +use graphene_core::raster::adjustments::BlendMode; use graphene_core::Color; use std::borrow::Cow; diff --git a/node-graph/gcore/src/application_io.rs b/node-graph/gcore/src/application_io.rs index d06a83711..857fc3ade 100644 --- a/node-graph/gcore/src/application_io.rs +++ b/node-graph/gcore/src/application_io.rs @@ -36,34 +36,42 @@ unsafe impl StaticType for SurfaceFrame { type Static = SurfaceFrame; } +impl From> for SurfaceFrame { + fn from(x: SurfaceHandleFrame) -> Self { + Self { + surface_id: x.surface_handle.surface_id, + transform: x.transform, + } + } +} + #[derive(Clone)] -pub struct SurfaceHandle<'a, Surface> { +pub struct SurfaceHandle { pub surface_id: SurfaceId, pub surface: Surface, - application_io: &'a dyn ApplicationIo, } -unsafe impl StaticType for SurfaceHandle<'_, T> { - type Static = SurfaceHandle<'static, T>; +unsafe impl StaticType for SurfaceHandle { + type Static = SurfaceHandle; } #[derive(Clone)] -pub struct SurfaceHandleFrame<'a, Surface> { - pub surface_handle: Arc>, +pub struct SurfaceHandleFrame { + pub surface_handle: Arc>, pub transform: DAffine2, } -unsafe impl StaticType for SurfaceHandleFrame<'_, T> { - type Static = SurfaceHandleFrame<'static, T>; +unsafe impl StaticType for SurfaceHandleFrame { + type Static = SurfaceHandleFrame; } -impl Transform for SurfaceHandleFrame<'_, T> { +impl Transform for SurfaceHandleFrame { fn transform(&self) -> DAffine2 { self.transform } } -impl TransformMut for SurfaceHandleFrame<'_, T> { +impl TransformMut for SurfaceHandleFrame { fn transform_mut(&mut self) -> &mut DAffine2 { &mut self.transform } @@ -142,10 +150,10 @@ impl<'a, T> AsRef> for EditorApi<'a, T> { #[derive(Debug, Clone, Copy, Default)] pub struct ExtractImageFrame; -impl<'a: 'input, 'input, T> Node<'input, &'a EditorApi<'a, T>> for ExtractImageFrame { +impl<'a: 'input, 'input, T> Node<'input, EditorApi<'a, T>> for ExtractImageFrame { type Output = ImageFrame; - fn eval(&'input self, editor_api: &'a EditorApi<'a, T>) -> Self::Output { - editor_api.image_frame.clone().unwrap_or(ImageFrame::identity()) + fn eval(&'input self, editor_api: EditorApi<'a, T>) -> Self::Output { + editor_api.image_frame.unwrap_or(ImageFrame::identity()) } } diff --git a/node-graph/gcore/src/application_io/wasm_application_io.rs b/node-graph/gcore/src/application_io/wasm_application_io.rs index a9a696dd4..b16ea643f 100644 --- a/node-graph/gcore/src/application_io/wasm_application_io.rs +++ b/node-graph/gcore/src/application_io/wasm_application_io.rs @@ -1,8 +1,8 @@ -use std::{cell::RefCell, collections::HashMap, sync::Mutex}; +use std::{cell::RefCell, collections::HashMap}; use super::{ApplicationIo, SurfaceHandle, SurfaceHandleFrame, SurfaceId}; use crate::{ - raster::{color::SRGBA8, ImageFrame, Pixel}, + raster::{color::SRGBA8, ImageFrame}, Node, }; use alloc::sync::Arc; @@ -35,7 +35,7 @@ impl ApplicationIo for WasmApplicationIo { type Surface = CanvasRenderingContext2d; fn create_surface(&self) -> SurfaceHandle { - let mut wrapper = || { + let wrapper = || { let document = window().expect("should have a window in this context").document().expect("window should have a document"); let canvas: HtmlCanvasElement = document.create_element("canvas")?.dyn_into::()?; @@ -53,7 +53,7 @@ impl ApplicationIo for WasmApplicationIo { let image_canvases_key = JsValue::from_str("imageCanvases"); let mut canvases = Reflect::get(&window, &image_canvases_key); - if let Err(e) = canvases { + if let Err(_) = canvases { Reflect::set(&JsValue::from(web_sys::window().unwrap()), &image_canvases_key, &Object::new()).unwrap(); canvases = Reflect::get(&window, &image_canvases_key); } @@ -66,11 +66,7 @@ impl ApplicationIo for WasmApplicationIo { // Use Reflect API to set property Reflect::set(&canvases, &js_key, &js_value)?; - Ok::<_, JsValue>(SurfaceHandle { - surface_id: id, - surface: context, - application_io: self, - }) + Ok::<_, JsValue>(SurfaceHandle { surface_id: id, surface: context }) }; wrapper().expect("should be able to set canvas in global scope") @@ -99,13 +95,13 @@ impl ApplicationIo for WasmApplicationIo { } } -pub type WasmSurfaceHandle<'a> = SurfaceHandle<'a, CanvasRenderingContext2d>; -pub type WasmSurfaceHandleFrame<'a> = SurfaceHandleFrame<'a, CanvasRenderingContext2d>; +pub type WasmSurfaceHandle = SurfaceHandle; +pub type WasmSurfaceHandleFrame = SurfaceHandleFrame; pub struct CreateSurfaceNode {} #[node_macro::node_fn(CreateSurfaceNode)] -fn create_surface_node<'a: 'input>(editor: &'a WasmEditorApi<'a>) -> Arc> { +fn create_surface_node<'a: 'input>(editor: WasmEditorApi<'a>) -> Arc> { editor.application_io.create_surface().into() } @@ -114,7 +110,7 @@ pub struct DrawImageFrameNode { } #[node_macro::node_fn(DrawImageFrameNode)] -async fn draw_image_frame_node<'a: 'input>(image: ImageFrame, surface_handle: Arc>) -> SurfaceHandleFrame<'a, CanvasRenderingContext2d> { +async fn draw_image_frame_node<'a: 'input>(image: ImageFrame, surface_handle: Arc>) -> SurfaceHandleFrame { let image_data = image.image.data; let array: Clamped<&[u8]> = Clamped(bytemuck::cast_slice(image_data.as_slice())); if image.image.width > 0 && image.image.height > 0 { diff --git a/node-graph/gcore/src/lib.rs b/node-graph/gcore/src/lib.rs index b1d90bada..c68fd4325 100644 --- a/node-graph/gcore/src/lib.rs +++ b/node-graph/gcore/src/lib.rs @@ -20,6 +20,7 @@ pub mod value; #[cfg(feature = "gpu")] pub mod gpu; +pub mod memo; pub mod storage; pub mod raster; @@ -44,7 +45,7 @@ pub use raster::Color; pub trait Node<'i, Input: 'i>: 'i { type Output: 'i; fn eval(&'i self, input: Input) -> Self::Output; - fn reset(self: Pin<&mut Self>) {} + fn reset(&self) {} #[cfg(feature = "std")] fn serialize(&self) -> Option> { log::warn!("Node::serialize not implemented for {}", core::any::type_name::()); diff --git a/node-graph/gcore/src/memo.rs b/node-graph/gcore/src/memo.rs new file mode 100644 index 000000000..851301222 --- /dev/null +++ b/node-graph/gcore/src/memo.rs @@ -0,0 +1,158 @@ +use crate::Node; +use core::future::Future; + +#[cfg(feature = "alloc")] +use alloc::sync::Arc; +use core::cell::Cell; +use core::pin::Pin; +use std::marker::PhantomData; + +// Caches the output of a given Node and acts as a proxy +#[derive(Default)] +pub struct MemoNode { + cache: Cell>, + node: CachedNode, +} +impl<'i, 'o: 'i, T: 'i + Clone + 'o, CachedNode: 'i> Node<'i, ()> for MemoNode +where + CachedNode: for<'any_input> Node<'any_input, ()>, + for<'a> >::Output: core::future::Future + 'a, +{ + // TODO: This should return a reference to the cached cached_value + // but that requires a lot of lifetime magic <- This was suggested by copilot but is pretty acurate xD + type Output = Pin + 'i>>; + fn eval(&'i self, input: ()) -> Self::Output { + Box::pin(async move { + if let Some(cached_value) = self.cache.take() { + self.cache.set(Some(cached_value.clone())); + cached_value + } else { + let value = self.node.eval(input).await; + self.cache.set(Some(value.clone())); + value + } + }) + } + + fn reset(&self) { + self.cache.set(None); + } +} + +impl MemoNode { + pub const fn new(node: CachedNode) -> MemoNode { + MemoNode { cache: Cell::new(None), node } + } +} + +#[cfg(feature = "alloc")] +/// Caches the output of the last graph evaluation for introspection +#[derive(Default)] +pub struct MonitorNode { + output: Cell>>, +} + +#[cfg(feature = "alloc")] +impl<'i, T: 'static + Clone> Node<'i, T> for MonitorNode { + type Output = T; + fn eval(&'i self, input: T) -> Self::Output { + self.output.set(Some(Arc::new(input.clone()))); + input + } + + fn serialize(&self) -> Option> { + let out = self.output.take(); + self.output.set(out.clone()); + (out).as_ref().map(|output| output.clone() as Arc) + } +} + +#[cfg(feature = "alloc")] +impl MonitorNode { + pub const fn new() -> MonitorNode { + MonitorNode { output: Cell::new(None) } + } +} + +// Caches the output of a given Node and acts as a proxy +/// It provides two modes of operation, it can either be set +/// when calling the node with a `Some` variant or the last +/// value that was added is returned when calling it with `None` +#[derive(Default)] +pub struct LetNode { + // We have to use an append only data structure to make sure the references + // to the cache entries are always valid + // TODO: We only ever access the last value so there is not really a reason for us + // to store the previous entries. This should be reworked in the future + cache: Cell>, +} +impl<'i, T: 'i + Clone> Node<'i, Option> for LetNode { + type Output = T; + fn eval(&'i self, input: Option) -> Self::Output { + if let Some(input) = input { + self.cache.set(Some(input.clone())); + input + } else { + let value = self.cache.take(); + self.cache.set(value.clone()); + value.expect("LetNode was not initialized. This can happen if you try to evaluate a node that depends on the EditorApi in the node_registry") + } + } + fn reset(&self) { + self.cache.set(None); + } +} + +impl std::marker::Unpin for LetNode {} + +impl LetNode { + pub fn new() -> LetNode { + LetNode { cache: Default::default() } + } +} + +/// Caches the output of a given Node and acts as a proxy +#[derive(Debug, Clone, PartialEq, Eq, Default)] +pub struct EndLetNode { + input: Input, +} +impl<'i, T: 'i, Input> Node<'i, T> for EndLetNode +where + Input: Node<'i, ()>, +{ + type Output = ::Output; + fn eval(&'i self, _: T) -> Self::Output { + let result = self.input.eval(()); + result + } +} + +impl EndLetNode { + pub const fn new(input: Input) -> EndLetNode { + EndLetNode { input } + } +} + +pub use crate::ops::SomeNode as InitNode; + +#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] +pub struct RefNode { + let_node: Let, + _t: PhantomData, +} + +impl<'i, T: 'i, Let> Node<'i, ()> for RefNode +where + Let: for<'a> Node<'a, Option>, +{ + type Output = >>::Output; + fn eval(&'i self, _: ()) -> Self::Output { + self.let_node.eval(None) + } +} + +impl RefNode { + pub const fn new(let_node: Let) -> RefNode { + RefNode { let_node, _t: PhantomData } + } +} diff --git a/node-graph/gcore/src/storage.rs b/node-graph/gcore/src/storage.rs index ae4bfb612..0ba60feb0 100644 --- a/node-graph/gcore/src/storage.rs +++ b/node-graph/gcore/src/storage.rs @@ -1,22 +1,10 @@ use crate::Node; -use core::cell::RefMut; -use core::marker::PhantomData; use core::ops::{Deref, DerefMut, Index, IndexMut}; pub struct SetNode { storage: Storage, } -/* -#[node_macro::node_fn(SetNode)] -fn set_node<_T, _I, A: 'input>(input: (_T, _I), mut storage: RefMut<'input, A>) -where - A: DerefMut, - A::Target: IndexMut<_I, Output = _T>, -{ - let (value, index) = input; - *storage.deref_mut().index_mut(index).deref_mut() = value; -}*/ impl<'input, T: 'input, I: 'input, A: 'input + 'input, S0: 'input> Node<'input, (T, I)> for SetNode where A: DerefMut, @@ -94,7 +82,7 @@ where #[cfg(test)] mod test { - use crate::value::{CopiedNode, OnceCellNode, RefCellMutNode, UnsafeMutValueNode, ValueNode}; + use crate::value::{CopiedNode, OnceCellNode}; use crate::Node; use super::*; diff --git a/node-graph/gcore/src/structural.rs b/node-graph/gcore/src/structural.rs index a3c053cf0..273db245c 100644 --- a/node-graph/gcore/src/structural.rs +++ b/node-graph/gcore/src/structural.rs @@ -172,6 +172,7 @@ mod test { } #[test] + #[allow(clippy::unit_cmp)] fn test_apply() { let mut array = [1, 2, 3]; let slice = &mut array; diff --git a/node-graph/gcore/src/text.rs b/node-graph/gcore/src/text.rs index 8b8004643..da1da20bf 100644 --- a/node-graph/gcore/src/text.rs +++ b/node-graph/gcore/src/text.rs @@ -15,7 +15,7 @@ pub struct TextGenerator { } #[node_fn(TextGenerator)] -fn generate_text<'a: 'input>(editor: &'a EditorApi<'a>, text: String, font_name: Font, font_size: f64) -> crate::vector::VectorData { +fn generate_text<'a: 'input>(editor: EditorApi<'a>, text: String, font_name: Font, font_size: f64) -> crate::vector::VectorData { let buzz_face = editor.font_cache.get(&font_name).map(|data| load_face(data)); crate::vector::VectorData::from_subpaths(to_path(&text, buzz_face, font_size, None)) } diff --git a/node-graph/gcore/src/types.rs b/node-graph/gcore/src/types.rs index a29e8f354..931ff323a 100644 --- a/node-graph/gcore/src/types.rs +++ b/node-graph/gcore/src/types.rs @@ -56,15 +56,11 @@ macro_rules! generic { #[macro_export] macro_rules! fn_type { - ($input:ty, $output:ty) => { - Type::Fn(Box::new(concrete!($input)), Box::new(concrete!($output))) + ($type:ty) => { + Type::Fn(Box::new(concrete!(())), Box::new(concrete!($type))) }; -} - -#[macro_export] -macro_rules! value_fn { - ($output:ty) => { - Type::Fn(Box::new(concrete!(())), Box::new(concrete!($output))) + ($in_type:ty, $type:ty) => { + Type::Fn(Box::new(concrete!(($in_type))), Box::new(concrete!($type))) }; } @@ -109,6 +105,7 @@ pub enum Type { Generic(Cow<'static, str>), Concrete(TypeDescriptor), Fn(Box, Box), + Future(Box), } impl Type { @@ -169,6 +166,7 @@ impl Type { Self::Generic(_) => None, Self::Concrete(ty) => Some(ty.size), Self::Fn(_, _) => None, + Self::Future(_) => None, } } @@ -177,6 +175,7 @@ impl Type { Self::Generic(_) => None, Self::Concrete(ty) => Some(ty.align), Self::Fn(_, _) => None, + Self::Future(_) => None, } } } @@ -190,6 +189,7 @@ impl core::fmt::Debug for Type { #[cfg(not(feature = "type_id_logging"))] Self::Concrete(arg0) => write!(f, "Concrete({})", arg0.name), Self::Fn(arg0, arg1) => write!(f, "({:?} -> {:?})", arg0, arg1), + Self::Future(arg0) => write!(f, "Future({:?})", arg0), } } } @@ -200,6 +200,7 @@ impl std::fmt::Display for Type { Type::Generic(name) => write!(f, "{}", name), Type::Concrete(ty) => write!(f, "{}", ty.name), Type::Fn(input, output) => write!(f, "({} -> {})", input, output), + Type::Future(ty) => write!(f, "Future<{}>", ty), } } } diff --git a/node-graph/gcore/src/value.rs b/node-graph/gcore/src/value.rs index 7c100f921..6a492aa7d 100644 --- a/node-graph/gcore/src/value.rs +++ b/node-graph/gcore/src/value.rs @@ -1,7 +1,6 @@ use crate::Node; use core::{ - borrow::BorrowMut, cell::{Cell, RefCell, RefMut}, marker::PhantomData, }; @@ -47,10 +46,7 @@ impl<'i, T: 'i> Node<'i, ()> for RefCellMutNode { type Output = RefMut<'i, T>; #[inline(always)] fn eval(&'i self, _input: ()) -> Self::Output { - #[cfg(not(target_arch = "spirv"))] let a = self.0.borrow_mut(); - #[cfg(target_arch = "spirv")] - let a = unsafe { self.0.try_borrow_mut().unwrap_unchecked() }; a } } @@ -60,24 +56,6 @@ impl RefCellMutNode { RefCellMutNode(RefCell::new(value)) } } -/// #Safety: Never use this as it is unsound. -#[derive(Default, Debug)] -pub struct UnsafeMutValueNode(pub T); - -/// #Safety: Never use this as it is unsound. -impl<'i, T: 'i> Node<'i, ()> for UnsafeMutValueNode { - type Output = &'i mut T; - #[inline(always)] - fn eval(&'i self, _input: ()) -> Self::Output { - unsafe { &mut *(&self.0 as &T as *const T as *mut T) } - } -} - -impl UnsafeMutValueNode { - pub const fn new(value: T) -> UnsafeMutValueNode { - UnsafeMutValueNode(value) - } -} #[derive(Default)] pub struct OnceCellNode(pub Cell); diff --git a/node-graph/graph-craft/src/document.rs b/node-graph/graph-craft/src/document.rs index cb6e57f1e..ec81831e6 100644 --- a/node-graph/graph-craft/src/document.rs +++ b/node-graph/graph-craft/src/document.rs @@ -603,7 +603,6 @@ impl NodeNetwork { fn replace_node_inputs(&mut self, old_input: NodeInput, new_input: NodeInput) { for node in self.nodes.values_mut() { - let node_string = format!("{:?}", node); node.inputs.iter_mut().for_each(|input| { if *input == old_input { *input = new_input.clone(); @@ -893,16 +892,15 @@ impl<'a> Iterator for RecursiveNodeIter<'a> { #[cfg(test)] mod test { + use std::{cell::Cell, sync::atomic::AtomicU64}; + use super::*; use crate::proto::{ConstructionArgs, ProtoNetwork, ProtoNode, ProtoNodeInput}; use graphene_core::NodeIdentifier; fn gen_node_id() -> NodeId { - static mut NODE_ID: NodeId = 3; - unsafe { - NODE_ID += 1; - NODE_ID - } + static NODE_ID: AtomicU64 = AtomicU64::new(4); + NODE_ID.fetch_add(1, std::sync::atomic::Ordering::SeqCst) } fn add_network() -> NodeNetwork { diff --git a/node-graph/graph-craft/src/document/value.rs b/node-graph/graph-craft/src/document/value.rs index 492be7290..aea9ccdf6 100644 --- a/node-graph/graph-craft/src/document/value.rs +++ b/node-graph/graph-craft/src/document/value.rs @@ -6,9 +6,8 @@ use crate::proto::{Any as DAny, FutureAny}; use graphene_core::raster::{to_primtive_string, BlendMode, LuminanceCalculation}; use graphene_core::{Color, Node, Type}; +use dyn_any::DynAny; pub use dyn_any::StaticType; -use dyn_any::{DynAny, Upcast}; -use dyn_clone::DynClone; pub use glam::{DAffine2, DVec2}; use std::hash::Hash; pub use std::sync::Arc; @@ -245,56 +244,60 @@ impl<'a> TaggedValue { } } - pub fn try_from_any(input: Box + 'a>) -> Option { + pub fn try_from_any(input: Box + 'a>) -> Result { use dyn_any::downcast; use std::any::TypeId; match DynAny::type_id(input.as_ref()) { - x if x == TypeId::of::<()>() => Some(TaggedValue::None), - x if x == TypeId::of::() => Some(TaggedValue::String(*downcast(input).unwrap())), - x if x == TypeId::of::() => Some(TaggedValue::U32(*downcast(input).unwrap())), - x if x == TypeId::of::() => Some(TaggedValue::F32(*downcast(input).unwrap())), - x if x == TypeId::of::() => Some(TaggedValue::F64(*downcast(input).unwrap())), - x if x == TypeId::of::() => Some(TaggedValue::Bool(*downcast(input).unwrap())), - x if x == TypeId::of::() => Some(TaggedValue::DVec2(*downcast(input).unwrap())), - x if x == TypeId::of::>() => Some(TaggedValue::OptionalDVec2(*downcast(input).unwrap())), - x if x == TypeId::of::>() => Some(TaggedValue::Image(*downcast(input).unwrap())), - x if x == TypeId::of::>>>() => Some(TaggedValue::RcImage(*downcast(input).unwrap())), - x if x == TypeId::of::>() => Some(TaggedValue::ImageFrame(*downcast(input).unwrap())), - x if x == TypeId::of::() => Some(TaggedValue::Color(*downcast(input).unwrap())), - x if x == TypeId::of::>>() => Some(TaggedValue::Subpaths(*downcast(input).unwrap())), - x if x == TypeId::of::>>() => Some(TaggedValue::RcSubpath(*downcast(input).unwrap())), - x if x == TypeId::of::() => Some(TaggedValue::BlendMode(*downcast(input).unwrap())), - x if x == TypeId::of::() => Some(TaggedValue::ImaginateSamplingMethod(*downcast(input).unwrap())), - x if x == TypeId::of::() => Some(TaggedValue::ImaginateMaskStartingFill(*downcast(input).unwrap())), - x if x == TypeId::of::() => Some(TaggedValue::ImaginateStatus(*downcast(input).unwrap())), - x if x == TypeId::of::>>() => Some(TaggedValue::LayerPath(*downcast(input).unwrap())), - x if x == TypeId::of::() => Some(TaggedValue::DAffine2(*downcast(input).unwrap())), - x if x == TypeId::of::() => Some(TaggedValue::LuminanceCalculation(*downcast(input).unwrap())), - x if x == TypeId::of::() => Some(TaggedValue::VectorData(*downcast(input).unwrap())), - x if x == TypeId::of::() => Some(TaggedValue::Fill(*downcast(input).unwrap())), - x if x == TypeId::of::() => Some(TaggedValue::Stroke(*downcast(input).unwrap())), - x if x == TypeId::of::>() => Some(TaggedValue::VecF32(*downcast(input).unwrap())), - x if x == TypeId::of::() => Some(TaggedValue::RedGreenBlue(*downcast(input).unwrap())), - x if x == TypeId::of::() => Some(TaggedValue::RelativeAbsolute(*downcast(input).unwrap())), - x if x == TypeId::of::() => Some(TaggedValue::SelectiveColorChoice(*downcast(input).unwrap())), - x if x == TypeId::of::() => Some(TaggedValue::LineCap(*downcast(input).unwrap())), - x if x == TypeId::of::() => Some(TaggedValue::LineJoin(*downcast(input).unwrap())), - x if x == TypeId::of::() => Some(TaggedValue::FillType(*downcast(input).unwrap())), - x if x == TypeId::of::() => Some(TaggedValue::GradientType(*downcast(input).unwrap())), - x if x == TypeId::of::)>>() => Some(TaggedValue::GradientPositions(*downcast(input).unwrap())), - x if x == TypeId::of::() => Some(TaggedValue::Quantization(*downcast(input).unwrap())), - x if x == TypeId::of::>() => Some(TaggedValue::OptionalColor(*downcast(input).unwrap())), - x if x == TypeId::of::>() => Some(TaggedValue::ManipulatorGroupIds(*downcast(input).unwrap())), - x if x == TypeId::of::() => Some(TaggedValue::Font(*downcast(input).unwrap())), - x if x == TypeId::of::>() => Some(TaggedValue::BrushStrokes(*downcast(input).unwrap())), - x if x == TypeId::of::>>>() => Some(TaggedValue::Segments(*downcast(input).unwrap())), - x if x == TypeId::of::() => Some(TaggedValue::DocumentNode(*downcast(input).unwrap())), - x if x == TypeId::of::() => Some(TaggedValue::GraphicGroup(*downcast(input).unwrap())), - x if x == TypeId::of::() => Some(TaggedValue::Artboard(*downcast(input).unwrap())), - x if x == TypeId::of::() => Some(TaggedValue::IVec2(*downcast(input).unwrap())), - x if x == TypeId::of::() => Some(TaggedValue::SurfaceFrame(*downcast(input).unwrap())), - _ => None, + x if x == TypeId::of::<()>() => Ok(TaggedValue::None), + x if x == TypeId::of::() => Ok(TaggedValue::String(*downcast(input).unwrap())), + x if x == TypeId::of::() => Ok(TaggedValue::U32(*downcast(input).unwrap())), + x if x == TypeId::of::() => Ok(TaggedValue::F32(*downcast(input).unwrap())), + x if x == TypeId::of::() => Ok(TaggedValue::F64(*downcast(input).unwrap())), + x if x == TypeId::of::() => Ok(TaggedValue::Bool(*downcast(input).unwrap())), + x if x == TypeId::of::() => Ok(TaggedValue::DVec2(*downcast(input).unwrap())), + x if x == TypeId::of::>() => Ok(TaggedValue::OptionalDVec2(*downcast(input).unwrap())), + x if x == TypeId::of::>() => Ok(TaggedValue::Image(*downcast(input).unwrap())), + x if x == TypeId::of::>>>() => Ok(TaggedValue::RcImage(*downcast(input).unwrap())), + x if x == TypeId::of::>() => Ok(TaggedValue::ImageFrame(*downcast(input).unwrap())), + x if x == TypeId::of::() => Ok(TaggedValue::Color(*downcast(input).unwrap())), + x if x == TypeId::of::>>() => Ok(TaggedValue::Subpaths(*downcast(input).unwrap())), + x if x == TypeId::of::>>() => Ok(TaggedValue::RcSubpath(*downcast(input).unwrap())), + x if x == TypeId::of::() => Ok(TaggedValue::BlendMode(*downcast(input).unwrap())), + x if x == TypeId::of::() => Ok(TaggedValue::ImaginateSamplingMethod(*downcast(input).unwrap())), + x if x == TypeId::of::() => Ok(TaggedValue::ImaginateMaskStartingFill(*downcast(input).unwrap())), + x if x == TypeId::of::() => Ok(TaggedValue::ImaginateStatus(*downcast(input).unwrap())), + x if x == TypeId::of::>>() => Ok(TaggedValue::LayerPath(*downcast(input).unwrap())), + x if x == TypeId::of::() => Ok(TaggedValue::DAffine2(*downcast(input).unwrap())), + x if x == TypeId::of::() => Ok(TaggedValue::LuminanceCalculation(*downcast(input).unwrap())), + x if x == TypeId::of::() => Ok(TaggedValue::VectorData(*downcast(input).unwrap())), + x if x == TypeId::of::() => Ok(TaggedValue::Fill(*downcast(input).unwrap())), + x if x == TypeId::of::() => Ok(TaggedValue::Stroke(*downcast(input).unwrap())), + x if x == TypeId::of::>() => Ok(TaggedValue::VecF32(*downcast(input).unwrap())), + x if x == TypeId::of::() => Ok(TaggedValue::RedGreenBlue(*downcast(input).unwrap())), + x if x == TypeId::of::() => Ok(TaggedValue::RelativeAbsolute(*downcast(input).unwrap())), + x if x == TypeId::of::() => Ok(TaggedValue::SelectiveColorChoice(*downcast(input).unwrap())), + x if x == TypeId::of::() => Ok(TaggedValue::LineCap(*downcast(input).unwrap())), + x if x == TypeId::of::() => Ok(TaggedValue::LineJoin(*downcast(input).unwrap())), + x if x == TypeId::of::() => Ok(TaggedValue::FillType(*downcast(input).unwrap())), + x if x == TypeId::of::() => Ok(TaggedValue::GradientType(*downcast(input).unwrap())), + x if x == TypeId::of::)>>() => Ok(TaggedValue::GradientPositions(*downcast(input).unwrap())), + x if x == TypeId::of::() => Ok(TaggedValue::Quantization(*downcast(input).unwrap())), + x if x == TypeId::of::>() => Ok(TaggedValue::OptionalColor(*downcast(input).unwrap())), + x if x == TypeId::of::>() => Ok(TaggedValue::ManipulatorGroupIds(*downcast(input).unwrap())), + x if x == TypeId::of::() => Ok(TaggedValue::Font(*downcast(input).unwrap())), + x if x == TypeId::of::>() => Ok(TaggedValue::BrushStrokes(*downcast(input).unwrap())), + x if x == TypeId::of::>>>() => Ok(TaggedValue::Segments(*downcast(input).unwrap())), + x if x == TypeId::of::() => Ok(TaggedValue::DocumentNode(*downcast(input).unwrap())), + x if x == TypeId::of::() => Ok(TaggedValue::GraphicGroup(*downcast(input).unwrap())), + x if x == TypeId::of::() => Ok(TaggedValue::Artboard(*downcast(input).unwrap())), + x if x == TypeId::of::() => Ok(TaggedValue::IVec2(*downcast(input).unwrap())), + x if x == TypeId::of::() => Ok(TaggedValue::SurfaceFrame(*downcast(input).unwrap())), + x if x == TypeId::of::() => { + let frame = *downcast::(input).unwrap(); + Ok(TaggedValue::SurfaceFrame(frame.into())) + } + _ => Err(format!("Cannot convert {:?} to TaggedValue", DynAny::type_name(input.as_ref()))), } } } @@ -314,82 +317,3 @@ impl UpcastNode { Self { value } } } - -pub type Value<'a> = Box ValueTrait<'i> + 'a>; - -pub trait ValueTrait<'a>: DynAny<'a> + Upcast + 'a> + std::fmt::Debug + DynClone + Sync + Send + 'a {} - -pub trait IntoValue<'a>: Sized + for<'i> ValueTrait<'i> + 'a { - fn into_any(self) -> Value<'a> { - Box::new(self) - } -} - -impl<'a, T: 'a + StaticType + Upcast + 'a> + std::fmt::Debug + PartialEq + Clone + Sync + Send + 'a> ValueTrait<'a> for T {} - -impl<'a, T: for<'i> ValueTrait<'i> + 'a> IntoValue<'a> for T {} - -#[repr(C)] -pub(crate) struct Vtable { - pub(crate) destructor: unsafe fn(*mut ()), - pub(crate) size: usize, - pub(crate) align: usize, -} - -#[repr(C)] -pub(crate) struct TraitObject { - pub(crate) self_ptr: *mut u8, - pub(crate) vtable: &'static Vtable, -} - -impl<'a> PartialEq for Box ValueTrait<'i> + 'a> { - #[cfg_attr(miri, ignore)] - fn eq(&self, other: &Self) -> bool { - if self.type_id() != other.type_id() { - return false; - } - let self_trait_object = unsafe { std::mem::transmute::<&dyn ValueTrait, TraitObject>(self.as_ref()) }; - let other_trait_object = unsafe { std::mem::transmute::<&dyn ValueTrait, TraitObject>(other.as_ref()) }; - let size = self_trait_object.vtable.size; - let self_mem = unsafe { std::slice::from_raw_parts(self_trait_object.self_ptr, size) }; - let other_mem = unsafe { std::slice::from_raw_parts(other_trait_object.self_ptr, size) }; - self_mem == other_mem - } -} - -impl<'a> Hash for Value<'a> { - fn hash(&self, state: &mut H) { - let self_trait_object = unsafe { std::mem::transmute::<&dyn ValueTrait, TraitObject>(self.as_ref()) }; - let size = self_trait_object.vtable.size; - let self_mem = unsafe { std::slice::from_raw_parts(self_trait_object.self_ptr, size) }; - self_mem.hash(state); - } -} - -impl<'a> Clone for Value<'a> { - fn clone(&self) -> Self { - let self_trait_object = unsafe { std::mem::transmute::<&dyn ValueTrait, TraitObject>(self.as_ref()) }; - let size = self_trait_object.vtable.size; - let self_mem = unsafe { std::slice::from_raw_parts(self_trait_object.self_ptr, size) }.to_owned(); - let ptr = Vec::leak(self_mem); - unsafe { - std::mem::transmute(TraitObject { - self_ptr: ptr as *mut [u8] as *mut u8, - vtable: self_trait_object.vtable, - }) - } - } -} - -#[cfg(test)] -mod test { - use super::*; - - #[test] - #[cfg_attr(miri, ignore)] - fn test_any_src() { - assert!(2_u32.into_any() == 2_u32.into_any()); - assert!(2_u32.into_any() != 3_u32.into_any()); - assert!(2_u32.into_any() != 3_i32.into_any()); - } -} diff --git a/node-graph/graph-craft/src/executor.rs b/node-graph/graph-craft/src/executor.rs index 4ed9a557b..fbdeb373e 100644 --- a/node-graph/graph-craft/src/executor.rs +++ b/node-graph/graph-craft/src/executor.rs @@ -38,6 +38,6 @@ impl Compiler { } pub type Any<'a> = Box + 'a>; -pub trait Executor { - fn execute<'a>(&'a self, input: Any<'a>) -> LocalFuture, Box>>; +pub trait Executor { + fn execute(&self, input: I) -> LocalFuture>>; } diff --git a/node-graph/graph-craft/src/proto.rs b/node-graph/graph-craft/src/proto.rs index 105b582ba..c829380e1 100644 --- a/node-graph/graph-craft/src/proto.rs +++ b/node-graph/graph-craft/src/proto.rs @@ -99,7 +99,6 @@ impl PartialEq for ConstructionArgs { (Self::Value(v1), Self::Value(v2)) => v1 == v2, _ => { use std::hash::Hasher; - use xxhash_rust::xxh3::Xxh3; let hash = |input: &Self| { let mut hasher = Xxh3::new(); input.hash(&mut hasher); diff --git a/node-graph/gstd/Cargo.toml b/node-graph/gstd/Cargo.toml index a9e561564..80f8fec1c 100644 --- a/node-graph/gstd/Cargo.toml +++ b/node-graph/gstd/Cargo.toml @@ -9,8 +9,7 @@ license = "MIT OR Apache-2.0" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [features] -memoization = ["once_cell"] -default = ["memoization", "wgpu"] +default = ["wgpu"] gpu = [ "graphene-core/gpu", "gpu-compiler-bin-wrapper", @@ -31,7 +30,6 @@ graphene-core = { path = "../gcore", features = [ "std", "serde", ], default-features = false } -borrow_stack = { path = "../borrow_stack" } dyn-any = { path = "../../libraries/dyn-any", features = ["derive"] } graph-craft = { path = "../graph-craft" } vulkan-executor = { path = "../vulkan-executor", optional = true } @@ -41,16 +39,6 @@ gpu-compiler-bin-wrapper = { path = "../gpu-compiler/gpu-compiler-bin-wrapper", compilation-client = { path = "../compilation-client", optional = true } bytemuck = { version = "1.8" } tempfile = "3" -once_cell = { version = "1.10", optional = true } -#pretty-token-stream = {path = "../../pretty-token-stream"} -syn = { version = "1.0", default-features = false, features = [ - "parsing", - "printing", -] } -proc-macro2 = { version = "1.0", default-features = false, features = [ - "proc-macro", -] } -quote = { version = "1.0", default-features = false } image = { version = "*", default-features = false } dyn-clone = "1.0" @@ -61,7 +49,6 @@ kurbo = { git = "https://github.com/linebender/kurbo.git", features = [ ] } glam = { version = "0.22", features = ["serde"] } node-macro = { path = "../node-macro" } -boxcar = "0.1.0" xxhash-rust = { workspace = true } serde_json = "1.0.96" reqwest = { version = "0.11.17", features = ["rustls", "rustls-tls"] } diff --git a/node-graph/gstd/src/any.rs b/node-graph/gstd/src/any.rs index 567132a59..3bb5149cc 100644 --- a/node-graph/gstd/src/any.rs +++ b/node-graph/gstd/src/any.rs @@ -29,9 +29,8 @@ where Box::pin(output) } - fn reset(self: std::pin::Pin<&mut Self>) { - let wrapped_node = unsafe { self.map_unchecked_mut(|e| &mut e.node) }; - Node::reset(wrapped_node); + fn reset(&self) { + self.node.reset(); } fn serialize(&self) -> Option> { @@ -67,9 +66,8 @@ where let output = async move { Box::new(result) as Any<'input> }; Box::pin(output) } - fn reset(self: std::pin::Pin<&mut Self>) { - let wrapped_node = unsafe { self.map_unchecked_mut(|e| &mut e.node) }; - Node::reset(wrapped_node); + fn reset(&self) { + self.node.reset(); } } @@ -114,9 +112,8 @@ where fn eval(&'i self, input: T) -> Self::Output { Box::pin(async move { self.node.eval(input) }) } - fn reset(self: std::pin::Pin<&mut Self>) { - let wrapped_node = unsafe { self.map_unchecked_mut(|e| &mut e.node) }; - Node::reset(wrapped_node); + fn reset(&self) { + self.node.reset(); } } @@ -207,7 +204,7 @@ impl<'n: 'input, 'input, O: 'input + StaticType, I: 'input + StaticType> Node<'i let node_name = self.node.node_name(); let input = Box::new(input); Box::pin(async move { - let out: Box<&_> = dyn_any::downcast::<&O>(self.node.eval(input).await).unwrap_or_else(|e| panic!("DowncastBothRefNode Input {e}")); + let out: Box<&_> = dyn_any::downcast::<&O>(self.node.eval(input).await).unwrap_or_else(|e| panic!("DowncastBothRefNode Input {e} in {node_name}")); *out }) } diff --git a/node-graph/gstd/src/executor.rs b/node-graph/gstd/src/executor.rs index 4c1e3a561..3748cd908 100644 --- a/node-graph/gstd/src/executor.rs +++ b/node-graph/gstd/src/executor.rs @@ -1,17 +1,13 @@ -use glam::{DAffine2, DMat2, DVec2, Mat2, UVec3, Vec2}; +use glam::{DAffine2, DVec2, Mat2, Vec2}; use gpu_executor::{Bindgroup, ComputePassDimensions, PipelineLayout, StorageBufferOptions}; use gpu_executor::{GpuExecutor, ShaderIO, ShaderInput}; use graph_craft::document::value::TaggedValue; use graph_craft::document::*; use graph_craft::proto::*; -use graphene_core::raster::bbox::{AxisAlignedBbox, Bbox}; use graphene_core::raster::*; use graphene_core::*; use wgpu_executor::NewExecutor; -use bytemuck::Pod; -use core::marker::PhantomData; -use dyn_any::StaticTypeSized; use std::sync::Arc; pub struct GpuCompiler { diff --git a/node-graph/gstd/src/lib.rs b/node-graph/gstd/src/lib.rs index 3a4c858cd..a9749c59f 100644 --- a/node-graph/gstd/src/lib.rs +++ b/node-graph/gstd/src/lib.rs @@ -5,9 +5,6 @@ extern crate log; //pub mod value; //#![feature(const_type_name)] -#[cfg(feature = "memoization")] -pub mod memo; - pub mod raster; pub mod http; diff --git a/node-graph/gstd/src/main.rs b/node-graph/gstd/src/main.rs deleted file mode 100644 index 67609113f..000000000 --- a/node-graph/gstd/src/main.rs +++ /dev/null @@ -1,148 +0,0 @@ -//#![feature(generic_associated_types)] -// use borrow_stack::BorrowStack; -// use dyn_any::{DynAny, StaticType}; -// use graphene_std::value::{AnyRefNode, AnyValueNode, StorageNode, ValueNode}; -// use graphene_std::*; - -/*fn mul(#[dyn_any(default)] a: f32, b: f32) -> f32 { - a * b -}*/ - -/* -mod mul { - use dyn_any::downcast_ref; - use graphene_std::{DynAnyNode, DynNode, DynamicInput, Node}; - pub struct MulNodeInput<'n> { - pub a: &'n f32, - pub b: &'n f32, - } - #[derive(Copy, Clone)] - pub struct MulNodeAnyProxy<'n> { - pub a: Option>, - pub b: Option>, - } - #[derive(Copy, Clone)] - pub struct MulNodeTypedProxy<'n> { - pub a: Option>, - pub b: Option>, - } - impl<'n> Node<'n> for MulNodeAnyProxy<'n> { - type Output = MulNodeInput<'n>; - fn eval(&'n self) -> >::Output { - // let a = self.a.unwrap().eval(); - let a: &f32 = self.a.map(|v| downcast_ref(v.eval()).unwrap()).unwrap_or(&1.); - /*let b: &f32 = self - .b - .map(|v| v.eval(&()).downcast_ref::<&'n f32, &'n f32>().unwrap()) - .unwrap_or(&&2.); - a * b*/ - MulNodeInput { a, b: a } - } - } - impl<'n> Node<'n> for MulNodeTypedProxy<'n> { - type Output = MulNodeInput<'n>; - fn eval(&'n self) -> >::Output { - let a = self.a.unwrap().eval(); - let b = self.b.unwrap().eval(); - MulNodeInput { a, b } - } - } - - /*macro_rules! new { - () => { - mul::MulNode { a: None, b: None } - }; - }*/ - //pub(crate) use new; - - impl<'n> DynamicInput<'n> for MulNodeAnyProxy<'n> { - fn set_kwarg_by_name(&mut self, _name: &str, _value: DynAnyNode<'n>) { - todo!() - } - fn set_arg_by_index(&mut self, index: usize, value: DynAnyNode<'n>) { - match index { - 0 => { - self.a = Some(value); - } - _ => todo!(), - } - } - } -} -// type SNode<'n> = dyn Node<'n, Output = &'n dyn DynAny<'n>>; -*/ -// struct NodeStore<'n>(borrow_stack::FixedSizeStack<'n, Box>>); - -// impl<'n> NodeStore<'n> { -// fn len(&self) -> usize { -// self.0.len() -// } - -// fn push(&'n mut self, f: fn(&'n [Box]) -> Box>) { -// unsafe { self.0.push(f(self.0.get())) }; -// } - -// /*fn get_index(&'n self, index: usize) -> &'n SNode<'n> { -// assert!(index < self.0.len()); -// &unsafe { self.0.get()[index] } -// }*/ -// } - -fn main() { - // use syn::parse::Parse; - /*let nodes = vec![ - NodeKind::Input, - NodeKind::Value(syn::parse_quote!(1u32)), - NodeKind::Node(syn::parse_quote!(graphene_core::ops::AddNode), vec![0, 0]), - ]; - - //println!("{}", node_graph(1)); - // - - let _nodegraph = NodeGraph { - nodes, - input: syn::Type::Verbatim(quote! {u32}), - output: syn::Type::Verbatim(quote! {u32}), - };*/ - - //let pretty = pretty_token_stream::Pretty::new(nodegraph.serialize_gpu("add")); - //pretty.print(); - /* - use dyn_any::{downcast_ref, DynAny, StaticType}; - //let mut mul = mul::MulNode::new(); - let mut stack: borrow_stack::FixedSizeStack>> = - borrow_stack::FixedSizeStack::new(42); - unsafe { stack.push(Box::new(AnyValueNode::new(1_f32))) }; - //let node = unsafe { stack.get(0) }; - //let boxed = Box::new(StorageNode::new(node)); - //unsafe { stack.push(boxed) }; - let result = unsafe { &stack.get()[0] }.eval(); - dbg!(downcast_ref::(result)); - /*unsafe { - stack - .push(Box::new(AnyRefNode::new(stack.get(0).as_ref())) - as Box>) - };*/ - let f = (3.2_f32, 3.1_f32); - let a = ValueNode::new(1.); - let id = std::any::TypeId::of::<&f32>(); - let any_a = AnyRefNode::new(&a); - /*let _mul2 = mul::MulNodeInput { - a: None, - b: Some(&any_a), - }; - let mut mul2 = mul::new!(); - //let cached = memo::CacheNode::new(&mul1); - //let foo = value::AnyRefNode::new(&cached); - mul2.set_arg_by_index(0, &any_a);*/ - let int = value::IntNode::<32>; - Node::eval(&int); - println!("{}", Node::eval(&int)); - //let _add: u32 = ops::AddNode::::default().eval((int.exec(), int.exec())); - //let fnode = generic::FnNode::new(|(a, b): &(i32, i32)| a - b); - //let sub = fnode.any(&("a", 2)); - //let cache = memo::CacheNode::new(&fnode); - //let cached_result = cache.eval(&(2, 3)); - */ - //println!("{}", cached_result) -} diff --git a/node-graph/gstd/src/memo.rs b/node-graph/gstd/src/memo.rs deleted file mode 100644 index 45d00bc4a..000000000 --- a/node-graph/gstd/src/memo.rs +++ /dev/null @@ -1,177 +0,0 @@ -use futures::Future; - -use graphene_core::Node; - -use std::hash::{Hash, Hasher}; -use std::marker::PhantomData; -use std::pin::Pin; -use std::sync::atomic::AtomicBool; -use std::sync::{Arc, Mutex}; -use xxhash_rust::xxh3::Xxh3; - -/// Caches the output of a given Node and acts as a proxy -#[derive(Default)] -pub struct CacheNode { - // We have to use an append only data structure to make sure the references - // to the cache entries are always valid - cache: boxcar::Vec<(u64, T, AtomicBool)>, - node: CachedNode, -} -impl<'i, T: 'i + Clone, I: 'i + Hash, CachedNode: 'i> Node<'i, I> for CacheNode -where - CachedNode: for<'any_input> Node<'any_input, I>, - for<'a> >::Output: core::future::Future + 'a, -{ - // TODO: This should return a reference to the cached cached_value - // but that requires a lot of lifetime magic <- This was suggested by copilot but is pretty acurate xD - type Output = Pin + 'i>>; - fn eval(&'i self, input: I) -> Self::Output { - Box::pin(async move { - let mut hasher = Xxh3::new(); - input.hash(&mut hasher); - let hash = hasher.finish(); - - if let Some((_, cached_value, keep)) = self.cache.iter().find(|(h, _, _)| *h == hash) { - keep.store(true, std::sync::atomic::Ordering::Relaxed); - cached_value.clone() - } else { - trace!("Cache miss"); - let output = self.node.eval(input).await; - let index = self.cache.push((hash, output, AtomicBool::new(true))); - self.cache[index].1.clone() - } - }) - } - - fn reset(mut self: Pin<&mut Self>) { - let old_cache = std::mem::take(&mut self.cache); - self.cache = old_cache.into_iter().filter(|(_, _, keep)| keep.swap(false, std::sync::atomic::Ordering::Relaxed)).collect(); - } -} - -impl std::marker::Unpin for CacheNode {} - -impl CacheNode { - pub fn new(node: CachedNode) -> CacheNode { - CacheNode { cache: boxcar::Vec::new(), node } - } -} - -/// Caches the output of the last graph evaluation for introspection -#[derive(Default)] -pub struct MonitorNode { - output: Mutex>>, -} -impl<'i, T: 'static + Clone> Node<'i, T> for MonitorNode { - type Output = T; - fn eval(&'i self, input: T) -> Self::Output { - *self.output.lock().unwrap() = Some(Arc::new(input.clone())); - input - } - - fn serialize(&self) -> Option> { - let output = self.output.lock().unwrap(); - (*output).as_ref().map(|output| output.clone() as Arc) - } -} - -impl MonitorNode { - pub const fn new() -> MonitorNode { - MonitorNode { output: Mutex::new(None) } - } -} - -/// Caches the output of a given Node and acts as a proxy -/// It provides two modes of operation, it can either be set -/// when calling the node with a `Some` variant or the last -/// value that was added is returned when calling it with `None` -#[derive(Debug, Clone, PartialEq, Eq, Default)] -pub struct LetNode { - // We have to use an append only data structure to make sure the references - // to the cache entries are always valid - // TODO: We only ever access the last value so there is not really a reason for us - // to store the previous entries. This should be reworked in the future - cache: boxcar::Vec<(u64, T)>, -} -impl<'i, T: 'i + Hash> Node<'i, Option> for LetNode { - type Output = &'i T; - fn eval(&'i self, input: Option) -> Self::Output { - match input { - Some(input) => { - let mut hasher = Xxh3::new(); - input.hash(&mut hasher); - let hash = hasher.finish(); - - if let Some((cached_hash, cached_value)) = self.cache.iter().last() { - if hash == *cached_hash { - return cached_value; - } - } - trace!("Cache miss"); - let index = self.cache.push((hash, input)); - &self.cache[index].1 - } - None => &self.cache.iter().last().expect("Let node was not initialized").1, - } - } - - fn reset(mut self: Pin<&mut Self>) { - if let Some(last) = std::mem::take(&mut self.cache).into_iter().last() { - self.cache = boxcar::vec![last]; - } - } -} - -impl std::marker::Unpin for LetNode {} - -impl LetNode { - pub fn new() -> LetNode { - LetNode { cache: boxcar::Vec::new() } - } -} - -/// Caches the output of a given Node and acts as a proxy -#[derive(Debug, Clone, PartialEq, Eq, Default)] -pub struct EndLetNode { - input: Input, -} -impl<'i, T: 'i, Input> Node<'i, &'i T> for EndLetNode -where - Input: Node<'i, ()>, -{ - type Output = ::Output; - fn eval(&'i self, _: &'i T) -> Self::Output { - let result = self.input.eval(()); - result - } -} - -impl EndLetNode { - pub const fn new(input: Input) -> EndLetNode { - EndLetNode { input } - } -} - -pub use graphene_core::ops::SomeNode as InitNode; - -#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] -pub struct RefNode { - let_node: Let, - _t: PhantomData, -} - -impl<'i, T: 'i, Let> Node<'i, ()> for RefNode -where - Let: for<'a> Node<'a, Option>, -{ - type Output = >>::Output; - fn eval(&'i self, _: ()) -> Self::Output { - self.let_node.eval(None) - } -} - -impl RefNode { - pub const fn new(let_node: Let) -> RefNode { - RefNode { let_node, _t: PhantomData } - } -} diff --git a/node-graph/interpreted-executor/Cargo.toml b/node-graph/interpreted-executor/Cargo.toml index 036cfd89a..5f32433fc 100644 --- a/node-graph/interpreted-executor/Cargo.toml +++ b/node-graph/interpreted-executor/Cargo.toml @@ -13,18 +13,17 @@ quantization = ["graphene-std/quantization"] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -graphene-core = { path = "../gcore", features = ["async", "std" ] } +graphene-core = { path = "../gcore", features = ["async", "std"] } graphene-std = { path = "../gstd" } graph-craft = { path = "../graph-craft" } -dyn-any = { path = "../../libraries/dyn-any", features = ["log-bad-types", "glam"] } +dyn-any = { path = "../../libraries/dyn-any", features = [ + "log-bad-types", + "glam", +] } num-traits = "0.2" -borrow_stack = { path = "../borrow_stack" } dyn-clone = "1.0" log = "0.4" serde = { version = "1", features = ["derive"], optional = true } glam = { version = "0.22" } once_cell = "1.17.0" futures = "0.3.28" - -[dev-dependencies] -tokio = { version = "1.12", features = ["rt", "macros"] } diff --git a/node-graph/interpreted-executor/src/executor.rs b/node-graph/interpreted-executor/src/executor.rs index 376a1df6f..c5d81ed8b 100644 --- a/node-graph/interpreted-executor/src/executor.rs +++ b/node-graph/interpreted-executor/src/executor.rs @@ -3,12 +3,12 @@ use std::error::Error; use std::sync::{Arc, RwLock}; use dyn_any::StaticType; -use graph_craft::document::value::UpcastNode; +use graph_craft::document::value::{TaggedValue, UpcastNode}; use graph_craft::document::NodeId; use graph_craft::executor::Executor; use graph_craft::proto::{ConstructionArgs, LocalFuture, ProtoNetwork, ProtoNode, TypingContext}; use graph_craft::Type; -use graphene_std::any::{Any, TypeErasedPinned, TypeErasedPinnedRef}; +use graphene_std::any::{TypeErasedPinned, TypeErasedPinnedRef}; use crate::node_registry; @@ -73,9 +73,9 @@ impl DynamicExecutor { } } -impl Executor for DynamicExecutor { - fn execute<'a>(&'a self, input: Any<'a>) -> LocalFuture, Box>> { - Box::pin(async move { self.tree.eval_any(self.output, input).await.ok_or_else(|| "Failed to execute".into()) }) +impl<'a, I: StaticType + 'a> Executor for &'a DynamicExecutor { + fn execute(&self, input: I) -> LocalFuture>> { + Box::pin(async move { self.tree.eval_tagged_value(self.output, input).await.map_err(|e| e.into()) }) } } @@ -167,18 +167,17 @@ impl BorrowTree { self.nodes.get(&id).cloned() } - pub async fn eval<'i, I: StaticType + 'i + Send + Sync, O: StaticType + Send + Sync + 'i>(&'i self, id: NodeId, input: I) -> Option { + pub async fn eval<'i, I: StaticType + 'i, O: StaticType + 'i>(&'i self, id: NodeId, input: I) -> Option { let node = self.nodes.get(&id).cloned()?; let reader = node.read().unwrap(); let output = reader.node.eval(Box::new(input)); dyn_any::downcast::(output.await).ok().map(|o| *o) } - pub async fn eval_any<'i>(&'i self, id: NodeId, input: Any<'i>) -> Option> { - let node = self.nodes.get(&id)?; - // TODO: Comments by @TrueDoctor before this was merged: - // TODO: Oof I dislike the evaluation being an unsafe operation but I guess its fine because it only is a lifetime extension - // TODO: We should ideally let miri run on a test that evaluates the nodegraph multiple times to check if this contains any subtle UB but this looks fine for now - Some(unsafe { (*((&*node.read().unwrap()) as *const NodeContainer)).node.eval(input).await }) + pub async fn eval_tagged_value<'i, I: StaticType + 'i>(&'i self, id: NodeId, input: I) -> Result { + let node = self.nodes.get(&id).cloned().ok_or_else(|| "Output node not found in executor")?; + let reader = node.read().unwrap(); + let output = reader.node.eval(Box::new(input)); + TaggedValue::try_from_any(output.await) } pub fn free_node(&mut self, id: NodeId) { @@ -226,12 +225,15 @@ mod test { use super::*; - #[tokio::test] - async fn push_node() { + #[test] + fn push_node_sync() { let mut tree = BorrowTree::default(); let val_1_protonode = ProtoNode::value(ConstructionArgs::Value(TaggedValue::U32(2u32)), vec![]); - tree.push_node(0, val_1_protonode, &TypingContext::default()).await.unwrap(); + let context = TypingContext::default(); + let future = tree.push_node(0, val_1_protonode, &context); //.await.unwrap(); + futures::executor::block_on(future).unwrap(); let _node = tree.get(0).unwrap(); - assert_eq!(tree.eval(0, ()).await, Some(2u32)); + let result = futures::executor::block_on(tree.eval(0, ())); + assert_eq!(result, Some(2u32)); } } diff --git a/node-graph/interpreted-executor/src/lib.rs b/node-graph/interpreted-executor/src/lib.rs index d15e24ae0..6e72bee8b 100644 --- a/node-graph/interpreted-executor/src/lib.rs +++ b/node-graph/interpreted-executor/src/lib.rs @@ -7,45 +7,14 @@ pub mod node_registry; #[cfg(test)] mod tests { use dyn_any::IntoDynAny; + use graph_craft::document::value::TaggedValue; use graphene_core::*; use std::borrow::Cow; - /* + use futures::executor::block_on; + #[test] - fn borrow_stack() { - let stack = borrow_stack::FixedSizeStack::new(256); - unsafe { - let dynanynode: DynAnyNode, (), _, _> = DynAnyNode::new(ValueNode(2_u32)); - stack.push(dynanynode.into_box()); - } - stack.push_fn(|nodes| { - let pre_node = nodes.get(0).unwrap(); - let downcast: DowncastNode<&TypeErasedNode, &u32> = DowncastNode::new(pre_node); - let dynanynode: DynAnyNode>, u32, _, _> = DynAnyNode::new(ConsNode(downcast, PhantomData)); - dynanynode.into_box() - }); - stack.push_fn(|_| { - let dynanynode: DynAnyNode<_, (u32, &u32), _, _> = DynAnyNode::new(AddNode); - dynanynode.into_box() - }); - stack.push_fn(|nodes| { - let compose_node = nodes[1].after(&nodes[2]); - TypeErasedNode(Box::pin(compose_node)) - }); - - let result = unsafe { &stack.get()[0] }.eval_ref(().into_dyn()); - assert_eq!(*downcast::<&u32>(result).unwrap(), &2_u32); - let result = unsafe { &stack.get()[1] }.eval_ref(4_u32.into_dyn()); - assert_eq!(*downcast::<(u32, &u32)>(result).unwrap(), (4_u32, &2_u32)); - let result = unsafe { &stack.get()[1] }.eval_ref(4_u32.into_dyn()); - let add = unsafe { &stack.get()[2] }.eval_ref(result); - assert_eq!(*downcast::(add).unwrap(), 6_u32); - let add = unsafe { &stack.get()[3] }.eval_ref(4_u32.into_dyn()); - assert_eq!(*downcast::(add).unwrap(), 6_u32); - }*/ - - #[tokio::test] - async fn execute_add() { + fn execute_add() { use graph_craft::document::*; use graph_craft::*; @@ -109,15 +78,14 @@ mod tests { let compiler = Compiler {}; let protograph = compiler.compile_single(network, true).expect("Graph should be generated"); - let exec = DynamicExecutor::new(protograph).await.unwrap_or_else(|e| panic!("Failed to create executor: {}", e)); + let exec = block_on(DynamicExecutor::new(protograph)).unwrap_or_else(|e| panic!("Failed to create executor: {}", e)); - let result = exec.execute(32_u32.into_dyn()).await.unwrap(); - let val = *dyn_any::downcast::(result).unwrap(); - assert_eq!(val, 33_u32); + let result = block_on((&exec).execute(32_u32)).unwrap(); + assert_eq!(result, TaggedValue::U32(33)); } - #[tokio::test] - async fn double_number() { + #[test] + fn double_number() { use graph_craft::document::*; use graph_craft::*; @@ -158,6 +126,6 @@ mod tests { let compiler = Compiler {}; let protograph = compiler.compile_single(network, true).expect("Graph should be generated"); - let _exec = DynamicExecutor::new(protograph).await.map(|e| panic!("The network should not type check: {:#?}", e)).unwrap_err(); + let _exec = block_on(DynamicExecutor::new(protograph)).map(|e| panic!("The network should not type check: {:#?}", e)).unwrap_err(); } } diff --git a/node-graph/interpreted-executor/src/node_registry.rs b/node-graph/interpreted-executor/src/node_registry.rs index 01539befe..63bd4389f 100644 --- a/node-graph/interpreted-executor/src/node_registry.rs +++ b/node-graph/interpreted-executor/src/node_registry.rs @@ -1,4 +1,3 @@ -use graph_craft::document::DocumentNode; use graph_craft::proto::{NodeConstructor, TypeErasedPinned}; use graphene_core::ops::IdNode; use graphene_core::quantization::QuantizationChannels; @@ -10,13 +9,11 @@ use graphene_core::vector::brush_stroke::BrushStroke; use graphene_core::vector::VectorData; use graphene_core::wasm_application_io::WasmSurfaceHandle; use graphene_core::wasm_application_io::*; -use graphene_core::{concrete, generic, value_fn}; +use graphene_core::{concrete, generic}; use graphene_core::{fn_type, raster::*}; use graphene_core::{Cow, NodeIdentifier, Type, TypeDescriptor}; use graphene_core::{Node, NodeIO, NodeIOTypes}; -use graphene_std::any::{ComposeTypeErased, DowncastBothNode, DynAnyInRefNode, DynAnyNode, FutureWrapperNode, IntoTypeErasedNode, TypeErasedPinnedRef}; -use graphene_std::brush::*; -use graphene_std::memo::{CacheNode, LetNode}; +use graphene_std::any::{ComposeTypeErased, DowncastBothNode, DynAnyNode, FutureWrapperNode, IntoTypeErasedNode}; use graphene_std::raster::BlendImageTupleNode; use graphene_std::raster::*; @@ -27,8 +24,8 @@ use std::collections::HashMap; use std::sync::Arc; macro_rules! construct_node { - ($args: ident, $path:ty, [$($type:tt),*]) => { async move { - let mut args: Vec> = $args.clone(); + ($args: ident, $path:ty, [$($type:ty),*]) => { async move { + let mut args = $args.clone(); args.reverse(); let node = <$path>::new($( { @@ -60,7 +57,7 @@ macro_rules! register_node { let node = <$path>::new($( graphene_std::any::PanicNode::<(), $type>::new() ),*); - let params = vec![$(value_fn!($type)),*]; + let params = vec![$(fn_type!((), $type)),*]; let mut node_io = <$path as NodeIO<'_, $input>>::to_node_io(&node, params); node_io.input = concrete!(<$input as StaticType>::Static); node_io @@ -69,6 +66,38 @@ macro_rules! register_node { ] }; } +macro_rules! async_node { + // TODO: we currently need to annotate the type here because the compiler would otherwise (correctly) + // assign a Pin>> type to the node, which is not what we want for now. + ($path:ty, input: $input:ty, output: $output:ty, params: [ $($type:ty),*]) => { + vec![ + ( + NodeIdentifier::new(stringify!($path)), + |mut args| { + Box::pin(async move { + args.reverse(); + let node = <$path>::new($(graphene_std::any::input_node::<$type>(args.pop().expect("Not enough arguments provided to construct node"))),*); + let any: DynAnyNode<$input, _, _> = graphene_std::any::DynAnyNode::new(graphene_core::value::ValueNode::new(node)); + Box::pin(any) as TypeErasedPinned + }) + }, + { + let node = <$path>::new($( + graphene_std::any::PanicNode::<(), core::pin::Pin>>>::new() + ),*); + // TODO: Propagate the future type through the node graph + //let params = vec![$(Type::Fn(Box::new(concrete!(())), Box::new(Type::Future(Box::new(concrete!($type)))))),*]; + let params = vec![$(Type::Fn(Box::new(concrete!(())), Box::new(concrete!($type)))),*]; + let mut node_io = NodeIO::<'_, $input>::to_node_io(&node, params); + node_io.input = concrete!(<$input as StaticType>::Static); + node_io.input = concrete!(<$input as StaticType>::Static); + node_io.output = concrete!(<$output as StaticType>::Static); + node_io + }, + ) + ] + }; +} macro_rules! raster_node { ($path:ty, params: [$($type:ty),*]) => {{ // this function could also be inlined but serves as a workaround for @@ -92,7 +121,7 @@ macro_rules! raster_node { }) }, { - let params = vec![$(value_fn!($type)),*]; + let params = vec![$(fn_type!($type)),*]; NodeIOTypes::new(concrete!(Color), concrete!(Color), params) }, ), @@ -108,7 +137,7 @@ macro_rules! raster_node { }) }, { - let params = vec![$(value_fn!($type)),*]; + let params = vec![$(fn_type!($type)),*]; NodeIOTypes::new(concrete!(Image), concrete!(Image), params) }, ), @@ -124,7 +153,7 @@ macro_rules! raster_node { }) }, { - let params = vec![$(value_fn!($type)),*]; + let params = vec![$(fn_type!($type)),*]; NodeIOTypes::new(concrete!(ImageFrame), concrete!(ImageFrame), params) }, ) @@ -208,38 +237,32 @@ fn node_registry() -> HashMap), - vec![value_fn!(ImageFrame), value_fn!(ImageFrame), value_fn!(ImageFrame), value_fn!(ImageFrame)], + vec![fn_type!(ImageFrame), fn_type!(ImageFrame), fn_type!(ImageFrame), fn_type!(ImageFrame)], ), )], register_node!(graphene_std::raster::EmptyImageNode<_, _>, input: DAffine2, params: [Color]), - register_node!(graphene_std::memo::MonitorNode<_>, input: ImageFrame, params: []), - register_node!(graphene_std::memo::MonitorNode<_>, input: graphene_core::GraphicGroup, params: []), - register_node!(graphene_core::wasm_application_io::CreateSurfaceNode, input: &graphene_core::EditorApi, params: []), - vec![( - NodeIdentifier::new("graphene_core::wasm_application_io::DrawImageFrameNode<_>"), - |args| { - Box::pin(async move { - let surface: DowncastBothNode<(), Arc> = DowncastBothNode::new(args[0]); - let node = graphene_core::wasm_application_io::DrawImageFrameNode::new(surface); - let any: DynAnyNode, _, _> = graphene_std::any::DynAnyNode::new(graphene_core::value::ValueNode::new(node)); - Box::pin(any) as TypeErasedPinned - }) - }, - NodeIOTypes::new(concrete!(ImageFrame), concrete!(WasmSurfaceHandleFrame), vec![value_fn!(Arc)]), - )], + register_node!(graphene_core::memo::MonitorNode<_>, input: ImageFrame, params: []), + register_node!(graphene_core::memo::MonitorNode<_>, input: graphene_core::GraphicGroup, params: []), + register_node!(graphene_core::wasm_application_io::CreateSurfaceNode, input: graphene_core::EditorApi, params: []), + async_node!( + graphene_core::wasm_application_io::DrawImageFrameNode<_>, + input: ImageFrame, + output: WasmSurfaceHandleFrame, + params: [Arc] + ), #[cfg(feature = "gpu")] vec![( NodeIdentifier::new("graphene_std::executor::MapGpuSingleImageNode<_>"), |args| { Box::pin(async move { - let document_node: DowncastBothNode<(), DocumentNode> = DowncastBothNode::new(args[0]); + let document_node: DowncastBothNode<(), graph_craft::document::DocumentNode> = DowncastBothNode::new(args[0]); //let document_node = ClonedNode::new(document_node.eval(())); let node = graphene_std::executor::MapGpuNode::new(document_node); let any: DynAnyNode, _, _> = graphene_std::any::DynAnyNode::new(graphene_core::value::ValueNode::new(node)); Box::pin(any) as TypeErasedPinned }) }, - NodeIOTypes::new(concrete!(ImageFrame), concrete!(ImageFrame), vec![value_fn!(DocumentNode)]), + NodeIOTypes::new(concrete!(ImageFrame), concrete!(ImageFrame), vec![fn_type!(graph_craft::document::DocumentNode)]), )], #[cfg(feature = "gpu")] vec![( @@ -258,7 +281,7 @@ fn node_registry() -> HashMap), concrete!(ImageFrame), - vec![value_fn!(ImageFrame), value_fn!(BlendMode), value_fn!(f32)], + vec![fn_type!(ImageFrame), fn_type!(BlendMode), fn_type!(f32)], ), )], vec![( @@ -327,28 +350,7 @@ fn node_registry() -> HashMap), - vec![value_fn!(ImageFrame), value_fn!(ImageFrame), value_fn!(Vec)], - ), - )], - vec![( - NodeIdentifier::new("graphene_std::brush::ReduceNode<_, _>"), - |args| { - Box::pin(async move { - let acc: DowncastBothNode<(), ImageFrame> = DowncastBothNode::new(args[0]); - let image = acc.eval(()).await; - let blend_node = graphene_core::raster::BlendNode::new(ClonedNode::new(BlendMode::Normal), ClonedNode::new(1.0)); - let _ = &blend_node as &dyn for<'i> Node<'i, (Color, Color), Output = Color>; - let node = ReduceNode::new(ClonedNode::new(image), ValueNode::new(BlendImageTupleNode::new(ValueNode::new(blend_node)))); - //let _ = &node as &dyn for<'i> Node<'i, core::slice::Iter>, Output = ImageFrame>; - let node = FutureWrapperNode::new(node); - let any: DynAnyNode> + Sync + Send>, _, _> = graphene_std::any::DynAnyNode::new(ValueNode::new(node)); - any.into_type_erased() - }) - }, - NodeIOTypes::new( - concrete!(Box> + Sync + Send>), - concrete!(ImageFrame), - vec![value_fn!(ImageFrame)], + vec![fn_type!(ImageFrame), fn_type!(ImageFrame), fn_type!(Vec)], ), )], // Filters @@ -374,7 +376,7 @@ fn node_registry() -> HashMap), concrete!(ImageFrame), - vec![value_fn!(ImageFrame), value_fn!(BlendMode), value_fn!(f64)], + vec![fn_type!(ImageFrame), fn_type!(BlendMode), fn_type!(f64)], ), )], raster_node!(graphene_core::raster::GrayscaleNode<_, _, _, _, _, _, _>, params: [Color, f64, f64, f64, f64, f64, f64]), @@ -417,265 +419,59 @@ fn node_registry() -> HashMap), concrete!(ImageFrame), vec![value_fn!(f64), value_fn!(f64), value_fn!(bool)]), + NodeIOTypes::new(concrete!(ImageFrame), concrete!(ImageFrame), vec![fn_type!(f64), fn_type!(f64), fn_type!(bool)]), )], 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]), - vec![ - ( - NodeIdentifier::new("graphene_std::memo::LetNode<_>"), - |_| { - Box::pin(async move { - let node: LetNode> = graphene_std::memo::LetNode::new(); - let any = graphene_std::any::DynAnyRefNode::new(node); - any.into_type_erased() - }) - }, - NodeIOTypes::new(concrete!(Option>), concrete!(&ImageFrame), vec![]), + register_node!(graphene_core::memo::LetNode<_>, input: Option>, params: []), + register_node!(graphene_core::memo::LetNode<_>, input: Option, params: []), + async_node!( + graphene_core::memo::EndLetNode<_>, + input: graphene_core::EditorApi, + output: ImageFrame, + params: [ImageFrame] + ), + async_node!(graphene_core::memo::EndLetNode<_>, input: graphene_core::EditorApi, output: VectorData, params: [VectorData]), + async_node!( + graphene_core::memo::EndLetNode<_>, + input: graphene_core::EditorApi, + output: graphene_core::GraphicGroup, + params: [graphene_core::GraphicGroup] + ), + async_node!( + graphene_core::memo::EndLetNode<_>, + input: graphene_core::EditorApi, + output: graphene_core::Artboard, + params: [graphene_core::Artboard] + ), + async_node!( + graphene_core::memo::EndLetNode<_>, + input: graphene_core::EditorApi, + output: WasmSurfaceHandleFrame, + params: [WasmSurfaceHandleFrame] + ), + vec![( + NodeIdentifier::new("graphene_core::memo::RefNode<_, _>"), + |args| { + Box::pin(async move { + let node: DowncastBothNode, graphene_core::EditorApi> = graphene_std::any::DowncastBothNode::new(args[0]); + let node = >::new(node); + let any: DynAnyNode<(), _, _> = graphene_std::any::DynAnyNode::new(graphene_core::value::ValueNode::new(node)); + Box::pin(any) as TypeErasedPinned + }) + }, + NodeIOTypes::new( + concrete!(()), + concrete!(graphene_core::EditorApi), + vec![fn_type!(Option, graphene_core::EditorApi)], ), - ( - NodeIdentifier::new("graphene_std::memo::LetNode<_>"), - |_| { - Box::pin(async move { - let node: LetNode = graphene_std::memo::LetNode::new(); - let any = graphene_std::any::DynAnyRefNode::new(node); - any.into_type_erased() - }) - }, - NodeIOTypes::new(concrete!(Option), concrete!(&graphene_core::EditorApi), vec![]), - ), - ( - NodeIdentifier::new("graphene_std::memo::EndLetNode<_>"), - |args| { - Box::pin(async move { - let input: DowncastBothNode<(), ImageFrame> = DowncastBothNode::new(args[0]); - let node = graphene_std::memo::EndLetNode::new(input); - let any: DynAnyInRefNode = graphene_std::any::DynAnyInRefNode::new(node); - Box::pin(any) as TypeErasedPinned<'_> - }) - }, - NodeIOTypes::new(generic!(T), concrete!(ImageFrame), vec![value_fn!(ImageFrame)]), - ), - ( - NodeIdentifier::new("graphene_std::memo::EndLetNode<_>"), - |args| { - Box::pin(async move { - let input: DowncastBothNode<(), VectorData> = DowncastBothNode::new(args[0]); - let node = graphene_std::memo::EndLetNode::new(input); - let any: DynAnyInRefNode = graphene_std::any::DynAnyInRefNode::new(node); - Box::pin(any) as TypeErasedPinned - }) - }, - NodeIOTypes::new(generic!(T), concrete!(VectorData), vec![value_fn!(VectorData)]), - ), - ( - NodeIdentifier::new("graphene_std::memo::EndLetNode<_>"), - |args| { - Box::pin(async move { - let input: DowncastBothNode<(), graphene_core::GraphicGroup> = DowncastBothNode::new(args[0]); - let node = graphene_std::memo::EndLetNode::new(input); - let any: DynAnyInRefNode = graphene_std::any::DynAnyInRefNode::new(node); - Box::pin(any) as TypeErasedPinned - }) - }, - NodeIOTypes::new(generic!(T), concrete!(graphene_core::GraphicGroup), vec![value_fn!(graphene_core::GraphicGroup)]), - ), - ( - NodeIdentifier::new("graphene_std::memo::EndLetNode<_>"), - |args| { - Box::pin(async move { - let input: DowncastBothNode<(), graphene_core::Artboard> = DowncastBothNode::new(args[0]); - let node = graphene_std::memo::EndLetNode::new(input); - let any: DynAnyInRefNode = graphene_std::any::DynAnyInRefNode::new(node); - Box::pin(any) as TypeErasedPinned - }) - }, - NodeIOTypes::new(generic!(T), concrete!(graphene_core::Artboard), vec![value_fn!(graphene_core::Artboard)]), - ), - ( - NodeIdentifier::new("graphene_std::memo::EndLetNode<_>"), - |args| { - Box::pin(async move { - let input: DowncastBothNode<(), WasmSurfaceHandleFrame> = DowncastBothNode::new(args[0]); - let node = graphene_std::memo::EndLetNode::new(input); - let any: DynAnyInRefNode = graphene_std::any::DynAnyInRefNode::new(node); - Box::pin(any) as TypeErasedPinned - }) - }, - NodeIOTypes::new(generic!(T), concrete!(WasmSurfaceHandleFrame), vec![value_fn!(WasmSurfaceHandleFrame)]), - ), - ( - NodeIdentifier::new("graphene_std::memo::RefNode<_, _>"), - |args| { - Box::pin(async move { - let map_fn: DowncastBothNode, &graphene_core::EditorApi> = DowncastBothNode::new(args[0]); - //let map_fn = map_fn.then(EvalSyncNode::new()); - let node = graphene_std::memo::RefNode::new(map_fn); - let any = graphene_std::any::DynAnyNode::new(ValueNode::new(node)); - Box::pin(any) as TypeErasedPinned - }) - }, - NodeIOTypes::new( - concrete!(()), - concrete!(&graphene_core::EditorApi), - vec![fn_type!(Option, &graphene_core::EditorApi)], - ), - ), - /* - ( - NodeIdentifier::new("graphene_std::raster::ImaginateNode<_>"), - |args| { - Box::pin(async move { - let cached = graphene_std::any::input_node::>>>(args[15]); - let cached = cached.then(EvalSyncNode::new()); - let node = graphene_std::raster::ImaginateNode::new(cached); - let node = FutureWrapperNode::new(node); - let any = DynAnyNode::new(ValueNode::new(node)); - Box::pin(any) as TypeErasedPinned - }) - }, - NodeIOTypes::new( - concrete!(ImageFrame), - concrete!(ImageFrame), - vec![ - value_fn!(f64), - value_fn!(Option), - value_fn!(f64), - value_fn!(ImaginateSamplingMethod), - value_fn!(f64), - value_fn!(String), - value_fn!(String), - value_fn!(bool), - value_fn!(f64), - value_fn!(Option>), - value_fn!(bool), - value_fn!(f64), - value_fn!(ImaginateMaskStartingFill), - value_fn!(bool), - value_fn!(bool), - value_fn!(Option>>), - value_fn!(f64), - value_fn!(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>::new(args[2]); - let image = image.then(EvalSyncNode::new()); - let empty_image: ValueNode> = ValueNode::new(Image::empty()); - let empty: TypeNode<_, (), Image> = TypeNode::new(empty_image.then(CloneNode::new())); - use graphene_core::Node; - let radius = ClonedNode::new(radius.eval(())); - let sigma = ClonedNode::new(sigma.eval(())); - - //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 new_image = FutureWrapperNode::new(new_image); - let node: DynAnyNode<&Image, _, _> = DynAnyNode::new(ValueNode::new(new_image)); - Box::pin(node) - }, - NodeIOTypes::new(concrete!(Image), concrete!(Image), vec![value_fn!(u32), value_fn!(f64)]), - ), - //register_node!(graphene_std::memo::CacheNode<_>, input: Image, params: []), - */ - ( - NodeIdentifier::new("graphene_std::memo::CacheNode"), - |args| { - Box::pin(async move { - let input: DowncastBothNode<(), Image> = DowncastBothNode::new(args[0]); - let node: CacheNode, _> = graphene_std::memo::CacheNode::new(input); - let any = DynAnyNode::new(ValueNode::new(node)); - Box::pin(any) as TypeErasedPinned - }) - }, - NodeIOTypes::new(concrete!(()), concrete!(Image), vec![value_fn!(Image)]), - ), - ( - NodeIdentifier::new("graphene_std::memo::CacheNode"), - |args| { - Box::pin(async move { - let input: DowncastBothNode<(), ImageFrame> = DowncastBothNode::new(args[0]); - let node: CacheNode, _> = graphene_std::memo::CacheNode::new(input); - let any = DynAnyNode::new(ValueNode::new(node)); - Box::pin(any) as TypeErasedPinned - }) - }, - NodeIOTypes::new(concrete!(()), concrete!(ImageFrame), vec![value_fn!(ImageFrame)]), - ), - ( - NodeIdentifier::new("graphene_std::memo::CacheNode"), - |args| { - Box::pin(async move { - let input: DowncastBothNode, ImageFrame> = DowncastBothNode::new(args[0]); - let node: CacheNode, _> = graphene_std::memo::CacheNode::new(input); - let any = DynAnyNode::new(ValueNode::new(node)); - Box::pin(any) as TypeErasedPinned - }) - }, - NodeIOTypes::new(concrete!(ImageFrame), concrete!(ImageFrame), vec![fn_type!(ImageFrame, ImageFrame)]), - ), - ( - NodeIdentifier::new("graphene_std::memo::CacheNode"), - |args| { - Box::pin(async move { - let input: DowncastBothNode<(), QuantizationChannels> = DowncastBothNode::new(args[0]); - let node: CacheNode = graphene_std::memo::CacheNode::new(input); - let any = DynAnyNode::new(ValueNode::new(node)); - Box::pin(any) as TypeErasedPinned - }) - }, - NodeIOTypes::new(concrete!(()), concrete!(QuantizationChannels), vec![value_fn!(QuantizationChannels)]), - ), - ( - NodeIdentifier::new("graphene_std::memo::CacheNode"), - |args| { - Box::pin(async move { - let input: DowncastBothNode<(), Vec> = DowncastBothNode::new(args[0]); - let node: CacheNode, _> = graphene_std::memo::CacheNode::new(input); - let any = DynAnyNode::new(ValueNode::new(node)); - Box::pin(any) as TypeErasedPinned - }) - }, - NodeIOTypes::new(concrete!(()), concrete!(Vec), vec![value_fn!(Vec)]), - ), - ( - NodeIdentifier::new("graphene_std::memo::CacheNode"), - |args| { - Box::pin(async move { - let input: DowncastBothNode<(), Arc> = DowncastBothNode::new(args[0]); - let node: CacheNode, _> = graphene_std::memo::CacheNode::new(input); - let any = DynAnyNode::new(ValueNode::new(node)); - Box::pin(any) as TypeErasedPinned - }) - }, - NodeIOTypes::new(concrete!(()), concrete!(Arc), vec![value_fn!(Arc)]), - ), - ], + )], + async_node!(graphene_core::memo::MemoNode<_, _>, input: (), output: Image, params: [Image]), + async_node!(graphene_core::memo::MemoNode<_, _>, input: (), output: ImageFrame, params: [ImageFrame]), + async_node!(graphene_core::memo::MemoNode<_, _>, input: (), output: QuantizationChannels, params: [QuantizationChannels]), + async_node!(graphene_core::memo::MemoNode<_, _>, input: (), output: Vec, params: [Vec]), + async_node!(graphene_core::memo::MemoNode<_, _>, input: (), output: Arc, params: [Arc]), register_node!(graphene_core::structural::ConsNode<_, _>, input: Image, params: [&str]), register_node!(graphene_std::raster::ImageFrameNode<_, _>, input: Image, params: [DAffine2]), #[cfg(feature = "quantization")] @@ -698,9 +494,9 @@ fn node_registry() -> HashMap>, params: [Vec] ), - register_node!(graphene_core::text::TextGenerator<_, _, _>, input: &graphene_core::EditorApi, params: [String, graphene_core::text::Font, f64]), + register_node!(graphene_core::text::TextGenerator<_, _, _>, input: graphene_core::EditorApi, params: [String, graphene_core::text::Font, f64]), register_node!(graphene_std::brush::VectorPointsNode, input: VectorData, params: []), - register_node!(graphene_core::ExtractImageFrame, input: &graphene_core::EditorApi, params: []), + register_node!(graphene_core::ExtractImageFrame, input: graphene_core::EditorApi, params: []), register_node!(graphene_core::ConstructLayerNode<_, _, _, _, _, _, _>, input: graphene_core::vector::VectorData, params: [String, BlendMode, f32, bool, bool, bool, graphene_core::GraphicGroup]), register_node!(graphene_core::ConstructLayerNode<_, _, _, _, _, _, _>, input: ImageFrame, params: [String, BlendMode, f32, bool, bool, bool, graphene_core::GraphicGroup]), register_node!(graphene_core::ConstructLayerNode<_, _, _, _, _, _, _>, input: graphene_core::GraphicGroup, params: [String, BlendMode, f32, bool, bool, bool, graphene_core::GraphicGroup]), diff --git a/node-graph/node-macro/src/lib.rs b/node-graph/node-macro/src/lib.rs index 0ba212e5f..6403d6928 100644 --- a/node-graph/node-macro/src/lib.rs +++ b/node-graph/node-macro/src/lib.rs @@ -86,16 +86,14 @@ fn args(node: &syn::PathSegment) -> Vec { fn node_impl_proxy(attr: TokenStream, item: TokenStream) -> TokenStream { let fn_item = item.clone(); let function = parse_macro_input!(fn_item as ItemFn); - let sync_input = if function.sig.asyncness.is_some() { + if function.sig.asyncness.is_some() { node_impl_impl(attr, item, Asyncness::AllAsync) } else { node_impl_impl(attr, item, Asyncness::Sync) - }; - sync_input + } } enum Asyncness { Sync, - AsyncOut, AllAsync, } @@ -111,7 +109,7 @@ fn node_impl_impl(attr: TokenStream, item: TokenStream, asyncness: Asyncness) -> let async_out = match asyncness { Asyncness::Sync => false, - Asyncness::AsyncOut | Asyncness::AllAsync => true, + Asyncness::AllAsync => true, }; let async_in = matches!(asyncness, Asyncness::AllAsync); diff --git a/node-graph/vulkan-executor/src/executor.rs b/node-graph/vulkan-executor/src/executor.rs index 473d46144..31bf2fb55 100644 --- a/node-graph/vulkan-executor/src/executor.rs +++ b/node-graph/vulkan-executor/src/executor.rs @@ -1,6 +1,8 @@ +use std::error::Error; + use super::context::Context; -use graph_craft::executor::{Any, Executor}; +use graph_craft::executor::Executor; use graph_craft::proto::LocalFuture; use graphene_core::gpu::PushConstants; @@ -38,9 +40,8 @@ impl GpuExecutor { } } -impl Executor for GpuExecutor { - fn execute<'i>(&'i self, input: Any<'i>) -> LocalFuture, Box>> { - let input = dyn_any::downcast::>(input).expect("Wrong input type"); +impl<'a, I: StaticTypeSized + Sync + Pod + Send + 'a, O: StaticTypeSized + Send + Sync + Pod + 'a> Executor, Vec> for &'a GpuExecutor { + fn execute(&self, input: Vec) -> LocalFuture, Box>> { let context = &self.context; let result: Vec = execute_shader( context.device.clone(), @@ -48,9 +49,9 @@ impl { @@ -29,16 +26,15 @@ impl<'a, I: StaticTypeSized, O> GpuExecutor<'a, I, O> { } } -impl<'a, I: StaticTypeSized + Sync + Pod + Send, O: StaticTypeSized + Send + Sync + Pod> Executor for GpuExecutor<'a, I, O> { - fn execute<'i>(&'i self, input: Any<'i>) -> LocalFuture, Box>> { - let input = dyn_any::downcast::>(input).expect("Wrong input type"); +impl<'a, I: StaticTypeSized + Sync + Pod + Send, O: StaticTypeSized + Send + Sync + Pod> Executor, Vec> for GpuExecutor<'a, I, O> { + fn execute(&self, input: Vec) -> LocalFuture, Box>> { let context = &self.context; - let future = execute_shader(context.device.clone(), context.queue.clone(), self.shader.to_vec(), *input, self.entry_point.clone()); + let future = execute_shader(context.device.clone(), context.queue.clone(), self.shader.to_vec(), input, self.entry_point.clone()); Box::pin(async move { let result = future.await; let result: Vec = result.ok_or_else(|| String::from("Failed to execute shader"))?; - Ok(Box::new(result) as Any) + Ok(result) }) } }