mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-19 02:48:12 +08:00
Context nullification, cached monitor nodes
This commit is contained in:
@@ -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,
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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>),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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>;
|
||||
|
||||
@@ -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() {
|
||||
|
||||
Reference in New Issue
Block a user