mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2025-08-03 21:08:18 +00:00
Implement experimental WebGPU support (#1238)
* Web gpu execution MVP Ready infrastructure for wgpu experimentation Start implementing simple gpu test case Fix Extract Node not working with nested networks Convert inputs for extracted node to network inputs Fix missing cors headers Feature gate gcore to make it once again no-std compatible Add skeleton structure gpu shader Work on gpu node graph output saving Fix Get and Set nodes Fix storage nodes Fix shader construction errors -> spirv errors Add unsafe version Add once cell node Web gpu execution MVP
This commit is contained in:
parent
4bd9fbd073
commit
0586d52f3a
33 changed files with 1080 additions and 239 deletions
|
@ -107,6 +107,15 @@ impl<'i, 's: 'i, I: 'i, O: 'i, N: Node<'i, I, Output = O>> Node<'i, I> for &'s N
|
|||
(**self).eval(input)
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "alloc")]
|
||||
impl<'i, 's: 'i, I: 'i, O: 'i, N: Node<'i, I, Output = O>> Node<'i, I> for Box<N> {
|
||||
type Output = O;
|
||||
|
||||
fn eval(&'i self, input: I) -> Self::Output {
|
||||
(**self).eval(input)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'i, I: 'i, O: 'i> Node<'i, I> for &'i dyn for<'a> Node<'a, I, Output = O> {
|
||||
type Output = O;
|
||||
|
||||
|
|
|
@ -192,6 +192,14 @@ pub trait Sample {
|
|||
fn sample(&self, pos: DVec2, area: DVec2) -> Option<Self::Pixel>;
|
||||
}
|
||||
|
||||
impl<'i, T: Sample> Sample for &'i T {
|
||||
type Pixel = T::Pixel;
|
||||
|
||||
fn sample(&self, pos: DVec2, area: DVec2) -> Option<Self::Pixel> {
|
||||
(**self).sample(pos, area)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: We might rename this to Bitmap at some point
|
||||
pub trait Raster {
|
||||
type Pixel: Pixel;
|
||||
|
@ -200,6 +208,38 @@ pub trait Raster {
|
|||
fn get_pixel(&self, x: u32, y: u32) -> Option<Self::Pixel>;
|
||||
}
|
||||
|
||||
impl<'i, T: Raster> Raster for &'i T {
|
||||
type Pixel = T::Pixel;
|
||||
|
||||
fn width(&self) -> u32 {
|
||||
(**self).width()
|
||||
}
|
||||
|
||||
fn height(&self) -> u32 {
|
||||
(**self).height()
|
||||
}
|
||||
|
||||
fn get_pixel(&self, x: u32, y: u32) -> Option<Self::Pixel> {
|
||||
(**self).get_pixel(x, y)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'i, T: Raster> Raster for &'i mut T {
|
||||
type Pixel = T::Pixel;
|
||||
|
||||
fn width(&self) -> u32 {
|
||||
(**self).width()
|
||||
}
|
||||
|
||||
fn height(&self) -> u32 {
|
||||
(**self).height()
|
||||
}
|
||||
|
||||
fn get_pixel(&self, x: u32, y: u32) -> Option<Self::Pixel> {
|
||||
(**self).get_pixel(x, y)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait RasterMut: Raster {
|
||||
fn get_pixel_mut(&mut self, x: u32, y: u32) -> Option<&mut Self::Pixel>;
|
||||
fn set_pixel(&mut self, x: u32, y: u32, pixel: Self::Pixel) {
|
||||
|
@ -215,6 +255,12 @@ pub trait RasterMut: Raster {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'i, T: RasterMut + Raster> RasterMut for &'i mut T {
|
||||
fn get_pixel_mut(&mut self, x: u32, y: u32) -> Option<&mut Self::Pixel> {
|
||||
(*self).get_pixel_mut(x, y)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct MapNode<MapFn> {
|
||||
map_fn: MapFn,
|
||||
|
|
|
@ -363,6 +363,19 @@ fn invert_image(color: Color) -> Color {
|
|||
color.to_linear_srgb()
|
||||
}
|
||||
|
||||
// TODO replace with trait based implementation
|
||||
impl<'i> Node<'i, &'i Color> for InvertRGBNode {
|
||||
type Output = Color;
|
||||
|
||||
fn eval(&'i self, color: &'i Color) -> Self::Output {
|
||||
let color = color.to_gamma_srgb();
|
||||
|
||||
let color = color.map_rgb(|c| color.a() - c);
|
||||
|
||||
color.to_linear_srgb()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct ThresholdNode<MinLuminance, MaxLuminance, LuminanceCalc> {
|
||||
min_luminance: MinLuminance,
|
||||
|
|
|
@ -1,34 +1,158 @@
|
|||
use crate::Node;
|
||||
|
||||
use core::cell::RefMut;
|
||||
use core::marker::PhantomData;
|
||||
use core::ops::{DerefMut, Index, IndexMut};
|
||||
use core::ops::{Deref, DerefMut, Index, IndexMut};
|
||||
|
||||
struct SetNode<S, I, Storage, Index> {
|
||||
pub struct SetNode<Storage> {
|
||||
storage: Storage,
|
||||
index: Index,
|
||||
_s: PhantomData<S>,
|
||||
_i: PhantomData<I>,
|
||||
}
|
||||
|
||||
#[node_macro::node_fn(SetNode<_S, _I>)]
|
||||
fn set_node<T, _S, _I>(value: T, storage: &'input mut _S, index: _I)
|
||||
/*
|
||||
#[node_macro::node_fn(SetNode)]
|
||||
fn set_node<_T, _I, A: 'input>(input: (_T, _I), mut storage: RefMut<'input, A>)
|
||||
where
|
||||
_S: IndexMut<_I>,
|
||||
_S::Output: DerefMut<Target = T> + Sized,
|
||||
A: DerefMut,
|
||||
A::Target: IndexMut<_I, Output = _T>,
|
||||
{
|
||||
*storage.index_mut(index).deref_mut() = value;
|
||||
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,
|
||||
A::Target: IndexMut<I, Output = T>,
|
||||
S0: for<'any_input> Node<'input, (), Output = A>,
|
||||
{
|
||||
type Output = ();
|
||||
#[inline]
|
||||
fn eval(&'input self, input: (T, I)) -> Self::Output {
|
||||
let mut storage = self.storage.eval(());
|
||||
let (value, index) = input;
|
||||
*storage.deref_mut().index_mut(index).deref_mut() = value;
|
||||
}
|
||||
}
|
||||
impl<'input, S0: 'input> SetNode<S0> {
|
||||
pub const fn new(storage: S0) -> Self {
|
||||
Self { storage }
|
||||
}
|
||||
}
|
||||
|
||||
struct GetNode<S, Storage> {
|
||||
pub struct ExtractXNode {}
|
||||
|
||||
#[node_macro::node_fn(ExtractXNode)]
|
||||
fn extract_x_node(input: glam::UVec3) -> usize {
|
||||
input.x as usize
|
||||
}
|
||||
|
||||
pub struct SetOwnedNode<Storage> {
|
||||
storage: core::cell::RefCell<Storage>,
|
||||
}
|
||||
|
||||
impl<Storage> SetOwnedNode<Storage> {
|
||||
pub fn new(storage: Storage) -> Self {
|
||||
Self {
|
||||
storage: core::cell::RefCell::new(storage),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'input, I: 'input, T: 'input, Storage, A: ?Sized> Node<'input, (T, I)> for SetOwnedNode<Storage>
|
||||
where
|
||||
Storage: DerefMut<Target = A> + 'input,
|
||||
A: IndexMut<I, Output = T> + 'input,
|
||||
{
|
||||
type Output = ();
|
||||
fn eval(&'input self, input: (T, I)) -> Self::Output {
|
||||
let (value, index) = input;
|
||||
*self.storage.borrow_mut().index_mut(index) = value;
|
||||
}
|
||||
}
|
||||
|
||||
pub struct GetNode<Storage> {
|
||||
storage: Storage,
|
||||
_s: PhantomData<S>,
|
||||
}
|
||||
|
||||
#[node_macro::node_fn(GetNode<_S>)]
|
||||
fn get_node<_S, I>(index: I, storage: &'input _S) -> &'input _S::Output
|
||||
where
|
||||
_S: Index<I>,
|
||||
_S::Output: Sized,
|
||||
{
|
||||
storage.index(index)
|
||||
impl<Storage> GetNode<Storage> {
|
||||
pub fn new(storage: Storage) -> Self {
|
||||
Self { storage }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'input, I: 'input, T: 'input, Storage, SNode, A: ?Sized> Node<'input, I> for GetNode<SNode>
|
||||
where
|
||||
SNode: Node<'input, (), Output = Storage>,
|
||||
Storage: Deref<Target = A> + 'input,
|
||||
A: Index<I, Output = T> + 'input,
|
||||
T: Clone,
|
||||
{
|
||||
type Output = T;
|
||||
fn eval(&'input self, index: I) -> Self::Output {
|
||||
let storage = self.storage.eval(());
|
||||
storage.deref().index(index).deref().clone()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::value::{CopiedNode, OnceCellNode, RefCellMutNode, UnsafeMutValueNode, ValueNode};
|
||||
use crate::Node;
|
||||
|
||||
use super::*;
|
||||
#[test]
|
||||
fn get_node_array() {
|
||||
let storage = [1, 2, 3];
|
||||
let node = GetNode::new(CopiedNode::new(&storage));
|
||||
assert_eq!((&node as &dyn Node<'_, usize, Output = i32>).eval(1), 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn get_node_vec() {
|
||||
let storage = vec![1, 2, 3];
|
||||
let node = GetNode::new(CopiedNode::new(&storage));
|
||||
assert_eq!(node.eval(1), 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn get_node_slice() {
|
||||
let storage: &[i32] = &[1, 2, 3];
|
||||
let node = GetNode::new(CopiedNode::new(storage));
|
||||
let _ = &node as &dyn Node<'_, usize, Output = i32>;
|
||||
assert_eq!(node.eval(1), 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn set_node_slice() {
|
||||
let mut backing_storage = [1, 2, 3];
|
||||
let storage: &mut [i32] = &mut backing_storage;
|
||||
let storage_node = OnceCellNode::new(storage);
|
||||
let node = SetNode::new(storage_node);
|
||||
node.eval((4, 1));
|
||||
assert_eq!(backing_storage, [1, 4, 3]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn set_owned_node_array() {
|
||||
let mut storage = [1, 2, 3];
|
||||
let node = SetOwnedNode::new(&mut storage);
|
||||
node.eval((4, 1));
|
||||
assert_eq!(storage, [1, 4, 3]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn set_owned_node_vec() {
|
||||
let mut storage = vec![1, 2, 3];
|
||||
let node = SetOwnedNode::new(&mut storage);
|
||||
node.eval((4, 1));
|
||||
assert_eq!(storage, [1, 4, 3]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn set_owned_node_slice() {
|
||||
let mut backing_storage = [1, 2, 3];
|
||||
let storage: &mut [i32] = &mut backing_storage;
|
||||
let node = SetOwnedNode::new(storage);
|
||||
let node = &node as &dyn Node<'_, (i32, usize), Output = ()>;
|
||||
node.eval((4, 1));
|
||||
assert_eq!(backing_storage, [1, 4, 3]);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -39,6 +39,7 @@ pub struct AsyncComposeNode<First, Second, I> {
|
|||
phantom: PhantomData<I>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
impl<'i, 'f: 'i, 's: 'i, Input: 'static, First, Second> Node<'i, Input> for AsyncComposeNode<First, Second, Input>
|
||||
where
|
||||
First: Node<'i, Input>,
|
||||
|
@ -54,6 +55,7 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
impl<'i, First, Second, Input: 'i> AsyncComposeNode<First, Second, Input>
|
||||
where
|
||||
First: Node<'i, Input>,
|
||||
|
@ -77,6 +79,7 @@ pub trait Then<'i, Input: 'i>: Sized {
|
|||
|
||||
impl<'i, First: Node<'i, Input>, Input: 'i> Then<'i, Input> for First {}
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
pub trait AndThen<'i, Input: 'i>: Sized {
|
||||
fn and_then<Second>(self, second: Second) -> AsyncComposeNode<Self, Second, Input>
|
||||
where
|
||||
|
@ -88,6 +91,7 @@ pub trait AndThen<'i, Input: 'i>: Sized {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
impl<'i, First: Node<'i, Input>, Input: 'i> AndThen<'i, Input> for First {}
|
||||
|
||||
pub struct ConsNode<I: From<()>, Root>(pub Root, PhantomData<I>);
|
||||
|
@ -108,6 +112,38 @@ impl<'i, Root: Node<'i, I>, I: 'i + From<()>> ConsNode<I, Root> {
|
|||
}
|
||||
}
|
||||
|
||||
pub struct ApplyNode<O, N> {
|
||||
pub node: N,
|
||||
_o: PhantomData<O>,
|
||||
}
|
||||
/*
|
||||
#[node_macro::node_fn(ApplyNode)]
|
||||
fn apply<In, N>(input: In, node: &'any_input N) -> ()
|
||||
where
|
||||
// TODO: try to allows this to return output other than ()
|
||||
N: for<'any_input> Node<'any_input, In, Output = ()>,
|
||||
{
|
||||
node.eval(input)
|
||||
}
|
||||
*/
|
||||
impl<'input, In: 'input, N: 'input, S0: 'input, O: 'input> Node<'input, In> for ApplyNode<O, S0>
|
||||
where
|
||||
N: Node<'input, In, Output = O>,
|
||||
S0: Node<'input, (), Output = &'input N>,
|
||||
{
|
||||
type Output = <N as Node<'input, In>>::Output;
|
||||
#[inline]
|
||||
fn eval(&'input self, input: In) -> Self::Output {
|
||||
let node = self.node.eval(());
|
||||
node.eval(input)
|
||||
}
|
||||
}
|
||||
impl<'input, S0: 'input, O: 'static> ApplyNode<O, S0> {
|
||||
pub const fn new(node: S0) -> Self {
|
||||
Self { node, _o: PhantomData }
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::{ops::IdNode, value::ValueNode};
|
||||
|
@ -134,4 +170,15 @@ mod test {
|
|||
|
||||
assert_eq!(compose.eval(()), &5);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_apply() {
|
||||
let mut array = [1, 2, 3];
|
||||
let slice = &mut array;
|
||||
let set_node = crate::storage::SetOwnedNode::new(slice);
|
||||
|
||||
let apply = ApplyNode::new(ValueNode::new(set_node));
|
||||
|
||||
assert_eq!(apply.eval((1, 2)), ());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,10 @@
|
|||
use crate::Node;
|
||||
|
||||
use core::marker::PhantomData;
|
||||
use core::{
|
||||
borrow::BorrowMut,
|
||||
cell::{Cell, RefCell, RefMut},
|
||||
marker::PhantomData,
|
||||
};
|
||||
|
||||
#[derive(Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
|
||||
pub struct IntNode<const N: u32>;
|
||||
|
@ -13,7 +17,7 @@ impl<'i, const N: u32> Node<'i, ()> for IntNode<N> {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Debug)]
|
||||
#[derive(Default, Debug, Clone, Copy)]
|
||||
pub struct ValueNode<T>(pub T);
|
||||
|
||||
impl<'i, T: 'i> Node<'i, ()> for ValueNode<T> {
|
||||
|
@ -35,12 +39,62 @@ impl<T> From<T> for ValueNode<T> {
|
|||
ValueNode::new(value)
|
||||
}
|
||||
}
|
||||
impl<T: Clone> Clone for ValueNode<T> {
|
||||
fn clone(&self) -> Self {
|
||||
Self(self.0.clone())
|
||||
|
||||
#[derive(Default, Debug, Clone)]
|
||||
pub struct RefCellMutNode<T>(pub RefCell<T>);
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> RefCellMutNode<T> {
|
||||
pub const fn new(value: 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>);
|
||||
|
||||
impl<'i, T: Default + 'i> Node<'i, ()> for OnceCellNode<T> {
|
||||
type Output = T;
|
||||
#[inline(always)]
|
||||
fn eval(&'i self, _input: ()) -> Self::Output {
|
||||
self.0.replace(T::default())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> OnceCellNode<T> {
|
||||
pub const fn new(value: T) -> OnceCellNode<T> {
|
||||
OnceCellNode(Cell::new(value))
|
||||
}
|
||||
}
|
||||
impl<T: Clone + Copy> Copy for ValueNode<T> {}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct ClonedNode<T: Clone>(pub T);
|
||||
|
@ -75,6 +129,7 @@ impl<'i, T: Clone + 'i> Node<'i, ()> for DebugClonedNode<T> {
|
|||
#[inline(always)]
|
||||
fn eval(&'i self, _input: ()) -> Self::Output {
|
||||
// KEEP THIS `debug!()` - It acts as the output for the debug node itself
|
||||
#[cfg(not(target_arch = "spirv"))]
|
||||
log::debug!("DebugClonedNode::eval");
|
||||
|
||||
self.0.clone()
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue