Bundle Graphite using Tauri (#873)

* Setup tauri component for graphite editor

Integrate graphite into tauri app

Split interpreted-executor out of graph-craft

* Add gpu execution node

* General Cleanup
This commit is contained in:
TrueDoctor 2022-12-07 12:49:34 +01:00 committed by Keavon Chambers
parent 52cc770a1e
commit 7d8f94462a
109 changed files with 5661 additions and 544 deletions

View file

@ -9,17 +9,26 @@ license = "MIT OR Apache-2.0"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[features]
std = ["dyn-any"]
default = ["async", "serde"]
gpu = ["spirv-std", "bytemuck"]
async = ["async-trait"]
std = ["dyn-any", "dyn-any/std"]
default = ["async", "serde", "kurbo", "log"]
log = ["dep:log"]
serde = ["dep:serde", "glam/serde"]
gpu = ["spirv-std", "bytemuck", "glam/bytemuck", "dyn-any"]
async = ["async-trait", "alloc"]
nightly = []
serde = ["dep:serde"]
alloc = ["dyn-any", "bezier-rs"]
[dependencies]
dyn-any = {path = "../../libraries/dyn-any", features = ["derive"], optional = true}
dyn-any = {path = "../../libraries/dyn-any", features = ["derive"], optional = true, default-features = false }
spirv-std = { git = "https://github.com/EmbarkStudios/rust-gpu", features = ["glam"] , optional = true}
bytemuck = {version = "1.8", features = ["derive"], optional = true}
async-trait = {version = "0.1", optional = true}
serde = {version = "1.0", features = ["derive"], optional = true}
serde = {version = "1.0", features = ["derive"], optional = true, default-features = false }
log = {version = "0.4", optional = true}
bezier-rs = { path = "../../libraries/bezier-rs", optional = true }
kurbo = { git = "https://github.com/linebender/kurbo.git", features = [
"serde",
], optional = true }
glam = { version = "^0.22", default-features = false, features = ["scalar-math", "libm"]}

View file

@ -1,7 +1,12 @@
#![no_std]
#[cfg(feature = "async")]
#[cfg(feature = "alloc")]
extern crate alloc;
#[cfg_attr(feature = "log", macro_use)]
#[cfg(feature = "log")]
extern crate log;
#[cfg(feature = "async")]
use alloc::boxed::Box;
#[cfg(feature = "async")]
@ -17,6 +22,9 @@ pub mod gpu;
pub mod raster;
#[cfg(feature = "alloc")]
pub mod vector;
pub trait Node<T> {
type Output;

View file

@ -1,7 +1,7 @@
use crate::Node;
pub mod color;
use self::color::Color;
pub use self::color::Color;
#[derive(Debug, Clone, Copy)]
pub struct GrayscaleColorNode;
@ -21,6 +21,12 @@ impl<'n> Node<Color> for &'n GrayscaleColorNode {
}
}
impl GrayscaleColorNode {
pub fn new() -> Self {
Self
}
}
#[derive(Debug, Clone, Copy)]
pub struct BrightenColorNode<N: Node<(), Output = f32>>(N);
@ -48,8 +54,10 @@ impl<N: Node<(), Output = f32> + Copy> BrightenColorNode<N> {
}
#[derive(Debug, Clone, Copy)]
#[cfg(not(target_arch = "spirv"))]
pub struct HueShiftColorNode<N: Node<(), Output = f32>>(N);
#[cfg(not(target_arch = "spirv"))]
impl<N: Node<(), Output = f32>> Node<Color> for HueShiftColorNode<N> {
type Output = Color;
fn eval(self, color: Color) -> Color {
@ -58,6 +66,7 @@ impl<N: Node<(), Output = f32>> Node<Color> for HueShiftColorNode<N> {
Color::from_hsla(hue + hue_shift / 360., saturation, lightness, alpha)
}
}
#[cfg(not(target_arch = "spirv"))]
impl<N: Node<(), Output = f32> + Copy> Node<Color> for &HueShiftColorNode<N> {
type Output = Color;
fn eval(self, color: Color) -> Color {
@ -67,6 +76,7 @@ impl<N: Node<(), Output = f32> + Copy> Node<Color> for &HueShiftColorNode<N> {
}
}
#[cfg(not(target_arch = "spirv"))]
impl<N: Node<(), Output = f32> + Copy> HueShiftColorNode<N> {
pub fn new(node: N) -> Self {
Self(node)
@ -85,6 +95,48 @@ where
}
}
#[cfg(feature = "alloc")]
pub use image::Image;
#[cfg(feature = "alloc")]
mod image {
use super::Color;
use alloc::vec::Vec;
use dyn_any::{DynAny, StaticType};
#[derive(Clone, Debug, PartialEq, DynAny, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Image {
pub width: u32,
pub height: u32,
pub data: Vec<Color>,
}
impl Image {
pub const fn empty() -> Self {
Self {
width: 0,
height: 0,
data: Vec::new(),
}
}
}
impl IntoIterator for Image {
type Item = Color;
type IntoIter = alloc::vec::IntoIter<Color>;
fn into_iter(self) -> Self::IntoIter {
self.data.into_iter()
}
}
impl<'a> IntoIterator for &'a Image {
type Item = &'a Color;
type IntoIter = alloc::slice::Iter<'a, Color>;
fn into_iter(self) -> Self::IntoIter {
self.data.iter()
}
}
}
/*pub struct MutWrapper<N>(pub N);
impl<'n, T: Clone, N> Node<&'n mut T> for &'n MutWrapper<N>

View file

@ -3,13 +3,23 @@ use dyn_any::{DynAny, StaticType};
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
#[cfg(target_arch = "spirv")]
use spirv_std::num_traits::float::Float;
#[cfg(target_arch = "spirv")]
use spirv_std::num_traits::Euclid;
#[cfg(feature = "gpu")]
use bytemuck::{Pod, Zeroable};
/// Structure that represents a color.
/// Internally alpha is stored as `f32` that ranges from `0.0` (transparent) to `1.0` (opaque).
/// The other components (RGB) are stored as `f32` that range from `0.0` up to `f32::MAX`,
/// the values encode the brightness of each channel proportional to the light intensity in cd/m² (nits) in HDR, and `0.0` (black) to `1.0` (white) in SDR color.
#[repr(C)]
#[cfg_attr(feature = "std", derive(Debug, Clone, Copy, PartialEq, Default, Serialize, Deserialize, DynAny))]
#[cfg_attr(not(feature = "std"), derive(Debug, Clone, Copy, PartialEq, Default))]
#[cfg_attr(feature = "std", derive(Serialize, Deserialize, DynAny))]
#[cfg_attr(feature = "gpu", derive(Pod, Zeroable))]
#[derive(Debug, Clone, Copy, PartialEq, Default)]
pub struct Color {
red: f32,
green: f32,
@ -92,6 +102,7 @@ impl Color {
/// use graphene_core::raster::color::Color;
/// let color = Color::from_hsla(0.5, 0.2, 0.3, 1.);
/// ```
#[cfg(not(target_arch = "spirv"))]
pub fn from_hsla(hue: f32, saturation: f32, lightness: f32, alpha: f32) -> Color {
let temp1 = if lightness < 0.5 {
lightness * (saturation + 1.)
@ -219,7 +230,10 @@ impl Color {
} else {
4. + (self.red - self.green) / (max_channel - min_channel)
} / 6.;
#[cfg(not(target_arch = "spirv"))]
let hue = hue.rem_euclid(1.);
#[cfg(target_arch = "spirv")]
let hue = hue.rem_euclid(&1.);
[hue, saturation, lightness, self.alpha]
}

View file

@ -1,4 +1,4 @@
use std::ops::{Index, IndexMut};
use core::ops::{Index, IndexMut};
use serde::{Deserialize, Serialize};

View file

@ -1,7 +1,5 @@
use std::sync::Arc;
use crate::Node;
use glam::{DAffine2, DVec2};
use graphene_core::Node;
use super::subpath::Subpath;
@ -40,8 +38,10 @@ impl Node<()> for &UnitSquareGenerator {
}
}
// TODO: I removed the Arc requirement we shouuld think about when it makes sense to use its
// vs making a generic value node
#[derive(Debug, Clone)]
pub struct PathGenerator(Arc<Subpath>);
pub struct PathGenerator(Subpath);
impl Node<()> for PathGenerator {
type Output = VectorData;
@ -53,7 +53,7 @@ impl Node<()> for PathGenerator {
impl Node<()> for &PathGenerator {
type Output = VectorData;
fn eval(self, _input: ()) -> Self::Output {
(*self.0).clone()
(self.0).clone()
}
}
use crate::raster::Image;
@ -65,7 +65,7 @@ impl<N: Node<(), Output = Subpath>> Node<Image> for BlitSubpath<N> {
type Output = Image;
fn eval(self, input: Image) -> Self::Output {
let subpath = self.0.eval(());
info!("Blitting subpath {subpath:?}");
log::info!("Blitting subpath {subpath:?}");
input
}
}
@ -74,7 +74,7 @@ impl<N: Node<(), Output = Subpath> + Copy> Node<Image> for &BlitSubpath<N> {
type Output = Image;
fn eval(self, input: Image) -> Self::Output {
let subpath = self.0.eval(());
info!("Blitting subpath {subpath:?}");
log::info!("Blitting subpath {subpath:?}");
input
}
}

View file

@ -1,5 +1,8 @@
use core::ops::{Deref, DerefMut};
use serde::{Deserialize, Serialize};
use std::ops::{Deref, DerefMut};
use alloc::vec;
use alloc::vec::Vec;
/// Brief description: A vec that allows indexing elements by both index and an assigned unique ID
/// Goals of this Data Structure:
@ -121,7 +124,7 @@ impl<T> IdBackedVec<T> {
}
/// Enumerate the ids and elements in this container `(&ElementId, &T)`
pub fn enumerate(&self) -> std::iter::Zip<core::slice::Iter<u64>, core::slice::Iter<T>> {
pub fn enumerate(&self) -> core::iter::Zip<core::slice::Iter<u64>, core::slice::Iter<T>> {
self.element_ids.iter().zip(self.elements.iter())
}

View file

@ -3,6 +3,9 @@ use super::id_vec::IdBackedVec;
use super::manipulator_group::ManipulatorGroup;
use super::manipulator_point::ManipulatorPoint;
use alloc::string::String;
use alloc::vec;
use alloc::vec::Vec;
use dyn_any::{DynAny, StaticType};
use glam::{DAffine2, DVec2};
use kurbo::{BezPath, PathEl, Shape};
@ -92,7 +95,7 @@ impl Subpath {
pub fn new_ngon(center: DVec2, sides: u64, radius: f64) -> Self {
let mut manipulator_groups = vec![];
for i in 0..sides {
let angle = (i as f64) * std::f64::consts::TAU / (sides as f64);
let angle = (i as f64) * core::f64::consts::TAU / (sides as f64);
let center = center + DVec2::ONE * radius;
let position = ManipulatorGroup::new_with_anchor(DVec2::new(center.x + radius * f64::cos(angle), center.y + radius * f64::sin(angle)) * 0.5);
@ -346,7 +349,7 @@ impl Subpath {
/// Generate an SVG `path` elements's `d` attribute: `<path d="...">`.
pub fn to_svg(&mut self) -> String {
fn write_positions(result: &mut String, values: [Option<DVec2>; 3]) {
use std::fmt::Write;
use core::fmt::Write;
let count = values.into_iter().flatten().count();
for (index, pos) in values.into_iter().flatten().enumerate() {
write!(result, "{},{}", pos.x, pos.y).unwrap();
@ -442,7 +445,7 @@ impl BezierId {
/// An iterator over [`bezier_rs::Bezier`] segments constructable via [`Subpath::bezier_iter`].
pub struct PathIter<'a> {
path: std::iter::Zip<core::slice::Iter<'a, u64>, core::slice::Iter<'a, ManipulatorGroup>>,
path: core::iter::Zip<core::slice::Iter<'a, u64>, core::slice::Iter<'a, ManipulatorGroup>>,
last_anchor: Option<DVec2>,
last_out_handle: Option<DVec2>,

View file

@ -8,13 +8,12 @@ license = "MIT OR Apache-2.0"
default = []
profiling = ["nvtx", "gpu"]
gpu = ["serde", "vulkano", "spirv-builder", "tera", "graphene-core/gpu"]
serde = ["dep:serde", "graphene-std/serde", "glam/serde"]
serde = ["dep:serde", "graphene-core/serde", "glam/serde"]
# 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-std = { path = "../gstd" }
graphene-core = { path = "../gcore", features = ["async", "std", "alloc"] }
dyn-any = { path = "../../libraries/dyn-any", features = ["log-bad-types", "rc", "glam"] }
num-traits = "0.2"
borrow_stack = { path = "../borrow_stack" }
@ -22,7 +21,7 @@ dyn-clone = "1.0"
rand_chacha = "0.3.1"
log = "0.4"
serde = { version = "1", features = ["derive", "rc"], optional = true }
glam = { version = "0.17" }
glam = { version = "0.22" }
vulkano = {git = "https://github.com/GraphiteEditor/vulkano", branch = "fix_rust_gpu", optional = true}
bytemuck = {version = "1.8" }

View file

@ -5,6 +5,7 @@ use std::sync::Mutex;
pub mod value;
use dyn_any::{DynAny, StaticType};
use rand_chacha::{
rand_core::{RngCore, SeedableRng},
ChaCha20Rng,
@ -138,7 +139,7 @@ pub enum DocumentNodeImplementation {
Unresolved(NodeIdentifier),
}
#[derive(Clone, Debug, Default, PartialEq)]
#[derive(Clone, Debug, Default, PartialEq, DynAny)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct NodeNetwork {
pub inputs: Vec<NodeId>,

View file

@ -15,10 +15,10 @@ pub enum TaggedValue {
F64(f64),
Bool(bool),
DVec2(DVec2),
Image(graphene_std::raster::Image),
Image(graphene_core::raster::Image),
Color(graphene_core::raster::color::Color),
Subpath(graphene_std::vector::subpath::Subpath),
RcSubpath(Arc<graphene_std::vector::subpath::Subpath>),
Subpath(graphene_core::vector::subpath::Subpath),
RcSubpath(Arc<graphene_core::vector::subpath::Subpath>),
}
impl TaggedValue {

View file

@ -1,10 +1,8 @@
use std::error::Error;
use borrow_stack::{BorrowStack, FixedSizeStack};
use graphene_core::Node;
use graphene_std::any::{Any, TypeErasedNode};
use dyn_any::DynAny;
use crate::{document::NodeNetwork, node_registry::push_node, proto::ProtoNetwork};
use crate::{document::NodeNetwork, proto::ProtoNetwork};
pub struct Compiler {}
@ -25,30 +23,8 @@ impl Compiler {
proto_network
}
}
pub type Any<'a> = Box<dyn DynAny<'a> + 'a>;
pub trait Executor {
fn execute(&self, input: Any<'static>) -> Result<Any<'static>, Box<dyn Error>>;
}
pub struct DynamicExecutor {
stack: FixedSizeStack<TypeErasedNode<'static>>,
}
impl DynamicExecutor {
pub fn new(proto_network: ProtoNetwork) -> Self {
assert_eq!(proto_network.inputs.len(), 1);
let node_count = proto_network.nodes.len();
let stack = FixedSizeStack::new(node_count);
for (_id, node) in proto_network.nodes {
push_node(node, &stack);
}
Self { stack }
}
}
impl Executor for DynamicExecutor {
fn execute(&self, input: Any<'static>) -> Result<Any<'static>, Box<dyn Error>> {
let result = unsafe { self.stack.get().last().unwrap().eval(input) };
Ok(result)
}
}

View file

@ -30,6 +30,10 @@ pub fn create_files(matadata: &Metadata, network: &ProtoNetwork, compile_dir: &P
let cargo_toml = create_cargo_toml(matadata)?;
std::fs::write(cargo_file, cargo_toml)?;
let toolchain_file = compile_dir.join("rust-toolchain");
let toolchain = include_str!("templates/rust-toolchain");
std::fs::write(toolchain_file, toolchain)?;
// create src dir
match std::fs::create_dir(&src) {
Ok(_) => {}

View file

@ -1,7 +1,7 @@
use std::path::Path;
use super::{compiler::Metadata, context::Context};
use crate::gpu::compiler;
use crate::{executor::Any, gpu::compiler};
use bytemuck::Pod;
use dyn_any::StaticTypeSized;
use vulkano::{
@ -44,7 +44,7 @@ impl<I: StaticTypeSized, O> GpuExecutor<I, O> {
}
impl<I: StaticTypeSized + Sync + Pod + Send, O: StaticTypeSized + Send + Sync + Pod> crate::executor::Executor for GpuExecutor<I, O> {
fn execute(&self, input: graphene_std::any::Any<'static>) -> Result<graphene_std::any::Any<'static>, Box<dyn std::error::Error>> {
fn execute(&self, input: Any<'static>) -> Result<Any<'static>, Box<dyn std::error::Error>> {
let input = dyn_any::downcast::<Vec<I>>(input).expect("Wrong input type");
let context = &self.context;
let result: Vec<O> = execute_shader(
@ -91,7 +91,7 @@ fn execute_shader<I: Pod + Send + Sync, O: Pod + Send + Sync>(
.bind_pipeline_compute(compute_pipeline.clone())
.bind_descriptor_sets(PipelineBindPoint::Compute, compute_pipeline.layout().clone(), 0, set)
.push_constants(compute_pipeline.layout().clone(), 0, constants)
.dispatch([1024, 1, 1])
.dispatch([((constants.n as isize - 1) / 1024 + 1) as u32 * 1024, 1, 1])
.unwrap();
let command_buffer = builder.build().unwrap();

View file

@ -1,8 +1,6 @@
#[macro_use]
extern crate log;
pub mod node_registry;
pub mod document;
pub mod proto;
@ -10,124 +8,3 @@ pub mod executor;
#[cfg(feature = "gpu")]
pub mod gpu;
#[cfg(test)]
mod tests {
use std::marker::PhantomData;
use graphene_core::value::ValueNode;
use graphene_core::{structural::*, RefNode};
use borrow_stack::BorrowStack;
use dyn_any::{downcast, IntoDynAny};
use graphene_std::any::{Any, DowncastNode, DynAnyNode, TypeErasedNode};
use graphene_std::ops::AddNode;
#[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::new(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);
}
#[test]
fn execute_add() {
use crate::document::*;
use crate::proto::*;
fn add_network() -> NodeNetwork {
NodeNetwork {
inputs: vec![0, 0],
output: 1,
nodes: [
(
0,
DocumentNode {
name: "Cons".into(),
inputs: vec![NodeInput::Network, NodeInput::Network],
implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new(
"graphene_core::structural::ConsNode",
&[Type::Concrete(std::borrow::Cow::Borrowed("u32")), Type::Concrete(std::borrow::Cow::Borrowed("u32"))],
)),
metadata: DocumentNodeMetadata::default(),
},
),
(
1,
DocumentNode {
name: "Add".into(),
inputs: vec![NodeInput::Node(0)],
implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new(
"graphene_core::ops::AddNode",
&[Type::Concrete(std::borrow::Cow::Borrowed("u32")), Type::Concrete(std::borrow::Cow::Borrowed("u32"))],
)),
metadata: DocumentNodeMetadata::default(),
},
),
]
.into_iter()
.collect(),
}
}
let network = NodeNetwork {
inputs: vec![0],
output: 0,
nodes: [(
0,
DocumentNode {
name: "Inc".into(),
inputs: vec![
NodeInput::Network,
NodeInput::Value {
tagged_value: value::TaggedValue::U32(1),
exposed: false,
},
],
implementation: DocumentNodeImplementation::Network(add_network()),
metadata: DocumentNodeMetadata::default(),
},
)]
.into_iter()
.collect(),
};
use crate::executor::{Compiler, DynamicExecutor, Executor};
let compiler = Compiler {};
let protograph = compiler.compile(network, false);
let exec = DynamicExecutor::new(protograph);
let result = exec.execute(32_u32.into_dyn()).unwrap();
let val = *dyn_any::downcast::<u32>(result).unwrap();
assert_eq!(val, 33_u32);
}
}

View file

@ -321,17 +321,12 @@ impl ProtoNetwork {
}
fn replace_node_references(&mut self, lookup: &HashMap<u64, u64>) {
self.nodes.iter_mut().for_each(|(sid, node)| {
self.nodes.iter_mut().for_each(|(_, node)| {
node.map_ids(|id| *lookup.get(&id).expect("node not found in lookup table"));
});
self.inputs = self.inputs.iter().map(|id| *lookup.get(id).unwrap()).collect();
self.output = *lookup.get(&self.output).unwrap();
}
fn replace_ids_with_lookup(&mut self, lookup: HashMap<u64, u64>) {
self.nodes.iter_mut().for_each(|(id, _)| *id = *lookup.get(id).unwrap());
self.replace_node_references(&lookup);
}
}
#[cfg(test)]

View file

@ -12,13 +12,17 @@ license = "MIT OR Apache-2.0"
derive = ["graph-proc-macros"]
memoization = ["once_cell"]
default = ["derive", "memoization"]
gpu = ["graph-craft/gpu", "graphene-core/gpu"]
[dependencies]
graphene-core = {path = "../gcore", features = ["async", "std"], default-features = false}
graphene-core = {path = "../gcore", features = ["async", "std" ], default-features = false}
borrow_stack = {path = "../borrow_stack"}
dyn-any = {path = "../../libraries/dyn-any", features = ["derive"]}
graph-proc-macros = {path = "../proc-macro", optional = true}
graph-craft = {path = "../graph-craft"}
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"]}
@ -32,7 +36,7 @@ bezier-rs = { path = "../../libraries/bezier-rs" }
kurbo = { git = "https://github.com/linebender/kurbo.git", features = [
"serde",
] }
glam = { version = "0.17", features = ["serde"] }
glam = { version = "0.22", features = ["serde"] }
[dependencies.serde]
version = "1.0"

View file

@ -30,7 +30,9 @@ where
input.borrow().hash(&mut hasher);
let hash = hasher.finish();
self.map.get_or_create_with(&hash, || CacheNode::new(self.node))
self.map.get_or_create_with(&hash, ||{
trace!("Creating new cache node");
CacheNode::new(self.node)})
}
}

View file

@ -0,0 +1,135 @@
use bytemuck::Pod;
use core::marker::PhantomData;
use dyn_any::StaticTypeSized;
use graph_craft::document::*;
use graph_craft::proto::*;
use graphene_core::{raster::Image, value::ValueNode, Node};
pub struct MapGpuNode<NN: Node<()>, I: IntoIterator<Item = S>, S: StaticTypeSized + Sync + Send + Pod, O: StaticTypeSized + Sync + Send + Pod>(pub NN, PhantomData<(S, I, O)>);
impl<'n, I: IntoIterator<Item = S>, NN: Node<(), Output = &'n NodeNetwork> + Copy, S: StaticTypeSized + Sync + Send + Pod, O: StaticTypeSized + Sync + Send + Pod> Node<I>
for &MapGpuNode<NN, I, S, O>
{
type Output = Vec<O>;
fn eval(self, input: I) -> Self::Output {
let network = self.0.eval(());
use graph_craft::executor::Compiler;
use graph_craft::executor::Executor;
use graph_craft::gpu::compiler::Metadata;
let compiler = Compiler {};
let proto_network = compiler.compile(network.clone(), true);
let m = Metadata::new("project".to_owned(), vec!["test@example.com".to_owned()]);
let temp_dir = tempfile::tempdir().expect("failed to create tempdir");
use graph_craft::gpu::context::Context;
use graph_craft::gpu::executor::GpuExecutor;
let executor: GpuExecutor<S, O> = GpuExecutor::new(Context::new(), proto_network, m, temp_dir.path()).unwrap();
let data: Vec<_> = input.into_iter().collect();
let result = executor.execute(Box::new(data)).unwrap();
let result = dyn_any::downcast::<Vec<O>>(result).unwrap();
*result
}
}
impl<'n, I: IntoIterator<Item = S>, NN: Node<(), Output = &'n NodeNetwork> + Copy, S: StaticTypeSized + Sync + Send + Pod, O: StaticTypeSized + Sync + Send + Pod> Node<I> for MapGpuNode<NN, I, S, O> {
type Output = Vec<O>;
fn eval(self, input: I) -> Self::Output {
let network = self.0.eval(());
use graph_craft::executor::Compiler;
use graph_craft::executor::Executor;
use graph_craft::gpu::compiler::Metadata;
let compiler = Compiler {};
let proto_network = compiler.compile(network.clone(), true);
let m = Metadata::new("project".to_owned(), vec!["test@example.com".to_owned()]);
let temp_dir = tempfile::tempdir().expect("failed to create tempdir");
use graph_craft::gpu::context::Context;
use graph_craft::gpu::executor::GpuExecutor;
let executor: GpuExecutor<S, O> = GpuExecutor::new(Context::new(), proto_network, m, temp_dir.path()).unwrap();
let data: Vec<_> = input.into_iter().collect();
let result = executor.execute(Box::new(data)).unwrap();
let result = dyn_any::downcast::<Vec<O>>(result).unwrap();
*result
}
}
impl<I: IntoIterator<Item = S>, NN: Node<()>, S: StaticTypeSized + Sync + Pod + Send, O: StaticTypeSized + Sync + Send + Pod> MapGpuNode<NN, I, S, O> {
pub const fn new(network: NN) -> Self {
MapGpuNode(network, PhantomData)
}
}
pub struct MapGpuSingleImageNode<NN: Node<(), Output = String>>(pub NN);
impl<NN: Node<(), Output = String> + Copy> Node<Image> for MapGpuSingleImageNode<NN> {
type Output = Image;
fn eval(self, input: Image) -> Self::Output {
let node = self.0.eval(());
use graph_craft::document::*;
let identifier = NodeIdentifier {
name: std::borrow::Cow::Owned(node),
types: std::borrow::Cow::Borrowed(&[]),
};
let network = NodeNetwork {
inputs: vec![0],
output: 0,
nodes: [(
0,
DocumentNode {
name: "Image filter Node".into(),
inputs: vec![NodeInput::Network],
implementation: DocumentNodeImplementation::Unresolved(identifier),
metadata: DocumentNodeMetadata::default(),
},
)]
.into_iter()
.collect(),
};
let value_network = ValueNode::new(network);
let map_node = MapGpuNode::new(&value_network);
let data = map_node.eval(input.data.clone());
Image { data, ..input }
}
}
impl<NN: Node<(), Output = String> + Copy> Node<Image> for &MapGpuSingleImageNode<NN> {
type Output = Image;
fn eval(self, input: Image) -> Self::Output {
let node = self.0.eval(());
use graph_craft::document::*;
let identifier = NodeIdentifier {
name: std::borrow::Cow::Owned(node),
types: std::borrow::Cow::Borrowed(&[]),
};
let network = NodeNetwork {
inputs: vec![0],
output: 0,
nodes: [(
0,
DocumentNode {
name: "Image filter Node".into(),
inputs: vec![NodeInput::Network],
implementation: DocumentNodeImplementation::Unresolved(identifier),
metadata: DocumentNodeMetadata::default(),
},
)]
.into_iter()
.collect(),
};
let value_network = ValueNode::new(network);
let map_node = MapGpuNode::new(&value_network);
let data = map_node.eval(input.data.clone());
Image { data, ..input }
}
}

View file

@ -9,8 +9,10 @@ extern crate log;
pub mod memo;
pub mod raster;
pub mod vector;
pub mod any;
#[cfg(feature = "gpu")]
pub mod executor;
pub use graphene_core::*;

View file

@ -9,7 +9,10 @@ pub struct CacheNode<CachedNode: Node<I>, I> {
impl<'n, CashedNode: Node<I> + Copy, I> Node<I> for &'n CacheNode<CashedNode, I> {
type Output = &'n CashedNode::Output;
fn eval(self, input: I) -> Self::Output {
self.cache.get_or_init(|| self.node.eval(input))
self.cache.get_or_init(|| {
trace!("Creating new cache node");
self.node.eval(input)
})
}
}

View file

@ -1,7 +1,7 @@
use core::marker::PhantomData;
use dyn_any::{DynAny, StaticType};
use graphene_core::ops::FlatMapResultNode;
use graphene_core::raster::color::Color;
use graphene_core::raster::{Color, Image};
use graphene_core::structural::{ComposeNode, ConsNode};
use graphene_core::{generic::FnNode, ops::MapResultNode, structural::Then, value::ValueNode, Node};
use image::Pixel;
@ -98,40 +98,6 @@ impl<Reader: std::io::Read> Node<Reader> for BufferNode {
}
}
#[derive(Clone, Debug, PartialEq, DynAny, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Image {
pub width: u32,
pub height: u32,
pub data: Vec<Color>,
}
impl Image {
pub const fn empty() -> Self {
Self {
width: 0,
height: 0,
data: Vec::new(),
}
}
}
impl IntoIterator for Image {
type Item = Color;
type IntoIter = std::vec::IntoIter<Color>;
fn into_iter(self) -> Self::IntoIter {
self.data.into_iter()
}
}
impl<'a> IntoIterator for &'a Image {
type Item = &'a Color;
type IntoIter = std::slice::Iter<'a, Color>;
fn into_iter(self) -> Self::IntoIter {
self.data.iter()
}
}
pub fn file_node<'n, P: AsRef<Path> + 'n>() -> impl Node<P, Output = Result<Vec<u8>, Error>> {
let fs = ValueNode(StdFs).clone();
let fs = ConsNode::new(fs);

View file

@ -0,0 +1,25 @@
[package]
name = "interpreted-executor"
version = "0.1.0"
edition = "2021"
license = "MIT OR Apache-2.0"
[features]
default = []
serde = ["dep:serde", "graphene-std/serde", "glam/serde"]
gpu = ["graphene-std/gpu"]
# 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-std = { path = "../gstd" }
graph-craft = { path = "../graph-craft" }
dyn-any = { path = "../../libraries/dyn-any", features = ["log-bad-types"] }
num-traits = "0.2"
borrow_stack = { path = "../borrow_stack" }
dyn-clone = "1.0"
rand_chacha = "0.3.1"
log = "0.4"
serde = { version = "1", features = ["derive"], optional = true }
glam = { version = "0.22" }

View file

@ -0,0 +1,31 @@
use std::error::Error;
use borrow_stack::{BorrowStack, FixedSizeStack};
use graphene_core::Node;
use graphene_std::any::{Any, TypeErasedNode};
use crate::node_registry::push_node;
use graph_craft::{executor::Executor, proto::ProtoNetwork};
pub struct DynamicExecutor {
stack: FixedSizeStack<TypeErasedNode<'static>>,
}
impl DynamicExecutor {
pub fn new(proto_network: ProtoNetwork) -> Self {
assert_eq!(proto_network.inputs.len(), 1);
let node_count = proto_network.nodes.len();
let stack = FixedSizeStack::new(node_count);
for (_id, node) in proto_network.nodes {
push_node(node, &stack);
}
Self { stack }
}
}
impl Executor for DynamicExecutor {
fn execute(&self, input: Any<'static>) -> Result<Any<'static>, Box<dyn Error>> {
let result = unsafe { self.stack.get().last().unwrap().eval(input) };
Ok(result)
}
}

View file

@ -0,0 +1,127 @@
#[macro_use]
extern crate log;
pub mod executor;
pub mod node_registry;
#[cfg(test)]
mod tests {
use std::marker::PhantomData;
use graphene_core::value::ValueNode;
use graphene_core::{structural::*, RefNode};
use borrow_stack::BorrowStack;
use dyn_any::{downcast, IntoDynAny};
use graphene_std::any::{Any, DowncastNode, DynAnyNode, TypeErasedNode};
use graphene_std::ops::AddNode;
#[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::new(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);
}
#[test]
fn execute_add() {
use graph_craft::document::*;
use graph_craft::proto::*;
fn add_network() -> NodeNetwork {
NodeNetwork {
inputs: vec![0, 0],
output: 1,
nodes: [
(
0,
DocumentNode {
name: "Cons".into(),
inputs: vec![NodeInput::Network, NodeInput::Network],
implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new(
"graphene_core::structural::ConsNode",
&[Type::Concrete(std::borrow::Cow::Borrowed("u32")), Type::Concrete(std::borrow::Cow::Borrowed("u32"))],
)),
metadata: DocumentNodeMetadata::default(),
},
),
(
1,
DocumentNode {
name: "Add".into(),
inputs: vec![NodeInput::Node(0)],
implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new(
"graphene_core::ops::AddNode",
&[Type::Concrete(std::borrow::Cow::Borrowed("u32")), Type::Concrete(std::borrow::Cow::Borrowed("u32"))],
)),
metadata: DocumentNodeMetadata::default(),
},
),
]
.into_iter()
.collect(),
}
}
let network = NodeNetwork {
inputs: vec![0],
output: 0,
nodes: [(
0,
DocumentNode {
name: "Inc".into(),
inputs: vec![
NodeInput::Network,
NodeInput::Value {
tagged_value: value::TaggedValue::U32(1),
exposed: false,
},
],
implementation: DocumentNodeImplementation::Network(add_network()),
metadata: DocumentNodeMetadata::default(),
},
)]
.into_iter()
.collect(),
};
use crate::executor::DynamicExecutor;
use graph_craft::executor::{Compiler, Executor};
let compiler = Compiler {};
let protograph = compiler.compile(network, false);
let exec = DynamicExecutor::new(protograph);
let result = exec.execute(32_u32.into_dyn()).unwrap();
let val = *dyn_any::downcast::<u32>(result).unwrap();
assert_eq!(val, 33_u32);
}
}

View file

@ -3,19 +3,19 @@ use glam::DVec2;
use graphene_core::generic::FnNode;
use graphene_core::ops::{AddNode, IdNode};
use graphene_core::raster::color::Color;
use graphene_core::raster::Image;
use graphene_core::structural::{ConsNode, Then};
use graphene_core::vector::subpath::Subpath;
use graphene_core::Node;
use graphene_std::any::DowncastBothNode;
use graphene_std::any::{Any, DowncastNode, DynAnyNode, IntoTypeErasedNode, TypeErasedNode};
use graphene_std::raster::Image;
use graphene_std::vector::subpath::Subpath;
use crate::proto::Type;
use crate::proto::{ConstructionArgs, NodeIdentifier, ProtoNode, ProtoNodeInput};
use graph_craft::proto::Type;
use graph_craft::proto::{ConstructionArgs, NodeIdentifier, ProtoNode, ProtoNodeInput};
type NodeConstructor = fn(ProtoNode, &FixedSizeStack<TypeErasedNode<'static>>);
use crate::{concrete, generic};
use graph_craft::{concrete, generic};
//TODO: turn into hasmap
static NODE_REGISTRY: &[(NodeIdentifier, NodeConstructor)] = &[
@ -25,7 +25,7 @@ static NODE_REGISTRY: &[(NodeIdentifier, NodeConstructor)] = &[
let pre_node = nodes.get(pre_id as usize).unwrap();
pre_node.into_type_erased()
} else {
graphene_core::ops::IdNode.into_type_erased()
IdNode.into_type_erased()
}
})
}),
@ -35,7 +35,7 @@ static NODE_REGISTRY: &[(NodeIdentifier, NodeConstructor)] = &[
let pre_node = nodes.get(pre_id as usize).unwrap();
pre_node.into_type_erased()
} else {
graphene_core::ops::IdNode.into_type_erased()
IdNode.into_type_erased()
}
})
}),
@ -221,6 +221,54 @@ static NODE_REGISTRY: &[(NodeIdentifier, NodeConstructor)] = &[
}
})
}),
#[cfg(feature = "gpu")]
(
NodeIdentifier::new("graphene_std::executor::MapGpuNode", &[concrete!("&TypeErasedNode"), concrete!("Color"), concrete!("Color")]),
|proto_node, stack| {
if let ConstructionArgs::Nodes(operation_node_id) = proto_node.construction_args {
stack.push_fn(move |nodes| {
info!("Map image Depending upon id {:?}", operation_node_id);
let operation_node = nodes.get(operation_node_id[0] as usize).unwrap();
let input_node: DowncastBothNode<_, (), &graph_craft::document::NodeNetwork> = DowncastBothNode::new(operation_node);
let map_node: graphene_std::executor::MapGpuNode<_, Vec<u32>, u32, u32> = graphene_std::executor::MapGpuNode::new(input_node);
let map_node = DynAnyNode::new(map_node);
if let ProtoNodeInput::Node(node_id) = proto_node.input {
let pre_node = nodes.get(node_id as usize).unwrap();
(pre_node).then(map_node).into_type_erased()
} else {
map_node.into_type_erased()
}
})
} else {
unimplemented!()
}
},
),
#[cfg(feature = "gpu")]
(
NodeIdentifier::new("graphene_std::executor::MapGpuSingleImageNode", &[concrete!("&TypeErasedNode")]),
|proto_node, stack| {
if let ConstructionArgs::Nodes(operation_node_id) = proto_node.construction_args {
stack.push_fn(move |nodes| {
info!("Map image Depending upon id {:?}", operation_node_id);
let operation_node = nodes.get(operation_node_id[0] as usize).unwrap();
let input_node: DowncastBothNode<_, (), String> = DowncastBothNode::new(operation_node);
let map_node = graphene_std::executor::MapGpuSingleImageNode(input_node);
let map_node = DynAnyNode::new(map_node);
if let ProtoNodeInput::Node(node_id) = proto_node.input {
let pre_node = nodes.get(node_id as usize).unwrap();
(pre_node).then(map_node).into_type_erased()
} else {
map_node.into_type_erased()
}
})
} else {
unimplemented!()
}
},
),
(NodeIdentifier::new("graphene_std::raster::MapImageNode", &[]), |proto_node, stack| {
if let ConstructionArgs::Nodes(operation_node_id) = proto_node.construction_args {
stack.push_fn(move |nodes| {
@ -442,7 +490,7 @@ pub fn push_node<'a>(proto_node: ProtoNode, stack: &'a FixedSizeStack<TypeErased
.filter(|id| id.name.as_ref() == proto_node.identifier.name.as_ref())
.collect::<Vec<_>>();
panic!(
"NodeImplementation: {:?} not found in Registry types for which the node is implemented:\n {:#?}",
"NodeImplementation: {:?} not found in Registry. Types for which the node is implemented:\n {:#?}",
proto_node.identifier, other_types
);
}