Add GPU support for node graph

This commit is contained in:
Dennis
2022-04-24 11:58:31 +02:00
committed by Keavon Chambers
parent bfe21cb179
commit aa5e6f43f6
11 changed files with 232 additions and 52 deletions

View File

@@ -1,38 +1,38 @@
use core::{borrow::Borrow, marker::PhantomData};
use core::marker::PhantomData;
use crate::Node;
pub struct FnNode<T: Fn(&In) -> O, In, O>(T, PhantomData<In>, PhantomData<O>);
impl<'n, T: Fn(&In) -> O, In, O: 'n> Node<'n, In> for FnNode<T, In, O> {
pub struct FnNode<T: Fn(In) -> O, In, O>(T, PhantomData<In>, PhantomData<O>);
impl<'n, T: Fn(In) -> O, In, O: 'n> Node<'n, In> for FnNode<T, In, O> {
type Output = O;
fn eval(&'n self, input: &'n In) -> Self::Output {
self.0(input.borrow())
fn eval(&'n self, input: In) -> Self::Output {
self.0(input)
}
}
impl<T: Fn(&In) -> O, In, O> FnNode<T, In, O> {
impl<T: Fn(In) -> O, In, O> FnNode<T, In, O> {
pub fn new(f: T) -> Self {
FnNode(f, PhantomData::default(), PhantomData::default())
}
}
pub struct FnNodeWithState<T: Fn(&In, &State) -> O, In, O, State>(
pub struct FnNodeWithState<T: Fn(In, &State) -> O, In, O, State>(
T,
State,
PhantomData<In>,
PhantomData<O>,
);
impl<'n, T: Fn(&In, &State) -> O, In, O: 'n, State> Node<'n, In>
impl<'n, T: Fn(In, &State) -> O, In, O: 'n, State> Node<'n, In>
for FnNodeWithState<T, In, O, State>
{
type Output = O;
fn eval(&'n self, input: &'n In) -> Self::Output {
self.0(input.borrow(), &self.1)
fn eval(&'n self, input: In) -> Self::Output {
self.0(input, &self.1)
}
}
impl<T: Fn(&In, &State) -> O, In, O, State> FnNodeWithState<T, In, O, State> {
impl<T: Fn(In, &State) -> O, In, O, State> FnNodeWithState<T, In, O, State> {
pub fn new(f: T, state: State) -> Self {
FnNodeWithState(f, state, PhantomData::default(), PhantomData::default())
}

View File

@@ -1,4 +1,5 @@
#![no_std]
#![cfg_attr(target_arch = "spirv", feature(register_attr), register_attr(spirv))]
pub mod generic;
pub mod ops;
@@ -9,12 +10,13 @@ pub mod value;
pub trait Node< 'n, Input> {
type Output : 'n;
fn eval(&'n self, input: &'n Input) -> Self::Output;
fn eval(&'n self, input: Input) -> Self::Output;
}
// TODO: Fix exec trait
pub trait Exec<'n>: Node<'n, ()> {
fn exec(&'n self) -> Self::Output {
self.eval(&())
self.eval(())
}
}
impl<'n, T: Node<'n, ()>> Exec<'n> for T {}
@@ -23,7 +25,9 @@ pub trait Cache {
fn clear(&mut self);
}
#[cfg(not(feature = "gpu"))]
extern crate alloc;
#[cfg(not(feature = "gpu"))]
impl<'n, I, O: 'n> Node<'n, I> for alloc::boxed::Box<dyn Node<'n, I, Output = O>> {
type Output = O;

View File

@@ -1,35 +1,94 @@
use core::{borrow::Borrow, marker::PhantomData, ops::Add};
use core::{marker::PhantomData, ops::Add};
use crate::Node;
#[repr(C)]
#[derive(Default)]
pub struct AddNode<T>(PhantomData<T>);
impl<'n, T: Add + Copy + 'n> Node<'n, (T, T)> for AddNode<T> {
type Output = <T as Add>::Output;
fn eval(&'n self, input: &'n (T, T)) -> T::Output {
let (ref a, ref b) = input.borrow();
*a + *b
fn eval(&'n self, input: (T, T)) -> T::Output {
let (a, b) = input;
a + b
}
}
#[repr(C)]
#[derive(Default)]
/// Destructures a Tuple of two values and returns the first one
pub struct FstNode<T, U>(PhantomData<T>, PhantomData<U>);
impl<'n, T: Copy + 'n, U> Node<'n, (T, U)> for FstNode<T, U> {
type Output = &'n T;
fn eval(&'n self, input: &'n (T, U)) -> Self::Output {
let &(ref a, _) = input.borrow();
type Output = T;
fn eval(&'n self, input: (T, U)) -> Self::Output {
let (a, _) = input;
a
}
}
#[repr(C)]
#[derive(Default)]
/// Destructures a Tuple of two values and returns the first one
pub struct SndNode<T, U>(PhantomData<T>, PhantomData<U>);
impl<'n, T, U: Copy + 'n> Node<'n, (T, U)> for SndNode<T, U> {
type Output = &'n U;
fn eval(&'n self, input: &'n (T, U)) -> Self::Output {
let &(_, ref b) = input.borrow();
type Output = U;
fn eval(&'n self, input: (T, U)) -> Self::Output {
let (_, b) = input;
b
}
}
#[repr(C)]
#[derive(Default)]
/// Destructures a Tuple of two values and returns the first one
pub struct DupNode<T>(PhantomData<T>);
impl<'n, T: Copy + 'n> Node<'n, T> for DupNode<T> {
type Output = (T, T);
fn eval(&'n self, input: T) -> Self::Output {
(input, input)
}
}
#[cfg(target_arch = "spirv")]
pub mod gpu {
//#![deny(warnings)]
#[repr(C)]
pub struct PushConsts {
n: u32,
node: u32,
}
use super::*;
use crate::{structural::ComposeNodeOwned, Node};
//use crate::Node;
use spirv_std::glam::UVec3;
const ADD: AddNode<u32> = AddNode(PhantomData);
const OPERATION: ComposeNodeOwned<'_, (u32, u32), u32, FstNode<u32, u32>, DupNode<u32>> =
ComposeNodeOwned::new(FstNode(PhantomData, PhantomData), DupNode(PhantomData));
#[allow(unused)]
#[spirv(compute(threads(64)))]
pub fn spread(
#[spirv(global_invocation_id)] global_id: UVec3,
#[spirv(storage_buffer, descriptor_set = 0, binding = 0)] a: &[(u32, u32)],
#[spirv(storage_buffer, descriptor_set = 0, binding = 1)] y: &mut [(u32, u32)],
#[spirv(push_constant)] push_consts: &PushConsts,
) {
let gid = global_id.x as usize;
// Only process up to n, which is the length of the buffers.
if global_id.x < push_consts.n {
y[gid] = OPERATION.eval(a[gid]);
}
}
#[allow(unused)]
#[spirv(compute(threads(64)))]
pub fn add(
#[spirv(global_invocation_id)] global_id: UVec3,
#[spirv(storage_buffer, descriptor_set = 0, binding = 0)] a: &[(u32, u32)],
#[spirv(storage_buffer, descriptor_set = 0, binding = 1)] y: &mut [u32],
#[spirv(push_constant)] push_consts: &PushConsts,
) {
let gid = global_id.x as usize;
// Only process up to n, which is the length of the buffers.
if global_id.x < push_consts.n {
y[gid] = ADD.eval(a[gid]);
}
}
}

View File

@@ -12,20 +12,36 @@ pub struct ComposeNode<'n, Input, Inter, FIRST, SECOND> {
impl<'n, Input: 'n, Inter: 'n, First, Second> Node<'n, Input>
for ComposeNode<'n, Input, Inter, First, Second>
where
First: Node<'n, Input, Output = &'n Inter>,
First: Node<'n, Input, Output = Inter>,
Second: Node<'n, Inter>, /*+ Node<<First as Node<Input>>::Output<'n>>*/
{
type Output = <Second as Node<'n, Inter>>::Output;
fn eval(&'n self, input: &'n Input) -> Self::Output {
fn eval(&'n self, input: Input) -> Self::Output {
// evaluate the first node with the given input
// and then pipe the result from the first computation
// into the second node
let arg: &Inter = self.first.eval(input);
let arg: Inter = self.first.eval(input);
self.second.eval(arg)
}
}
#[cfg(feature = "nightly")]
impl<'n, Input, Inter, FIRST, SECOND> ComposeNode<'n, Input, Inter, FIRST, SECOND>
where
FIRST: Node<'n, Input>,
{
pub const fn new(first: &'n FIRST, second: &'n SECOND) -> Self {
ComposeNode::<'n, Input, Inter, FIRST, SECOND> {
first,
second,
_phantom: PhantomData,
_phantom2: PhantomData,
}
}
}
#[cfg(not(feature = "nightly"))]
impl<'n, Input, Inter, FIRST, SECOND> ComposeNode<'n, Input, Inter, FIRST, SECOND>
where
FIRST: Node<'n, Input>,
@@ -39,6 +55,54 @@ where
}
}
}
#[repr(C)]
pub struct ComposeNodeOwned<'n, Input, Inter, FIRST, SECOND> {
first: FIRST,
second: SECOND,
_phantom: PhantomData<&'n Input>,
_phantom2: PhantomData<Inter>,
}
impl<'n, Input: 'n, Inter: 'n, First, Second> Node<'n, Input>
for ComposeNodeOwned<'n, Input, Inter, First, Second>
where
First: Node<'n, Input, Output = Inter>,
Second: Node<'n, Inter>,
{
type Output = <Second as Node<'n, Inter>>::Output;
fn eval(&'n self, input: Input) -> Self::Output {
// evaluate the first node with the given input
// and then pipe the result from the first computation
// into the second node
let arg: Inter = self.first.eval(input);
self.second.eval(arg)
}
}
impl<'n, Input, Inter, First: 'n, Second> ComposeNodeOwned<'n, Input, Inter, First, Second>
where
First: Node<'n, Input, Output = Inter>,
{
#[cfg(feature = "nightly")]
pub const fn new(first: First, second: Second) -> Self {
ComposeNodeOwned::<'n, Input, Inter, First, Second> {
first,
second,
_phantom: PhantomData,
_phantom2: PhantomData,
}
}
#[cfg(not(feature = "nightly"))]
pub fn new(first: First, second: Second) -> Self {
ComposeNodeOwned::<'n, Input, Inter, First, Second> {
first,
second,
_phantom: PhantomData,
_phantom2: PhantomData,
}
}
}
pub trait After<I>: Sized {
fn after<'n, First: Node<'n, I>>(

View File

@@ -5,25 +5,20 @@ use crate::Node;
pub struct IntNode<const N: u32>;
impl<'n, const N: u32> Node<'n, ()> for IntNode<N> {
type Output = u32;
fn eval(&self, _input: &()) -> u32 {
fn eval(&self, _input: ()) -> u32 {
N
}
}
use dyn_any::{DynAny, StaticType, StaticTypeSized};
#[derive(Default)]
pub struct ValueNode<'n, T>(T, PhantomData<&'n ()>);
impl<'n, T: 'n> Node<'n, ()> for ValueNode<'n, T> {
type Output = &'n T;
fn eval(&self, _input: &()) -> &T {
fn eval(&self, _input: ()) -> &T {
&self.0
}
}
impl<'n, T: StaticTypeSized> StaticType for ValueNode<'n, T> {
type Static = ValueNode<'static, <T as StaticTypeSized>::Static>;
}
impl<'n, T> ValueNode<'n, T> {
pub const fn new(value: T) -> ValueNode<'n, T> {
ValueNode(value, PhantomData)
@@ -34,7 +29,7 @@ impl<'n, T> ValueNode<'n, T> {
pub struct DefaultNode<T>(PhantomData<T>);
impl<'n, T: Default + 'n> Node<'n, ()> for DefaultNode<T> {
type Output = T;
fn eval(&self, _input: &()) -> T {
fn eval(&self, _input: ()) -> T {
T::default()
}
}
@@ -47,8 +42,8 @@ impl<T> DefaultNode<T> {
pub struct DefaultRefNode<'n, T>(ValueNode<'n, T>);
impl<'n, T: 'n> Node<'n, ()> for DefaultRefNode<'n, T> {
type Output = &'n T;
fn eval(&'n self, _input: &'n ()) -> &'n T {
self.0.eval(&())
fn eval(&'n self, _input: ()) -> &'n T {
self.0.eval(())
}
}
impl<'n, T: Default> Default for DefaultRefNode<'n, T> {