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
This commit is contained in:
Dennis Kobert 2023-06-02 11:05:32 +02:00 committed by Keavon Chambers
parent 259dcdc628
commit 4e1bfddcd8
43 changed files with 520 additions and 1252 deletions

View file

@ -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

48
Cargo.lock generated
View file

@ -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"

View file

@ -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",

View file

@ -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::<Operation>())
}
/// 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()
}
}

View file

@ -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"

View file

@ -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

View file

@ -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::<usize, Key>(&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::<usize, Key>(&i) }
(i as u8).try_into().unwrap()
})
.collect::<Vec<_>>();

View file

@ -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,

View file

@ -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::<Message>())
}
/// 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.

View file

@ -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);

View file

@ -148,7 +148,7 @@ fn static_nodes() -> Vec<DocumentNodeType> {
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<DocumentNodeType> {
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<DocumentNodeType> {
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<DocumentNodeType> {
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<DocumentNodeType> {
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> {
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<DocumentNodeType> {
],
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<Color>))],
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<DocumentNodeType> {
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<Color>))],
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> {
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());

View file

@ -1222,9 +1222,7 @@ fn edit_layer_deepest_manipulation(intersect: &Layer, responses: &mut VecDeque<M
fn recursive_search(document: &DocumentMessageHandler, layer_path: &Vec<u64>, incoming_layer_path_vector: &Vec<u64>) -> 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;
}
}

View file

@ -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<Vec<NodeId>>) {
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::<WasmSurfaceHandleFrame>() {
let Ok(value) = dyn_any::downcast::<WasmSurfaceHandleFrame>(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<Arc<dyn std::any::Any>> {

View file

@ -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]

View file

@ -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"}

View file

@ -1,80 +0,0 @@
use std::{
mem::MaybeUninit,
pin::Pin,
sync::atomic::{AtomicUsize, Ordering},
};
use dyn_any::StaticTypeSized;
pub trait BorrowStack<T: StaticTypeSized> {
/// # Safety
unsafe fn push(&self, value: T);
/// # Safety
unsafe fn pop(&self);
/// # Safety
unsafe fn get<'a>(&self) -> &'a [<T as StaticTypeSized>::Static];
}
#[derive(Debug)]
pub struct FixedSizeStack<T: dyn_any::StaticTypeSized> {
data: Pin<Box<[MaybeUninit<T>]>>,
capacity: usize,
len: AtomicUsize,
}
impl<'n, T: 'n + dyn_any::StaticTypeSized> FixedSizeStack<T> {
pub fn new(capacity: usize) -> Self {
let layout = std::alloc::Layout::array::<MaybeUninit<T>>(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<T>, 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 [<T as StaticTypeSized>::Static]) -> T) {
assert_eq!(std::mem::size_of::<T>(), std::mem::size_of::<T::Static>());
unsafe { self.push(f(self.get())) }
}
}
impl<T: dyn_any::StaticTypeSized> BorrowStack<T> for FixedSizeStack<T> {
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);
}
}

View file

@ -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;

View file

@ -36,34 +36,42 @@ unsafe impl StaticType for SurfaceFrame {
type Static = SurfaceFrame;
}
impl<S> From<SurfaceHandleFrame<S>> for SurfaceFrame {
fn from(x: SurfaceHandleFrame<S>) -> Self {
Self {
surface_id: x.surface_handle.surface_id,
transform: x.transform,
}
}
}
#[derive(Clone)]
pub struct SurfaceHandle<'a, Surface> {
pub struct SurfaceHandle<Surface> {
pub surface_id: SurfaceId,
pub surface: Surface,
application_io: &'a dyn ApplicationIo<Surface = Surface>,
}
unsafe impl<T: 'static> StaticType for SurfaceHandle<'_, T> {
type Static = SurfaceHandle<'static, T>;
unsafe impl<T: 'static> StaticType for SurfaceHandle<T> {
type Static = SurfaceHandle<T>;
}
#[derive(Clone)]
pub struct SurfaceHandleFrame<'a, Surface> {
pub surface_handle: Arc<SurfaceHandle<'a, Surface>>,
pub struct SurfaceHandleFrame<Surface> {
pub surface_handle: Arc<SurfaceHandle<Surface>>,
pub transform: DAffine2,
}
unsafe impl<T: 'static> StaticType for SurfaceHandleFrame<'_, T> {
type Static = SurfaceHandleFrame<'static, T>;
unsafe impl<T: 'static> StaticType for SurfaceHandleFrame<T> {
type Static = SurfaceHandleFrame<T>;
}
impl<T> Transform for SurfaceHandleFrame<'_, T> {
impl<T> Transform for SurfaceHandleFrame<T> {
fn transform(&self) -> DAffine2 {
self.transform
}
}
impl<T> TransformMut for SurfaceHandleFrame<'_, T> {
impl<T> TransformMut for SurfaceHandleFrame<T> {
fn transform_mut(&mut self) -> &mut DAffine2 {
&mut self.transform
}
@ -142,10 +150,10 @@ impl<'a, T> AsRef<EditorApi<'a, T>> 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<Color>;
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())
}
}

View file

@ -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<Self::Surface> {
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::<HtmlCanvasElement>()?;
@ -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<CanvasRenderingContext2d>;
pub type WasmSurfaceHandleFrame = SurfaceHandleFrame<CanvasRenderingContext2d>;
pub struct CreateSurfaceNode {}
#[node_macro::node_fn(CreateSurfaceNode)]
fn create_surface_node<'a: 'input>(editor: &'a WasmEditorApi<'a>) -> Arc<SurfaceHandle<'a, CanvasRenderingContext2d>> {
fn create_surface_node<'a: 'input>(editor: WasmEditorApi<'a>) -> Arc<SurfaceHandle<CanvasRenderingContext2d>> {
editor.application_io.create_surface().into()
}
@ -114,7 +110,7 @@ pub struct DrawImageFrameNode<Surface> {
}
#[node_macro::node_fn(DrawImageFrameNode)]
async fn draw_image_frame_node<'a: 'input>(image: ImageFrame<SRGBA8>, surface_handle: Arc<SurfaceHandle<'a, CanvasRenderingContext2d>>) -> SurfaceHandleFrame<'a, CanvasRenderingContext2d> {
async fn draw_image_frame_node<'a: 'input>(image: ImageFrame<SRGBA8>, surface_handle: Arc<SurfaceHandle<CanvasRenderingContext2d>>) -> SurfaceHandleFrame<CanvasRenderingContext2d> {
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 {

View file

@ -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<std::sync::Arc<dyn core::any::Any>> {
log::warn!("Node::serialize not implemented for {}", core::any::type_name::<Self>());

View file

@ -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<T, CachedNode> {
cache: Cell<Option<T>>,
node: CachedNode,
}
impl<'i, 'o: 'i, T: 'i + Clone + 'o, CachedNode: 'i> Node<'i, ()> for MemoNode<T, CachedNode>
where
CachedNode: for<'any_input> Node<'any_input, ()>,
for<'a> <CachedNode as Node<'a, ()>>::Output: core::future::Future<Output = T> + '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<Box<dyn Future<Output = T> + '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<T, CachedNode> MemoNode<T, CachedNode> {
pub const fn new(node: CachedNode) -> MemoNode<T, CachedNode> {
MemoNode { cache: Cell::new(None), node }
}
}
#[cfg(feature = "alloc")]
/// Caches the output of the last graph evaluation for introspection
#[derive(Default)]
pub struct MonitorNode<T> {
output: Cell<Option<Arc<T>>>,
}
#[cfg(feature = "alloc")]
impl<'i, T: 'static + Clone> Node<'i, T> for MonitorNode<T> {
type Output = T;
fn eval(&'i self, input: T) -> Self::Output {
self.output.set(Some(Arc::new(input.clone())));
input
}
fn serialize(&self) -> Option<Arc<dyn core::any::Any>> {
let out = self.output.take();
self.output.set(out.clone());
(out).as_ref().map(|output| output.clone() as Arc<dyn core::any::Any>)
}
}
#[cfg(feature = "alloc")]
impl<T> MonitorNode<T> {
pub const fn new() -> MonitorNode<T> {
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<T>` variant or the last
/// value that was added is returned when calling it with `None`
#[derive(Default)]
pub struct LetNode<T> {
// 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<Option<T>>,
}
impl<'i, T: 'i + Clone> Node<'i, Option<T>> for LetNode<T> {
type Output = T;
fn eval(&'i self, input: Option<T>) -> 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<T> std::marker::Unpin for LetNode<T> {}
impl<T> LetNode<T> {
pub fn new() -> LetNode<T> {
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: Input,
}
impl<'i, T: 'i, Input> Node<'i, T> for EndLetNode<Input>
where
Input: Node<'i, ()>,
{
type Output = <Input>::Output;
fn eval(&'i self, _: T) -> Self::Output {
let result = self.input.eval(());
result
}
}
impl<Input> EndLetNode<Input> {
pub const fn new(input: Input) -> EndLetNode<Input> {
EndLetNode { input }
}
}
pub use crate::ops::SomeNode as InitNode;
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
pub struct RefNode<T, Let> {
let_node: Let,
_t: PhantomData<T>,
}
impl<'i, T: 'i, Let> Node<'i, ()> for RefNode<T, Let>
where
Let: for<'a> Node<'a, Option<T>>,
{
type Output = <Let as Node<'i, Option<T>>>::Output;
fn eval(&'i self, _: ()) -> Self::Output {
self.let_node.eval(None)
}
}
impl<Let, T> RefNode<T, Let> {
pub const fn new(let_node: Let) -> RefNode<T, Let> {
RefNode { let_node, _t: PhantomData }
}
}

View file

@ -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: 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<S0>
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::*;

View file

@ -172,6 +172,7 @@ mod test {
}
#[test]
#[allow(clippy::unit_cmp)]
fn test_apply() {
let mut array = [1, 2, 3];
let slice = &mut array;

View file

@ -15,7 +15,7 @@ pub struct TextGenerator<Text, FontName, Size> {
}
#[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))
}

View file

@ -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<Type>, Box<Type>),
Future(Box<Type>),
}
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),
}
}
}

View file

@ -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<T> {
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<T> RefCellMutNode<T> {
RefCellMutNode(RefCell::new(value))
}
}
/// #Safety: Never use this as it is unsound.
#[derive(Default, Debug)]
pub struct UnsafeMutValueNode<T>(pub T);
/// #Safety: Never use this as it is unsound.
impl<'i, T: 'i> Node<'i, ()> for UnsafeMutValueNode<T> {
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<T> UnsafeMutValueNode<T> {
pub const fn new(value: T) -> UnsafeMutValueNode<T> {
UnsafeMutValueNode(value)
}
}
#[derive(Default)]
pub struct OnceCellNode<T>(pub Cell<T>);

View file

@ -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 {

View file

@ -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<dyn DynAny<'a> + 'a>) -> Option<Self> {
pub fn try_from_any(input: Box<dyn DynAny<'a> + 'a>) -> Result<Self, String> {
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::<String>() => Some(TaggedValue::String(*downcast(input).unwrap())),
x if x == TypeId::of::<u32>() => Some(TaggedValue::U32(*downcast(input).unwrap())),
x if x == TypeId::of::<f32>() => Some(TaggedValue::F32(*downcast(input).unwrap())),
x if x == TypeId::of::<f64>() => Some(TaggedValue::F64(*downcast(input).unwrap())),
x if x == TypeId::of::<bool>() => Some(TaggedValue::Bool(*downcast(input).unwrap())),
x if x == TypeId::of::<DVec2>() => Some(TaggedValue::DVec2(*downcast(input).unwrap())),
x if x == TypeId::of::<Option<DVec2>>() => Some(TaggedValue::OptionalDVec2(*downcast(input).unwrap())),
x if x == TypeId::of::<graphene_core::raster::Image<Color>>() => Some(TaggedValue::Image(*downcast(input).unwrap())),
x if x == TypeId::of::<Option<Arc<graphene_core::raster::Image<Color>>>>() => Some(TaggedValue::RcImage(*downcast(input).unwrap())),
x if x == TypeId::of::<graphene_core::raster::ImageFrame<Color>>() => Some(TaggedValue::ImageFrame(*downcast(input).unwrap())),
x if x == TypeId::of::<graphene_core::raster::Color>() => Some(TaggedValue::Color(*downcast(input).unwrap())),
x if x == TypeId::of::<Vec<bezier_rs::Subpath<graphene_core::uuid::ManipulatorGroupId>>>() => Some(TaggedValue::Subpaths(*downcast(input).unwrap())),
x if x == TypeId::of::<Arc<bezier_rs::Subpath<graphene_core::uuid::ManipulatorGroupId>>>() => Some(TaggedValue::RcSubpath(*downcast(input).unwrap())),
x if x == TypeId::of::<BlendMode>() => Some(TaggedValue::BlendMode(*downcast(input).unwrap())),
x if x == TypeId::of::<ImaginateSamplingMethod>() => Some(TaggedValue::ImaginateSamplingMethod(*downcast(input).unwrap())),
x if x == TypeId::of::<ImaginateMaskStartingFill>() => Some(TaggedValue::ImaginateMaskStartingFill(*downcast(input).unwrap())),
x if x == TypeId::of::<ImaginateStatus>() => Some(TaggedValue::ImaginateStatus(*downcast(input).unwrap())),
x if x == TypeId::of::<Option<Vec<u64>>>() => Some(TaggedValue::LayerPath(*downcast(input).unwrap())),
x if x == TypeId::of::<DAffine2>() => Some(TaggedValue::DAffine2(*downcast(input).unwrap())),
x if x == TypeId::of::<LuminanceCalculation>() => Some(TaggedValue::LuminanceCalculation(*downcast(input).unwrap())),
x if x == TypeId::of::<graphene_core::vector::VectorData>() => Some(TaggedValue::VectorData(*downcast(input).unwrap())),
x if x == TypeId::of::<graphene_core::vector::style::Fill>() => Some(TaggedValue::Fill(*downcast(input).unwrap())),
x if x == TypeId::of::<graphene_core::vector::style::Stroke>() => Some(TaggedValue::Stroke(*downcast(input).unwrap())),
x if x == TypeId::of::<Vec<f32>>() => Some(TaggedValue::VecF32(*downcast(input).unwrap())),
x if x == TypeId::of::<graphene_core::raster::RedGreenBlue>() => Some(TaggedValue::RedGreenBlue(*downcast(input).unwrap())),
x if x == TypeId::of::<graphene_core::raster::RelativeAbsolute>() => Some(TaggedValue::RelativeAbsolute(*downcast(input).unwrap())),
x if x == TypeId::of::<graphene_core::raster::SelectiveColorChoice>() => Some(TaggedValue::SelectiveColorChoice(*downcast(input).unwrap())),
x if x == TypeId::of::<graphene_core::vector::style::LineCap>() => Some(TaggedValue::LineCap(*downcast(input).unwrap())),
x if x == TypeId::of::<graphene_core::vector::style::LineJoin>() => Some(TaggedValue::LineJoin(*downcast(input).unwrap())),
x if x == TypeId::of::<graphene_core::vector::style::FillType>() => Some(TaggedValue::FillType(*downcast(input).unwrap())),
x if x == TypeId::of::<graphene_core::vector::style::GradientType>() => Some(TaggedValue::GradientType(*downcast(input).unwrap())),
x if x == TypeId::of::<Vec<(f64, Option<graphene_core::Color>)>>() => Some(TaggedValue::GradientPositions(*downcast(input).unwrap())),
x if x == TypeId::of::<graphene_core::quantization::QuantizationChannels>() => Some(TaggedValue::Quantization(*downcast(input).unwrap())),
x if x == TypeId::of::<Option<graphene_core::Color>>() => Some(TaggedValue::OptionalColor(*downcast(input).unwrap())),
x if x == TypeId::of::<Vec<graphene_core::uuid::ManipulatorGroupId>>() => Some(TaggedValue::ManipulatorGroupIds(*downcast(input).unwrap())),
x if x == TypeId::of::<graphene_core::text::Font>() => Some(TaggedValue::Font(*downcast(input).unwrap())),
x if x == TypeId::of::<Vec<graphene_core::vector::brush_stroke::BrushStroke>>() => Some(TaggedValue::BrushStrokes(*downcast(input).unwrap())),
x if x == TypeId::of::<graphene_core::raster::IndexNode<Vec<graphene_core::raster::ImageFrame<Color>>>>() => Some(TaggedValue::Segments(*downcast(input).unwrap())),
x if x == TypeId::of::<crate::document::DocumentNode>() => Some(TaggedValue::DocumentNode(*downcast(input).unwrap())),
x if x == TypeId::of::<graphene_core::GraphicGroup>() => Some(TaggedValue::GraphicGroup(*downcast(input).unwrap())),
x if x == TypeId::of::<graphene_core::Artboard>() => Some(TaggedValue::Artboard(*downcast(input).unwrap())),
x if x == TypeId::of::<glam::IVec2>() => Some(TaggedValue::IVec2(*downcast(input).unwrap())),
x if x == TypeId::of::<graphene_core::SurfaceFrame>() => Some(TaggedValue::SurfaceFrame(*downcast(input).unwrap())),
_ => None,
x if x == TypeId::of::<()>() => Ok(TaggedValue::None),
x if x == TypeId::of::<String>() => Ok(TaggedValue::String(*downcast(input).unwrap())),
x if x == TypeId::of::<u32>() => Ok(TaggedValue::U32(*downcast(input).unwrap())),
x if x == TypeId::of::<f32>() => Ok(TaggedValue::F32(*downcast(input).unwrap())),
x if x == TypeId::of::<f64>() => Ok(TaggedValue::F64(*downcast(input).unwrap())),
x if x == TypeId::of::<bool>() => Ok(TaggedValue::Bool(*downcast(input).unwrap())),
x if x == TypeId::of::<DVec2>() => Ok(TaggedValue::DVec2(*downcast(input).unwrap())),
x if x == TypeId::of::<Option<DVec2>>() => Ok(TaggedValue::OptionalDVec2(*downcast(input).unwrap())),
x if x == TypeId::of::<graphene_core::raster::Image<Color>>() => Ok(TaggedValue::Image(*downcast(input).unwrap())),
x if x == TypeId::of::<Option<Arc<graphene_core::raster::Image<Color>>>>() => Ok(TaggedValue::RcImage(*downcast(input).unwrap())),
x if x == TypeId::of::<graphene_core::raster::ImageFrame<Color>>() => Ok(TaggedValue::ImageFrame(*downcast(input).unwrap())),
x if x == TypeId::of::<graphene_core::raster::Color>() => Ok(TaggedValue::Color(*downcast(input).unwrap())),
x if x == TypeId::of::<Vec<bezier_rs::Subpath<graphene_core::uuid::ManipulatorGroupId>>>() => Ok(TaggedValue::Subpaths(*downcast(input).unwrap())),
x if x == TypeId::of::<Arc<bezier_rs::Subpath<graphene_core::uuid::ManipulatorGroupId>>>() => Ok(TaggedValue::RcSubpath(*downcast(input).unwrap())),
x if x == TypeId::of::<BlendMode>() => Ok(TaggedValue::BlendMode(*downcast(input).unwrap())),
x if x == TypeId::of::<ImaginateSamplingMethod>() => Ok(TaggedValue::ImaginateSamplingMethod(*downcast(input).unwrap())),
x if x == TypeId::of::<ImaginateMaskStartingFill>() => Ok(TaggedValue::ImaginateMaskStartingFill(*downcast(input).unwrap())),
x if x == TypeId::of::<ImaginateStatus>() => Ok(TaggedValue::ImaginateStatus(*downcast(input).unwrap())),
x if x == TypeId::of::<Option<Vec<u64>>>() => Ok(TaggedValue::LayerPath(*downcast(input).unwrap())),
x if x == TypeId::of::<DAffine2>() => Ok(TaggedValue::DAffine2(*downcast(input).unwrap())),
x if x == TypeId::of::<LuminanceCalculation>() => Ok(TaggedValue::LuminanceCalculation(*downcast(input).unwrap())),
x if x == TypeId::of::<graphene_core::vector::VectorData>() => Ok(TaggedValue::VectorData(*downcast(input).unwrap())),
x if x == TypeId::of::<graphene_core::vector::style::Fill>() => Ok(TaggedValue::Fill(*downcast(input).unwrap())),
x if x == TypeId::of::<graphene_core::vector::style::Stroke>() => Ok(TaggedValue::Stroke(*downcast(input).unwrap())),
x if x == TypeId::of::<Vec<f32>>() => Ok(TaggedValue::VecF32(*downcast(input).unwrap())),
x if x == TypeId::of::<graphene_core::raster::RedGreenBlue>() => Ok(TaggedValue::RedGreenBlue(*downcast(input).unwrap())),
x if x == TypeId::of::<graphene_core::raster::RelativeAbsolute>() => Ok(TaggedValue::RelativeAbsolute(*downcast(input).unwrap())),
x if x == TypeId::of::<graphene_core::raster::SelectiveColorChoice>() => Ok(TaggedValue::SelectiveColorChoice(*downcast(input).unwrap())),
x if x == TypeId::of::<graphene_core::vector::style::LineCap>() => Ok(TaggedValue::LineCap(*downcast(input).unwrap())),
x if x == TypeId::of::<graphene_core::vector::style::LineJoin>() => Ok(TaggedValue::LineJoin(*downcast(input).unwrap())),
x if x == TypeId::of::<graphene_core::vector::style::FillType>() => Ok(TaggedValue::FillType(*downcast(input).unwrap())),
x if x == TypeId::of::<graphene_core::vector::style::GradientType>() => Ok(TaggedValue::GradientType(*downcast(input).unwrap())),
x if x == TypeId::of::<Vec<(f64, Option<graphene_core::Color>)>>() => Ok(TaggedValue::GradientPositions(*downcast(input).unwrap())),
x if x == TypeId::of::<graphene_core::quantization::QuantizationChannels>() => Ok(TaggedValue::Quantization(*downcast(input).unwrap())),
x if x == TypeId::of::<Option<graphene_core::Color>>() => Ok(TaggedValue::OptionalColor(*downcast(input).unwrap())),
x if x == TypeId::of::<Vec<graphene_core::uuid::ManipulatorGroupId>>() => Ok(TaggedValue::ManipulatorGroupIds(*downcast(input).unwrap())),
x if x == TypeId::of::<graphene_core::text::Font>() => Ok(TaggedValue::Font(*downcast(input).unwrap())),
x if x == TypeId::of::<Vec<graphene_core::vector::brush_stroke::BrushStroke>>() => Ok(TaggedValue::BrushStrokes(*downcast(input).unwrap())),
x if x == TypeId::of::<graphene_core::raster::IndexNode<Vec<graphene_core::raster::ImageFrame<Color>>>>() => Ok(TaggedValue::Segments(*downcast(input).unwrap())),
x if x == TypeId::of::<crate::document::DocumentNode>() => Ok(TaggedValue::DocumentNode(*downcast(input).unwrap())),
x if x == TypeId::of::<graphene_core::GraphicGroup>() => Ok(TaggedValue::GraphicGroup(*downcast(input).unwrap())),
x if x == TypeId::of::<graphene_core::Artboard>() => Ok(TaggedValue::Artboard(*downcast(input).unwrap())),
x if x == TypeId::of::<glam::IVec2>() => Ok(TaggedValue::IVec2(*downcast(input).unwrap())),
x if x == TypeId::of::<graphene_core::SurfaceFrame>() => Ok(TaggedValue::SurfaceFrame(*downcast(input).unwrap())),
x if x == TypeId::of::<graphene_core::wasm_application_io::WasmSurfaceHandleFrame>() => {
let frame = *downcast::<graphene_core::wasm_application_io::WasmSurfaceHandleFrame>(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<dyn for<'i> ValueTrait<'i> + 'a>;
pub trait ValueTrait<'a>: DynAny<'a> + Upcast<dyn DynAny<'a> + '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<dyn DynAny<'a> + '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<dyn for<'i> 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<H: std::hash::Hasher>(&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());
}
}

View file

@ -38,6 +38,6 @@ impl Compiler {
}
pub type Any<'a> = Box<dyn DynAny<'a> + 'a>;
pub trait Executor {
fn execute<'a>(&'a self, input: Any<'a>) -> LocalFuture<Result<Any<'a>, Box<dyn Error>>>;
pub trait Executor<I, O> {
fn execute(&self, input: I) -> LocalFuture<Result<O, Box<dyn Error>>>;
}

View file

@ -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);

View file

@ -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"] }

View file

@ -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<std::sync::Arc<dyn core::any::Any>> {
@ -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
})
}

View file

@ -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<TypingContext, ShaderIO> {

View file

@ -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;

View file

@ -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<DynAnyNode<'n>>,
pub b: Option<DynAnyNode<'n>>,
}
#[derive(Copy, Clone)]
pub struct MulNodeTypedProxy<'n> {
pub a: Option<DynNode<'n, &'n f32>>,
pub b: Option<DynNode<'n, &'n f32>>,
}
impl<'n> Node<'n> for MulNodeAnyProxy<'n> {
type Output = MulNodeInput<'n>;
fn eval(&'n self) -> <Self as graphene_std::Node<'n>>::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) -> <Self as graphene_std::Node<'n>>::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<SNode<'n>>>);
// impl<'n> NodeStore<'n> {
// fn len(&self) -> usize {
// self.0.len()
// }
// fn push(&'n mut self, f: fn(&'n [Box<SNode>]) -> Box<SNode<'n>>) {
// 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<Box<dyn Node<'_, Output = &dyn DynAny>>> =
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::<f32>(result));
/*unsafe {
stack
.push(Box::new(AnyRefNode::new(stack.get(0).as_ref()))
as Box<dyn Node<(), Output = &dyn DynAny>>)
};*/
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::<u32>::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)
}

View file

@ -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<T, CachedNode> {
// 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<T, CachedNode>
where
CachedNode: for<'any_input> Node<'any_input, I>,
for<'a> <CachedNode as Node<'a, I>>::Output: core::future::Future<Output = T> + '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<Box<dyn Future<Output = T> + '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<T, CachedNode> std::marker::Unpin for CacheNode<T, CachedNode> {}
impl<T, CachedNode> CacheNode<T, CachedNode> {
pub fn new(node: CachedNode) -> CacheNode<T, CachedNode> {
CacheNode { cache: boxcar::Vec::new(), node }
}
}
/// Caches the output of the last graph evaluation for introspection
#[derive(Default)]
pub struct MonitorNode<T> {
output: Mutex<Option<Arc<T>>>,
}
impl<'i, T: 'static + Clone> Node<'i, T> for MonitorNode<T> {
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<Arc<dyn core::any::Any>> {
let output = self.output.lock().unwrap();
(*output).as_ref().map(|output| output.clone() as Arc<dyn core::any::Any>)
}
}
impl<T> MonitorNode<T> {
pub const fn new() -> MonitorNode<T> {
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<T>` variant or the last
/// value that was added is returned when calling it with `None`
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct LetNode<T> {
// 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<T>> for LetNode<T> {
type Output = &'i T;
fn eval(&'i self, input: Option<T>) -> 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<T> std::marker::Unpin for LetNode<T> {}
impl<T> LetNode<T> {
pub fn new() -> LetNode<T> {
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: Input,
}
impl<'i, T: 'i, Input> Node<'i, &'i T> for EndLetNode<Input>
where
Input: Node<'i, ()>,
{
type Output = <Input>::Output;
fn eval(&'i self, _: &'i T) -> Self::Output {
let result = self.input.eval(());
result
}
}
impl<Input> EndLetNode<Input> {
pub const fn new(input: Input) -> EndLetNode<Input> {
EndLetNode { input }
}
}
pub use graphene_core::ops::SomeNode as InitNode;
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
pub struct RefNode<T, Let> {
let_node: Let,
_t: PhantomData<T>,
}
impl<'i, T: 'i, Let> Node<'i, ()> for RefNode<T, Let>
where
Let: for<'a> Node<'a, Option<T>>,
{
type Output = <Let as Node<'i, Option<T>>>::Output;
fn eval(&'i self, _: ()) -> Self::Output {
self.let_node.eval(None)
}
}
impl<Let, T> RefNode<T, Let> {
pub const fn new(let_node: Let) -> RefNode<T, Let> {
RefNode { let_node, _t: PhantomData }
}
}

View file

@ -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"] }

View file

@ -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<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()) })
impl<'a, I: StaticType + 'a> Executor<I, TaggedValue> for &'a DynamicExecutor {
fn execute(&self, input: I) -> LocalFuture<Result<TaggedValue, Box<dyn Error>>> {
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<O> {
pub async fn eval<'i, I: StaticType + 'i, O: StaticType + '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.await).ok().map(|o| *o)
}
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).await })
pub async fn eval_tagged_value<'i, I: StaticType + 'i>(&'i self, id: NodeId, input: I) -> Result<TaggedValue, String> {
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));
}
}

View file

@ -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<ValueNode<u32>, (), _, _> = 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<ConsNode<_, Any<'_>>, 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::<u32>(add).unwrap(), 6_u32);
let add = unsafe { &stack.get()[3] }.eval_ref(4_u32.into_dyn());
assert_eq!(*downcast::<u32>(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::<u32>(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();
}
}

View file

@ -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<TypeErasedPinnedRef<'static>> = $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<Box<dyn Fututure<Output=T>>> 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<Box<dyn core::future::Future<Output = $type>>>>::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<Color>), concrete!(Image<Color>), 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<Color>), concrete!(ImageFrame<Color>), params)
},
)
@ -208,38 +237,32 @@ fn node_registry() -> HashMap<NodeIdentifier, HashMap<NodeIOTypes, NodeConstruct
NodeIOTypes::new(
concrete!(()),
concrete!(ImageFrame<Color>),
vec![value_fn!(ImageFrame<Color>), value_fn!(ImageFrame<Color>), value_fn!(ImageFrame<Color>), value_fn!(ImageFrame<Color>)],
vec![fn_type!(ImageFrame<Color>), fn_type!(ImageFrame<Color>), fn_type!(ImageFrame<Color>), fn_type!(ImageFrame<Color>)],
),
)],
register_node!(graphene_std::raster::EmptyImageNode<_, _>, input: DAffine2, params: [Color]),
register_node!(graphene_std::memo::MonitorNode<_>, input: ImageFrame<Color>, 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<WasmSurfaceHandle>> = DowncastBothNode::new(args[0]);
let node = graphene_core::wasm_application_io::DrawImageFrameNode::new(surface);
let any: DynAnyNode<ImageFrame<SRGBA8>, _, _> = graphene_std::any::DynAnyNode::new(graphene_core::value::ValueNode::new(node));
Box::pin(any) as TypeErasedPinned
})
},
NodeIOTypes::new(concrete!(ImageFrame<SRGBA8>), concrete!(WasmSurfaceHandleFrame), vec![value_fn!(Arc<WasmSurfaceHandle>)]),
)],
register_node!(graphene_core::memo::MonitorNode<_>, input: ImageFrame<Color>, 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<SRGBA8>,
output: WasmSurfaceHandleFrame,
params: [Arc<WasmSurfaceHandle>]
),
#[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<ImageFrame<Color>, _, _> = graphene_std::any::DynAnyNode::new(graphene_core::value::ValueNode::new(node));
Box::pin(any) as TypeErasedPinned
})
},
NodeIOTypes::new(concrete!(ImageFrame<Color>), concrete!(ImageFrame<Color>), vec![value_fn!(DocumentNode)]),
NodeIOTypes::new(concrete!(ImageFrame<Color>), concrete!(ImageFrame<Color>), vec![fn_type!(graph_craft::document::DocumentNode)]),
)],
#[cfg(feature = "gpu")]
vec![(
@ -258,7 +281,7 @@ fn node_registry() -> HashMap<NodeIdentifier, HashMap<NodeIOTypes, NodeConstruct
NodeIOTypes::new(
concrete!(ImageFrame<Color>),
concrete!(ImageFrame<Color>),
vec![value_fn!(ImageFrame<Color>), value_fn!(BlendMode), value_fn!(f32)],
vec![fn_type!(ImageFrame<Color>), fn_type!(BlendMode), fn_type!(f32)],
),
)],
vec![(
@ -327,28 +350,7 @@ fn node_registry() -> HashMap<NodeIdentifier, HashMap<NodeIOTypes, NodeConstruct
NodeIOTypes::new(
concrete!(()),
concrete!(ImageFrame<Color>),
vec![value_fn!(ImageFrame<Color>), value_fn!(ImageFrame<Color>), value_fn!(Vec<BrushStroke>)],
),
)],
vec![(
NodeIdentifier::new("graphene_std::brush::ReduceNode<_, _>"),
|args| {
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>),
concrete!(ImageFrame<Color>),
vec![value_fn!(ImageFrame<Color>)],
vec![fn_type!(ImageFrame<Color>), fn_type!(ImageFrame<Color>), fn_type!(Vec<BrushStroke>)],
),
)],
// Filters
@ -374,7 +376,7 @@ fn node_registry() -> HashMap<NodeIdentifier, HashMap<NodeIOTypes, NodeConstruct
NodeIOTypes::new(
concrete!(ImageFrame<Color>),
concrete!(ImageFrame<Color>),
vec![value_fn!(ImageFrame<Color>), value_fn!(BlendMode), value_fn!(f64)],
vec![fn_type!(ImageFrame<Color>), 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<NodeIdentifier, HashMap<NodeIOTypes, NodeConstruct
}
})
},
NodeIOTypes::new(concrete!(ImageFrame<Color>), concrete!(ImageFrame<Color>), vec![value_fn!(f64), value_fn!(f64), value_fn!(bool)]),
NodeIOTypes::new(concrete!(ImageFrame<Color>), concrete!(ImageFrame<Color>), 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<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![]),
register_node!(graphene_core::memo::LetNode<_>, input: Option<ImageFrame<Color>>, params: []),
register_node!(graphene_core::memo::LetNode<_>, input: Option<graphene_core::EditorApi>, params: []),
async_node!(
graphene_core::memo::EndLetNode<_>,
input: graphene_core::EditorApi,
output: ImageFrame<Color>,
params: [ImageFrame<Color>]
),
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<Option<graphene_core::EditorApi>, graphene_core::EditorApi> = graphene_std::any::DowncastBothNode::new(args[0]);
let node = <graphene_core::memo::RefNode<_, _>>::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>, graphene_core::EditorApi)],
),
(
NodeIdentifier::new("graphene_std::memo::LetNode<_>"),
|_| {
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| {
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!(ImageFrame<Color>), vec![value_fn!(ImageFrame<Color>)]),
),
(
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_core::EditorApi, _, _> = 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_core::EditorApi, _, _> = 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_core::EditorApi, _, _> = 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_core::EditorApi, _, _> = 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<Option<graphene_core::EditorApi>, &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>, &graphene_core::EditorApi)],
),
),
/*
(
NodeIdentifier::new("graphene_std::raster::ImaginateNode<_>"),
|args| {
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>),
concrete!(ImageFrame<Color>),
vec![
value_fn!(f64),
value_fn!(Option<DVec2>),
value_fn!(f64),
value_fn!(ImaginateSamplingMethod),
value_fn!(f64),
value_fn!(String),
value_fn!(String),
value_fn!(bool),
value_fn!(f64),
value_fn!(Option<Vec<u64>>),
value_fn!(bool),
value_fn!(f64),
value_fn!(ImaginateMaskStartingFill),
value_fn!(bool),
value_fn!(bool),
value_fn!(Option<std::sync::Arc<Image<Color>>>),
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<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;
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<Color>, _, _> = DynAnyNode::new(ValueNode::new(new_image));
Box::pin(node)
},
NodeIOTypes::new(concrete!(Image<Color>), concrete!(Image<Color>), vec![value_fn!(u32), value_fn!(f64)]),
),
//register_node!(graphene_std::memo::CacheNode<_>, input: Image<Color>, params: []),
*/
(
NodeIdentifier::new("graphene_std::memo::CacheNode"),
|args| {
Box::pin(async move {
let input: DowncastBothNode<(), Image<Color>> = DowncastBothNode::new(args[0]);
let node: CacheNode<Image<Color>, _> = graphene_std::memo::CacheNode::new(input);
let any = DynAnyNode::new(ValueNode::new(node));
Box::pin(any) as TypeErasedPinned
})
},
NodeIOTypes::new(concrete!(()), concrete!(Image<Color>), vec![value_fn!(Image<Color>)]),
),
(
NodeIdentifier::new("graphene_std::memo::CacheNode"),
|args| {
Box::pin(async move {
let input: DowncastBothNode<(), ImageFrame<Color>> = DowncastBothNode::new(args[0]);
let node: CacheNode<ImageFrame<Color>, _> = graphene_std::memo::CacheNode::new(input);
let any = DynAnyNode::new(ValueNode::new(node));
Box::pin(any) as TypeErasedPinned
})
},
NodeIOTypes::new(concrete!(()), concrete!(ImageFrame<Color>), vec![value_fn!(ImageFrame<Color>)]),
),
(
NodeIdentifier::new("graphene_std::memo::CacheNode"),
|args| {
Box::pin(async move {
let input: DowncastBothNode<ImageFrame<Color>, ImageFrame<Color>> = DowncastBothNode::new(args[0]);
let node: CacheNode<ImageFrame<Color>, _> = graphene_std::memo::CacheNode::new(input);
let any = DynAnyNode::new(ValueNode::new(node));
Box::pin(any) as TypeErasedPinned
})
},
NodeIOTypes::new(concrete!(ImageFrame<Color>), concrete!(ImageFrame<Color>), vec![fn_type!(ImageFrame<Color>, ImageFrame<Color>)]),
),
(
NodeIdentifier::new("graphene_std::memo::CacheNode"),
|args| {
Box::pin(async move {
let input: DowncastBothNode<(), QuantizationChannels> = DowncastBothNode::new(args[0]);
let node: CacheNode<QuantizationChannels, _> = 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<DVec2>> = DowncastBothNode::new(args[0]);
let node: CacheNode<Vec<DVec2>, _> = graphene_std::memo::CacheNode::new(input);
let any = DynAnyNode::new(ValueNode::new(node));
Box::pin(any) as TypeErasedPinned
})
},
NodeIOTypes::new(concrete!(()), concrete!(Vec<DVec2>), vec![value_fn!(Vec<DVec2>)]),
),
(
NodeIdentifier::new("graphene_std::memo::CacheNode"),
|args| {
Box::pin(async move {
let input: DowncastBothNode<(), Arc<WasmSurfaceHandle>> = DowncastBothNode::new(args[0]);
let node: CacheNode<Arc<WasmSurfaceHandle>, _> = graphene_std::memo::CacheNode::new(input);
let any = DynAnyNode::new(ValueNode::new(node));
Box::pin(any) as TypeErasedPinned
})
},
NodeIOTypes::new(concrete!(()), concrete!(Arc<WasmSurfaceHandle>), vec![value_fn!(Arc<WasmSurfaceHandle>)]),
),
],
)],
async_node!(graphene_core::memo::MemoNode<_, _>, input: (), output: Image<Color>, params: [Image<Color>]),
async_node!(graphene_core::memo::MemoNode<_, _>, input: (), output: ImageFrame<Color>, params: [ImageFrame<Color>]),
async_node!(graphene_core::memo::MemoNode<_, _>, input: (), output: QuantizationChannels, params: [QuantizationChannels]),
async_node!(graphene_core::memo::MemoNode<_, _>, input: (), output: Vec<DVec2>, params: [Vec<DVec2>]),
async_node!(graphene_core::memo::MemoNode<_, _>, input: (), output: Arc<WasmSurfaceHandle>, params: [Arc<WasmSurfaceHandle>]),
register_node!(graphene_core::structural::ConsNode<_, _>, input: Image<Color>, params: [&str]),
register_node!(graphene_std::raster::ImageFrameNode<_, _>, input: Image<Color>, params: [DAffine2]),
#[cfg(feature = "quantization")]
@ -698,9 +494,9 @@ fn node_registry() -> HashMap<NodeIdentifier, HashMap<NodeIOTypes, NodeConstruct
input: Vec<graphene_core::vector::bezier_rs::Subpath<graphene_core::uuid::ManipulatorGroupId>>,
params: [Vec<graphene_core::uuid::ManipulatorGroupId>]
),
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<Color>, 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]),

View file

@ -86,16 +86,14 @@ fn args(node: &syn::PathSegment) -> Vec<Type> {
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);

View file

@ -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<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>(&'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");
impl<'a, I: StaticTypeSized + Sync + Pod + Send + 'a, O: StaticTypeSized + Send + Sync + Pod + 'a> Executor<Vec<I>, Vec<O>> for &'a GpuExecutor<I, O> {
fn execute(&self, input: Vec<I>) -> LocalFuture<Result<Vec<O>, Box<dyn Error>>> {
let context = &self.context;
let result: Vec<O> = execute_shader(
context.device.clone(),
@ -48,9 +49,9 @@ impl<I: StaticTypeSized + Sync + Pod + Send, O: StaticTypeSized + Send + Sync +
self.shader.entry_point(&self.entry_point).expect("Entry point not found in shader"),
&context.allocator,
&context.command_buffer_allocator,
*input,
input,
);
Box::pin(async move { Ok(Box::new(result) as Any) })
Box::pin(async move { Ok(result) })
}
}

View file

@ -1,14 +1,11 @@
use std::borrow::Cow;
use std::sync::Arc;
use std::{borrow::Cow, error::Error};
use wgpu::util::DeviceExt;
use super::context::Context;
use bytemuck::Pod;
use dyn_any::StaticTypeSized;
use graph_craft::{
executor::{Any, Executor},
proto::LocalFuture,
};
use graph_craft::{executor::Executor, proto::LocalFuture};
#[derive(Debug)]
pub struct GpuExecutor<'a, I: StaticTypeSized, O> {
@ -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<Result<Any<'i>, Box<dyn std::error::Error>>> {
let input = dyn_any::downcast::<Vec<I>>(input).expect("Wrong input type");
impl<'a, I: StaticTypeSized + Sync + Pod + Send, O: StaticTypeSized + Send + Sync + Pod> Executor<Vec<I>, Vec<O>> for GpuExecutor<'a, I, O> {
fn execute(&self, input: Vec<I>) -> LocalFuture<Result<Vec<O>, Box<dyn Error>>> {
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<O> = result.ok_or_else(|| String::from("Failed to execute shader"))?;
Ok(Box::new(result) as Any)
Ok(result)
})
}
}