Context nullification, cached monitor nodes

This commit is contained in:
Adam
2025-07-10 01:47:40 -07:00
parent 1398405529
commit cf0a32b9b1
82 changed files with 2235 additions and 1684 deletions

View File

@@ -1,4 +1,4 @@
use crate::{Ctx, ExtractAnimationTime, ExtractTime};
use crate::{Ctx, ExtractAnimationTime, ExtractRealTime};
const DAY: f64 = 1000. * 3600. * 24.;
@@ -21,8 +21,8 @@ pub enum AnimationTimeMode {
}
#[node_macro::node(category("Animation"))]
fn real_time(ctx: impl Ctx + ExtractTime, _primary: (), mode: RealTimeMode) -> f64 {
let time = ctx.try_time().unwrap_or_default();
fn real_time(ctx: impl Ctx + ExtractRealTime, _primary: (), mode: RealTimeMode) -> f64 {
let time = ctx.try_real_time().unwrap_or_default();
// TODO: Implement proper conversion using and existing time implementation
match mode {
RealTimeMode::Utc => time,

View File

@@ -1,13 +1,13 @@
use glam::{DAffine2, UVec2};
use crate::transform::Footprint;
use std::any::Any;
use std::borrow::Borrow;
use std::panic::Location;
use std::sync::Arc;
pub trait Ctx: Clone + Send {}
pub trait ExtractFootprint {
#[track_caller]
fn try_footprint(&self) -> Option<&Footprint>;
#[track_caller]
fn footprint(&self) -> &Footprint {
@@ -18,8 +18,12 @@ pub trait ExtractFootprint {
}
}
pub trait ExtractTime {
fn try_time(&self) -> Option<f64>;
pub trait ExtractDownstreamTransform {
fn try_downstream_transform(&self) -> Option<&DAffine2>;
}
pub trait ExtractRealTime {
fn try_real_time(&self) -> Option<f64>;
}
pub trait ExtractAnimationTime {
@@ -42,9 +46,32 @@ pub trait CloneVarArgs: ExtractVarArgs {
fn arc_clone(&self) -> Option<Arc<dyn ExtractVarArgs + Send + Sync>>;
}
pub trait ExtractAll: ExtractFootprint + ExtractIndex + ExtractTime + ExtractAnimationTime + ExtractVarArgs {}
pub trait ExtractAll: ExtractFootprint + ExtractDownstreamTransform + ExtractIndex + ExtractRealTime + ExtractAnimationTime + ExtractVarArgs {}
impl<T: ?Sized + ExtractFootprint + ExtractIndex + ExtractTime + ExtractAnimationTime + ExtractVarArgs> ExtractAll for T {}
impl<T: ?Sized + ExtractFootprint + ExtractDownstreamTransform + ExtractIndex + ExtractRealTime + ExtractAnimationTime + ExtractVarArgs> ExtractAll for T {}
#[derive(Debug, Clone, PartialEq)]
pub enum ContextDependency {
ExtractFootprint,
// Can be used by cull nodes to check if the final output would be outside the footprint viewport
ExtractDownstreamTransform,
ExtractRealTime,
ExtractAnimationTime,
ExtractIndex,
ExtractVarArgs,
}
pub fn all_context_dependencies() -> Vec<ContextDependency> {
vec![
ContextDependency::ExtractFootprint,
// Can be used by cull nodes to check if the final output would be outside the footprint viewport
ContextDependency::ExtractDownstreamTransform,
ContextDependency::ExtractRealTime,
ContextDependency::ExtractAnimationTime,
ContextDependency::ExtractIndex,
ContextDependency::ExtractVarArgs,
]
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum VarArgsResult {
@@ -72,17 +99,30 @@ impl<T: ExtractFootprint + Sync> ExtractFootprint for Option<T> {
fn try_footprint(&self) -> Option<&Footprint> {
self.as_ref().and_then(|x| x.try_footprint())
}
#[track_caller]
fn footprint(&self) -> &Footprint {
self.try_footprint().unwrap_or_else(|| {
log::warn!("trying to extract footprint from context None {} ", Location::caller());
&Footprint::DEFAULT
})
}
impl ExtractDownstreamTransform for () {
fn try_downstream_transform(&self) -> Option<&DAffine2> {
log::error!("tried to extract downstream transform form (), {}", Location::caller());
None
}
}
impl<T: ExtractTime + Sync> ExtractTime for Option<T> {
fn try_time(&self) -> Option<f64> {
self.as_ref().and_then(|x| x.try_time())
impl<T: ExtractDownstreamTransform + Ctx + Sync + Send> ExtractDownstreamTransform for &T {
fn try_downstream_transform(&self) -> Option<&DAffine2> {
(*self).try_downstream_transform()
}
}
impl<T: ExtractDownstreamTransform + Sync> ExtractDownstreamTransform for Option<T> {
fn try_downstream_transform(&self) -> Option<&DAffine2> {
self.as_ref().and_then(|x| x.try_downstream_transform())
}
}
impl<T: ExtractRealTime + Sync> ExtractRealTime for Option<T> {
fn try_real_time(&self) -> Option<f64> {
self.as_ref().and_then(|x| x.try_real_time())
}
}
impl<T: ExtractAnimationTime + Sync> ExtractAnimationTime for Option<T> {
@@ -111,9 +151,16 @@ impl<T: ExtractFootprint + Sync> ExtractFootprint for Arc<T> {
(**self).try_footprint()
}
}
impl<T: ExtractTime + Sync> ExtractTime for Arc<T> {
fn try_time(&self) -> Option<f64> {
(**self).try_time()
impl<T: ExtractDownstreamTransform + Sync> ExtractDownstreamTransform for Arc<T> {
fn try_downstream_transform(&self) -> Option<&DAffine2> {
(**self).try_downstream_transform()
}
}
impl<T: ExtractRealTime + Sync> ExtractRealTime for Arc<T> {
fn try_real_time(&self) -> Option<f64> {
(**self).try_real_time()
}
}
impl<T: ExtractAnimationTime + Sync> ExtractAnimationTime for Arc<T> {
@@ -156,43 +203,22 @@ impl<T: CloneVarArgs + Sync> CloneVarArgs for Arc<T> {
}
}
impl Ctx for ContextImpl<'_> {}
impl Ctx for Arc<OwnedContextImpl> {}
impl ExtractFootprint for ContextImpl<'_> {
fn try_footprint(&self) -> Option<&Footprint> {
self.footprint
}
}
impl ExtractTime for ContextImpl<'_> {
fn try_time(&self) -> Option<f64> {
self.time
}
}
impl ExtractIndex for ContextImpl<'_> {
fn try_index(&self) -> Option<Vec<usize>> {
self.index.clone()
}
}
impl ExtractVarArgs for ContextImpl<'_> {
fn vararg(&self, index: usize) -> Result<DynRef<'_>, VarArgsResult> {
let Some(inner) = self.varargs else { return Err(VarArgsResult::NoVarArgs) };
inner.get(index).ok_or(VarArgsResult::IndexOutOfBounds).copied()
}
fn varargs_len(&self) -> Result<usize, VarArgsResult> {
let Some(inner) = self.varargs else { return Err(VarArgsResult::NoVarArgs) };
Ok(inner.len())
}
}
impl ExtractFootprint for OwnedContextImpl {
fn try_footprint(&self) -> Option<&Footprint> {
self.footprint.as_ref()
}
}
impl ExtractTime for OwnedContextImpl {
fn try_time(&self) -> Option<f64> {
impl ExtractDownstreamTransform for OwnedContextImpl {
fn try_downstream_transform(&self) -> Option<&DAffine2> {
self.downstream_transform.as_ref()
}
}
impl ExtractRealTime for OwnedContextImpl {
fn try_real_time(&self) -> Option<f64> {
self.real_time
}
}
@@ -234,20 +260,26 @@ impl CloneVarArgs for Arc<OwnedContextImpl> {
}
}
// Lifetime isnt necessary?
pub type Context<'a> = Option<Arc<OwnedContextImpl>>;
type DynRef<'a> = &'a (dyn Any + Send + Sync);
type DynBox = Box<dyn Any + Send + Sync>;
#[derive(dyn_any::DynAny)]
pub struct OwnedContextImpl {
// The footprint represents the document to viewport render metadata
footprint: Option<Footprint>,
varargs: Option<Arc<[DynBox]>>,
parent: Option<Arc<dyn ExtractVarArgs + Sync + Send>>,
// This could be converted into a single enum to save extra bytes
index: Option<Vec<usize>>,
// The transform node does not modify the document to viewport, it instead modifies this,
// which can be used to transform the evaluated data from the node and check if it is within the
// document to viewport transform.
downstream_transform: Option<DAffine2>,
index: Option<usize>,
real_time: Option<f64>,
animation_time: Option<f64>,
// varargs: Option<(Vec<String>, Arc<[DynBox]>)>,
varargs: Option<Arc<[DynBox]>>,
parent: Option<Arc<dyn ExtractVarArgs + Sync + Send>>,
}
impl std::fmt::Debug for OwnedContextImpl {
@@ -285,8 +317,9 @@ impl OwnedContextImpl {
#[track_caller]
pub fn from<T: ExtractAll + CloneVarArgs>(value: T) -> Self {
let footprint = value.try_footprint().copied();
let downstream_transform = value.try_downstream_transform().copied();
let index = value.try_index();
let time = value.try_time();
let time = value.try_real_time();
let frame_time = value.try_animation_time();
let parent = match value.varargs_len() {
Ok(x) if x > 0 => value.arc_clone(),
@@ -294,21 +327,40 @@ impl OwnedContextImpl {
};
OwnedContextImpl {
footprint,
varargs: None,
parent,
downstream_transform,
index,
real_time: time,
animation_time: frame_time,
varargs: None,
parent,
}
}
pub const fn empty() -> Self {
OwnedContextImpl {
footprint: None,
varargs: None,
parent: None,
downstream_transform: None,
index: None,
real_time: None,
animation_time: None,
varargs: None,
parent: None,
}
}
pub fn nullify(&mut self, nullify: &Vec<ContextDependency>) {
for context_dependency in nullify {
match context_dependency {
ContextDependency::ExtractFootprint => self.footprint = None,
ContextDependency::ExtractDownstreamTransform => self.downstream_transform = None,
ContextDependency::ExtractRealTime => self.real_time = None,
ContextDependency::ExtractAnimationTime => self.animation_time = None,
ContextDependency::ExtractIndex => self.index = None,
ContextDependency::ExtractVarArgs => {
self.varargs = None;
self.parent = None
}
}
}
}
}
@@ -317,10 +369,31 @@ impl OwnedContextImpl {
pub fn set_footprint(&mut self, footprint: Footprint) {
self.footprint = Some(footprint);
}
pub fn set_downstream_transform(&mut self, transform: DAffine2) {
self.downstream_transform = Some(transform);
}
pub fn try_apply_downstream_transform(&mut self, transform: DAffine2) {
if let Some(downstream_transform) = self.downstream_transform {
self.downstream_transform = Some(downstream_transform * transform);
}
}
pub fn set_real_time(&mut self, time: f64) {
self.real_time = Some(time);
}
pub fn set_animation_time(&mut self, animation_time: f64) {
self.animation_time = Some(animation_time);
}
pub fn set_index(&mut self, index: usize) {
self.index = Some(index);
}
pub fn with_footprint(mut self, footprint: Footprint) -> Self {
self.footprint = Some(footprint);
self
}
pub fn with_downstream_transform(mut self, downstream_transform: DAffine2) -> Self {
self.downstream_transform = Some(downstream_transform);
self
}
pub fn with_real_time(mut self, time: f64) -> Self {
self.real_time = Some(time);
self
@@ -329,11 +402,6 @@ impl OwnedContextImpl {
self.animation_time = Some(animation_time);
self
}
pub fn with_vararg(mut self, value: Box<dyn Any + Send + Sync>) -> Self {
assert!(self.varargs.is_none_or(|value| value.is_empty()));
self.varargs = Some(Arc::new([value]));
self
}
pub fn with_index(mut self, index: usize) -> Self {
if let Some(current_index) = &mut self.index {
current_index.push(index);
@@ -345,31 +413,193 @@ impl OwnedContextImpl {
pub fn into_context(self) -> Option<Arc<Self>> {
Some(Arc::new(self))
}
pub fn add_vararg(mut self, _variable_name: String, value: Box<dyn Any + Send + Sync>) -> Self {
assert!(self.varargs.is_none_or(|value| value.is_empty()));
// self.varargs = Some((vec![variable_name], Arc::new([value])));
self.varargs = Some(Arc::new([value]));
self
}
pub fn set_varargs(&mut self, var_args: (Vec<String>, Arc<[DynBox]>)) {
self.varargs = Some(var_args.1)
}
pub fn with_vararg(mut self, var_args: (impl Into<String>, DynBox)) -> Self {
self.varargs = Some(Arc::new([var_args.1]));
self
}
pub fn erase_parent(mut self) -> Self {
self.parent = None;
self
}
}
#[derive(Default, Clone, dyn_any::DynAny)]
pub struct ContextImpl<'a> {
pub(crate) footprint: Option<&'a Footprint>,
varargs: Option<&'a [DynRef<'a>]>,
// This could be converted into a single enum to save extra bytes
index: Option<Vec<usize>>,
time: Option<f64>,
// #[derive(Default, Clone, Copy, dyn_any::DynAny)]
// pub struct ContextImpl<'a> {
// pub(crate) footprint: Option<&'a Footprint>,
// varargs: Option<&'a [DynRef<'a>]>,
// // This could be converted into a single enum to save extra bytes
// index: Option<usize>,
// time: Option<f64>,
// }
// impl<'a> ContextImpl<'a> {
// pub fn with_footprint<'f>(&self, new_footprint: &'f Footprint, varargs: Option<&'f impl Borrow<[DynRef<'f>]>>) -> ContextImpl<'f>
// where
// 'a: 'f,
// {
// ContextImpl {
// footprint: Some(new_footprint),
// varargs: varargs.map(|x| x.borrow()),
// ..*self
// }
// }
// }
#[node_macro::node(category("Context Getter"))]
fn get_footprint(ctx: impl Ctx + ExtractFootprint) -> Option<Footprint> {
ctx.try_footprint().copied()
}
impl<'a> ContextImpl<'a> {
pub fn with_footprint<'f>(&self, new_footprint: &'f Footprint, varargs: Option<&'f impl Borrow<[DynRef<'f>]>>) -> ContextImpl<'f>
where
'a: 'f,
{
ContextImpl {
footprint: Some(new_footprint),
varargs: varargs.map(|x| x.borrow()),
index: self.index.clone(),
..*self
}
}
#[node_macro::node(category("Context Getter"))]
fn get_document_to_viewport(ctx: impl Ctx + ExtractFootprint) -> Option<DAffine2> {
ctx.try_footprint().map(|footprint| footprint.transform.clone())
}
#[node_macro::node(category("Context Getter"))]
fn get_resolution(ctx: impl Ctx + ExtractFootprint) -> Option<UVec2> {
ctx.try_footprint().map(|footprint| footprint.resolution.clone())
}
#[node_macro::node(category("Context Getter"))]
fn get_downstream_transform(ctx: impl Ctx + ExtractDownstreamTransform) -> Option<DAffine2> {
ctx.try_downstream_transform().copied()
}
#[node_macro::node(category("Context Getter"))]
fn get_real_time(ctx: impl Ctx + ExtractRealTime) -> Option<f64> {
ctx.try_real_time()
}
#[node_macro::node(category("Context Getter"))]
fn get_animation_time(ctx: impl Ctx + ExtractAnimationTime) -> Option<f64> {
ctx.try_animation_time()
}
#[node_macro::node(category("Context Getter"))]
fn get_index(ctx: impl Ctx + ExtractIndex) -> Option<u32> {
ctx.try_index().map(|index| index as u32)
}
// #[node_macro::node(category("Loop"))]
// async fn loop_node<T: Default>(
// ctx: impl Ctx + CloneVarArgs + ExtractAll,
// #[implementations(
// Context -> Option<u32>,
// )]
// return_if_some: impl Node<Context<'static>, Output = Option<T>>,
// #[implementations(
// Context -> (),
// )]
// run_if_none: impl Node<Context<'static>, Output = ()>,
// ) -> T {
// let mut context = OwnedContextImpl::from(ctx.clone());
// context.arc_mutex = Some(Arc::new(Mutex::new(None)));
// loop {
// if let Some(return_value) = return_if_some.eval(context.clone().into_context()).await {
// return return_value;
// }
// run_if_none.eval(context.clone().into_context()).await;
// let Some(context_after_loop) = context.arc_mutex.unwrap().lock().unwrap().take() else {
// log::error!("Loop context was not set, breaking loop to avoid infinite loop");
// return T::default();
// };
// context = context_after_loop;
// context.arc_mutex = Some(Arc::new(Mutex::new(None)));
// }
// }
// #[node_macro::node(category("Loop"))]
// async fn update_loop_node_context(ctx: impl Ctx + ExtractAll + CloneVarArgs + Sync) -> () {
// let mut context = OwnedContextImpl::from(ctx.clone());
// let context_after_loop = OwnedContextImpl::from(ctx.clone());
// if let Some(arc_mutex) = context.arc_mutex.as_ref() {
// *arc_mutex.lock().unwrap() = Some(context_after_loop);
// }
// }
#[node_macro::node(category("Loop"))]
async fn set_index<T: 'n + 'static>(
ctx: impl Ctx + ExtractAll + CloneVarArgs + Sync,
#[expose]
#[implementations(
Context -> u32,
Context -> (),
)]
input: impl Node<Context<'static>, Output = T>,
number: u32,
) -> T {
let mut new_context = OwnedContextImpl::from(ctx);
new_context.index = Some(number.try_into().unwrap());
input.eval(new_context.into_context()).await
}
// #[node_macro::node(category("Loop"))]
// fn create_arc_mutex(_ctx: impl Ctx) -> Arc<Mutex<Option<OwnedContextImpl>>> {
// Arc::new(Mutex::new(0))
// }
// #[node_macro::node(category("Loop"))]
// fn get_arc_mutex(ctx: impl Ctx + ExtractArcMutex) -> Option<Arc<Mutex<Option<OwnedContextImpl>>>> {
// ctx.try_arc_mutex()
// }
// #[node_macro::node(category("Loop"))]
// async fn set_arc_mutex<T: 'n + 'static>(
// ctx: impl Ctx + ExtractAll + CloneVarArgs + Sync,
// // Auto generate for each tagged value type
// #[expose]
// #[implementations(
// Context -> u32,
// )]
// input: impl Node<Context<'static>, Output = T>,
// arc_mutex: Arc<Mutex<Option<OwnedContextImpl>>>,
// ) -> T {
// let mut new_context = OwnedContextImpl::from(ctx);
// new_context.arc_mutex = Some(arc_mutex);
// input.eval(new_context.into_context()).await
// }
// // TODO: Discard node + return () for loop if none branch
// #[node_macro::node(category("Loop"))]
// fn set_arc_mutex_value<T>(_ctx: impl Ctx, #[implementations(Arc<Mutex<u32>>)] arc_mutex: Arc<Mutex<T>>, #[implementations(u32)] value: T) -> () {
// let mut guard = arc_mutex.lock().unwrap(); // lock the mutex
// *guard = value;
// }
// #[node_macro::node(category("Loop"))]
// fn get_context(ctx: impl Ctx + ExtractAll + CloneVarArgs + Sync) -> OwnedContextImpl {
// OwnedContextImpl::from(ctx)
// }
// #[node_macro::node(category("Loop"))]
// fn discard(_ctx: impl Ctx, _input: u32) -> () {}
#[node_macro::node(category("Debug"))]
fn is_none<T>(_: impl Ctx, #[implementations(Option<f64>, Option<f32>, Option<u32>, Option<u64>, Option<String>)] input: Option<T>) -> bool {
input.is_none()
}
// #[node_macro::node(category("Debug"))]
// fn unwrap<T>(_: impl Ctx, #[implementations(Option<f64>, Option<f32>, Option<u32>, Option<u64>, Option<String>, Option<Vec<u32>>)] input: Option<T>) -> T {
// input.unwrap()
// }
#[node_macro::node(category("Debug"))]
fn to_option<T>(_: impl Ctx, boolean: bool, #[implementations(u32)] input: T) -> Option<T> {
boolean.then(|| input)
}
#[node_macro::node(category("Debug"))]
fn to_usize(_: impl Ctx, u32: u32) -> usize {
u32.try_into().unwrap()
}

View File

@@ -62,7 +62,7 @@ pub trait Node<'i, Input> {
/// Get the call argument or output data for the monitor node on the next evaluation after set_introspect_input
/// Also returns a boolean of whether the node was evaluated
fn introspect(&self, _introspect_mode: IntrospectMode) -> Option<Box<dyn std::any::Any + Send + Sync>> {
fn introspect(&self, _introspect_mode: IntrospectMode) -> Option<std::sync::Arc<dyn std::any::Any + Send + Sync>> {
log::warn!("Node::introspect not implemented for {}", std::any::type_name::<Self>());
None
}

View File

@@ -7,6 +7,88 @@ use std::ops::Deref;
use std::sync::Arc;
use std::sync::Mutex;
/// Caches the output of a given Node and acts as a proxy
#[derive(Default)]
pub struct MonitorMemoNode<T, CachedNode> {
// Introspection cache, uses the hash of the nullified context with default var args
// cache: Arc<Mutex<std::collections::HashMap<u64, Arc<T>>>>,
// Return value cache,
cache: Arc<Mutex<Option<(u64, Arc<T>)>>>,
node: CachedNode,
changed_since_last_eval: Arc<Mutex<bool>>,
}
impl<'i, I: Hash + 'i + std::fmt::Debug, T: 'static + Clone + Send + Sync, CachedNode: 'i> Node<'i, I> for MonitorMemoNode<T, CachedNode>
where
CachedNode: for<'any_input> Node<'any_input, I>,
for<'a> <CachedNode as Node<'a, I>>::Output: Future<Output = T> + WasmNotSend,
{
// 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 accurate xD
type Output = DynFuture<'i, T>;
// fn eval(&'i self, input: I) -> Self::Output {
// let mut hasher = DefaultHasher::new();
// input.hash(&mut hasher);
// let hash = hasher.finish();
// if let Some(data) = self.cache.lock().unwrap().get(&hash) {
// let cloned_data = (**data).clone();
// Box::pin(async move { cloned_data })
// } else {
// let fut = self.node.eval(input);
// let cache = self.cache.clone();
// Box::pin(async move {
// let value = fut.await;
// cache.lock().unwrap().insert(hash, Arc::new(value.clone()));
// value
// })
// }
// }
// fn introspect(&self, _introspect_mode: IntrospectMode) -> Option<std::sync::Arc<dyn std::any::Any + Send + Sync>> {
// let mut hasher = DefaultHasher::new();
// OwnedContextImpl::default().into_context().hash(&mut hasher);
// let hash = hasher.finish();
// self.cache.lock().unwrap().get(&hash).map(|data| (*data).clone() as Arc<dyn std::any::Any + Send + Sync>)
// }
fn eval(&'i self, input: I) -> Self::Output {
let mut hasher = DefaultHasher::new();
input.hash(&mut hasher);
let hash = hasher.finish();
if let Some(data) = self.cache.lock().as_ref().unwrap().as_ref().and_then(|data| (data.0 == hash).then_some(data.1.clone())) {
let cloned_data = (*data).clone();
Box::pin(async move { cloned_data })
} else {
let fut = self.node.eval(input);
let cache = self.cache.clone();
*self.changed_since_last_eval.lock().unwrap() = true;
Box::pin(async move {
let value = fut.await;
*cache.lock().unwrap() = Some((hash, Arc::new(value.clone())));
value
})
}
}
fn introspect(&self, _introspect_mode: IntrospectMode) -> Option<std::sync::Arc<dyn std::any::Any + Send + Sync>> {
if *self.changed_since_last_eval.lock().unwrap() {
*self.changed_since_last_eval.lock().unwrap() = false;
Some(self.cache.lock().unwrap().as_ref().expect("Cached data should always be evaluated before introspection").1.clone() as Arc<dyn std::any::Any + Send + Sync>)
} else {
None
}
}
}
impl<T, CachedNode> MonitorMemoNode<T, CachedNode> {
pub fn new(node: CachedNode) -> MonitorMemoNode<T, CachedNode> {
MonitorMemoNode {
cache: Default::default(),
node,
changed_since_last_eval: Arc::new(Mutex::new(true)),
}
}
}
/// Caches the output of a given Node and acts as a proxy
#[derive(Default)]
pub struct MemoNode<T, CachedNode> {
@@ -107,7 +189,7 @@ pub mod impure_memo {
pub const IDENTIFIER: crate::ProtoNodeIdentifier = crate::ProtoNodeIdentifier::new("graphene_core::memo::ImpureMemoNode");
}
#[derive(Copy, Clone, Debug, serde::Serialize, serde::Deserialize)]
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
pub enum IntrospectMode {
Input,
Data,
@@ -117,8 +199,8 @@ pub enum IntrospectMode {
#[derive(Default)]
pub struct MonitorNode<I, O, N> {
#[allow(clippy::type_complexity)]
input: Arc<Mutex<Option<Box<I>>>>,
output: Arc<Mutex<Option<Box<O>>>>,
input: Arc<Mutex<Option<Arc<I>>>>,
output: Arc<Mutex<Option<Arc<O>>>>,
// Gets set to true by the editor when before evaluating the network, then reset when the monitor node is evaluated
introspect_input: Arc<Mutex<bool>>,
introspect_output: Arc<Mutex<bool>>,
@@ -137,12 +219,12 @@ where
let output = self.node.eval(input.clone()).await;
let mut introspect_input = self.introspect_input.lock().unwrap();
if *introspect_input {
*self.input.lock().unwrap() = Some(Box::new(input));
*self.input.lock().unwrap() = Some(Arc::new(input));
*introspect_input = false;
}
let mut introspect_output = self.introspect_output.lock().unwrap();
if *introspect_output {
*self.output.lock().unwrap() = Some(Box::new(output.clone()));
*self.output.lock().unwrap() = Some(Arc::new(output.clone()));
*introspect_output = false;
}
output
@@ -150,10 +232,10 @@ where
}
// After introspecting, the input/output get set to None because the Arc is moved to the editor where it can be directly accessed.
fn introspect(&self, introspect_mode: IntrospectMode) -> Option<Box<dyn std::any::Any + Send + Sync>> {
fn introspect(&self, introspect_mode: IntrospectMode) -> Option<Arc<dyn std::any::Any + Send + Sync>> {
match introspect_mode {
IntrospectMode::Input => self.input.lock().unwrap().take().map(|input| input as Box<dyn std::any::Any + Send + Sync>),
IntrospectMode::Data => self.output.lock().unwrap().take().map(|output| output as Box<dyn std::any::Any + Send + Sync>),
IntrospectMode::Input => self.input.lock().unwrap().take().map(|input| input as Arc<dyn std::any::Any + Send + Sync>),
IntrospectMode::Data => self.output.lock().unwrap().take().map(|output| output as Arc<dyn std::any::Any + Send + Sync>),
}
}

View File

@@ -109,6 +109,8 @@ pub static NODE_REGISTRY: NodeRegistry = LazyLock::new(|| Mutex::new(HashMap::ne
pub static NODE_METADATA: LazyLock<Mutex<HashMap<ProtoNodeIdentifier, NodeMetadata>>> = LazyLock::new(|| Mutex::new(HashMap::new()));
pub static NODE_CONTEXT_DEPENDENCY: LazyLock<Mutex<HashMap<String, Vec<crate::ContextDependency>>>> = LazyLock::new(|| Mutex::new(HashMap::new()));
#[cfg(not(target_arch = "wasm32"))]
pub type DynFuture<'n, T> = Pin<Box<dyn Future<Output = T> + 'n + Send>>;
#[cfg(target_arch = "wasm32")]
@@ -132,7 +134,7 @@ pub type TypeErasedPinned<'n> = Pin<Box<TypeErasedNode<'n>>>;
pub type SharedNodeContainer = std::sync::Arc<NodeContainer>;
pub type NodeConstructor = fn(Vec<SharedNodeContainer>) -> DynFuture<'static, TypeErasedBox<'static>>;
pub type MonitorConstructor = fn(SharedNodeContainer) -> TypeErasedBox<'static>;
pub type CacheConstructor = fn(SharedNodeContainer) -> TypeErasedBox<'static>;
#[derive(Clone)]
pub struct NodeContainer {
@@ -277,17 +279,24 @@ where
type Output = FutureAny<'input>;
#[inline]
fn eval(&'input self, input: Any<'input>) -> Self::Output {
let node_name = std::any::type_name::<N>();
let output = |input| {
let result = self.node.eval(input);
async move { Box::new(result.await) as Any<'input> }
};
match dyn_any::downcast(input) {
Ok(input) => Box::pin(output(*input)),
Err(e) => panic!("DynAnyNode Input, {0} in:\n{1}", e, node_name),
Err(e) => panic!("DynAnyNode Input, {0} in:\n{1}", e, std::any::type_name::<N>()),
}
}
fn introspect(&self, introspect_mode: crate::IntrospectMode) -> Option<std::sync::Arc<dyn std::any::Any + Send + Sync>> {
self.node.introspect(introspect_mode)
}
fn set_introspect(&self, introspect_mode: crate::IntrospectMode) {
self.node.set_introspect(introspect_mode);
}
fn reset(&self) {
self.node.reset();
}

View File

@@ -1,4 +1,4 @@
use crate::registry::Node;
use crate::Node;
use std::marker::PhantomData;
/// This is how we can generically define composition of two nodes.

View File

@@ -91,5 +91,5 @@ pub type SNI = NodeId;
// An input of a compiled protonode, used to reference thumbnails, which are stored on a per input basis
pub type CompiledProtonodeInput = (NodeId, usize);
// Path to the protonode in the document network
pub type ProtonodePath = Box<[NodeId]>;
// Path to the protonode in the wrapped network (document network prefixed with NodeId(0))
pub type ProtonodePath = Vec<NodeId>;

View File

@@ -22,7 +22,7 @@ async fn instance_on_points<T: Into<GraphicElement> + Default + Send + Clone + '
let mut iteration = async |index, point| {
let transformed_point = transform.transform_point2(point);
let new_ctx = OwnedContextImpl::from(ctx.clone()).with_index(index).with_vararg(Box::new(transformed_point));
let new_ctx = OwnedContextImpl::from(ctx.clone()).with_index(index).with_vararg(("Transformed point", Box::new(transformed_point)));
let generated_instance = instance.eval(new_ctx.into_context()).await;
for mut instanced in generated_instance.instance_iter() {