Add 'Freeze Real Time' and 'Boundless Footprint' nodes as alternatives to using 'Memoize Impure' (#2509)
* WIP debugging * Only create parent ref if var args are used in context + Cleanup * Eval nodes with None instead of relying on MemoImpure * Remove unused imports * Show parent in debug output * Remove TODO comment
This commit is contained in:
parent
bc03941174
commit
8d8e2edc5f
|
|
@ -705,7 +705,7 @@ mod test_transform_layer {
|
||||||
use crate::messages::portfolio::document::graph_operation::transform_utils;
|
use crate::messages::portfolio::document::graph_operation::transform_utils;
|
||||||
use crate::test_utils::test_prelude::*;
|
use crate::test_utils::test_prelude::*;
|
||||||
// Use ModifyInputsContext to locate the transform node
|
// Use ModifyInputsContext to locate the transform node
|
||||||
use crate::messages::portfolio::document::graph_operation::utility_types::{ModifyInputsContext, TransformIn};
|
use crate::messages::portfolio::document::graph_operation::utility_types::ModifyInputsContext;
|
||||||
use crate::messages::prelude::Message;
|
use crate::messages::prelude::Message;
|
||||||
use glam::DAffine2;
|
use glam::DAffine2;
|
||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ pub trait ExtractFootprint {
|
||||||
fn footprint(&self) -> &Footprint {
|
fn footprint(&self) -> &Footprint {
|
||||||
self.try_footprint().unwrap_or_else(|| {
|
self.try_footprint().unwrap_or_else(|| {
|
||||||
log::error!("Context did not have a footprint, called from: {}", Location::caller());
|
log::error!("Context did not have a footprint, called from: {}", Location::caller());
|
||||||
&const { Footprint::empty() }
|
&Footprint::DEFAULT
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -76,7 +76,7 @@ impl<T: ExtractFootprint + Sync> ExtractFootprint for Option<T> {
|
||||||
fn footprint(&self) -> &Footprint {
|
fn footprint(&self) -> &Footprint {
|
||||||
self.try_footprint().unwrap_or_else(|| {
|
self.try_footprint().unwrap_or_else(|| {
|
||||||
log::warn!("trying to extract footprint from context None {} ", Location::caller());
|
log::warn!("trying to extract footprint from context None {} ", Location::caller());
|
||||||
&const { Footprint::empty() }
|
&Footprint::DEFAULT
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -193,7 +193,7 @@ impl ExtractFootprint for OwnedContextImpl {
|
||||||
}
|
}
|
||||||
impl ExtractTime for OwnedContextImpl {
|
impl ExtractTime for OwnedContextImpl {
|
||||||
fn try_time(&self) -> Option<f64> {
|
fn try_time(&self) -> Option<f64> {
|
||||||
self.time
|
self.real_time
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl ExtractAnimationTime for OwnedContextImpl {
|
impl ExtractAnimationTime for OwnedContextImpl {
|
||||||
|
|
@ -245,10 +245,23 @@ pub struct OwnedContextImpl {
|
||||||
parent: Option<Arc<dyn ExtractVarArgs + Sync + Send>>,
|
parent: Option<Arc<dyn ExtractVarArgs + Sync + Send>>,
|
||||||
// This could be converted into a single enum to save extra bytes
|
// This could be converted into a single enum to save extra bytes
|
||||||
index: Option<usize>,
|
index: Option<usize>,
|
||||||
time: Option<f64>,
|
real_time: Option<f64>,
|
||||||
animation_time: Option<f64>,
|
animation_time: Option<f64>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl core::fmt::Debug for OwnedContextImpl {
|
||||||
|
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||||
|
f.debug_struct("OwnedContextImpl")
|
||||||
|
.field("footprint", &self.footprint)
|
||||||
|
.field("varargs", &self.varargs)
|
||||||
|
.field("parent", &self.parent.as_ref().map(|_| "<Parent>"))
|
||||||
|
.field("index", &self.index)
|
||||||
|
.field("real_time", &self.real_time)
|
||||||
|
.field("animation_time", &self.animation_time)
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Default for OwnedContextImpl {
|
impl Default for OwnedContextImpl {
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
|
|
@ -259,10 +272,11 @@ impl Default for OwnedContextImpl {
|
||||||
impl core::hash::Hash for OwnedContextImpl {
|
impl core::hash::Hash for OwnedContextImpl {
|
||||||
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
|
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
|
||||||
self.footprint.hash(state);
|
self.footprint.hash(state);
|
||||||
self.index.hash(state);
|
|
||||||
self.time.map(|x| x.to_bits()).hash(state);
|
|
||||||
self.parent.as_ref().map(|x| Arc::as_ptr(x).addr()).hash(state);
|
|
||||||
self.varargs.as_ref().map(|x| Arc::as_ptr(x).addr()).hash(state);
|
self.varargs.as_ref().map(|x| Arc::as_ptr(x).addr()).hash(state);
|
||||||
|
self.parent.as_ref().map(|x| Arc::as_ptr(x).addr()).hash(state);
|
||||||
|
self.index.hash(state);
|
||||||
|
self.real_time.map(|x| x.to_bits()).hash(state);
|
||||||
|
self.animation_time.map(|x| x.to_bits()).hash(state);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -273,13 +287,16 @@ impl OwnedContextImpl {
|
||||||
let index = value.try_index();
|
let index = value.try_index();
|
||||||
let time = value.try_time();
|
let time = value.try_time();
|
||||||
let frame_time = value.try_animation_time();
|
let frame_time = value.try_animation_time();
|
||||||
let parent = value.arc_clone();
|
let parent = match value.varargs_len() {
|
||||||
|
Ok(x) if x > 0 => value.arc_clone(),
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
OwnedContextImpl {
|
OwnedContextImpl {
|
||||||
footprint,
|
footprint,
|
||||||
varargs: None,
|
varargs: None,
|
||||||
parent,
|
parent,
|
||||||
index,
|
index,
|
||||||
time,
|
real_time: time,
|
||||||
animation_time: frame_time,
|
animation_time: frame_time,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -289,7 +306,7 @@ impl OwnedContextImpl {
|
||||||
varargs: None,
|
varargs: None,
|
||||||
parent: None,
|
parent: None,
|
||||||
index: None,
|
index: None,
|
||||||
time: None,
|
real_time: None,
|
||||||
animation_time: None,
|
animation_time: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -303,8 +320,8 @@ impl OwnedContextImpl {
|
||||||
self.footprint = Some(footprint);
|
self.footprint = Some(footprint);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
pub fn with_time(mut self, time: f64) -> Self {
|
pub fn with_real_time(mut self, time: f64) -> Self {
|
||||||
self.time = Some(time);
|
self.real_time = Some(time);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
pub fn with_animation_time(mut self, animation_time: f64) -> Self {
|
pub fn with_animation_time(mut self, animation_time: f64) -> Self {
|
||||||
|
|
@ -314,6 +331,10 @@ impl OwnedContextImpl {
|
||||||
pub fn into_context(self) -> Option<Arc<Self>> {
|
pub fn into_context(self) -> Option<Arc<Self>> {
|
||||||
Some(Arc::new(self))
|
Some(Arc::new(self))
|
||||||
}
|
}
|
||||||
|
pub fn erase_parent(mut self) -> Self {
|
||||||
|
self.parent = None;
|
||||||
|
self
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, Clone, Copy, dyn_any::DynAny)]
|
#[derive(Default, Clone, Copy, dyn_any::DynAny)]
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@ where
|
||||||
let mut hasher = DefaultHasher::new();
|
let mut hasher = DefaultHasher::new();
|
||||||
input.hash(&mut hasher);
|
input.hash(&mut hasher);
|
||||||
let hash = hasher.finish();
|
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())) {
|
if let Some(data) = self.cache.lock().as_ref().unwrap().as_ref().and_then(|data| (data.0 == hash).then_some(data.1.clone())) {
|
||||||
Box::pin(async move { data })
|
Box::pin(async move { data })
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,8 @@ use crate::raster::bbox::AxisAlignedBbox;
|
||||||
use crate::raster::image::ImageFrameTable;
|
use crate::raster::image::ImageFrameTable;
|
||||||
use crate::vector::VectorDataTable;
|
use crate::vector::VectorDataTable;
|
||||||
use crate::{Artboard, ArtboardGroupTable, CloneVarArgs, Color, Context, Ctx, ExtractAll, GraphicGroupTable, OwnedContextImpl};
|
use crate::{Artboard, ArtboardGroupTable, CloneVarArgs, Color, Context, Ctx, ExtractAll, GraphicGroupTable, OwnedContextImpl};
|
||||||
use glam::{DAffine2, DVec2};
|
use core::f64;
|
||||||
|
use glam::{DAffine2, DMat2, DVec2};
|
||||||
|
|
||||||
pub trait Transform {
|
pub trait Transform {
|
||||||
fn transform(&self) -> DAffine2;
|
fn transform(&self) -> DAffine2;
|
||||||
|
|
@ -94,18 +95,26 @@ pub struct Footprint {
|
||||||
|
|
||||||
impl Default for Footprint {
|
impl Default for Footprint {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self::empty()
|
Self::DEFAULT
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Footprint {
|
impl Footprint {
|
||||||
pub const fn empty() -> Self {
|
pub const DEFAULT: Self = Self {
|
||||||
Self {
|
transform: DAffine2::IDENTITY,
|
||||||
transform: DAffine2::IDENTITY,
|
resolution: glam::UVec2::new(1920, 1080),
|
||||||
resolution: glam::UVec2::new(1920, 1080),
|
quality: RenderQuality::Full,
|
||||||
quality: RenderQuality::Full,
|
};
|
||||||
}
|
|
||||||
}
|
pub const BOUNDLESS: Self = Self {
|
||||||
|
transform: DAffine2 {
|
||||||
|
matrix2: DMat2::from_diagonal(DVec2::splat(f64::INFINITY)),
|
||||||
|
translation: DVec2::ZERO,
|
||||||
|
},
|
||||||
|
resolution: glam::UVec2::new(0, 0),
|
||||||
|
quality: RenderQuality::Full,
|
||||||
|
};
|
||||||
|
|
||||||
pub fn viewport_bounds_in_local_space(&self) -> AxisAlignedBbox {
|
pub fn viewport_bounds_in_local_space(&self) -> AxisAlignedBbox {
|
||||||
let inverse = self.transform.inverse();
|
let inverse = self.transform.inverse();
|
||||||
let start = inverse.transform_point2((0., 0.).into());
|
let start = inverse.transform_point2((0., 0.).into());
|
||||||
|
|
@ -198,3 +207,38 @@ fn replace_transform<Data, TransformInput: Transform>(
|
||||||
}
|
}
|
||||||
data
|
data
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[node_macro::node(category("Debug"))]
|
||||||
|
async fn boundless_footprint<T: 'n + 'static>(
|
||||||
|
ctx: impl Ctx + CloneVarArgs + ExtractAll,
|
||||||
|
#[implementations(
|
||||||
|
Context -> VectorDataTable,
|
||||||
|
Context -> GraphicGroupTable,
|
||||||
|
Context -> ImageFrameTable<Color>,
|
||||||
|
Context -> TextureFrameTable,
|
||||||
|
Context -> String,
|
||||||
|
Context -> f64,
|
||||||
|
)]
|
||||||
|
transform_target: impl Node<Context<'static>, Output = T>,
|
||||||
|
) -> T {
|
||||||
|
let ctx = OwnedContextImpl::from(ctx).with_footprint(Footprint::BOUNDLESS);
|
||||||
|
|
||||||
|
transform_target.eval(ctx.into_context()).await
|
||||||
|
}
|
||||||
|
#[node_macro::node(category("Debug"))]
|
||||||
|
async fn freeze_real_time<T: 'n + 'static>(
|
||||||
|
ctx: impl Ctx + CloneVarArgs + ExtractAll,
|
||||||
|
#[implementations(
|
||||||
|
Context -> VectorDataTable,
|
||||||
|
Context -> GraphicGroupTable,
|
||||||
|
Context -> ImageFrameTable<Color>,
|
||||||
|
Context -> TextureFrameTable,
|
||||||
|
Context -> String,
|
||||||
|
Context -> f64,
|
||||||
|
)]
|
||||||
|
transform_target: impl Node<Context<'static>, Output = T>,
|
||||||
|
) -> T {
|
||||||
|
let ctx = OwnedContextImpl::from(ctx).with_real_time(0.);
|
||||||
|
|
||||||
|
transform_target.eval(ctx.into_context()).await
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -237,7 +237,7 @@ async fn render<'a: 'n, T: 'n + GraphicElementRendered + WasmNotSend>(
|
||||||
let footprint = render_config.viewport;
|
let footprint = render_config.viewport;
|
||||||
let ctx = OwnedContextImpl::default()
|
let ctx = OwnedContextImpl::default()
|
||||||
.with_footprint(footprint)
|
.with_footprint(footprint)
|
||||||
.with_time(render_config.time.time)
|
.with_real_time(render_config.time.time)
|
||||||
.with_animation_time(render_config.time.animation_time.as_secs_f64())
|
.with_animation_time(render_config.time.animation_time.as_secs_f64())
|
||||||
.into_context();
|
.into_context();
|
||||||
ctx.footprint();
|
ctx.footprint();
|
||||||
|
|
@ -246,10 +246,10 @@ async fn render<'a: 'n, T: 'n + GraphicElementRendered + WasmNotSend>(
|
||||||
let render_params = RenderParams::new(render_config.view_mode, None, false, hide_artboards, for_export);
|
let render_params = RenderParams::new(render_config.view_mode, None, false, hide_artboards, for_export);
|
||||||
|
|
||||||
let data = data.eval(ctx.clone()).await;
|
let data = data.eval(ctx.clone()).await;
|
||||||
let editor_api = editor_api.eval(ctx.clone()).await;
|
let editor_api = editor_api.eval(None).await;
|
||||||
|
|
||||||
#[cfg(all(feature = "vello", target_arch = "wasm32"))]
|
#[cfg(all(feature = "vello", target_arch = "wasm32"))]
|
||||||
let surface_handle = _surface_handle.eval(ctx.clone()).await;
|
let surface_handle = _surface_handle.eval(None).await;
|
||||||
|
|
||||||
let use_vello = editor_api.editor_preferences.use_vello();
|
let use_vello = editor_api.editor_preferences.use_vello();
|
||||||
#[cfg(all(feature = "vello", target_arch = "wasm32"))]
|
#[cfg(all(feature = "vello", target_arch = "wasm32"))]
|
||||||
|
|
|
||||||
|
|
@ -307,6 +307,8 @@ fn node_registry() -> HashMap<ProtoNodeIdentifier, HashMap<NodeIOTypes, NodeCons
|
||||||
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => wgpu_executor::WindowHandle]),
|
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => wgpu_executor::WindowHandle]),
|
||||||
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => graphene_std::SurfaceFrame]),
|
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => graphene_std::SurfaceFrame]),
|
||||||
async_node!(graphene_core::memo::MemoNode<_, _>, input: UVec2, fn_params: [UVec2 => graphene_std::SurfaceFrame]),
|
async_node!(graphene_core::memo::MemoNode<_, _>, input: UVec2, fn_params: [UVec2 => graphene_std::SurfaceFrame]),
|
||||||
|
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => f64]),
|
||||||
|
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => String]),
|
||||||
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => RenderOutput]),
|
async_node!(graphene_core::memo::MemoNode<_, _>, input: Context, fn_params: [Context => RenderOutput]),
|
||||||
async_node!(graphene_core::memo::ImpureMemoNode<_, _, _>, input: Context, fn_params: [Context => GraphicElement]),
|
async_node!(graphene_core::memo::ImpureMemoNode<_, _, _>, input: Context, fn_params: [Context => GraphicElement]),
|
||||||
async_node!(graphene_core::memo::ImpureMemoNode<_, _, _>, input: Context, fn_params: [Context => GraphicGroupTable]),
|
async_node!(graphene_core::memo::ImpureMemoNode<_, _, _>, input: Context, fn_params: [Context => GraphicGroupTable]),
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue