This commit is contained in:
Dennis Kobert
2026-07-30 20:48:30 +00:00
parent 07b95ba59b
commit 4556e320df
22 changed files with 813 additions and 1603 deletions

View File

@@ -1,17 +0,0 @@
use crate::Node;
use std::marker::PhantomData;
#[derive(Clone)]
pub struct FnNode<T: Fn(I) -> O, I, O>(T, PhantomData<(I, O)>);
impl<'i, T: Fn(I) -> O + 'i, O: 'i, I: 'i> Node<'i, I> for FnNode<T, I, O> {
type Output = O;
fn eval(&'i self, input: I) -> Self::Output {
self.0(input)
}
}
impl<T: Fn(I) -> O, I, O> FnNode<T, I, O> {
pub fn new(f: T) -> Self {
FnNode(f, PhantomData)
}
}

View File

@@ -5,13 +5,12 @@ pub mod bounds;
pub mod consts;
pub mod context;
pub mod frame_table;
pub mod generic;
pub mod gnode;
pub mod gpoll;
pub mod list;
pub mod math;
pub mod memo;
pub mod misc;
pub mod node;
pub mod ops;
pub mod registry;
pub mod render_complexity;
@@ -39,113 +38,15 @@ pub use no_std_types::blending;
pub use no_std_types::choice_type;
pub use no_std_types::color;
pub use no_std_types::shaders;
pub use node::Node;
pub use num_traits;
use std::any::TypeId;
use std::future::Future;
use std::pin::Pin;
#[cfg(feature = "wasm")]
pub use tsify;
pub use types::Cow;
// pub trait Node: for<'n> NodeIO<'n> {
/// The node trait allows for defining any node. Nodes can only take one call argument input, however they can store references to other nodes inside the struct.
/// See `node-graph/README.md` for information on how to define a new node.
pub trait Node<'i, Input> {
type Output: 'i;
/// Evaluates the node with the single specified input.
fn eval(&'i self, input: Input) -> Self::Output;
/// Resets the node, e.g. the LetNode's cache is set to None.
fn reset(&self) {}
/// Returns the name of the node for diagnostic purposes.
fn node_name(&self) -> &'static str {
std::any::type_name::<Self>()
}
/// Serialize the node which is used for the `introspect` function which can retrieve values from monitor nodes.
fn serialize(&self) -> Option<std::sync::Arc<dyn std::any::Any + Send + Sync>> {
log::warn!("Node::serialize not implemented for {}", std::any::type_name::<Self>());
None
}
}
mod types;
pub use types::*;
pub trait NodeIO<'i, Input>: Node<'i, Input>
where
Self::Output: 'i + StaticTypeSized,
Input: StaticTypeSized,
{
fn input_type(&self) -> TypeId {
TypeId::of::<Input::Static>()
}
fn input_type_name(&self) -> &'static str {
std::any::type_name::<Input>()
}
fn output_type(&self) -> TypeId {
TypeId::of::<<Self::Output as StaticTypeSized>::Static>()
}
fn output_type_name(&self) -> &'static str {
std::any::type_name::<Self::Output>()
}
fn to_node_io(&self, inputs: Vec<Type>) -> NodeIOTypes {
NodeIOTypes {
call_argument: concrete!(<Input as StaticTypeSized>::Static),
return_value: concrete!(<Self::Output as StaticTypeSized>::Static),
inputs,
}
}
fn to_async_node_io(&self, inputs: Vec<Type>) -> NodeIOTypes
where
<Self::Output as Future>::Output: StaticTypeSized,
Self::Output: Future,
{
NodeIOTypes {
call_argument: concrete!(<Input as StaticTypeSized>::Static),
return_value: future!(<<Self::Output as Future>::Output as StaticTypeSized>::Static),
inputs,
}
}
}
impl<'i, N: Node<'i, I>, I> NodeIO<'i, I> for N
where
N::Output: 'i + StaticTypeSized,
I: StaticTypeSized,
{
}
impl<'i, I: 'i, N: Node<'i, I> + ?Sized> Node<'i, I> for &'i N {
type Output = N::Output;
fn eval(&'i self, input: I) -> N::Output {
(*self).eval(input)
}
}
impl<'i, I: 'i, O: 'i, N: Node<'i, I, Output = O> + ?Sized> Node<'i, I> for Box<N> {
type Output = O;
fn eval(&'i self, input: I) -> O {
(**self).eval(input)
}
}
impl<'i, I: 'i, O: 'i, N: Node<'i, I, Output = O> + ?Sized> Node<'i, I> for std::sync::Arc<N> {
type Output = O;
fn eval(&'i self, input: I) -> O {
(**self).eval(input)
}
}
impl<'i, I, O: 'i> Node<'i, I> for Pin<Box<dyn Node<'i, I, Output = O> + 'i>> {
type Output = O;
fn eval(&'i self, input: I) -> O {
(**self).eval(input)
}
}
impl<'i, I, O: 'i> Node<'i, I> for Pin<&'i (dyn NodeIO<'i, I, Output = O> + 'i)> {
type Output = O;
fn eval(&'i self, input: I) -> O {
(**self).eval(input)
}
}
pub trait InputAccessorSource<'a, T>: InputAccessorSourceIdentifier + std::fmt::Debug {
fn get_input(&'a self, index: usize) -> Option<&'a T>;
fn set_input(&'a mut self, index: usize, value: T);

View File

@@ -22,7 +22,7 @@ pub unsafe fn assume_init_prefix_mut<T>(scratch: &mut [MaybeUninit<T>], len: usi
unsafe { std::slice::from_raw_parts_mut(scratch.as_mut_ptr().cast::<T>(), len) }
}
pub trait GNode<Input> {
pub trait Node<Input> {
type Output;
fn eval(&self, input: &Input) -> GPoll<Self::Output>;
@@ -80,9 +80,9 @@ pub trait GNode<Input> {
}
}
impl<Input, N> GNode<Input> for &N
impl<Input, N> Node<Input> for &N
where
N: GNode<Input> + ?Sized,
N: Node<Input> + ?Sized,
{
type Output = N::Output;
@@ -102,9 +102,9 @@ where
}
}
impl<Input, N> GNode<Input> for Box<N>
impl<Input, N> Node<Input> for Box<N>
where
N: GNode<Input> + ?Sized,
N: Node<Input> + ?Sized,
{
type Output = N::Output;
@@ -124,9 +124,9 @@ where
}
}
impl<Input, N> GNode<Input> for std::sync::Arc<N>
impl<Input, N> Node<Input> for std::sync::Arc<N>
where
N: GNode<Input> + ?Sized,
N: Node<Input> + ?Sized,
{
type Output = N::Output;
@@ -171,7 +171,7 @@ impl StatusCell {
Self { no_partial: true, ..Self::new() }
}
pub fn eval_input<Input, N: GNode<Input>>(&self, input_index: usize, node: &N, input: &Input) -> Result<N::Output, Interrupt> {
pub fn eval_input<Input, N: Node<Input>>(&self, input_index: usize, node: &N, input: &Input) -> Result<N::Output, Interrupt> {
match node.eval(input) {
GPoll::Final(value) => Ok(value),
GPoll::Partial(_) if self.no_partial => Err(Interrupt::Pending),
@@ -233,15 +233,15 @@ impl<'a, N> LazyInput<'a, N> {
pub fn eval<Input>(&self, ctx: &Input) -> Result<N::Output, Interrupt>
where
N: GNode<Input>,
N: Node<Input>,
{
self.cell.eval_input(self.input_index, self.node, ctx)
}
}
impl<'a, Input, N> GNode<Input> for LazyInput<'a, N>
impl<'a, Input, N> Node<Input> for LazyInput<'a, N>
where
N: GNode<Input>,
N: Node<Input>,
{
type Output = N::Output;
@@ -279,7 +279,7 @@ mod tests {
struct Double;
impl GNode<TestInput> for Double {
impl Node<TestInput> for Double {
type Output = u64;
fn eval(&self, input: &TestInput) -> GPoll<u64> {
@@ -315,7 +315,7 @@ mod tests {
#[test]
fn partial_lane_downgrades_batch_finality() {
struct PartialAtThree;
impl GNode<TestInput> for PartialAtThree {
impl Node<TestInput> for PartialAtThree {
type Output = u64;
fn eval(&self, input: &TestInput) -> GPoll<u64> {
match input.index {
@@ -344,7 +344,7 @@ mod tests {
}
}
struct PendingAtTwo;
impl GNode<TestInput> for PendingAtTwo {
impl Node<TestInput> for PendingAtTwo {
type Output = Probe;
fn eval(&self, input: &TestInput) -> GPoll<Probe> {
match input.index {
@@ -362,7 +362,7 @@ mod tests {
#[test]
fn trait_is_object_safe_across_erased_edges() {
let erased: Box<dyn GNode<TestInput, Output = u64>> = Box::new(Double);
let erased: Box<dyn Node<TestInput, Output = u64>> = Box::new(Double);
let input = TestInput { index: 21 };
assert_eq!(erased.eval(&input), GPoll::Final(42));
let mut scratch = [const { MaybeUninit::uninit() }; 2];

View File

@@ -1,43 +1,7 @@
use crate::Node;
use crate::list::{Attribute, AttributeDyn, AttributeValueDyn, Item, List, ListDyn};
use crate::transform::Footprint;
use glam::DVec2;
use graphene_hash::CacheHash;
use std::future::Future;
use std::marker::PhantomData;
// Type
// TODO: Document this
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
pub struct TypeNode<N: for<'a> Node<'a, I>, I, O>(pub N, pub PhantomData<(I, O)>);
impl<'i, N, I: 'i, O: 'i> Node<'i, I> for TypeNode<N, I, O>
where
N: for<'n> Node<'n, I, Output = O>,
{
type Output = O;
fn eval(&'i self, input: I) -> Self::Output {
self.0.eval(input)
}
fn reset(&self) {
self.0.reset();
}
fn serialize(&self) -> Option<std::sync::Arc<dyn std::any::Any + Send + Sync>> {
self.0.serialize()
}
}
impl<'i, N: for<'a> Node<'a, I>, I: 'i> TypeNode<N, I, <N as Node<'i, I>>::Output> {
pub fn new(node: N) -> Self {
Self(node, PhantomData)
}
}
impl<'i, N: for<'a> Node<'a, I> + Clone, I: 'i> Clone for TypeNode<N, I, <N as Node<'i, I>>::Output> {
fn clone(&self) -> Self {
Self(self.0.clone(), self.1)
}
}
impl<'i, N: for<'a> Node<'a, I> + Copy, I: 'i> Copy for TypeNode<N, I, <N as Node<'i, I>>::Output> {}
/// The [`Convert`] trait allows for conversion between Rust primitive numeric types.
/// Because number casting is lossy, we cannot use the normal [`Into`] trait like we do for other types.

View File

@@ -1,13 +1,12 @@
use crate::concrete;
use crate::context::{Context, ContextImpl};
use crate::gnode::GNode;
use crate::node::Node;
use crate::{ContextFeature, ProtoNodeIdentifier, Type, WasmNotSend, WasmNotSync};
use dyn_any::DynAny;
use graphene_hash::CacheHash;
pub use no_std_types::registry::types;
use std::collections::HashMap;
use std::hash::Hasher;
use std::pin::Pin;
use std::sync::{LazyLock, Mutex};
// Translation struct between macro and definition
@@ -70,13 +69,13 @@ pub static NODE_METADATA: LazyLock<Mutex<HashMap<ProtoNodeIdentifier, NodeMetada
pub use crate::NodeIOTypes;
#[cfg(not(target_family = "wasm"))]
pub type ErasedGNode<T> = dyn for<'c> GNode<ContextImpl<'c>, Output = T> + Send + Sync;
pub type ErasedNode<T> = dyn for<'c> Node<ContextImpl<'c>, Output = T> + Send + Sync;
#[cfg(target_family = "wasm")]
pub type ErasedGNode<T> = dyn for<'c> GNode<ContextImpl<'c>, Output = T>;
pub type ErasedNode<T> = dyn for<'c> Node<ContextImpl<'c>, Output = T>;
#[cfg(not(target_family = "wasm"))]
pub type ErasedLendGNode<T> = dyn for<'c> GNode<ContextImpl<'c>, Output = &'c T> + Send + Sync;
pub type ErasedLendNode<T> = dyn for<'c> Node<ContextImpl<'c>, Output = &'c T> + Send + Sync;
#[cfg(target_family = "wasm")]
pub type ErasedLendGNode<T> = dyn for<'c> GNode<ContextImpl<'c>, Output = &'c T>;
pub type ErasedLendNode<T> = dyn for<'c> Node<ContextImpl<'c>, Output = &'c T>;
#[cfg(not(target_family = "wasm"))]
type DynEdge = dyn std::any::Any + Send + Sync;
@@ -127,9 +126,9 @@ unsafe impl<N: ?Sized + Send + Sync> Send for SharedEdge<N> {}
// SAFETY: as in Send.
unsafe impl<N: ?Sized + Send + Sync> Sync for SharedEdge<N> {}
impl<Input, N> GNode<Input> for SharedEdge<N>
impl<Input, N> Node<Input> for SharedEdge<N>
where
N: GNode<Input> + ?Sized,
N: Node<Input> + ?Sized,
{
type Output = N::Output;
@@ -149,7 +148,7 @@ where
unsafe { self.ptr.as_ref() }.serialize()
}
fn eval_batch<'a>(&self, input: &'a Input, range: std::ops::Range<u64>, scratch: Option<&'a mut [std::mem::MaybeUninit<Self::Output>]>) -> crate::gnode::BatchStatus<'a, Self::Output>
fn eval_batch<'a>(&self, input: &'a Input, range: std::ops::Range<u64>, scratch: Option<&'a mut [std::mem::MaybeUninit<Self::Output>]>) -> crate::node::BatchStatus<'a, Self::Output>
where
Input: crate::context::InjectIndex + Copy,
{
@@ -179,23 +178,23 @@ unsafe impl Send for EdgeHandle {}
unsafe impl Sync for EdgeHandle {}
impl EdgeHandle {
pub fn new<T: 'static>(node: std::sync::Arc<ErasedGNode<T>>) -> Self {
pub fn new<T: 'static>(node: std::sync::Arc<ErasedNode<T>>) -> Self {
Self::new_erased(node, edge_type::<T>())
}
pub fn new_ref<T: 'static>(node: std::sync::Arc<ErasedLendGNode<T>>) -> Self {
pub fn new_ref<T: 'static>(node: std::sync::Arc<ErasedLendNode<T>>) -> Self {
Self::new_erased(node, lend_edge_type::<T>())
}
pub fn new_erased<N: ?Sized + 'static>(node: std::sync::Arc<N>, ty: Type) -> Self
where
N: for<'c> GNode<ContextImpl<'c>>,
N: for<'c> Node<ContextImpl<'c>>,
SharedEdge<N>: WasmNotSend + WasmNotSync,
{
Self {
node: Box::new(SharedEdge::new(node)),
share: |edge| Box::new(edge.downcast_ref::<SharedEdge<N>>().expect("share hook matches the stored edge type").share()),
serialize: |edge| GNode::<ContextImpl>::serialize(edge.downcast_ref::<SharedEdge<N>>().expect("serialize hook matches the stored edge type")),
serialize: |edge| Node::<ContextImpl>::serialize(edge.downcast_ref::<SharedEdge<N>>().expect("serialize hook matches the stored edge type")),
ty,
}
}
@@ -217,11 +216,11 @@ impl EdgeHandle {
(self.serialize)(&*self.node)
}
pub fn downcast<T: 'static>(self) -> Result<SharedEdge<ErasedGNode<T>>, ConstructionError> {
pub fn downcast<T: 'static>(self) -> Result<SharedEdge<ErasedNode<T>>, ConstructionError> {
self.downcast_erased(edge_type::<T>())
}
pub fn downcast_lend<T: 'static>(self) -> Result<SharedEdge<ErasedLendGNode<T>>, ConstructionError> {
pub fn downcast_lend<T: 'static>(self) -> Result<SharedEdge<ErasedLendNode<T>>, ConstructionError> {
self.downcast_erased(lend_edge_type::<T>())
}
@@ -257,7 +256,6 @@ pub fn construct(entry: &RegistryEntry, inputs: Vec<EdgeHandle>) -> Result<EdgeH
(entry.constructor)(inputs)
}
pub type LocalFuture<'n, T> = Pin<Box<dyn Future<Output = T> + 'n>>;
#[cfg(not(target_family = "wasm"))]
pub type Any<'n> = Box<dyn DynAny<'n> + 'n + Send>;
#[cfg(target_family = "wasm")]
@@ -275,7 +273,7 @@ mod tests {
struct CountingNode(AtomicU32);
impl<Input> GNode<Input> for CountingNode {
impl<Input> Node<Input> for CountingNode {
type Output = u32;
fn eval(&self, _input: &Input) -> GPoll<u32> {
@@ -285,7 +283,7 @@ mod tests {
struct ValueNode<T>(T);
impl<T: Clone, Input> GNode<Input> for ValueNode<T> {
impl<T: Clone, Input> Node<Input> for ValueNode<T> {
type Output = T;
fn eval(&self, _input: &Input) -> GPoll<T> {
@@ -295,7 +293,7 @@ mod tests {
struct LendNode(String);
impl<'e, Input: Ctx + ExtractArena<ArenaRef = &'e Arena>> GNode<Input> for LendNode {
impl<'e, Input: Ctx + ExtractArena<ArenaRef = &'e Arena>> Node<Input> for LendNode {
type Output = &'e String;
fn eval(&self, input: &Input) -> GPoll<&'e String> {
@@ -318,10 +316,10 @@ mod tests {
content: Node0,
}
impl<'e, Input, Node0> GNode<Input> for SplitNode<Node0>
impl<'e, Input, Node0> Node<Input> for SplitNode<Node0>
where
Input: Ctx,
Node0: GNode<Input, Output = &'e String>,
Node0: Node<Input, Output = &'e String>,
{
type Output = SplitBorrow<'e>;
@@ -330,14 +328,14 @@ mod tests {
}
}
type ErasedSplitEdge = dyn for<'c> GNode<ContextImpl<'c>, Output = SplitBorrow<'c>> + Send + Sync;
type ErasedSplitEdge = dyn for<'c> Node<ContextImpl<'c>, Output = SplitBorrow<'c>> + Send + Sync;
let arena = Arena::new(4096);
let generations = [];
let scope = scope_fixture(&generations, &arena);
let ctx = ContextImpl::root(&scope);
let lending = EdgeHandle::new_ref(Arc::new(LendNode("held".to_string())) as Arc<ErasedLendGNode<String>>);
let lending = EdgeHandle::new_ref(Arc::new(LendNode("held".to_string())) as Arc<ErasedLendNode<String>>);
let upstream = lending.downcast_lend::<String>().unwrap();
let node: Arc<ErasedSplitEdge> = Arc::new(SplitNode { content: upstream });
let handle = EdgeHandle::new_erased(node, concrete!(SplitBorrow<'static>));
@@ -359,10 +357,10 @@ mod tests {
content: Node0,
}
impl<C, T, Node0> GNode<C> for RepeatNode<Node0>
impl<C, T, Node0> Node<C> for RepeatNode<Node0>
where
C: Ctx + DeriveCtx,
Node0: for<'x> GNode<Derived<'x, C>, Output = T>,
Node0: for<'x> Node<Derived<'x, C>, Output = T>,
{
type Output = Vec<T>;
@@ -382,7 +380,7 @@ mod tests {
struct LevelsNode;
impl<Input: ExtractIndex> GNode<Input> for LevelsNode {
impl<Input: ExtractIndex> Node<Input> for LevelsNode {
type Output = Vec<usize>;
fn eval(&self, input: &Input) -> GPoll<Vec<usize>> {
@@ -398,7 +396,7 @@ mod tests {
let nested = RepeatNode {
content: RepeatNode { content: LevelsNode },
};
let erased: Box<ErasedGNode<Vec<Vec<Vec<usize>>>>> = Box::new(nested);
let erased: Box<ErasedNode<Vec<Vec<Vec<usize>>>>> = Box::new(nested);
let GPoll::Final(outer) = erased.eval(&ctx) else {
panic!("nested repeat must evaluate");
@@ -417,10 +415,10 @@ mod tests {
content: Node0,
}
impl<C, T, Node0> GNode<C> for ShiftFootprintNode<Node0>
impl<C, T, Node0> Node<C> for ShiftFootprintNode<Node0>
where
C: Ctx + DeriveCtx + ExtractFootprint,
Node0: for<'x> GNode<Derived<'x, C>, Output = T>,
Node0: for<'x> Node<Derived<'x, C>, Output = T>,
{
type Output = T;
@@ -434,7 +432,7 @@ mod tests {
struct ResolutionNode;
impl<Input: ExtractFootprint> GNode<Input> for ResolutionNode {
impl<Input: ExtractFootprint> Node<Input> for ResolutionNode {
type Output = u32;
fn eval(&self, input: &Input) -> GPoll<u32> {
@@ -447,7 +445,7 @@ mod tests {
let scope = scope_fixture(&generations, &arena);
let ctx = ContextImpl::root(&scope);
let graph: Box<ErasedGNode<u32>> = Box::new(ShiftFootprintNode {
let graph: Box<ErasedNode<u32>> = Box::new(ShiftFootprintNode {
content: ShiftFootprintNode { content: ResolutionNode },
});
assert_eq!(graph.eval(&ctx), GPoll::Final(Footprint::DEFAULT.resolution.x + 14));
@@ -459,19 +457,19 @@ mod tests {
let mut args = args.into_iter();
let value = args.next().ok_or(ConstructionError::Arity { expected: 1, got: 0 })?.downcast::<String>()?;
drop(value);
Ok(EdgeHandle::new(Arc::new(ValueNode(0u32)) as Arc<ErasedGNode<u32>>))
Ok(EdgeHandle::new(Arc::new(ValueNode(0u32)) as Arc<ErasedNode<u32>>))
}
let entry = RegistryEntry {
io: NodeIOTypes::new(concrete!(Context), concrete!(u32), vec![edge_type::<String>()]),
constructor: construct_strlen,
};
let owned = EdgeHandle::new(Arc::new(ValueNode("typed".to_string())) as Arc<ErasedGNode<String>>);
let owned = EdgeHandle::new(Arc::new(ValueNode("typed".to_string())) as Arc<ErasedNode<String>>);
assert!(construct(&entry, vec![owned]).is_ok());
assert_eq!(construct(&entry, vec![]).unwrap_err(), ConstructionError::Arity { expected: 1, got: 0 });
let mistyped = EdgeHandle::new(Arc::new(ValueNode(1.0f64)) as Arc<ErasedGNode<f64>>);
let mistyped = EdgeHandle::new(Arc::new(ValueNode(1.0f64)) as Arc<ErasedNode<f64>>);
assert_eq!(
construct(&entry, vec![mistyped]).unwrap_err(),
ConstructionError::Type {
@@ -480,7 +478,7 @@ mod tests {
}
);
let lent = EdgeHandle::new_ref(Arc::new(LendNode("typed".to_string())) as Arc<ErasedLendGNode<String>>);
let lent = EdgeHandle::new_ref(Arc::new(LendNode("typed".to_string())) as Arc<ErasedLendNode<String>>);
assert_eq!(
construct(&entry, vec![lent]).unwrap_err(),
ConstructionError::Type {
@@ -497,7 +495,7 @@ mod tests {
let scope = scope_fixture(&generations, &arena);
let ctx = ContextImpl::root(&scope);
let handle = EdgeHandle::new(Arc::new(CountingNode(AtomicU32::new(0))) as Arc<ErasedGNode<u32>>);
let handle = EdgeHandle::new(Arc::new(CountingNode(AtomicU32::new(0))) as Arc<ErasedNode<u32>>);
let duplicate = handle.duplicate();
assert_eq!(*duplicate.ty(), edge_type::<u32>());

View File

@@ -152,8 +152,8 @@ mod tests {
use super::*;
use crate::arena::Arena;
use crate::context::{ContextImpl, Ctx, CtxSnapshot, EvalScope, ExtractFootprint, ExtractVarArgs, VarArgLink, VarArgSlots};
use crate::gnode::GNode;
use crate::gpoll::GPoll;
use crate::node::Node;
use crate::transform::Footprint;
use std::sync::Mutex;
use std::sync::atomic::{AtomicU32, Ordering};
@@ -208,7 +208,7 @@ mod tests {
struct SourceNode<T>(T);
impl<T: Clone, Input> GNode<Input> for SourceNode<T> {
impl<T: Clone, Input> Node<Input> for SourceNode<T> {
type Output = T;
fn eval(&self, _input: &Input) -> GPoll<T> {
@@ -264,7 +264,7 @@ mod tests {
struct GatedSource(Arc<std::sync::atomic::AtomicBool>, f64);
impl<Input> GNode<Input> for GatedSource {
impl<Input> Node<Input> for GatedSource {
type Output = f64;
fn eval(&self, _input: &Input) -> GPoll<f64> {
@@ -289,13 +289,13 @@ mod tests {
let runtime = Arc::new(MockRuntime::default());
let graph = SlowDoubleNode::new(SourceNode(21.0f64), SourceNode(RuntimeHandle(runtime.clone())), SourceNode(7u64));
assert_eq!(GNode::eval(&graph, &ctx), GPoll::Pending);
assert_eq!(GNode::eval(&graph, &ctx), GPoll::Pending);
assert_eq!(Node::eval(&graph, &ctx), GPoll::Pending);
assert_eq!(Node::eval(&graph, &ctx), GPoll::Pending);
assert_eq!(SLOW_DOUBLE_RUNS.load(Ordering::Relaxed), 0);
assert_eq!(runtime.drain(), vec![7]);
assert_eq!(SLOW_DOUBLE_RUNS.load(Ordering::Relaxed), 1);
assert_eq!(GNode::eval(&graph, &ctx), GPoll::Final(42.0));
assert_eq!(GNode::eval(&graph, &ctx), GPoll::Final(42.0));
assert_eq!(Node::eval(&graph, &ctx), GPoll::Final(42.0));
assert_eq!(Node::eval(&graph, &ctx), GPoll::Final(42.0));
assert_eq!(SLOW_DOUBLE_RUNS.load(Ordering::Relaxed), 1);
}
@@ -309,9 +309,9 @@ mod tests {
let runtime = Arc::new(MockRuntime::default());
let graph = PreviewDoubleNode::new(SourceNode(21.0f64), SourceNode(RuntimeHandle(runtime.clone())), SourceNode(1u64));
assert_eq!(GNode::eval(&graph, &ctx), GPoll::Partial(-1.0));
assert_eq!(Node::eval(&graph, &ctx), GPoll::Partial(-1.0));
runtime.drain();
assert_eq!(GNode::eval(&graph, &ctx), GPoll::Final(42.0));
assert_eq!(Node::eval(&graph, &ctx), GPoll::Final(42.0));
}
#[test]
@@ -324,9 +324,9 @@ mod tests {
let runtime = Arc::new(MockRuntime::default());
let graph = StrictDoubleNode::new(SourceNode(21.0f64), SourceNode(RuntimeHandle(runtime.clone())), SourceNode(2u64));
assert_eq!(GNode::eval(&graph, &ctx), GPoll::Pending);
assert_eq!(Node::eval(&graph, &ctx), GPoll::Pending);
runtime.drain();
assert_eq!(GNode::eval(&graph, &ctx), GPoll::Final(42.0));
assert_eq!(Node::eval(&graph, &ctx), GPoll::Final(42.0));
}
#[test]
@@ -339,12 +339,12 @@ mod tests {
let runtime = Arc::new(MockRuntime::default());
let graph = StagedDoubleNode::new(SourceNode(21.0f64), SourceNode(RuntimeHandle(runtime.clone())), SourceNode(8u64));
assert_eq!(GNode::eval(&graph, &ctx), GPoll::Pending);
assert_eq!(Node::eval(&graph, &ctx), GPoll::Pending);
assert_eq!(STAGED_RUNS.load(Ordering::Relaxed), 1, "the prologue runs synchronously on the miss");
assert_eq!(GNode::eval(&graph, &ctx), GPoll::Pending);
assert_eq!(Node::eval(&graph, &ctx), GPoll::Pending);
assert_eq!(STAGED_RUNS.load(Ordering::Relaxed), 1, "in flight must not rerun the prologue");
assert_eq!(runtime.drain(), vec![8]);
assert_eq!(GNode::eval(&graph, &ctx), GPoll::Final(42.0));
assert_eq!(Node::eval(&graph, &ctx), GPoll::Final(42.0));
assert_eq!(STAGED_RUNS.load(Ordering::Relaxed), 1);
}
@@ -359,12 +359,12 @@ mod tests {
let runtime = Arc::new(MockRuntime::default());
let graph = StagedSumNode::new(SourceNode(40.0f64), GatedSource(gate.clone(), 2.0), SourceNode(RuntimeHandle(runtime.clone())), SourceNode(9u64));
assert_eq!(GNode::eval(&graph, &ctx), GPoll::Pending);
assert_eq!(Node::eval(&graph, &ctx), GPoll::Pending);
assert_eq!(runtime.drain(), Vec::<SourceId>::new(), "an interrupted prologue must not spawn or claim the slot");
gate.store(true, Ordering::Relaxed);
assert_eq!(GNode::eval(&graph, &ctx), GPoll::Pending);
assert_eq!(Node::eval(&graph, &ctx), GPoll::Pending);
assert_eq!(runtime.drain(), vec![9]);
assert_eq!(GNode::eval(&graph, &ctx), GPoll::Final(42.0));
assert_eq!(Node::eval(&graph, &ctx), GPoll::Final(42.0));
}
#[test]
@@ -383,9 +383,9 @@ mod tests {
let runtime = Arc::new(MockRuntime::default());
let graph = SnapshotVarargNode::new(SourceNode(()), SourceNode(RuntimeHandle(runtime.clone())), SourceNode(5u64));
assert_eq!(GNode::eval(&graph, &ctx), GPoll::Pending);
assert_eq!(Node::eval(&graph, &ctx), GPoll::Pending);
runtime.drain();
assert_eq!(GNode::eval(&graph, &ctx), GPoll::Final(21.5));
assert_eq!(Node::eval(&graph, &ctx), GPoll::Final(21.5));
}
#[test]
@@ -400,9 +400,9 @@ mod tests {
let runtime = Arc::new(MockRuntime::default());
let graph = SnapshotResolutionNode::new(SourceNode(()), SourceNode(RuntimeHandle(runtime.clone())), SourceNode(3u64));
assert_eq!(GNode::eval(&graph, &ctx), GPoll::Pending);
assert_eq!(Node::eval(&graph, &ctx), GPoll::Pending);
runtime.drain();
assert_eq!(GNode::eval(&graph, &ctx), GPoll::Final(Footprint::DEFAULT.resolution.x));
assert_eq!(Node::eval(&graph, &ctx), GPoll::Final(Footprint::DEFAULT.resolution.x));
}
#[test]
@@ -492,7 +492,7 @@ mod tests {
let snapshot = runtime.snapshot();
let scope = EvalScope::new(None, None, None, &snapshot, &arena);
let ctx = ContextImpl::root(&scope);
assert_eq!(GNode::eval(&graph, &ctx), GPoll::Pending);
assert_eq!(Node::eval(&graph, &ctx), GPoll::Pending);
assert!(!runtime.take_dirty());
assert_eq!(runtime.spawner().drain(), 1);
@@ -502,7 +502,7 @@ mod tests {
let bumped_scope = EvalScope::new(None, None, None, &bumped, &arena);
let bumped_ctx = ContextImpl::root(&bumped_scope);
assert_eq!(GNode::eval(&graph, &bumped_ctx), GPoll::Final(42.0), "the own-generation-excluded key replays the landed slot");
assert_eq!(Node::eval(&graph, &bumped_ctx), GPoll::Final(42.0), "the own-generation-excluded key replays the landed slot");
assert_eq!(runtime.spawner().drain(), 0, "a slot hit must not respawn");
let downstream_key = crate::registry::cache_key(&ContextImpl::root(&scope));

View File

@@ -1,104 +1,7 @@
use crate::Node;
use std::cell::{Cell, RefCell, RefMut};
use std::marker::PhantomData;
#[derive(Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
pub struct IntNode<const N: u32>;
impl<'i, const N: u32, I> Node<'i, I> for IntNode<N> {
type Output = u32;
#[inline(always)]
fn eval(&'i self, _input: I) -> Self::Output {
N
}
}
#[derive(Default, Debug, Clone, Copy)]
pub struct ValueNode<T>(pub T);
impl<'i, T: 'i, I> Node<'i, I> for ValueNode<T> {
type Output = &'i T;
#[inline(always)]
fn eval(&'i self, _input: I) -> Self::Output {
&self.0
}
}
impl<T> ValueNode<T> {
pub const fn new(value: T) -> ValueNode<T> {
ValueNode(value)
}
}
impl<T> From<T> for ValueNode<T> {
fn from(value: T) -> Self {
ValueNode::new(value)
}
}
#[derive(Default, Debug, Clone, Copy)]
pub struct AsRefNode<T: AsRef<U>, U>(pub T, PhantomData<U>);
impl<'i, T: 'i + AsRef<U>, U: 'i> Node<'i, ()> for AsRefNode<T, U> {
type Output = &'i U;
#[inline(always)]
fn eval(&'i self, _input: ()) -> Self::Output {
self.0.as_ref()
}
}
impl<T: AsRef<U>, U> AsRefNode<T, U> {
pub const fn new(value: T) -> AsRefNode<T, U> {
AsRefNode(value, PhantomData)
}
}
#[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 {
self.0.borrow_mut()
}
}
impl<T> RefCellMutNode<T> {
pub const fn new(value: T) -> RefCellMutNode<T> {
RefCellMutNode(RefCell::new(value))
}
}
#[derive(Default)]
pub struct OnceCellNode<T>(pub Cell<T>);
impl<'i, T: Default + 'i, I> Node<'i, I> for OnceCellNode<T> {
type Output = T;
#[inline(always)]
fn eval(&'i self, _input: I) -> Self::Output {
self.0.replace(T::default())
}
}
impl<T> OnceCellNode<T> {
pub const fn new(value: T) -> OnceCellNode<T> {
OnceCellNode(Cell::new(value))
}
}
#[derive(Clone, Copy)]
pub struct ClonedNode<T: Clone>(pub T);
impl<'i, T: Clone + 'i, I> Node<'i, I> for ClonedNode<T> {
type Output = T;
#[inline(always)]
fn eval(&'i self, _input: I) -> Self::Output {
self.0.clone()
}
}
impl<T: Clone, Input> crate::gnode::GNode<Input> for ClonedNode<T> {
impl<T: Clone, Input> crate::node::Node<Input> for ClonedNode<T> {
type Output = T;
fn eval(&self, _input: &Input) -> crate::gpoll::GPoll<T> {
@@ -107,7 +10,7 @@ impl<T: Clone, Input> crate::gnode::GNode<Input> for ClonedNode<T> {
}
pub fn value_edge<T: Clone + crate::WasmNotSend + crate::WasmNotSync + 'static>(value: T) -> crate::registry::EdgeHandle {
crate::registry::EdgeHandle::new(std::sync::Arc::new(ClonedNode(value)) as std::sync::Arc<crate::registry::ErasedGNode<T>>)
crate::registry::EdgeHandle::new(std::sync::Arc::new(ClonedNode(value)) as std::sync::Arc<crate::registry::ErasedNode<T>>)
}
impl<T: Clone> ClonedNode<T> {
@@ -121,103 +24,3 @@ impl<T: Clone> From<T> for ClonedNode<T> {
ClonedNode::new(value)
}
}
#[derive(Clone, Copy)]
/// The DebugClonedNode logs every time it is evaluated.
/// This is useful for debugging.
pub struct DebugClonedNode<T: Clone>(pub T);
impl<'i, T: Clone + 'i> Node<'i, ()> for DebugClonedNode<T> {
type Output = T;
#[inline(always)]
fn eval(&'i self, _input: ()) -> Self::Output {
// KEEP THIS `debug!()` - It acts as the output for the debug node itself
log::debug!("DebugClonedNode::eval");
self.0.clone()
}
}
impl<T: Clone> DebugClonedNode<T> {
pub const fn new(value: T) -> DebugClonedNode<T> {
DebugClonedNode(value)
}
}
#[derive(Clone, Copy)]
pub struct CopiedNode<T: Copy>(pub T);
impl<'i, T: Copy + 'i, I> Node<'i, I> for CopiedNode<T> {
type Output = T;
#[inline(always)]
fn eval(&'i self, _input: I) -> Self::Output {
self.0
}
}
impl<T: Copy> CopiedNode<T> {
pub const fn new(value: T) -> CopiedNode<T> {
CopiedNode(value)
}
}
#[derive(Default)]
pub struct DefaultNode<T>(PhantomData<T>);
impl<'i, T: Default + 'i, I> Node<'i, I> for DefaultNode<T> {
type Output = T;
fn eval(&'i self, _input: I) -> Self::Output {
T::default()
}
}
impl<T> DefaultNode<T> {
pub fn new() -> Self {
Self(PhantomData)
}
}
#[repr(C)]
/// Return the unit value
#[derive(Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
pub struct ForgetNode;
impl<'i, T: 'i> Node<'i, T> for ForgetNode {
type Output = ();
fn eval(&'i self, _input: T) -> Self::Output {}
}
impl ForgetNode {
pub const fn new() -> Self {
ForgetNode
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_int_node() {
let node = IntNode::<5>;
assert_eq!(node.eval(()), 5);
}
#[test]
fn test_value_node() {
let node = ValueNode::new(5);
assert_eq!(node.eval(()), &5);
let type_erased = &node as &dyn for<'a> Node<'a, (), Output = &'a i32>;
assert_eq!(type_erased.eval(()), &5);
}
#[test]
fn test_default_node() {
let node = DefaultNode::<u32>::new();
assert_eq!(node.eval(42), 0);
}
#[test]
#[allow(clippy::unit_cmp)]
fn test_unit_node() {
let node = ForgetNode::new();
assert_eq!(node.eval(()), ());
}
}