Make node graph execution async

Make node macro generate async node implementations

Start propagating async through the node system

Async checkpoint

Make Any<'i> Send + Sync

Determine node io type using panic node

Fix types for raster_node macro

Finish porting node registry?

Fix lifetime errors

Remove Send + Sync requirements and start making node construction async

Async MVP

Fix tests

Clippy fix
This commit is contained in:
Dennis Kobert 2023-05-12 19:12:00 +02:00 committed by Keavon Chambers
parent 0c7b55949d
commit b62ce1d03c
30 changed files with 545 additions and 239 deletions

4
Cargo.lock generated
View file

@ -1659,6 +1659,7 @@ dependencies = [
"compilation-client",
"dyn-any",
"dyn-clone",
"futures",
"glam",
"gpu-compiler-bin-wrapper",
"gpu-executor",
@ -1671,6 +1672,7 @@ dependencies = [
"once_cell",
"proc-macro2",
"quote",
"reqwest",
"serde",
"serde_json",
"syn 1.0.109",
@ -2140,6 +2142,7 @@ dependencies = [
"borrow_stack",
"dyn-any",
"dyn-clone",
"futures",
"glam",
"graph-craft",
"graphene-core",
@ -2148,6 +2151,7 @@ dependencies = [
"num-traits",
"once_cell",
"serde",
"tokio",
]
[[package]]

View file

@ -1505,7 +1505,7 @@ impl DocumentMessageHandler {
/// When working with an insert index, deleting the layers may cause the insert index to point to a different location (if the layer being deleted was located before the insert index).
///
/// This function updates the insert index so that it points to the same place after the specified `layers` are deleted.
fn update_insert_index<'a>(&self, layers: &[&'a [LayerId]], path: &[LayerId], insert_index: isize, reverse_index: bool) -> Result<isize, DocumentError> {
fn update_insert_index(&self, layers: &[&[LayerId]], path: &[LayerId], insert_index: isize, reverse_index: bool) -> Result<isize, DocumentError> {
let folder = self.document_legacy.folder(path)?;
let insert_index = if reverse_index { folder.layer_ids.len() as isize - insert_index } else { insert_index };
let layer_ids_above = if insert_index < 0 { &folder.layer_ids } else { &folder.layer_ids[..(insert_index as usize)] };

View file

@ -24,7 +24,7 @@ struct ManipulatorGroupOverlays {
pub out_line: Option<Vec<LayerId>>,
}
impl ManipulatorGroupOverlays {
pub fn iter<'a>(&'a self) -> impl Iterator<Item = &'a Option<Vec<LayerId>>> {
pub fn iter(&self) -> impl Iterator<Item = &'_ Option<Vec<LayerId>>> {
[&self.anchor, &self.in_handle, &self.in_line, &self.out_handle, &self.out_line].into_iter()
}
}

View file

@ -145,7 +145,7 @@ impl ShapeState {
}
/// Provide the currently selected points by reference.
pub fn selected_points<'a>(&'a self) -> impl Iterator<Item = &'a ManipulatorPointId> {
pub fn selected_points(&self) -> impl Iterator<Item = &'_ ManipulatorPointId> {
self.selected_shape_state.values().flat_map(|state| &state.selected_points)
}

View file

@ -23,6 +23,7 @@ use interpreted_executor::executor::DynamicExecutor;
use glam::{DAffine2, DVec2};
use std::borrow::Cow;
use std::cell::RefCell;
use std::rc::Rc;
use std::sync::mpsc::{Receiver, Sender};
use std::sync::Arc;
@ -58,7 +59,7 @@ pub(crate) struct GenerationResponse {
}
thread_local! {
static NODE_RUNTIME: RefCell<Option<NodeRuntime>> = RefCell::new(None);
pub(crate) static NODE_RUNTIME: Rc<RefCell<Option<NodeRuntime>>> = Rc::new(RefCell::new(None));
}
impl NodeRuntime {
@ -72,7 +73,7 @@ impl NodeRuntime {
thumbnails: Default::default(),
}
}
pub fn run(&mut self) {
pub async fn run(&mut self) {
let mut requests = self.receiver.try_iter().collect::<Vec<_>>();
// TODO: Currently we still render the document after we submit the node graph execution request.
// This should be avoided in the future.
@ -94,7 +95,7 @@ impl NodeRuntime {
}) => {
let (network, monitor_nodes) = Self::wrap_network(graph);
let result = self.execute_network(network, image_frame);
let result = self.execute_network(network, image_frame).await;
let mut responses = VecDeque::new();
self.update_thumbnails(&path, monitor_nodes, &mut responses);
let response = GenerationResponse {
@ -125,7 +126,7 @@ impl NodeRuntime {
(scoped_network, monitor_nodes)
}
fn execute_network<'a>(&'a mut self, scoped_network: NodeNetwork, image_frame: Option<ImageFrame<Color>>) -> Result<TaggedValue, String> {
async fn execute_network<'a>(&'a mut self, scoped_network: NodeNetwork, image_frame: Option<ImageFrame<Color>>) -> Result<TaggedValue, String> {
let editor_api = EditorApi {
font_cache: Some(&self.font_cache),
image_frame,
@ -137,7 +138,7 @@ impl NodeRuntime {
let proto_network = c.compile_single(scoped_network, true)?;
assert_ne!(proto_network.nodes.len(), 0, "No protonodes exist?");
if let Err(e) = self.executor.update(proto_network) {
if let Err(e) = self.executor.update(proto_network).await {
error!("Failed to update executor:\n{}", e);
return Err(e);
}
@ -146,8 +147,8 @@ impl NodeRuntime {
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()).map_err(|e| e.to_string()),
Some(t) if t == concrete!(()) => self.executor.execute(().into_dyn()).map_err(|e| e.to_string()),
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()),
_ => Err("Invalid input type".to_string()),
};
match result {
@ -200,13 +201,21 @@ impl NodeRuntime {
}
}
pub fn run_node_graph() {
NODE_RUNTIME.with(|runtime| {
let mut runtime = runtime.borrow_mut();
if let Some(runtime) = runtime.as_mut() {
runtime.run();
pub async fn run_node_graph() {
let result = NODE_RUNTIME.try_with(|runtime| {
let runtime = runtime.clone();
async move {
let mut runtime = runtime.try_borrow_mut();
if let Ok(ref mut runtime) = runtime {
if let Some(ref mut runtime) = runtime.as_mut() {
runtime.run().await;
}
}
}
});
if let Ok(result) = result {
result.await;
}
}
#[derive(Debug)]
@ -231,8 +240,7 @@ impl Default for NodeGraphExecutor {
let (request_sender, request_reciever) = std::sync::mpsc::channel();
let (response_sender, response_reciever) = std::sync::mpsc::channel();
NODE_RUNTIME.with(|runtime| {
let mut runtime = runtime.borrow_mut();
*runtime = Some(NodeRuntime::new(request_reciever, response_sender));
runtime.borrow_mut().replace(NodeRuntime::new(request_reciever, response_sender));
});
Self {

View file

@ -12,12 +12,15 @@ use axum::routing::get;
use axum::Router;
use fern::colors::{Color, ColoredLevelConfig};
use http::{Response, StatusCode};
use std::cell::RefCell;
use std::collections::HashMap;
use std::sync::Arc;
use std::sync::Mutex;
static IMAGES: Mutex<Option<HashMap<String, FrontendImageData>>> = Mutex::new(None);
static EDITOR: Mutex<Option<Editor>> = Mutex::new(None);
thread_local! {
static EDITOR: RefCell<Option<Editor>> = RefCell::new(None);
}
async fn respond_to(id: Path<String>) -> impl IntoResponse {
let builder = Response::builder().header("Access-Control-Allow-Origin", "*").status(StatusCode::OK);
@ -55,7 +58,7 @@ async fn main() {
*(IMAGES.lock().unwrap()) = Some(HashMap::new());
graphite_editor::application::set_uuid_seed(0);
*(EDITOR.lock().unwrap()) = Some(Editor::new());
EDITOR.with(|editor| editor.borrow_mut().replace(Editor::new()));
let app = Router::new().route("/", get(|| async { "Hello, World!" })).route("/image/:id", get(respond_to));
// run it with hyper on localhost:3000
@ -84,9 +87,10 @@ fn handle_message(message: String) -> String {
let Ok(message) = ron::from_str::<graphite_editor::messages::message::Message>(&message) else {
panic!("Error parsing message: {}", message)
};
let mut guard = EDITOR.lock().unwrap();
let editor = (*guard).as_mut().unwrap();
let responses = editor.handle_message(message);
let responses = EDITOR.with(|editor| {
let mut editor = editor.borrow_mut();
editor.as_mut().unwrap().handle_message(message)
});
// Sends a FrontendMessage to JavaScript
fn send_frontend_message_to_js(message: FrontendMessage) -> FrontendMessage {

View file

@ -59,16 +59,17 @@ fn window() -> web_sys::Window {
}
fn request_animation_frame(f: &Closure<dyn FnMut()>) {
window().request_animation_frame(f.as_ref().unchecked_ref()).expect("should register `requestAnimationFrame` OK");
window().request_idle_callback(f.as_ref().unchecked_ref()).unwrap();
//window().request_animation_frame(f.as_ref().unchecked_ref()).expect("should register `requestAnimationFrame` OK");
}
// Sends a message to the dispatcher in the Editor Backend
fn poll_node_graph_evaluation() {
async fn poll_node_graph_evaluation() {
// Process no further messages after a crash to avoid spamming the console
if EDITOR_HAS_CRASHED.load(Ordering::SeqCst) {
return;
}
editor::node_graph_executor::run_node_graph();
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| {
@ -218,7 +219,7 @@ impl JsEditorHandle {
let g = f.clone();
*g.borrow_mut() = Some(Closure::new(move || {
poll_node_graph_evaluation();
wasm_bindgen_futures::spawn_local(poll_node_graph_evaluation());
// Schedule ourself for another requestAnimationFrame callback.
request_animation_frame(f.borrow().as_ref().unwrap());
@ -788,7 +789,7 @@ impl JsEditorHandle {
Some(message_data)
})
});
frontend_messages.unwrap().unwrap_or_default().into()
frontend_messages.unwrap().unwrap_or_default()
}
}

View file

@ -637,7 +637,7 @@ mod tests {
let outline = subpath.outline(10., crate::Join::Round, crate::Cap::Round).0;
assert!(outline.manipulator_groups.windows(2).all(|pair| !pair[0].anchor.abs_diff_eq(pair[1].anchor, MAX_ABSOLUTE_DIFFERENCE)));
assert_eq!(outline.closed(), true);
assert!(outline.closed());
}
#[test]

View file

@ -193,9 +193,18 @@ unsafe impl<T: StaticTypeSized, const N: usize> StaticType for [T; N] {
type Static = [<T as StaticTypeSized>::Static; N];
}
unsafe impl<'a> StaticType for dyn DynAny<'a> + '_ {
unsafe impl StaticType for dyn for<'i> DynAny<'_> + '_ {
type Static = dyn DynAny<'static>;
}
unsafe impl StaticType for dyn for<'i> DynAny<'_> + Send + Sync + '_ {
type Static = dyn DynAny<'static> + Send + Sync;
}
unsafe impl<T: StaticTypeSized> StaticType for dyn core::future::Future<Output = T> + Send + Sync + '_ {
type Static = dyn core::future::Future<Output = T::Static> + Send + Sync;
}
unsafe impl<T: StaticTypeSized> StaticType for dyn core::future::Future<Output = T> + '_ {
type Static = dyn core::future::Future<Output = T::Static>;
}
#[cfg(feature = "alloc")]
pub trait IntoDynAny<'n>: Sized + StaticType + 'n {
fn into_dyn(self) -> Box<dyn DynAny<'n> + 'n> {
@ -228,13 +237,14 @@ use core::{
mem::{ManuallyDrop, MaybeUninit},
num::Wrapping,
ops::Range,
pin::Pin,
time::Duration,
};
impl_type!(
Option<T>, Result<T, E>, Cell<T>, UnsafeCell<T>, RefCell<T>, MaybeUninit<T>,
ManuallyDrop<T>, PhantomData<T>, PhantomPinned, Empty<T>, Range<T>,
Wrapping<T>, Duration, bool, f32, f64, char,
Wrapping<T>, Pin<T>, Duration, bool, f32, f64, char,
u8, AtomicU8, u16, AtomicU16, u32, AtomicU32, u64, usize, AtomicUsize,
i8, AtomicI8, i16, AtomicI16, i32, AtomicI32, i64, isize, AtomicIsize,
i128, u128, AtomicBool, AtomicPtr<T>

View file

@ -329,6 +329,7 @@ impl<'a> AsRef<EditorApi<'a>> for EditorApi<'a> {
}
}
#[derive(Debug, Clone, Copy, Default)]
pub struct ExtractImageFrame;
impl<'a: 'input, 'input> Node<'input, EditorApi<'a>> for ExtractImageFrame {

View file

@ -32,6 +32,39 @@ where
}
}
#[derive(Clone)]
pub struct AsyncComposeNode<First, Second, I> {
first: First,
second: Second,
phantom: PhantomData<I>,
}
impl<'i, 'f: 'i, 's: 'i, Input: 'static, First, Second> Node<'i, Input> for AsyncComposeNode<First, Second, Input>
where
First: Node<'i, Input>,
First::Output: core::future::Future,
Second: Node<'i, <<First as Node<'i, Input>>::Output as core::future::Future>::Output> + 'i,
{
type Output = core::pin::Pin<Box<dyn core::future::Future<Output = <Second as Node<'i, <<First as Node<'i, Input>>::Output as core::future::Future>::Output>>::Output> + 'i>>;
fn eval(&'i self, input: Input) -> Self::Output {
Box::pin(async move {
let arg = self.first.eval(input).await;
self.second.eval(arg)
})
}
}
impl<'i, First, Second, Input: 'i> AsyncComposeNode<First, Second, Input>
where
First: Node<'i, Input>,
First::Output: core::future::Future,
Second: Node<'i, <<First as Node<'i, Input>>::Output as core::future::Future>::Output> + 'i,
{
pub const fn new(first: First, second: Second) -> Self {
AsyncComposeNode::<First, Second, Input> { first, second, phantom: PhantomData }
}
}
pub trait Then<'i, Input: 'i>: Sized {
fn then<Second>(self, second: Second) -> ComposeNode<Self, Second, Input>
where
@ -44,6 +77,19 @@ pub trait Then<'i, Input: 'i>: Sized {
impl<'i, First: Node<'i, Input>, Input: 'i> Then<'i, Input> for First {}
pub trait AndThen<'i, Input: 'i>: Sized {
fn and_then<Second>(self, second: Second) -> AsyncComposeNode<Self, Second, Input>
where
Self: Node<'i, Input>,
Self::Output: core::future::Future,
Second: Node<'i, <<Self as Node<'i, Input>>::Output as core::future::Future>::Output> + 'i,
{
AsyncComposeNode::new(self, second)
}
}
impl<'i, First: Node<'i, Input>, Input: 'i> AndThen<'i, Input> for First {}
pub struct ConsNode<I: From<()>, Root>(pub Root, PhantomData<I>);
impl<'i, Root, Input: 'i, I: 'i + From<()>> Node<'i, Input> for ConsNode<I, Root>

View file

@ -158,6 +158,7 @@ mod test {
assert_eq!(node.eval(()), 0);
}
#[test]
#[allow(clippy::unit_cmp)]
fn test_unit_node() {
let node = ForgetNode::new();
assert_eq!(node.eval(()), ());

View file

@ -10,6 +10,7 @@ pub fn compile_spirv(request: &CompileRequest, compile_dir: Option<&str>, manife
io: request.shader_io.clone(),
})?;
#[cfg(not(feature = "profiling"))]
let features = "";
#[cfg(feature = "profiling")]
let features = "profiling";

View file

@ -295,7 +295,7 @@ impl NodeNetwork {
self.previous_outputs.as_ref().unwrap_or(&self.outputs)
}
pub fn input_types<'a>(&'a self) -> impl Iterator<Item = Type> + 'a {
pub fn input_types(&self) -> impl Iterator<Item = Type> + '_ {
self.inputs.iter().map(move |id| self.nodes[id].inputs.get(0).map(|i| i.ty()).unwrap_or(concrete!(())))
}

View file

@ -1,6 +1,7 @@
use super::DocumentNode;
use crate::executor::Any;
pub use crate::imaginate_input::{ImaginateMaskStartingFill, ImaginateSamplingMethod, ImaginateStatus};
use crate::proto::{Any as DAny, FutureAny};
use graphene_core::raster::{BlendMode, LuminanceCalculation};
use graphene_core::{Color, Node, Type};
@ -305,11 +306,11 @@ impl<'a> TaggedValue {
pub struct UpcastNode {
value: TaggedValue,
}
impl<'input> Node<'input, Box<dyn DynAny<'input> + 'input>> for UpcastNode {
type Output = Box<dyn DynAny<'input> + 'input>;
impl<'input> Node<'input, DAny<'input>> for UpcastNode {
type Output = FutureAny<'input>;
fn eval(&'input self, _: Box<dyn DynAny<'input> + 'input>) -> Self::Output {
self.value.clone().to_any()
fn eval(&'input self, _: DAny<'input>) -> Self::Output {
Box::pin(async move { self.value.clone().to_any() })
}
}
impl UpcastNode {

View file

@ -3,7 +3,7 @@ use std::error::Error;
use dyn_any::DynAny;
use crate::document::NodeNetwork;
use crate::proto::ProtoNetwork;
use crate::proto::{LocalFuture, ProtoNetwork};
pub struct Compiler {}
@ -37,5 +37,5 @@ impl Compiler {
pub type Any<'a> = Box<dyn DynAny<'a> + 'a>;
pub trait Executor {
fn execute<'a, 's: 'a>(&'s self, input: Any<'a>) -> Result<Any<'a>, Box<dyn Error>>;
fn execute<'a>(&'a self, input: Any<'a>) -> LocalFuture<Result<Any<'a>, Box<dyn Error>>>;
}

View file

@ -1,5 +1,6 @@
use std::borrow::Cow;
use std::collections::{HashMap, HashSet};
use std::hash::Hash;
use xxhash_rust::xxh3::Xxh3;
@ -11,12 +12,15 @@ use graphene_core::*;
use serde::{Deserialize, Serialize};
use std::pin::Pin;
pub type DynFuture<'n, T> = Pin<Box<dyn core::future::Future<Output = T> + 'n>>;
pub type LocalFuture<'n, T> = Pin<Box<dyn core::future::Future<Output = T> + 'n>>;
pub type Any<'n> = Box<dyn DynAny<'n> + 'n>;
pub type TypeErasedNode<'n> = dyn for<'i> NodeIO<'i, Any<'i>, Output = Any<'i>> + 'n + Send + Sync;
pub type TypeErasedPinnedRef<'n> = Pin<&'n (dyn for<'i> NodeIO<'i, Any<'i>, Output = Any<'i>> + 'n + Send + Sync)>;
pub type TypeErasedPinned<'n> = Pin<Box<dyn for<'i> NodeIO<'i, Any<'i>, Output = Any<'i>> + 'n + Send + Sync>>;
pub type FutureAny<'n> = DynFuture<'n, Any<'n>>;
pub type TypeErasedNode<'n> = dyn for<'i> NodeIO<'i, Any<'i>, Output = FutureAny<'i>> + 'n;
pub type TypeErasedPinnedRef<'n> = Pin<&'n (dyn for<'i> NodeIO<'i, Any<'i>, Output = FutureAny<'i>> + 'n)>;
pub type TypeErasedPinned<'n> = Pin<Box<dyn for<'i> NodeIO<'i, Any<'i>, Output = FutureAny<'i>> + 'n>>;
pub type NodeConstructor = for<'a> fn(Vec<TypeErasedPinnedRef<'static>>) -> TypeErasedPinned<'static>;
pub type NodeConstructor = for<'a> fn(Vec<TypeErasedPinnedRef<'static>>) -> DynFuture<'static, TypeErasedPinned<'static>>;
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Debug, Default, PartialEq, Clone)]

View file

@ -59,6 +59,8 @@ 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"] }
futures = "0.3.28"
[dependencies.serde]
version = "1.0"

View file

@ -1,9 +1,12 @@
use dyn_any::StaticType;
pub use graph_craft::proto::{Any, TypeErasedNode, TypeErasedPinned, TypeErasedPinnedRef};
use graph_craft::proto::{DynFuture, FutureAny};
use graphene_core::NodeIO;
pub use graphene_core::{generic, ops, Node};
use std::marker::PhantomData;
use crate::http::EvalSyncNode;
pub struct DynAnyNode<I, O, Node> {
node: Node,
_i: PhantomData<I>,
@ -12,18 +15,20 @@ pub struct DynAnyNode<I, O, Node> {
impl<'input, _I: 'input + StaticType, _O: 'input + StaticType, N: 'input, S0: 'input> Node<'input, Any<'input>> for DynAnyNode<_I, _O, S0>
where
N: for<'any_input> Node<'any_input, _I, Output = _O>,
N: for<'any_input> Node<'any_input, _I, Output = DynFuture<'any_input, _O>>,
S0: for<'any_input> Node<'any_input, (), Output = &'any_input N>,
{
type Output = Any<'input>;
type Output = FutureAny<'input>;
#[inline]
fn eval(&'input self, input: Any<'input>) -> Self::Output {
let node = self.node.eval(());
{
let node_name = core::any::type_name::<N>();
let input: Box<_I> = dyn_any::downcast(input).unwrap_or_else(|e| panic!("DynAnyNode Input, {0} in:\n{1}", e, node_name));
Box::new(node.eval(*input))
}
let node_name = core::any::type_name::<N>();
let input: Box<_I> = dyn_any::downcast(input).unwrap_or_else(|e| panic!("DynAnyNode Input, {0} in:\n{1}", e, node_name));
let output = async move {
let result = node.eval(*input).await;
Box::new(result) as Any<'input>
};
Box::pin(output)
}
fn reset(self: std::pin::Pin<&mut Self>) {
@ -56,11 +61,13 @@ impl<'input, _I: 'input + StaticType, _O: 'input + StaticType, N: 'input> Node<'
where
N: for<'any_input> Node<'any_input, _I, Output = &'any_input _O>,
{
type Output = Any<'input>;
type Output = FutureAny<'input>;
fn eval(&'input self, input: Any<'input>) -> Self::Output {
let node_name = core::any::type_name::<N>();
let input: Box<_I> = dyn_any::downcast(input).unwrap_or_else(|e| panic!("DynAnyRefNode Input, {e} in:\n{node_name}"));
Box::new(self.node.eval(*input))
let result = self.node.eval(*input);
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) };
@ -79,14 +86,15 @@ pub struct DynAnyInRefNode<I, O, Node> {
}
impl<'input, _I: 'input + StaticType, _O: 'input + StaticType, N: 'input> Node<'input, Any<'input>> for DynAnyInRefNode<_I, _O, N>
where
N: for<'any_input> Node<'any_input, &'any_input _I, Output = _O>,
N: for<'any_input> Node<'any_input, &'any_input _I, Output = DynFuture<'any_input, _O>>,
{
type Output = Any<'input>;
type Output = FutureAny<'input>;
fn eval(&'input self, input: Any<'input>) -> Self::Output {
{
let node_name = core::any::type_name::<N>();
let input: Box<&_I> = dyn_any::downcast(input).unwrap_or_else(|e| panic!("DynAnyInRefNode Input, {e} in:\n{node_name}"));
Box::new(self.node.eval(*input))
let result = self.node.eval(*input);
Box::pin(async move { Box::new(result.await) as Any<'_> })
}
}
}
@ -96,13 +104,37 @@ impl<_I, _O, S0> DynAnyInRefNode<_I, _O, S0> {
}
}
pub struct FutureWrapperNode<Node> {
node: Node,
}
impl<'i, T: 'i, N: Node<'i, T>> Node<'i, T> for FutureWrapperNode<N>
where
N: Node<'i, T>,
{
type Output = DynFuture<'i, N::Output>;
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);
}
}
impl<'i, N> FutureWrapperNode<N> {
pub const fn new(node: N) -> Self {
Self { node }
}
}
pub trait IntoTypeErasedNode<'n> {
fn into_type_erased(self) -> TypeErasedPinned<'n>;
}
impl<'n, N: 'n> IntoTypeErasedNode<'n> for N
where
N: for<'i> NodeIO<'i, Any<'i>, Output = Any<'i>> + Send + Sync + 'n,
N: for<'i> NodeIO<'i, Any<'i>, Output = FutureAny<'i>> + 'n,
{
fn into_type_erased(self) -> TypeErasedPinned<'n> {
Box::pin(self)
@ -139,17 +171,51 @@ pub struct DowncastBothNode<'a, I, O> {
_o: PhantomData<O>,
}
impl<'n: 'input, 'input, O: 'input + StaticType, I: 'input + StaticType> Node<'input, I> for DowncastBothNode<'n, I, O> {
type Output = DynFuture<'input, O>;
#[inline]
fn eval(&'input self, input: I) -> Self::Output {
{
let input = Box::new(input);
let future = self.node.eval(input);
Box::pin(async move {
let out = dyn_any::downcast(future.await).unwrap_or_else(|e| panic!("DowncastBothNode Input {e}"));
*out
})
}
}
}
impl<'n, I, O> DowncastBothNode<'n, I, O> {
pub const fn new(node: TypeErasedPinnedRef<'n>) -> Self {
Self {
node,
_i: core::marker::PhantomData,
_o: core::marker::PhantomData,
}
}
}
/// Boxes the input and downcasts the output.
/// Wraps around a node taking Box<dyn DynAny> and returning Box<dyn DynAny>
#[derive(Clone, Copy)]
pub struct DowncastBothSyncNode<'a, I, O> {
node: TypeErasedPinnedRef<'a>,
_i: PhantomData<I>,
_o: PhantomData<O>,
}
impl<'n: 'input, 'input, O: 'input + StaticType, I: 'input + StaticType> Node<'input, I> for DowncastBothSyncNode<'n, I, O> {
type Output = O;
#[inline]
fn eval(&'input self, input: I) -> Self::Output {
{
let input = Box::new(input);
let out = dyn_any::downcast(self.node.eval(input)).unwrap_or_else(|e| panic!("DowncastBothNode Input {e}"));
let future = self.node.eval(input);
let value = EvalSyncNode::new().eval(future);
let out = dyn_any::downcast(value).unwrap_or_else(|e| panic!("DowncastBothNode Input {e}"));
*out
}
}
}
impl<'n, I, O> DowncastBothNode<'n, I, O> {
impl<'n, I, O> DowncastBothSyncNode<'n, I, O> {
pub const fn new(node: TypeErasedPinnedRef<'n>) -> Self {
Self {
node,
@ -166,13 +232,15 @@ pub struct DowncastBothRefNode<'a, I, O> {
_i: PhantomData<(I, O)>,
}
impl<'n: 'input, 'input, O: 'input + StaticType, I: 'input + StaticType> Node<'input, I> for DowncastBothRefNode<'n, I, O> {
type Output = &'input O;
type Output = DynFuture<'input, &'input O>;
#[inline]
fn eval(&'input self, input: I) -> Self::Output {
{
let input = Box::new(input);
let out: Box<&_> = dyn_any::downcast::<&O>(self.node.eval(input)).unwrap_or_else(|e| panic!("DowncastBothRefNode Input {e}"));
*out
Box::pin(async move {
let out: Box<&_> = dyn_any::downcast::<&O>(self.node.eval(input).await).unwrap_or_else(|e| panic!("DowncastBothRefNode Input {e}"));
*out
})
}
}
}
@ -188,10 +256,12 @@ pub struct ComposeTypeErased<'a> {
}
impl<'i, 'a: 'i> Node<'i, Any<'i>> for ComposeTypeErased<'a> {
type Output = Any<'i>;
type Output = DynFuture<'i, Any<'i>>;
fn eval(&'i self, input: Any<'i>) -> Self::Output {
let arg = self.first.eval(input);
self.second.eval(arg)
Box::pin(async move {
let arg = self.first.eval(input).await;
self.second.eval(arg).await
})
}
}
@ -205,6 +275,21 @@ pub fn input_node<O: StaticType>(n: TypeErasedPinnedRef) -> DowncastBothNode<(),
DowncastBothNode::new(n)
}
pub struct PanicNode<I, O>(PhantomData<I>, PhantomData<O>);
impl<'i, I: 'i, O: 'i> Node<'i, I> for PanicNode<I, O> {
type Output = O;
fn eval(&'i self, _: I) -> Self::Output {
unimplemented!("This node should never be evaluated")
}
}
impl<I, O> PanicNode<I, O> {
pub const fn new() -> Self {
Self(PhantomData, PhantomData)
}
}
#[cfg(test)]
mod test {
use super::*;
@ -215,8 +300,8 @@ mod test {
pub fn dyn_input_invalid_eval_panic() {
//let add = DynAnyNode::new(AddNode::new()).into_type_erased();
//add.eval(Box::new(&("32", 32u32)));
let dyn_any = DynAnyNode::<(u32, u32), u32, _>::new(ValueNode::new(AddNode::new()));
let type_erased = dyn_any.into_type_erased();
let dyn_any = DynAnyNode::<(u32, u32), u32, _>::new(ValueNode::new(FutureWrapperNode { node: AddNode::new() }));
let type_erased = Box::pin(dyn_any) as TypeErasedPinned;
let _ref_type_erased = type_erased.as_ref();
//let type_erased = Box::pin(dyn_any) as TypeErasedPinned<'_>;
type_erased.eval(Box::new(&("32", 32u32)));
@ -226,10 +311,10 @@ mod test {
pub fn dyn_input_invalid_eval_panic_() {
//let add = DynAnyNode::new(AddNode::new()).into_type_erased();
//add.eval(Box::new(&("32", 32u32)));
let dyn_any = DynAnyNode::<(u32, u32), u32, _>::new(ValueNode::new(AddNode::new()));
let dyn_any = DynAnyNode::<(u32, u32), u32, _>::new(ValueNode::new(FutureWrapperNode { node: AddNode::new() }));
let type_erased = Box::pin(dyn_any) as TypeErasedPinned<'_>;
type_erased.eval(Box::new((4u32, 2u32)));
let id_node = IdNode::new();
let id_node = FutureWrapperNode::new(IdNode::new());
let type_erased_id = Box::pin(id_node) as TypeErasedPinned;
let type_erased = ComposeTypeErased::new(type_erased.as_ref(), type_erased_id.as_ref());
type_erased.eval(Box::new((4u32, 2u32)));

View file

@ -17,7 +17,7 @@ pub struct GpuCompiler<TypingContext, ShaderIO> {
// TODO: Move to graph-craft
#[node_macro::node_fn(GpuCompiler)]
fn compile_gpu(node: &'input DocumentNode, mut typing_context: TypingContext, io: ShaderIO) -> compilation_client::Shader {
async fn compile_gpu(node: &'input DocumentNode, mut typing_context: TypingContext, io: ShaderIO) -> compilation_client::Shader {
let compiler = graph_craft::executor::Compiler {};
let DocumentNodeImplementation::Network(network) = node.implementation;
let proto_network = compiler.compile_single(network, true).unwrap();
@ -25,7 +25,7 @@ fn compile_gpu(node: &'input DocumentNode, mut typing_context: TypingContext, io
let input_types = proto_network.inputs.iter().map(|id| typing_context.get_type(*id).unwrap()).map(|node_io| node_io.output).collect();
let output_type = typing_context.get_type(proto_network.output).unwrap().output;
let bytes = compilation_client::compile_sync(proto_network, input_types, output_type, io).unwrap();
let bytes = compilation_client::compile(proto_network, input_types, output_type, io).await.unwrap();
bytes
}
@ -34,7 +34,7 @@ pub struct MapGpuNode<Shader> {
}
#[node_macro::node_fn(MapGpuNode)]
fn map_gpu(inputs: Vec<ShaderInput<<NewExecutor as GpuExecutor>::BufferHandle>>, shader: &'any_input compilation_client::Shader) {
async fn map_gpu(inputs: Vec<ShaderInput<<NewExecutor as GpuExecutor>::BufferHandle>>, shader: &'any_input compilation_client::Shader) {
use graph_craft::executor::Executor;
let executor = NewExecutor::new().unwrap();
for input in shader.inputs.iter() {
@ -42,11 +42,13 @@ fn map_gpu(inputs: Vec<ShaderInput<<NewExecutor as GpuExecutor>::BufferHandle>>,
executor.write_buffer(buffer, input.data).unwrap();
}
todo!();
let executor: GpuExecutor = GpuExecutor::new(Context::new_sync().unwrap(), shader.into(), "gpu::eval".into()).unwrap();
/*
let executor: GpuExecutor = GpuExecutor::new(Context::new().await.unwrap(), shader.into(), "gpu::eval".into()).unwrap();
let data: Vec<_> = input.into_iter().collect();
let result = executor.execute(Box::new(data)).unwrap();
let result = dyn_any::downcast::<Vec<_O>>(result).unwrap();
*result
*/
}
pub struct MapGpuSingleImageNode<N> {

View file

@ -0,0 +1,32 @@
use std::future::Future;
use crate::Node;
pub struct GetNode;
#[node_macro::node_fn(GetNode)]
async fn get_node(url: String) -> reqwest::Response {
reqwest::get(url).await.unwrap()
}
pub struct PostNode<Body> {
body: Body,
}
#[node_macro::node_fn(PostNode)]
async fn post_node(url: String, body: String) -> reqwest::Response {
reqwest::Client::new().post(url).body(body).send().await.unwrap()
}
#[derive(Clone, Copy, Debug)]
pub struct EvalSyncNode {}
#[node_macro::node_fn(EvalSyncNode)]
fn eval_sync<F: Future + 'input>(future: F) -> F::Output {
let future = futures::future::maybe_done(future);
futures::pin_mut!(future);
match future.as_mut().take_output() {
Some(value) => value,
_ => panic!("Node construction future returned pending"),
}
}

View file

@ -10,6 +10,8 @@ pub mod memo;
pub mod raster;
pub mod http;
pub mod any;
#[cfg(feature = "gpu")]

View file

@ -12,7 +12,7 @@ pub struct GenerateQuantizationNode<N, M> {
}
#[node_macro::node_fn(GenerateQuantizationNode)]
fn generate_quantization_fn(image_frame: ImageFrame, samples: u32, function: u32) -> [Quantization; 4] {
fn generate_quantization_fn(image_frame: ImageFrame<Color>, samples: u32, function: u32) -> [Quantization; 4] {
let image = image_frame.image;
let len = image.data.len().min(10000);

View file

@ -24,3 +24,7 @@ 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"] }

View file

@ -6,7 +6,7 @@ use dyn_any::StaticType;
use graph_craft::document::value::UpcastNode;
use graph_craft::document::NodeId;
use graph_craft::executor::Executor;
use graph_craft::proto::{ConstructionArgs, ProtoNetwork, ProtoNode, TypingContext};
use graph_craft::proto::{ConstructionArgs, LocalFuture, ProtoNetwork, ProtoNode, TypingContext};
use graph_craft::Type;
use graphene_std::any::{Any, TypeErasedPinned, TypeErasedPinnedRef};
@ -33,11 +33,11 @@ impl Default for DynamicExecutor {
}
impl DynamicExecutor {
pub fn new(proto_network: ProtoNetwork) -> Result<Self, String> {
pub async fn new(proto_network: ProtoNetwork) -> Result<Self, String> {
let mut typing_context = TypingContext::new(&node_registry::NODE_REGISTRY);
typing_context.update(&proto_network)?;
let output = proto_network.output;
let tree = BorrowTree::new(proto_network, &typing_context)?;
let tree = BorrowTree::new(proto_network, &typing_context).await?;
Ok(Self {
tree,
@ -47,11 +47,11 @@ impl DynamicExecutor {
})
}
pub fn update(&mut self, proto_network: ProtoNetwork) -> Result<(), String> {
pub async fn update(&mut self, proto_network: ProtoNetwork) -> Result<(), String> {
self.output = proto_network.output;
self.typing_context.update(&proto_network)?;
trace!("setting output to {}", self.output);
let mut orphans = self.tree.update(proto_network, &self.typing_context)?;
let mut orphans = self.tree.update(proto_network, &self.typing_context).await?;
core::mem::swap(&mut self.orphaned_nodes, &mut orphans);
for node_id in orphans {
if self.orphaned_nodes.contains(&node_id) {
@ -75,8 +75,8 @@ impl DynamicExecutor {
}
impl Executor for DynamicExecutor {
fn execute<'a, 's: 'a>(&'s self, input: Any<'a>) -> Result<Any<'a>, Box<dyn Error>> {
self.tree.eval_any(self.output, input).ok_or_else(|| "Failed to execute".into())
fn execute<'a>(&'a self, input: Any<'a>) -> LocalFuture<Result<Any<'a>, Box<dyn Error>>> {
Box::pin(async move { self.tree.eval_any(self.output, input).await.ok_or_else(|| "Failed to execute".into()) })
}
}
@ -118,20 +118,20 @@ pub struct BorrowTree {
}
impl BorrowTree {
pub fn new(proto_network: ProtoNetwork, typing_context: &TypingContext) -> Result<Self, String> {
pub async fn new(proto_network: ProtoNetwork, typing_context: &TypingContext) -> Result<Self, String> {
let mut nodes = BorrowTree::default();
for (id, node) in proto_network.nodes {
nodes.push_node(id, node, typing_context)?
nodes.push_node(id, node, typing_context).await?
}
Ok(nodes)
}
/// Pushes new nodes into the tree and return orphaned nodes
pub fn update(&mut self, proto_network: ProtoNetwork, typing_context: &TypingContext) -> Result<Vec<NodeId>, String> {
pub async fn update(&mut self, proto_network: ProtoNetwork, typing_context: &TypingContext) -> Result<Vec<NodeId>, String> {
let mut old_nodes: HashSet<_> = self.nodes.keys().copied().collect();
for (id, node) in proto_network.nodes {
if !self.nodes.contains_key(&id) {
self.push_node(id, node, typing_context)?;
self.push_node(id, node, typing_context).await?;
} else {
let Some(node_container) = self.nodes.get_mut(&id) else { continue };
let mut node_container_writer = node_container.write().unwrap();
@ -168,25 +168,25 @@ impl BorrowTree {
self.nodes.get(&id).cloned()
}
pub fn eval<'i, I: StaticType + 'i, O: StaticType + 'i>(&'i self, id: NodeId, input: I) -> Option<O> {
pub async fn eval<'i, I: StaticType + 'i + Send + Sync, O: StaticType + Send + Sync + 'i>(&'i self, id: NodeId, input: I) -> Option<O> {
let node = self.nodes.get(&id).cloned()?;
let reader = node.read().unwrap();
let output = reader.node.eval(Box::new(input));
dyn_any::downcast::<O>(output).ok().map(|o| *o)
dyn_any::downcast::<O>(output.await).ok().map(|o| *o)
}
pub fn eval_any<'i>(&'i self, id: NodeId, input: Any<'i>) -> Option<Any<'i>> {
pub async fn eval_any<'i>(&'i self, id: NodeId, input: Any<'i>) -> Option<Any<'i>> {
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) })
Some(unsafe { (*((&*node.read().unwrap()) as *const NodeContainer)).node.eval(input).await })
}
pub fn free_node(&mut self, id: NodeId) {
self.nodes.remove(&id);
}
pub fn push_node(&mut self, id: NodeId, proto_node: ProtoNode, typing_context: &TypingContext) -> Result<(), String> {
pub async fn push_node(&mut self, id: NodeId, proto_node: ProtoNode, typing_context: &TypingContext) -> Result<(), String> {
let ProtoNode {
construction_args,
identifier,
@ -207,7 +207,7 @@ impl BorrowTree {
let ids: Vec<_> = ids.iter().map(|(id, _)| *id).collect();
let construction_nodes = self.node_refs(&ids);
let constructor = typing_context.constructor(id).ok_or(format!("No constructor found for node {:?}", identifier))?;
let node = constructor(construction_nodes);
let node = constructor(construction_nodes).await;
let node = NodeContainer {
node,
_dependencies: self.node_deps(&ids),
@ -226,12 +226,12 @@ mod test {
use super::*;
#[test]
fn push_node() {
#[tokio::test]
async fn push_node() {
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()).unwrap();
tree.push_node(0, val_1_protonode, &TypingContext::default()).await.unwrap();
let _node = tree.get(0).unwrap();
assert_eq!(tree.eval(0, ()), Some(2u32));
assert_eq!(tree.eval(0, ()).await, Some(2u32));
}
}

View file

@ -44,8 +44,8 @@ mod tests {
assert_eq!(*downcast::<u32>(add).unwrap(), 6_u32);
}*/
#[test]
fn execute_add() {
#[tokio::test]
async fn execute_add() {
use graph_craft::document::*;
use graph_craft::*;
@ -109,15 +109,15 @@ mod tests {
let compiler = Compiler {};
let protograph = compiler.compile_single(network, true).expect("Graph should be generated");
let exec = DynamicExecutor::new(protograph).unwrap_or_else(|e| panic!("Failed to create executor: {}", e));
let exec = DynamicExecutor::new(protograph).await.unwrap_or_else(|e| panic!("Failed to create executor: {}", e));
let result = exec.execute(32_u32.into_dyn()).unwrap();
let result = exec.execute(32_u32.into_dyn()).await.unwrap();
let val = *dyn_any::downcast::<u32>(result).unwrap();
assert_eq!(val, 33_u32);
}
#[test]
fn double_number() {
#[tokio::test]
async fn double_number() {
use graph_craft::document::*;
use graph_craft::*;
@ -158,6 +158,6 @@ mod tests {
let compiler = Compiler {};
let protograph = compiler.compile_single(network, true).expect("Graph should be generated");
let _exec = DynamicExecutor::new(protograph).map(|e| panic!("The network should not type check: {:#?}", e)).unwrap_err();
let _exec = DynamicExecutor::new(protograph).await.map(|e| panic!("The network should not type check: {:#?}", e)).unwrap_err();
}
}

View file

@ -1,6 +1,6 @@
use glam::{DAffine2, DVec2};
use graph_craft::imaginate_input::{ImaginateMaskStartingFill, ImaginateSamplingMethod, ImaginateStatus};
use graphene_core::ops::{CloneNode, IdNode, TypeNode};
use graphene_core::ops::IdNode;
use graphene_core::vector::VectorData;
use once_cell::sync::Lazy;
use std::collections::HashMap;
@ -8,36 +8,40 @@ use std::collections::HashMap;
use graphene_core::raster::color::Color;
use graphene_core::raster::*;
use graphene_core::structural::Then;
use graphene_core::value::{ClonedNode, CopiedNode, ForgetNode, ValueNode};
use graphene_core::value::{ClonedNode, ValueNode};
use graphene_core::{Node, NodeIO, NodeIOTypes};
use graphene_std::brush::*;
use graphene_std::raster::*;
use graphene_std::any::{ComposeTypeErased, DowncastBothNode, DowncastBothRefNode, DynAnyInRefNode, DynAnyNode, DynAnyRefNode, IntoTypeErasedNode, TypeErasedPinnedRef};
use graphene_std::any::{ComposeTypeErased, DowncastBothNode, DowncastBothRefNode, DynAnyInRefNode, DynAnyNode, FutureWrapperNode, IntoTypeErasedNode, TypeErasedPinnedRef};
use graphene_core::{Cow, NodeIdentifier, Type, TypeDescriptor};
use graph_craft::proto::NodeConstructor;
use graph_craft::proto::{NodeConstructor, TypeErasedPinned};
use graphene_core::{concrete, fn_type, generic, value_fn};
use graphene_std::memo::{CacheNode, LetNode};
use graphene_core::{concrete, generic, value_fn};
use graphene_std::http::EvalSyncNode;
use graphene_std::memo::LetNode;
use graphene_std::raster::BlendImageTupleNode;
use crate::executor::NodeContainer;
use dyn_any::StaticType;
use graphene_core::quantization::QuantizationChannels;
macro_rules! construct_node {
($args: ident, $path:ty, [$($type:tt),*]) => {{
($args: ident, $path:ty, [$($type:tt),*]) => { async move {
let mut args: Vec<TypeErasedPinnedRef<'static>> = $args.clone();
args.reverse();
<$path>::new($(graphene_core::value::ClonedNode::new(
graphene_std::any::input_node::<$type>(args.pop()
.expect("Not enough arguments provided to construct node")).eval(()))
let node = <$path>::new($(
{
let node = graphene_std::any::input_node::<$type>(args.pop().expect("Not enough arguments provided to construct node"));
let value = node.eval(()).await;
graphene_core::value::ClonedNode::new(value)
}
),*
)
);
node
}}
}
@ -47,16 +51,16 @@ macro_rules! register_node {
(
NodeIdentifier::new(stringify!($path)),
|args| {
let node = construct_node!(args, $path, [$($type),*]);
Box::pin(async move {
let node = construct_node!(args, $path, [$($type),*]).await;
let node = graphene_std::any::FutureWrapperNode::new(node);
let any: DynAnyNode<$input, _, _> = graphene_std::any::DynAnyNode::new(graphene_core::value::ValueNode::new(node));
Box::pin(any)
Box::pin(any) as TypeErasedPinned
})
},
{
let node = IdNode::new().into_type_erased();
let node = NodeContainer::new(node, vec![]);
let _node = unsafe { node.erase_lifetime().static_ref() };
let node = <$path>::new($(
graphene_std::any::input_node::<$type>(_node)
graphene_std::any::PanicNode::<(), $type>::new()
),*);
let params = vec![$(value_fn!($type)),*];
let mut node_io = <$path as NodeIO<'_, $input>>::to_node_io(&node, params);
@ -82,9 +86,12 @@ macro_rules! raster_node {
(
NodeIdentifier::new(stringify!($path)),
|args| {
let node = construct_node!(args, $path, [$($type),*]);
let any: DynAnyNode<Color, _, _> = graphene_std::any::DynAnyNode::new(graphene_core::value::ValueNode::new(node));
Box::pin(any)
Box::pin(async move {
let node = construct_node!(args, $path, [$($type),*]).await;
let node = graphene_std::any::FutureWrapperNode::new(node);
let any: DynAnyNode<Color, _, _> = graphene_std::any::DynAnyNode::new(graphene_core::value::ValueNode::new(node));
Box::pin(any) as TypeErasedPinned
})
},
{
let params = vec![$(value_fn!($type)),*];
@ -94,10 +101,13 @@ macro_rules! raster_node {
(
NodeIdentifier::new(stringify!($path)),
|args| {
let node = construct_node!(args, $path, [$($type),*]);
let map_node = graphene_std::raster::MapImageNode::new(graphene_core::value::ValueNode::new(node));
let any: DynAnyNode<Image<Color>, _, _> = graphene_std::any::DynAnyNode::new(graphene_core::value::ValueNode::new(map_node));
Box::pin(any)
Box::pin(async move {
let node = construct_node!(args, $path, [$($type),*]).await;
let map_node = graphene_std::raster::MapImageNode::new(graphene_core::value::ValueNode::new(node));
let map_node = graphene_std::any::FutureWrapperNode::new(map_node);
let any: DynAnyNode<Image<Color>, _, _> = graphene_std::any::DynAnyNode::new(graphene_core::value::ValueNode::new(map_node));
Box::pin(any) as TypeErasedPinned
})
},
{
let params = vec![$(value_fn!($type)),*];
@ -107,10 +117,13 @@ macro_rules! raster_node {
(
NodeIdentifier::new(stringify!($path)),
|args| {
let node = construct_node!(args, $path, [$($type),*]);
let map_node = graphene_std::raster::MapImageNode::new(graphene_core::value::ValueNode::new(node));
let any: DynAnyNode<ImageFrame<Color>, _, _> = graphene_std::any::DynAnyNode::new(graphene_core::value::ValueNode::new(map_node));
Box::pin(any)
Box::pin(async move {
let node = construct_node!(args, $path, [$($type),*]).await;
let map_node = graphene_std::raster::MapImageNode::new(graphene_core::value::ValueNode::new(node));
let map_node = graphene_std::any::FutureWrapperNode::new(map_node);
let any: DynAnyNode<ImageFrame<Color>, _, _> = graphene_std::any::DynAnyNode::new(graphene_core::value::ValueNode::new(map_node));
Box::pin(any) as TypeErasedPinned
})
},
{
let params = vec![$(value_fn!($type)),*];
@ -129,7 +142,7 @@ fn node_registry() -> HashMap<NodeIdentifier, HashMap<NodeIOTypes, NodeConstruct
//register_node!(graphene_core::ops::IdNode, input: Any<'_>, params: []),
vec![(
NodeIdentifier::new("graphene_core::ops::IdNode"),
|_| IdNode::new().into_type_erased(),
|_| Box::pin(async move { Box::pin(FutureWrapperNode::new(IdNode::new())) as TypeErasedPinned }),
NodeIOTypes::new(generic!(I), generic!(I), vec![]),
)],
// TODO: create macro to impl for all types
@ -204,8 +217,10 @@ fn node_registry() -> HashMap<NodeIdentifier, HashMap<NodeIOTypes, NodeConstruct
vec![(
NodeIdentifier::new("graphene_core::structural::ComposeNode<_, _, _>"),
|args| {
let node = ComposeTypeErased::new(args[0], args[1]);
node.into_type_erased()
Box::pin(async move {
let node = ComposeTypeErased::new(args[0], args[1]);
node.into_type_erased()
})
},
NodeIOTypes::new(
generic!(T),
@ -223,42 +238,47 @@ fn node_registry() -> HashMap<NodeIdentifier, HashMap<NodeIOTypes, NodeConstruct
use graphene_core::value::*;
use graphene_std::brush::*;
let image: DowncastBothNode<(), ImageFrame<Color>> = DowncastBothNode::new(args[0]);
let bounds: DowncastBothNode<(), ImageFrame<Color>> = DowncastBothNode::new(args[1]);
let trace: DowncastBothNode<(), Vec<DVec2>> = DowncastBothNode::new(args[2]);
let diameter: DowncastBothNode<(), f64> = DowncastBothNode::new(args[3]);
let hardness: DowncastBothNode<(), f64> = DowncastBothNode::new(args[4]);
let flow: DowncastBothNode<(), f64> = DowncastBothNode::new(args[5]);
let color: DowncastBothNode<(), Color> = DowncastBothNode::new(args[6]);
Box::pin(async move {
let image: DowncastBothNode<(), ImageFrame<Color>> = DowncastBothNode::new(args[0]);
let bounds: DowncastBothNode<(), ImageFrame<Color>> = DowncastBothNode::new(args[1]);
let trace: DowncastBothNode<(), Vec<DVec2>> = DowncastBothNode::new(args[2]);
let diameter: DowncastBothNode<(), f64> = DowncastBothNode::new(args[3]);
let hardness: DowncastBothNode<(), f64> = DowncastBothNode::new(args[4]);
let flow: DowncastBothNode<(), f64> = DowncastBothNode::new(args[5]);
let color: DowncastBothNode<(), Color> = DowncastBothNode::new(args[6]);
let stamp = BrushStampGeneratorNode::new(color, CopiedNode::new(hardness.eval(())), CopiedNode::new(flow.eval(())));
let stamp = stamp.eval(diameter.eval(()));
let stamp = BrushStampGeneratorNode::new(CopiedNode::new(color.eval(()).await), CopiedNode::new(hardness.eval(()).await), CopiedNode::new(flow.eval(()).await));
let stamp = stamp.eval(diameter.eval(()).await);
let frames = TranslateNode::new(CopiedNode::new(stamp));
let frames = MapNode::new(ValueNode::new(frames));
let frames = frames.eval(trace.eval(()).into_iter()).collect::<Vec<_>>();
let frames = TranslateNode::new(CopiedNode::new(stamp));
let frames = MapNode::new(ValueNode::new(frames));
let frames = frames.eval(trace.eval(()).await.into_iter()).collect::<Vec<_>>();
let background_bounds = ReduceNode::new(ClonedNode::new(None), ValueNode::new(MergeBoundingBoxNode::new()));
let background_bounds = background_bounds.eval(frames.clone().into_iter());
let background_bounds = MergeBoundingBoxNode::new().eval((background_bounds, image.eval(())));
let mut background_bounds = CopiedNode::new(background_bounds.unwrap().to_transform());
let background_bounds = ReduceNode::new(ClonedNode::new(None), ValueNode::new(MergeBoundingBoxNode::new()));
let background_bounds = background_bounds.eval(frames.clone().into_iter());
let background_bounds = MergeBoundingBoxNode::new().eval((background_bounds, image.eval(()).await));
let mut background_bounds = CopiedNode::new(background_bounds.unwrap().to_transform());
let http_node = graphene_std::http::GetNode::new();
log::info!("http node: {:?}", http_node.eval("http://localhost:8080".to_string()).await);
let bounds_transform = bounds.eval(()).transform;
if bounds_transform != DAffine2::ZERO {
background_bounds = CopiedNode::new(bounds_transform);
}
let bounds_transform = bounds.eval(()).await.transform;
if bounds_transform != DAffine2::ZERO {
background_bounds = CopiedNode::new(bounds_transform);
}
let background_image = background_bounds.then(EmptyImageNode::new(CopiedNode::new(Color::TRANSPARENT)));
let blend_node = graphene_core::raster::BlendNode::new(CopiedNode::new(BlendMode::Normal), CopiedNode::new(100.));
let background_image = background_bounds.then(EmptyImageNode::new(CopiedNode::new(Color::TRANSPARENT)));
let blend_node = graphene_core::raster::BlendNode::new(CopiedNode::new(BlendMode::Normal), CopiedNode::new(100.));
let background = ExtendImageNode::new(background_image);
let background_image = image.then(background);
let background = ExtendImageNode::new(background_image);
let background_image = image.and_then(background);
let final_image = ReduceNode::new(background_image, ValueNode::new(BlendImageTupleNode::new(ValueNode::new(blend_node))));
let final_image = ClonedNode::new(frames.into_iter()).then(final_image);
let final_image = ReduceNode::new(ClonedNode::new(background_image.eval(()).await), ValueNode::new(BlendImageTupleNode::new(ValueNode::new(blend_node))));
let final_image = ClonedNode::new(frames.into_iter()).then(final_image);
let any: DynAnyNode<(), _, _> = graphene_std::any::DynAnyNode::new(ValueNode::new(final_image));
Box::pin(any)
let final_image = FutureWrapperNode::new(final_image);
let any: DynAnyNode<(), _, _> = graphene_std::any::DynAnyNode::new(ValueNode::new(final_image));
any.into_type_erased()
})
},
NodeIOTypes::new(
concrete!(()),
@ -277,14 +297,17 @@ fn node_registry() -> HashMap<NodeIdentifier, HashMap<NodeIOTypes, NodeConstruct
vec![(
NodeIdentifier::new("graphene_std::brush::ReduceNode<_, _>"),
|args| {
let acc: DowncastBothNode<(), ImageFrame<Color>> = DowncastBothNode::new(args[0]);
let image = acc.eval(());
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<ImageFrame<Color>>, Output = ImageFrame<Color>>;
let any: DynAnyNode<Box<dyn Iterator<Item = ImageFrame<Color>> + Sync + Send>, _, _> = graphene_std::any::DynAnyNode::new(ValueNode::new(node));
Box::pin(any)
Box::pin(async move {
let acc: DowncastBothNode<(), ImageFrame<Color>> = 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<ImageFrame<Color>>, Output = ImageFrame<Color>>;
let node = FutureWrapperNode::new(node);
let any: DynAnyNode<Box<dyn Iterator<Item = ImageFrame<Color>> + Sync + Send>, _, _> = graphene_std::any::DynAnyNode::new(ValueNode::new(node));
any.into_type_erased()
})
},
NodeIOTypes::new(
concrete!(Box<dyn Iterator<Item = &ImageFrame<Color>> + Sync + Send>),
@ -299,18 +322,22 @@ fn node_registry() -> HashMap<NodeIdentifier, HashMap<NodeIOTypes, NodeConstruct
raster_node!(graphene_core::raster::LevelsNode<_, _, _, _, _>, params: [f64, f64, f64, f64, f64]),
register_node!(graphene_std::image_segmentation::ImageSegmentationNode<_>, input: ImageFrame<Color>, params: [ImageFrame<Color>]),
register_node!(graphene_core::raster::IndexNode<_>, input: Vec<ImageFrame<Color>>, params: [u32]),
/*
vec![
(
NodeIdentifier::new("graphene_core::raster::BlendNode<_, _, _, _>"),
|args| {
let image: DowncastBothNode<(), ImageFrame<Color>> = DowncastBothNode::new(args[0]);
let blend_mode: DowncastBothNode<(), BlendMode> = DowncastBothNode::new(args[1]);
let opacity: DowncastBothNode<(), f64> = DowncastBothNode::new(args[2]);
let blend_node = graphene_core::raster::BlendNode::new(CopiedNode::new(blend_mode.eval(())), CopiedNode::new(opacity.eval(())));
let node = graphene_std::raster::BlendImageNode::new(image, ValueNode::new(blend_node));
let _ = &node as &dyn for<'i> Node<'i, ImageFrame<Color>, Output = ImageFrame<Color>>;
let any: DynAnyNode<ImageFrame<Color>, _, _> = graphene_std::any::DynAnyNode::new(graphene_core::value::ValueNode::new(node));
any.into_type_erased()
Box::pin(async move {
let image: DowncastBothNode<(), ImageFrame<Color>> = DowncastBothNode::new(args[0]);
let blend_mode: DowncastBothNode<(), BlendMode> = DowncastBothNode::new(args[1]);
let opacity: DowncastBothNode<(), f64> = DowncastBothNode::new(args[2]);
let blend_node = graphene_core::raster::BlendNode::new(CopiedNode::new(blend_mode.eval(()).await), CopiedNode::new(opacity.eval(()).await));
let node = graphene_std::raster::BlendImageNode::new(image, ValueNode::new(blend_node));
let _ = &node as &dyn for<'i> Node<'i, ImageFrame<Color>, Output = ImageFrame<Color>>;
let node = FutureWrapperNode::new(node);
let any: DynAnyNode<ImageFrame<Color>, _, _> = graphene_std::any::DynAnyNode::new(graphene_core::value::ValueNode::new(node));
any.into_type_erased()
})
},
NodeIOTypes::new(
concrete!(ImageFrame<Color>),
@ -321,17 +348,21 @@ fn node_registry() -> HashMap<NodeIdentifier, HashMap<NodeIOTypes, NodeConstruct
(
NodeIdentifier::new("graphene_core::raster::EraseNode<_, _>"),
|args| {
let image: DowncastBothNode<(), ImageFrame<Color>> = DowncastBothNode::new(args[0]);
let opacity: DowncastBothNode<(), f64> = DowncastBothNode::new(args[1]);
let blend_node = graphene_std::brush::EraseNode::new(ClonedNode::new(opacity.eval(())));
let node = graphene_std::raster::BlendImageNode::new(image, ValueNode::new(blend_node));
let _ = &node as &dyn for<'i> Node<'i, ImageFrame<Color>, Output = ImageFrame<Color>>;
let any: DynAnyNode<ImageFrame<Color>, _, _> = graphene_std::any::DynAnyNode::new(graphene_core::value::ValueNode::new(node));
any.into_type_erased()
Box::pin(async move {
let image: DowncastBothNode<(), ImageFrame<Color>> = DowncastBothNode::new(args[0]);
let opacity: DowncastBothNode<(), f64> = DowncastBothNode::new(args[1]);
let blend_node = graphene_std::brush::EraseNode::new(ClonedNode::new(opacity.eval(()).await));
let node = graphene_std::raster::BlendImageNode::new(image, ValueNode::new(blend_node));
let _ = &node as &dyn for<'i> Node<'i, ImageFrame<Color>, Output = ImageFrame<Color>>;
let node = FutureWrapperNode::new(node);
let any: DynAnyNode<ImageFrame<Color>, _, _> = graphene_std::any::DynAnyNode::new(graphene_core::value::ValueNode::new(node));
any.into_type_erased()
})
},
NodeIOTypes::new(concrete!(ImageFrame<Color>), concrete!(ImageFrame<Color>), vec![value_fn!(ImageFrame<Color>), value_fn!(f64)]),
),
],
*/
raster_node!(graphene_core::raster::GrayscaleNode<_, _, _, _, _, _, _>, params: [Color, f64, f64, f64, f64, f64, f64]),
raster_node!(graphene_core::raster::HueSaturationNode<_, _, _>, params: [f64, f64, f64]),
raster_node!(graphene_core::raster::InvertRGBNode, params: []),
@ -345,6 +376,7 @@ fn node_registry() -> HashMap<NodeIdentifier, HashMap<NodeIOTypes, NodeConstruct
graphene_core::raster::SelectiveColorNode<_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _>,
params: [RelativeAbsolute, f64, f64, f64, f64, f64, f64, f64, f64, f64, f64, f64, f64, f64, f64, f64, f64, f64, f64, f64, f64, f64, f64, f64, f64, f64, f64, f64, f64, f64, f64, f64, f64, f64, f64, f64, f64]
),
/*
vec![(
NodeIdentifier::new("graphene_core::raster::BrightnessContrastNode<_, _, _>"),
|args| {
@ -359,17 +391,20 @@ fn node_registry() -> HashMap<NodeIdentifier, HashMap<NodeIOTypes, NodeConstruct
if use_legacy.eval(()) {
let generate_brightness_contrast_legacy_mapper_node = GenerateBrightnessContrastLegacyMapperNode::new(brightness, contrast);
let map_image_frame_node = graphene_std::raster::MapImageNode::new(ValueNode::new(generate_brightness_contrast_legacy_mapper_node.eval(())));
let map_image_frame_node = FutureWrapperNode::new(map_image_frame_node);
let any: DynAnyNode<ImageFrame<Color>, _, _> = graphene_std::any::DynAnyNode::new(ValueNode::new(map_image_frame_node));
Box::pin(any)
} else {
let generate_brightness_contrast_mapper_node = GenerateBrightnessContrastMapperNode::new(brightness, contrast);
let map_image_frame_node = graphene_std::raster::MapImageNode::new(ValueNode::new(generate_brightness_contrast_mapper_node.eval(())));
let map_image_frame_node = FutureWrapperNode::new(map_image_frame_node);
let any: DynAnyNode<ImageFrame<Color>, _, _> = graphene_std::any::DynAnyNode::new(ValueNode::new(map_image_frame_node));
Box::pin(any)
}
},
NodeIOTypes::new(concrete!(ImageFrame<Color>), concrete!(ImageFrame<Color>), vec![value_fn!(f64), value_fn!(f64), value_fn!(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]),
@ -377,88 +412,112 @@ fn node_registry() -> HashMap<NodeIdentifier, HashMap<NodeIOTypes, NodeConstruct
(
NodeIdentifier::new("graphene_std::memo::LetNode<_>"),
|_| {
let node: LetNode<ImageFrame<Color>> = graphene_std::memo::LetNode::new();
let any = graphene_std::any::DynAnyRefNode::new(node);
any.into_type_erased()
Box::pin(async move {
let node: LetNode<ImageFrame<Color>> = graphene_std::memo::LetNode::new();
let any = graphene_std::any::DynAnyRefNode::new(node);
any.into_type_erased()
})
},
NodeIOTypes::new(concrete!(Option<ImageFrame<Color>>), concrete!(&ImageFrame<Color>), vec![]),
),
(
NodeIdentifier::new("graphene_std::memo::LetNode<_>"),
|_| {
let node: LetNode<graphene_core::EditorApi> = graphene_std::memo::LetNode::new();
let any = graphene_std::any::DynAnyRefNode::new(node);
any.into_type_erased()
Box::pin(async move {
let node: LetNode<graphene_core::EditorApi> = graphene_std::memo::LetNode::new();
let any = graphene_std::any::DynAnyRefNode::new(node);
any.into_type_erased()
})
},
NodeIOTypes::new(concrete!(Option<graphene_core::EditorApi>), concrete!(&graphene_core::EditorApi), vec![]),
),
(
NodeIdentifier::new("graphene_std::memo::EndLetNode<_>"),
|args| {
let input: DowncastBothNode<(), ImageFrame<Color>> = DowncastBothNode::new(args[0]);
let node = graphene_std::memo::EndLetNode::new(input);
let any: DynAnyInRefNode<graphene_core::EditorApi, _, _> = graphene_std::any::DynAnyInRefNode::new(node);
any.into_type_erased()
Box::pin(async move {
let input: DowncastBothNode<(), ImageFrame<Color>> = DowncastBothNode::new(args[0]);
let node = graphene_std::memo::EndLetNode::new(input);
let any: DynAnyInRefNode<graphene_core::EditorApi, _, _> = graphene_std::any::DynAnyInRefNode::new(node);
Box::pin(any) as TypeErasedPinned<'_>
})
},
NodeIOTypes::new(generic!(T), concrete!(graphene_core::EditorApi), vec![value_fn!(ImageFrame<Color>)]),
),
(
NodeIdentifier::new("graphene_std::memo::EndLetNode<_>"),
|args| {
let input: DowncastBothNode<(), VectorData> = DowncastBothNode::new(args[0]);
let node = graphene_std::memo::EndLetNode::new(input);
let any: DynAnyInRefNode<graphene_core::EditorApi, _, _> = graphene_std::any::DynAnyInRefNode::new(node);
any.into_type_erased()
Box::pin(async move {
let input: DowncastBothNode<(), VectorData> = DowncastBothNode::new(args[0]);
let node = graphene_std::memo::EndLetNode::new(input);
let any: DynAnyInRefNode<graphene_core::EditorApi, _, _> = graphene_std::any::DynAnyInRefNode::new(node);
Box::pin(any) as TypeErasedPinned
})
},
NodeIOTypes::new(generic!(T), concrete!(graphene_core::EditorApi), vec![value_fn!(VectorData)]),
),
(
NodeIdentifier::new("graphene_std::memo::EndLetNode<_>"),
|args| {
let input: DowncastBothNode<(), graphene_core::GraphicGroup> = DowncastBothNode::new(args[0]);
let node = graphene_std::memo::EndLetNode::new(input);
let any: DynAnyInRefNode<graphene_core::EditorApi, _, _> = graphene_std::any::DynAnyInRefNode::new(node);
any.into_type_erased()
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_core::EditorApi, _, _> = graphene_std::any::DynAnyInRefNode::new(node);
Box::pin(any) as TypeErasedPinned
})
},
NodeIOTypes::new(generic!(T), concrete!(graphene_core::EditorApi), vec![value_fn!(graphene_core::GraphicGroup)]),
),
(
NodeIdentifier::new("graphene_std::memo::EndLetNode<_>"),
|args| {
let input: DowncastBothNode<(), graphene_core::Artboard> = DowncastBothNode::new(args[0]);
let node = graphene_std::memo::EndLetNode::new(input);
let any: DynAnyInRefNode<graphene_core::EditorApi, _, _> = graphene_std::any::DynAnyInRefNode::new(node);
any.into_type_erased()
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_core::EditorApi, _, _> = graphene_std::any::DynAnyInRefNode::new(node);
Box::pin(any) as TypeErasedPinned
})
},
NodeIOTypes::new(generic!(T), concrete!(graphene_core::EditorApi), vec![value_fn!(graphene_core::Artboard)]),
),
(
NodeIdentifier::new("graphene_std::memo::RefNode<_, _>"),
|args| {
let map_fn: DowncastBothRefNode<Option<graphene_core::EditorApi>, graphene_core::EditorApi> = DowncastBothRefNode::new(args[0]);
let node = graphene_std::memo::RefNode::new(map_fn);
let any = graphene_std::any::DynAnyRefNode::new(node);
any.into_type_erased()
Box::pin(async move {
let map_fn: DowncastBothRefNode<Option<graphene_core::EditorApi>, graphene_core::EditorApi> = DowncastBothRefNode::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::DynAnyRefNode::new(node);
Box::pin(any) as TypeErasedPinned
})
},
NodeIOTypes::new(concrete!(()), concrete!(&graphene_core::EditorApi), vec![]),
),
/*
(
NodeIdentifier::new("graphene_core::structural::MapImageNode"),
|args| {
Box::pin(async move {
let map_fn: DowncastBothNode<Color, Color> = DowncastBothNode::new(args[0]);
let node = graphene_std::raster::MapImageNode::new(ValueNode::new(map_fn));
let node = FutureWrapperNode::new(node);
let any: DynAnyNode<Image<Color>, _, _> = graphene_std::any::DynAnyNode::new(graphene_core::value::ValueNode::new(node));
any.into_type_erased()
Box::pin(any) as TypeErasedPinned
})
},
NodeIOTypes::new(concrete!(Image<Color>), concrete!(Image<Color>), vec![]),
),
*/
(
NodeIdentifier::new("graphene_std::raster::ImaginateNode<_>"),
|args| {
let cached = graphene_std::any::input_node::<Option<std::sync::Arc<Image<Color>>>>(args[15]);
let node = graphene_std::raster::ImaginateNode::new(cached);
let any = DynAnyNode::new(ValueNode::new(node));
any.into_type_erased()
Box::pin(async move {
let cached = graphene_std::any::input_node::<Option<std::sync::Arc<Image<Color>>>>(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<Color>),
@ -485,12 +544,14 @@ fn node_registry() -> HashMap<NodeIdentifier, HashMap<NodeIOTypes, NodeConstruct
],
),
),
/*
(
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<Color>, Image<Color>>::new(args[2]);
let image = image.then(EvalSyncNode::new());
let empty_image: ValueNode<Image<Color>> = ValueNode::new(Image::empty());
let empty: TypeNode<_, (), Image<Color>> = TypeNode::new(empty_image.then(CloneNode::new()));
use graphene_core::Node;
@ -517,8 +578,9 @@ fn node_registry() -> HashMap<NodeIdentifier, HashMap<NodeIOTypes, NodeConstruct
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<Color>, _, _> = DynAnyNode::new(ValueNode::new(new_image));
node.into_type_erased()
Box::pin(node)
},
NodeIOTypes::new(concrete!(Image<Color>), concrete!(Image<Color>), vec![value_fn!(u32), value_fn!(f64)]),
),
@ -529,7 +591,7 @@ fn node_registry() -> HashMap<NodeIdentifier, HashMap<NodeIOTypes, NodeConstruct
let input: DowncastBothNode<(), Image<Color>> = DowncastBothNode::new(args[0]);
let node: CacheNode<Image<Color>, _> = graphene_std::memo::CacheNode::new(input);
let any = DynAnyRefNode::new(node);
any.into_type_erased()
Box::pin(any)
},
NodeIOTypes::new(concrete!(()), concrete!(&Image<Color>), vec![value_fn!(Image<Color>)]),
),
@ -539,7 +601,7 @@ fn node_registry() -> HashMap<NodeIdentifier, HashMap<NodeIOTypes, NodeConstruct
let input: DowncastBothNode<(), ImageFrame<Color>> = DowncastBothNode::new(args[0]);
let node: CacheNode<ImageFrame<Color>, _> = graphene_std::memo::CacheNode::new(input);
let any = DynAnyRefNode::new(node);
any.into_type_erased()
Box::pin(any)
},
NodeIOTypes::new(concrete!(()), concrete!(&ImageFrame<Color>), vec![value_fn!(ImageFrame<Color>)]),
),
@ -549,7 +611,7 @@ fn node_registry() -> HashMap<NodeIdentifier, HashMap<NodeIOTypes, NodeConstruct
let input: DowncastBothNode<ImageFrame<Color>, ImageFrame<Color>> = DowncastBothNode::new(args[0]);
let node: CacheNode<ImageFrame<Color>, _> = graphene_std::memo::CacheNode::new(input);
let any = DynAnyRefNode::new(node);
any.into_type_erased()
Box::pin(any)
},
NodeIOTypes::new(concrete!(ImageFrame<Color>), concrete!(&ImageFrame<Color>), vec![fn_type!(ImageFrame<Color>, ImageFrame<Color>)]),
),
@ -559,7 +621,7 @@ fn node_registry() -> HashMap<NodeIdentifier, HashMap<NodeIOTypes, NodeConstruct
let input: DowncastBothNode<(), QuantizationChannels> = DowncastBothNode::new(args[0]);
let node: CacheNode<QuantizationChannels, _> = graphene_std::memo::CacheNode::new(input);
let any = DynAnyRefNode::new(node);
any.into_type_erased()
Box::pin(any)
},
NodeIOTypes::new(concrete!(()), concrete!(&QuantizationChannels), vec![value_fn!(QuantizationChannels)]),
),
@ -569,10 +631,10 @@ fn node_registry() -> HashMap<NodeIdentifier, HashMap<NodeIOTypes, NodeConstruct
let input: DowncastBothNode<(), Vec<DVec2>> = DowncastBothNode::new(args[0]);
let node: CacheNode<Vec<DVec2>, _> = graphene_std::memo::CacheNode::new(input);
let any = DynAnyRefNode::new(node);
any.into_type_erased()
Box::pin(any)
},
NodeIOTypes::new(concrete!(()), concrete!(&Vec<DVec2>), vec![value_fn!(Vec<DVec2>)]),
),
),*/
],
register_node!(graphene_core::structural::ConsNode<_, _>, input: Image<Color>, params: [&str]),
register_node!(graphene_std::raster::ImageFrameNode<_, _>, input: Image<Color>, params: [DAffine2]),

View file

@ -26,6 +26,9 @@ pub fn node_fn(attr: TokenStream, item: TokenStream) -> TokenStream {
.collect::<Vec<_>>(),
_ => Default::default(),
};
let asyncness = function.sig.asyncness.is_some();
let arg_idents = args
.iter()
.filter(|x| x.to_token_stream().to_string().starts_with('_'))
@ -141,21 +144,43 @@ pub fn node_fn(attr: TokenStream, item: TokenStream) -> TokenStream {
let input_lifetime = if generics.is_empty() { quote::quote!() } else { quote::quote!('input,) };
quote::quote! {
let node_impl = if asyncness {
quote::quote! {
impl <'input, #generics> Node<'input, #primary_input_ty> for #node_name<#(#args),*>
#where_clause
{
type Output = #output;
#[inline]
fn eval(&'input self, #primary_input_mutability #primary_input_ident: #primary_input_ty) -> Self::Output {
#(
let #parameter_mutability #parameter_idents = self.#parameter_idents.eval(());
)*
impl <'input, #generics> Node<'input, #primary_input_ty> for #node_name<#(#args),*>
#where_clause
{
type Output = core::pin::Pin<Box<dyn core::future::Future< Output = #output> + 'input>>;
#[inline]
fn eval(&'input self, #primary_input_mutability #primary_input_ident: #primary_input_ty) -> Self::Output {
#(
let #parameter_mutability #parameter_idents = self.#parameter_idents.eval(());
)*
#body
Box::pin(async move {#body})
}
}
}
} else {
quote::quote! {
impl <'input, #generics> Node<'input, #primary_input_ty> for #node_name<#(#args),*>
#where_clause
{
type Output = #output;
#[inline]
fn eval(&'input self, #primary_input_mutability #primary_input_ident: #primary_input_ty) -> Self::Output {
#(
let #parameter_mutability #parameter_idents = self.#parameter_idents.eval(());
)*
#body
}
}
}
};
let new_fn = quote::quote! {
impl <#input_lifetime #new_fn_generics> #node_name<#(#args),*>
where #(#extra_where_clause),*
@ -168,6 +193,10 @@ pub fn node_fn(attr: TokenStream, item: TokenStream) -> TokenStream {
}
}
};
quote::quote! {
#node_impl
#new_fn
}
.into()
}

View file

@ -2,6 +2,7 @@ use super::context::Context;
use graph_craft::executor::{Any, Executor};
use graph_craft::proto::LocalFuture;
use graphene_core::gpu::PushConstants;
use bytemuck::Pod;
@ -38,7 +39,7 @@ impl<I: StaticTypeSized, O> GpuExecutor<I, O> {
}
impl<I: StaticTypeSized + Sync + Pod + Send, O: StaticTypeSized + Send + Sync + Pod> Executor for GpuExecutor<I, O> {
fn execute<'i, 's: 'i>(&'s self, input: Any<'i>) -> Result<Any<'i>, Box<dyn std::error::Error>> {
fn execute<'i>(&'i self, input: Any<'i>) -> LocalFuture<Result<Any<'i>, Box<dyn std::error::Error>>> {
let input = dyn_any::downcast::<Vec<I>>(input).expect("Wrong input type");
let context = &self.context;
let result: Vec<O> = execute_shader(
@ -49,10 +50,11 @@ impl<I: StaticTypeSized + Sync + Pod + Send, O: StaticTypeSized + Send + Sync +
&context.command_buffer_allocator,
*input,
);
Ok(Box::new(result))
Box::pin(async move { Ok(Box::new(result) as Any) })
}
}
// TODO: make async
fn execute_shader<I: Pod + Send + Sync, O: Pod + Send + Sync>(
device: std::sync::Arc<Device>,
queue: std::sync::Arc<vulkano::device::Queue>,

View file

@ -5,7 +5,10 @@ use wgpu::util::DeviceExt;
use super::context::Context;
use bytemuck::Pod;
use dyn_any::StaticTypeSized;
use graph_craft::executor::{Any, Executor};
use graph_craft::{
executor::{Any, Executor},
proto::LocalFuture,
};
#[derive(Debug)]
pub struct GpuExecutor<'a, I: StaticTypeSized, O> {
@ -27,14 +30,16 @@ 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, 's: 'i>(&'s self, input: Any<'i>) -> Result<Any<'i>, Box<dyn std::error::Error>> {
fn execute<'i>(&'i self, input: Any<'i>) -> LocalFuture<Result<Any<'i>, Box<dyn std::error::Error>>> {
let input = dyn_any::downcast::<Vec<I>>(input).expect("Wrong input type");
let context = &self.context;
let future = execute_shader(context.device.clone(), context.queue.clone(), self.shader.to_vec(), *input, self.entry_point.clone());
let result = future_executor::block_on(future);
Box::pin(async move {
let result = future.await;
let result: Vec<O> = result.ok_or_else(|| String::from("Failed to execute shader"))?;
Ok(Box::new(result))
let result: Vec<O> = result.ok_or_else(|| String::from("Failed to execute shader"))?;
Ok(Box::new(result) as Any)
})
}
}