mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-27 01:28:12 +08:00
Capture the input context snapshot on the monitor io record
This commit is contained in:
@@ -145,14 +145,15 @@ impl DynamicExecutor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Calls the `Node::serialize` for that specific node, returning for example the cached value for a monitor node. The node path must match the document node path.
|
/// Calls the `Node::serialize` for that specific node, returning for example the cached value for a monitor node. The node path must match the document node path.
|
||||||
/// A record capture materializes its element here against the arena,
|
/// A monitor's record capture materializes its element here against the
|
||||||
/// inside the introspection window, so consumers downcast the element
|
/// arena, inside the introspection window, so consumers downcast the
|
||||||
/// type directly.
|
/// element type directly. The captured input context stays on the
|
||||||
|
/// serialized io record for consumers that need it.
|
||||||
pub fn introspect(&self, node_path: &[NodeId]) -> Result<Arc<dyn std::any::Any + Send + Sync + 'static>, IntrospectError> {
|
pub fn introspect(&self, node_path: &[NodeId]) -> Result<Arc<dyn std::any::Any + Send + Sync + 'static>, IntrospectError> {
|
||||||
let result = self.tree.introspect(node_path)?;
|
let result = self.tree.introspect(node_path)?;
|
||||||
if let Some(capture) = result.downcast_ref::<core_types::record::RecordCapture>() {
|
if let Some(io) = result.downcast_ref::<core_types::memo::IORecord<core_types::context::CtxSnapshot, core_types::record::RecordCapture>>() {
|
||||||
let arena = self.arena.lock().unwrap_or_else(PoisonError::into_inner);
|
let arena = self.arena.lock().unwrap_or_else(PoisonError::into_inner);
|
||||||
return capture.materialize_element(&arena).map(Arc::from).ok_or(IntrospectError::NoData);
|
return io.output.materialize_element(&arena).map(Arc::from).ok_or(IntrospectError::NoData);
|
||||||
}
|
}
|
||||||
Ok(result)
|
Ok(result)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,13 @@ use std::hash::{Hash, Hasher};
|
|||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
/// Stores both what a node was called with and what it returned.
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct IORecord<I, O> {
|
||||||
|
pub input: I,
|
||||||
|
pub output: O,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct MemoHash<T: CacheHash> {
|
pub struct MemoHash<T: CacheHash> {
|
||||||
hash: u64,
|
hash: u64,
|
||||||
|
|||||||
@@ -1042,6 +1042,9 @@ pub(crate) fn generate_node_impl(crate_ident: &CrateIdent, parsed: &ParsedNodeFn
|
|||||||
quote!(#node_generic: #bound)
|
quote!(#node_generic: #bound)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
ParsedFieldType::Node(NodeParsedField { output_type, .. }) if opaque && is_record_value(output_type) => {
|
||||||
|
quote!(#node_generic: #core_types::node::Node<#ctx_ident, Output = #output_type>)
|
||||||
|
}
|
||||||
ParsedFieldType::Node(NodeParsedField { output_type, .. }) => {
|
ParsedFieldType::Node(NodeParsedField { output_type, .. }) => {
|
||||||
let bound = lazy_bound(output_type);
|
let bound = lazy_bound(output_type);
|
||||||
quote!(#node_generic: #bound)
|
quote!(#node_generic: #bound)
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
use core_types::arena::{Arena, ArenaCell};
|
use core_types::arena::{Arena, ArenaCell};
|
||||||
use core_types::context::{Ctx, ExtractArena};
|
use core_types::context::{Ctx, CtxSnapshot, DeriveCtx, ExtractAll, ExtractArena};
|
||||||
use core_types::frame_table::{FrameTable, Lookup};
|
use core_types::frame_table::{FrameTable, Lookup};
|
||||||
use core_types::gpoll::{Extent, Finality, GPoll};
|
use core_types::gpoll::{Extent, Finality, GPoll};
|
||||||
use core_types::graphene_hash::CacheHash;
|
use core_types::graphene_hash::CacheHash;
|
||||||
|
use core_types::memo::IORecord;
|
||||||
use core_types::node::Node;
|
use core_types::node::Node;
|
||||||
use core_types::record::{OwnedRecord, RecordCapture, RecordValue};
|
use core_types::record::{OwnedRecord, RecordCapture, RecordValue};
|
||||||
use core_types::registry::cache_key;
|
use core_types::registry::cache_key;
|
||||||
@@ -119,23 +120,26 @@ fn lend<'e, T: Send + Sync>(ctx: impl Ctx + ExtractArena<'e>, value: T) -> GPoll
|
|||||||
park(ctx.arena(), GPoll::Final(value))
|
park(ctx.arena(), GPoll::Final(value))
|
||||||
}
|
}
|
||||||
|
|
||||||
type MonitorValue = Arc<Mutex<Option<RecordCapture>>>;
|
type MonitorValue = Arc<Mutex<Option<IORecord<CtxSnapshot, RecordCapture>>>>;
|
||||||
|
|
||||||
/// The Monitor node is used by the editor to access the data flowing through it.
|
/// The Monitor node is used by the editor to access the data flowing through it.
|
||||||
#[node_macro::node(category(""), path(graphene_core::memo), serialize(serialize_monitor), properties("monitor_properties"))]
|
#[node_macro::node(category(""), path(graphene_core::memo), serialize(serialize_monitor), properties("monitor_properties"))]
|
||||||
fn monitor<'e>(ctx: impl Ctx + ExtractArena<'e>, #[data] capture: MonitorValue, content: impl Node<Context<'_>, Output = RecordValue<'e>>) -> GPoll<RecordValue<'e>> {
|
fn monitor<'e>(ctx: impl Ctx + DeriveCtx + ExtractAll + ExtractArena<'e>, #[data] io: MonitorValue, content: impl Node<Context<'_>, Output = RecordValue<'e>>) -> GPoll<RecordValue<'e>> {
|
||||||
let result = content.eval(&ctx);
|
let result = content.eval(&ctx);
|
||||||
if let GPoll::Final(value) | GPoll::Partial(value) = &result {
|
if let GPoll::Final(value) | GPoll::Partial(value) = &result {
|
||||||
// SAFETY: the value came from this edge, so it carries the edge's layout.
|
// SAFETY: the value came from this edge, so it carries the edge's layout.
|
||||||
let captured = unsafe { RecordCapture::capture(content.layout(), content.layout().rec(value), ctx.arena()) };
|
let captured = unsafe { RecordCapture::capture(content.layout(), content.layout().rec(value), ctx.arena()) };
|
||||||
*capture.lock().unwrap() = captured;
|
*io.lock().unwrap() = captured.map(|output| IORecord {
|
||||||
|
input: CtxSnapshot::capture(ctx),
|
||||||
|
output,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
fn serialize_monitor(capture: &MonitorValue) -> Option<Arc<dyn std::any::Any + Send + Sync>> {
|
fn serialize_monitor(io: &MonitorValue) -> Option<Arc<dyn std::any::Any + Send + Sync>> {
|
||||||
let capture = capture.lock().unwrap();
|
let io = io.lock().unwrap();
|
||||||
capture.as_ref().map(|capture| Arc::new(capture.clone()) as Arc<dyn std::any::Any + Send + Sync>)
|
io.as_ref().map(|io| Arc::new(io.clone()) as Arc<dyn std::any::Any + Send + Sync>)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
@@ -201,9 +205,13 @@ mod tests {
|
|||||||
panic!("expected a final record");
|
panic!("expected a final record");
|
||||||
};
|
};
|
||||||
|
|
||||||
let capture = handle.serialize().expect("the eval landed a capture");
|
let io = handle.serialize().expect("the eval landed a capture");
|
||||||
let capture = capture.downcast_ref::<RecordCapture>().expect("the capture is a record capture");
|
let io = io.downcast_ref::<IORecord<CtxSnapshot, RecordCapture>>().expect("the capture is the monitor io");
|
||||||
let element = capture.materialize_element(&arena).expect("the capture materializes inside the window");
|
assert!(
|
||||||
|
core_types::context::ExtractFootprint::try_footprint(&io.input).is_none(),
|
||||||
|
"the root context has no footprint to capture"
|
||||||
|
);
|
||||||
|
let element = io.output.materialize_element(&arena).expect("the capture materializes inside the window");
|
||||||
assert_eq!(*element.downcast_ref::<u32>().unwrap(), 11);
|
assert_eq!(*element.downcast_ref::<u32>().unwrap(), 11);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -474,15 +474,17 @@ mod tests {
|
|||||||
assert_eq!(unsafe { layout.rec(&value).element::<f64>() }, 4.);
|
assert_eq!(unsafe { layout.rec(&value).element::<f64>() }, 4.);
|
||||||
}
|
}
|
||||||
|
|
||||||
let capture = Node::<ContextImpl>::serialize(&monitor).unwrap();
|
let io = Node::<ContextImpl>::serialize(&monitor).unwrap();
|
||||||
let capture = capture.downcast_ref::<core_types::record::RecordCapture>().unwrap();
|
let io = io
|
||||||
let fields = capture.materialize(&arena).unwrap();
|
.downcast_ref::<core_types::memo::IORecord<core_types::context::CtxSnapshot, core_types::record::RecordCapture>>()
|
||||||
|
.unwrap();
|
||||||
|
let fields = io.output.materialize(&arena).unwrap();
|
||||||
assert_eq!(fields.len(), 1);
|
assert_eq!(fields.len(), 1);
|
||||||
assert_eq!(fields[0].0, "opacity");
|
assert_eq!(fields[0].0, "opacity");
|
||||||
assert_eq!(*fields[0].1.as_any().downcast_ref::<f64>().unwrap(), 0.25);
|
assert_eq!(*fields[0].1.as_any().downcast_ref::<f64>().unwrap(), 0.25);
|
||||||
|
|
||||||
arena.reset();
|
arena.reset();
|
||||||
assert!(capture.materialize(&arena).is_none(), "a dead generation materializes to nothing");
|
assert!(io.output.materialize(&arena).is_none(), "a dead generation materializes to nothing");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
Reference in New Issue
Block a user