diff --git a/node-graph/gcore/src/generic.rs b/node-graph/gcore/src/generic.rs index 49f3123f95..bd82d3f080 100644 --- a/node-graph/gcore/src/generic.rs +++ b/node-graph/gcore/src/generic.rs @@ -1,6 +1,7 @@ use core::marker::PhantomData; use crate::Node; +#[derive(Clone)] pub struct FnNode O, I, O>(T, PhantomData<(I, O)>); impl<'i, T: Fn(I) -> O + 'i, O: 'i, I: 'i> Node<'i, I> for FnNode { diff --git a/node-graph/gcore/src/logic.rs b/node-graph/gcore/src/logic.rs index 494a473596..0e4214f060 100644 --- a/node-graph/gcore/src/logic.rs +++ b/node-graph/gcore/src/logic.rs @@ -11,7 +11,7 @@ fn log_to_console(_: impl Ctx, #[implementations(String, bo value } -#[node_macro::node(category("Debug"))] +#[node_macro::node(category("Debug"), skip_impl)] fn to_string(_: impl Ctx, #[implementations(String, bool, f64, u32, u64, DVec2, VectorDataTable, DAffine2)] value: T) -> String { format!("{:?}", value) } diff --git a/node-graph/gcore/src/ops.rs b/node-graph/gcore/src/ops.rs index bf91ae5a65..5da6e1f08e 100644 --- a/node-graph/gcore/src/ops.rs +++ b/node-graph/gcore/src/ops.rs @@ -485,7 +485,7 @@ fn dot_product(_: impl Ctx, vector_a: DVec2, vector_b: DVec2) -> f64 { // TODO: Rename to "Passthrough" /// Passes-through the input value without changing it. This is useful for rerouting wires for organization purposes. #[node_macro::node(skip_impl)] -fn identity<'i, T: 'i>(value: T) -> T { +fn identity<'i, T: 'i + Send>(value: T) -> T { value } diff --git a/node-graph/gstd/src/brush.rs b/node-graph/gstd/src/brush.rs index e1d88a812f..933aa14336 100644 --- a/node-graph/gstd/src/brush.rs +++ b/node-graph/gstd/src/brush.rs @@ -1,5 +1,7 @@ use crate::raster::{blend_image_closure, BlendImageTupleNode, EmptyImageNode, ExtendImageToBoundsNode}; +use graph_craft::generic::FnNode; +use graph_craft::proto::FutureWrapperNode; use graphene_core::raster::adjustments::blend_colors; use graphene_core::raster::bbox::{AxisAlignedBbox, Bbox}; use graphene_core::raster::brush_cache::BrushCache; @@ -131,14 +133,21 @@ where target } -pub fn create_brush_texture(brush_style: &BrushStyle) -> Image { +pub async fn create_brush_texture(brush_style: &BrushStyle) -> Image { let stamp = brush_stamp_generator(brush_style.diameter, brush_style.color, brush_style.hardness, brush_style.flow); let transform = DAffine2::from_scale_angle_translation(DVec2::splat(brush_style.diameter), 0., -DVec2::splat(brush_style.diameter / 2.)); - let blank_texture = EmptyImageNode::new(CopiedNode::new(transform), CopiedNode::new(Color::TRANSPARENT)).eval(()); - let normal_blend = BlendColorPairNode::new(ValueNode::new(CopiedNode::new(BlendMode::Normal)), ValueNode::new(CopiedNode::new(100.))); + use crate::raster::empty_image; + let blank_texture = empty_image((), transform, Color::TRANSPARENT); + let normal_blend = BlendColorPairNode::new( + FutureWrapperNode::new(ValueNode::new(CopiedNode::new(BlendMode::Normal))), + FutureWrapperNode::new(ValueNode::new(CopiedNode::new(100.))), + ); normal_blend.eval((Color::default(), Color::default())); - let blend_executor = BlendImageTupleNode::new(ValueNode::new(normal_blend)); - blend_executor.eval((blank_texture, stamp)).image + // use crate::raster::blend_image_tuple; + // blend_image_tuple((blank_texture, stamp), &normal_blend).await.image; + crate::raster::blend_image_closure(stamp, blank_texture, |a, b| blend_colors(a, b, BlendMode::Normal, 100.)).image + // let blend_executoc = BlendImageTupleNode::new(FutureWrapperNode::new(ValueNode::new(normal_blend))); + // blend_executor.eval((blank_texture, stamp)).image } macro_rules! inline_blend_funcs { @@ -202,7 +211,7 @@ pub fn blend_with_mode(background: ImageFrame, foreground: ImageFrame, bounds: ImageFrameTable, strokes: Vec, cache: BrushCache) -> ImageFrameTable { +async fn brush(_: impl Ctx, image: ImageFrameTable, bounds: ImageFrameTable, strokes: Vec, cache: BrushCache) -> ImageFrameTable { let image = image.one_item().clone(); let stroke_bbox = strokes.iter().map(|s| s.bounding_box()).reduce(|a, b| a.union(&b)).unwrap_or(AxisAlignedBbox::ZERO); @@ -225,11 +234,13 @@ fn brush(_: impl Ctx, image: ImageFrameTable, bounds: ImageFrameTable, bounds: ImageFrameTable, bounds: ImageFrameTable = stroke.compute_blit_points().into_iter().collect(); match stroke.style.blend_mode { BlendMode::Erase => { - let blend_params = BlendColorPairNode::new(ValueNode(CopiedNode::new(BlendMode::Erase)), ValueNode(CopiedNode::new(100.))); - let blit_node = BlitNode::new(ClonedNode::new(brush_texture), ClonedNode::new(positions), ClonedNode::new(blend_params)); - erase_restore_mask = blit_node.eval(erase_restore_mask); + let blend_params = FnNode::new(|(a, b)| blend_colors(a, b, BlendMode::Erase, 100.)); + let blit_node = BlitNode::new( + FutureWrapperNode::new(ClonedNode::new(brush_texture)), + FutureWrapperNode::new(ClonedNode::new(positions)), + FutureWrapperNode::new(ClonedNode::new(blend_params)), + ); + erase_restore_mask = blit_node.eval(erase_restore_mask).await; } // Yes, this is essentially the same as the above, but we duplicate to inline the blend mode. BlendMode::Restore => { - let blend_params = BlendColorPairNode::new(ValueNode(CopiedNode::new(BlendMode::Restore)), ValueNode(CopiedNode::new(100.))); - let blit_node = BlitNode::new(ClonedNode::new(brush_texture), ClonedNode::new(positions), ClonedNode::new(blend_params)); - erase_restore_mask = blit_node.eval(erase_restore_mask); + let blend_params = FnNode::new(|(a, b)| blend_colors(a, b, BlendMode::Restore, 100.)); + let blit_node = BlitNode::new( + FutureWrapperNode::new(ClonedNode::new(brush_texture)), + FutureWrapperNode::new(ClonedNode::new(positions)), + FutureWrapperNode::new(ClonedNode::new(blend_params)), + ); + erase_restore_mask = blit_node.eval(erase_restore_mask).await; } _ => unreachable!(), } } - let blend_params = BlendColorPairNode::new(ValueNode(CopiedNode::new(BlendMode::MultiplyAlpha)), ValueNode(CopiedNode::new(100.))); - let blend_executor = BlendImageTupleNode::new(ValueNode::new(blend_params)); - actual_image = blend_executor.eval((actual_image, erase_restore_mask)); + let blend_params = FnNode::new(|(a, b)| blend_colors(a, b, BlendMode::MultiplyAlpha, 100.)); + let blend_executor = BlendImageTupleNode::new(FutureWrapperNode::new(ValueNode::new(blend_params))); + actual_image = blend_executor.eval((actual_image, erase_restore_mask)).await; } ImageFrameTable::new(actual_image) diff --git a/node-graph/gstd/src/raster.rs b/node-graph/gstd/src/raster.rs index 27d393b938..c836703f2b 100644 --- a/node-graph/gstd/src/raster.rs +++ b/node-graph/gstd/src/raster.rs @@ -29,7 +29,7 @@ impl From for Error { } #[node_macro::node(category("Debug: Raster"))] -fn sample_image(ctx: impl ExtractFootprint + Clone, image_frame: ImageFrameTable) -> ImageFrameTable { +fn sample_image(ctx: impl ExtractFootprint + Clone + Send, image_frame: ImageFrameTable) -> ImageFrameTable { let image_frame = image_frame.one_item(); // Resize the image using the image crate @@ -248,17 +248,17 @@ fn mask_image< image } -#[derive(Debug, Clone, Copy)] -pub struct BlendImageTupleNode { - map_fn: MapFn, - _p: PhantomData

, - _fg: PhantomData, -} +// #[derive(Debug, Clone, Copy)] +// pub struct BlendImageTupleNode { +// map_fn: MapFn, +// _p: PhantomData

, +// _fg: PhantomData, +// } -#[node_macro::old_node_fn(BlendImageTupleNode<_P, _Fg>)] -fn blend_image_tuple<_P: Alpha + Pixel + Debug, MapFn, _Fg: Sample + Transform>(images: (ImageFrame<_P>, _Fg), map_fn: &'input MapFn) -> ImageFrame<_P> +#[node_macro::node(skip_impl)] +async fn blend_image_tuple<_P: Alpha + Pixel + Debug + Send, MapFn, _Fg: Sample + Transform + Clone + Send + 'n>(images: (ImageFrame<_P>, _Fg), map_fn: &'n MapFn) -> ImageFrame<_P> where - MapFn: for<'any_input> Node<'any_input, (_P, _P), Output = _P> + 'input + Clone, + MapFn: for<'any_input> Node<'any_input, (_P, _P), Output = _P> + 'n + Clone, { let (background, foreground) = images; @@ -628,7 +628,7 @@ fn noise_pattern( } #[node_macro::node(category("Raster: Generator"))] -fn mandelbrot(ctx: impl ExtractFootprint) -> ImageFrameTable { +fn mandelbrot(ctx: impl ExtractFootprint + Send) -> ImageFrameTable { let footprint = ctx.footprint(); let viewport_bounds = footprint.viewport_bounds_in_local_space(); diff --git a/node-graph/interpreted-executor/src/node_registry.rs b/node-graph/interpreted-executor/src/node_registry.rs index f8ad84c8e5..8fa25e3a9b 100644 --- a/node-graph/interpreted-executor/src/node_registry.rs +++ b/node-graph/interpreted-executor/src/node_registry.rs @@ -1,4 +1,4 @@ -use dyn_any::StaticType; +use dyn_any::{DynFuture, StaticType}; use graph_craft::document::value::RenderOutput; use graph_craft::proto::{NodeConstructor, TypeErasedBox}; use graphene_core::fn_type; @@ -32,6 +32,7 @@ macro_rules! construct_node { let node = <$path>::new($( { let node = graphene_std::any::downcast_node::<$arg, $type>(args.pop().expect("Not enough arguments provided to construct node")); + // let node = graphene_std::any::FutureWrapperNode::new(node); let value = node.eval(None).await; graphene_core::value::ClonedNode::new(value) } @@ -51,7 +52,6 @@ macro_rules! register_node { |args| { Box::pin(async move { let node = construct_node!(args, $path, [$($arg => $type),*]).await; - let node = graphene_std::any::FutureWrapperNode::new(node); let any: DynAnyNode<$input, _, _> = graphene_std::any::DynAnyNode::new(node); Box::new(any) as TypeErasedBox }) @@ -105,11 +105,11 @@ macro_rules! async_node { // TODO: turn into hashmap fn node_registry() -> HashMap> { let node_types: Vec<(ProtoNodeIdentifier, NodeConstructor, NodeIOTypes)> = vec![ - ( - ProtoNodeIdentifier::new("graphene_core::ops::IdentityNode"), - |_| Box::pin(async move { FutureWrapperNode::new(IdentityNode::new()).into_type_erased() }), - NodeIOTypes::new(generic!(I), generic!(I), vec![]), - ), + // ( + // ProtoNodeIdentifier::new("graphene_core::ops::IdentityNode"), + // |_| Box::pin(async move { FutureWrapperNode::new(IdentityNode::new()).into_type_erased() }), + // NodeIOTypes::new(generic!(I), generic!(I), vec![]), + // ), // async_node!(graphene_core::ops::IntoNode>, input: ImageFrameTable, params: []), // async_node!(graphene_core::ops::IntoNode>, input: ImageFrameTable, params: []), async_node!(graphene_core::ops::IntoNode, input: ImageFrameTable, params: []), @@ -128,7 +128,26 @@ fn node_registry() -> HashMap, input: Context, fn_params: [Context => GraphicElement]), async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => Artboard]), #[cfg(feature = "gpu")] - register_node!(wgpu_executor::CreateGpuSurfaceNode<_>, input: Context, params: [&WasmEditorApi]), + ( + ProtoNodeIdentifier::new(stringify!(wgpu_executor::CreateGpuSurfaceNode<_>)), + |args| { + Box::pin(async move { + let editor_api: DowncastBothNode = DowncastBothNode::new(args[1].clone()); + let node = >::new(editor_api); + // let node = FutureWrapperNode::new(node); + let any: DynAnyNode = graphene_std::any::DynAnyNode::new(node); + Box::new(any) as TypeErasedBox + }) + }, + { + let node = >::new(graphene_std::any::PanicNode::>::new()); + let params = vec![fn_type!(Context, DynFuture<'static, &WasmEditorApi>)]; + let mut node_io = as NodeIO<'_, Context>>::to_async_node_io(&node, params); + node_io.call_argument = concrete!(::Static); + node_io + }, + ), + // register_node!(wgpu_executor::CreateGpuSurfaceNode<_>, input: Context, params: [&WasmEditorApi]), #[cfg(feature = "gpu")] ( ProtoNodeIdentifier::new("graphene_std::executor::MapGpuSingleImageNode"), @@ -213,27 +232,27 @@ fn node_registry() -> HashMap), concrete!(ImageFrameTable), vec![fn_type!(graphene_core::raster::curve::Curve)]), // ), // TODO: Use channel split and merge for this instead of using LuminanceMut for the whole color. - ( - ProtoNodeIdentifier::new("graphene_core::raster::CurvesNode"), - |args| { - use graphene_core::raster::{curve::Curve, GenerateCurvesNode}; - let curve: DowncastBothNode<(), Curve> = DowncastBothNode::new(args[0].clone()); - Box::pin(async move { - let curve = ClonedNode::new(curve.eval(()).await); + // ( + // ProtoNodeIdentifier::new("graphene_core::raster::CurvesNode"), + // |args| { + // use graphene_core::raster::{curve::Curve, GenerateCurvesNode}; + // let curve: DowncastBothNode<(), Curve> = DowncastBothNode::new(args[0].clone()); + // Box::pin(async move { + // let curve = ValueNode::new(ClonedNode::new(curve.eval(()).await)); - let generate_curves_node = GenerateCurvesNode::new(curve, ClonedNode::new(0_f32)); - let map_image_frame_node = graphene_std::raster::MapImageNode::new(ValueNode::new(generate_curves_node.eval(()))); - let map_image_frame_node = FutureWrapperNode::new(map_image_frame_node); - let any: DynAnyNode, _, _> = graphene_std::any::DynAnyNode::new(map_image_frame_node); - any.into_type_erased() - }) - }, - NodeIOTypes::new( - concrete!(ImageFrameTable), - concrete!(ImageFrameTable), - vec![fn_type!(graphene_core::raster::curve::Curve)], - ), - ), + // let generate_curves_node = GenerateCurvesNode::new(FutureWrapperNode::new(curve), FutureWrapperNode::new(ClonedNode::new(0_f32))); + // let map_image_frame_node = graphene_std::raster::MapImageNode::new(FutureWrapperNode::new(ValueNode::new(generate_curves_node.eval(())))); + // let map_image_frame_node = FutureWrapperNode::new(map_image_frame_node); + // let any: DynAnyNode, _, _> = graphene_std::any::DynAnyNode::new(map_image_frame_node); + // any.into_type_erased() + // }) + // }, + // NodeIOTypes::new( + // concrete!(ImageFrameTable), + // concrete!(ImageFrameTable), + // vec![fn_type!(graphene_core::raster::curve::Curve)], + // ), + // ), // ( // ProtoNodeIdentifier::new("graphene_std::raster::ImaginateNode"), // |args: Vec| { diff --git a/node-graph/node-macro/src/codegen.rs b/node-graph/node-macro/src/codegen.rs index fa1a56cf7a..ad6abcbbc6 100644 --- a/node-graph/node-macro/src/codegen.rs +++ b/node-graph/node-macro/src/codegen.rs @@ -154,7 +154,7 @@ pub(crate) fn generate_node_code(parsed: &ParsedNodeFn) -> syn::Result { let name = &pat_ident.ident; - quote! { let #name = self.#name.eval(__input.clone()); } + quote! { let #name = self.#name.eval(__input.clone()).await; } // quote! { let #name = self.#name.eval(()); } } ParsedField::Node { pat_ident, .. } => { @@ -178,13 +178,18 @@ pub(crate) fn generate_node_code(parsed: &ParsedNodeFn) -> syn::Result { let all_lifetime_ty = substitute_lifetimes(ty.clone(), "all"); + let id = future_idents.len(); + let fut_ident = format_ident!("F{}", id); + future_idents.push(fut_ident.clone()); quote!( + #fut_ident: core::future::Future + #graphene_core::WasmNotSend + 'n, for<'all> #all_lifetime_ty: #graphene_core::WasmNotSend, // #name: 'n, - #name: #graphene_core::Node<'n, #input_type, Output = #ty> + #name: #graphene_core::Node<'n, #input_type, Output = #fut_ident> + #graphene_core::WasmNotSync ) } (ParsedField::Node { input_type, output_type, .. }, false) => { + unreachable!(); quote!( // #name: 'n, #name: #graphene_core::Node<'n, #input_type, Output = #output_type> + #graphene_core::WasmNotSync @@ -220,14 +225,17 @@ pub(crate) fn generate_node_code(parsed: &ParsedNodeFn) -> syn::Result; #[inline] fn eval(&'n self, __input: #input_type) -> Self::Output { - #(#eval_args)* - Box::pin(self::#fn_name(__input #(, #field_names)*)) + Box::pin(async move { + #(#eval_args)* + self::#fn_name(__input #(, #field_names)*) #await_keyword + }) } } } else { @@ -255,7 +263,7 @@ pub(crate) fn generate_node_code(parsed: &ParsedNodeFn) -> syn::Result (#input_ident: #input_type #(, #field_idents: #field_types)*) -> #output_type #where_clause #body + pub(crate) #async_keyword fn #fn_name <'n, #(#fn_generics,)*> (#input_ident: #input_type #(, #field_idents: #field_types)*) -> #output_type #where_clause #body #[automatically_derived] impl<'n, #(#fn_generics,)* #(#struct_generics,)* #(#future_idents,)*> #graphene_core::Node<'n, #input_type> for #mod_name::#struct_name<#(#struct_generics,)*> @@ -365,7 +373,7 @@ fn generate_register_node_impl(parsed: &ParsedNodeFn, field_names: &[&Ident], st .collect(); let max_implementations = parameter_types.iter().map(|x| x.len()).chain([parsed.input.implementations.len().max(1)]).max(); - let future_node = (!parsed.is_async).then(|| quote!(let node = gcore::registry::FutureWrapperNode::new(node);)); + let future_node = (!parsed.is_async && false).then(|| quote!(let node = gcore::registry::FutureWrapperNode::new(node);)); for i in 0..max_implementations.unwrap_or(0) { let mut temp_constructors = Vec::new(); @@ -389,14 +397,14 @@ fn generate_register_node_impl(parsed: &ParsedNodeFn, field_names: &[&Ident], st } else { quote!( #downcast_node - let #field_name = #field_name.eval(None).await; - let #field_name = ClonedNode::new(#field_name); - let #field_name: TypeNode<_, #input_type, #output_type> = TypeNode::new(#field_name); + // let #field_name = #field_name.eval(None).await; + // let #field_name = ClonedNode::new(#field_name); + // let #field_name: TypeNode<_, #input_type, #output_type> = TypeNode::new(#field_name); // try polling futures ) }); temp_node_io.push(quote!(fn_type!(#input_type, #output_type, alias: #output_type))); - match parsed.is_async && *impl_node { + match (parsed.is_async || true) && *impl_node || true { true => panic_node_types.push(quote!(#input_type, DynFuture<'static, #output_type>)), false => panic_node_types.push(quote!(#input_type, #output_type)), }; @@ -405,7 +413,7 @@ fn generate_register_node_impl(parsed: &ParsedNodeFn, field_names: &[&Ident], st true => parsed.input.ty.clone(), false => parsed.input.implementations[i.min(parsed.input.implementations.len() - 1)].clone(), }; - let node_io = if parsed.is_async { quote!(to_async_node_io) } else { quote!(to_node_io) }; + let node_io = if (parsed.is_async || true) { quote!(to_async_node_io) } else { quote!(to_node_io) }; constructors.push(quote!( ( |args| {