WIP: Thumbnails

This commit is contained in:
Adam
2025-07-14 00:25:57 -07:00
parent 8b665d158c
commit fcd379a62f
50 changed files with 1208 additions and 1053 deletions

View File

@@ -6,7 +6,9 @@ fn compile_to_proto(c: &mut Criterion) {
for name in DEMO_ART {
let network = load_from_name(name);
c.bench_function(name, |b: &mut criterion::Bencher<'_>| b.iter_batched(|| network.clone(), |mut network| black_box(network.flatten()), criterion::BatchSize::SmallInput));
c.bench_function(name, |b: &mut criterion::Bencher<'_>| {
b.iter_batched(|| network.clone(), |mut network| black_box(network.compile()), criterion::BatchSize::SmallInput)
});
}
}

View File

@@ -5,7 +5,7 @@ use iai_callgrind::{black_box, library_benchmark, library_benchmark_group, main}
#[library_benchmark]
#[benches::with_setup(args = ["isometric-fountain", "painted-dreams", "procedural-string-lights", "parametric-dunescape", "red-dress", "valley-of-spires"], setup = load_from_name)]
pub fn compile_to_proto(mut input: NodeNetwork) {
let _ = black_box(input.flatten());
let _ = black_box(input.compile());
}
library_benchmark_group!(name = compile_group; benchmarks = compile_to_proto);

View File

@@ -40,6 +40,9 @@ pub struct DocumentNode {
/// Represents the eye icon for hiding/showing the node in the graph UI. When hidden, a node gets replaced with an identity node during the graph flattening step.
#[serde(default = "return_true")]
pub visible: bool,
// Represents whether the output of the node should be cached. This is set to true whenever a node feeds into another node with more context dependencies
#[serde(default)]
pub cache_output: bool,
pub manual_composition: Option<Type>,
#[serde(default)]
pub skip_deduplication: bool,
@@ -50,6 +53,7 @@ impl Hash for DocumentNode {
self.inputs.hash(state);
self.implementation.hash(state);
self.visible.hash(state);
self.cache_output.hash(state);
}
}
@@ -59,6 +63,7 @@ impl Default for DocumentNode {
inputs: Default::default(),
implementation: Default::default(),
visible: true,
cache_output: false,
manual_composition: Some(generic!(T)),
skip_deduplication: false,
}
@@ -518,7 +523,7 @@ impl NodeNetwork {
/// Functions for compiling the network
impl NodeNetwork {
// Returns a topologically sorted vec of protonodes, as well as metadata extracted during compilation
pub fn flatten(&mut self) -> Result<(ProtoNetwork, Vec<(OriginalLocation, SNI)>), String> {
pub fn compile(&mut self) -> Result<(ProtoNetwork, Vec<(OriginalLocation, SNI)>), String> {
// These three arrays are stored in parallel
let mut protonetwork = Vec::new();
@@ -544,8 +549,8 @@ impl NodeNetwork {
let Some(upstream_metadata) = upstream_metadata else {
panic!("All inputs should be when the upstream SNI was generated");
};
if upstream_metadata.is_value {
context_dependencies.add_dependencies(&upstream_metadata.context_dependencies);
if !upstream_metadata.is_value {
context_dependencies.add_dependencies(&upstream_metadata.nullify);
}
}
// The context_dependencies are now the union of all inputs and the dependencies of the protonode. Set the dependencies of each input to the difference, which represents the data to nullify
@@ -554,9 +559,9 @@ impl NodeNetwork {
panic!("All inputs should be when the upstream SNI was generated");
};
match upstream_metadata.is_value {
true => upstream_metadata.context_dependencies.difference(&context_dependencies),
false => upstream_metadata.nullify.difference(&context_dependencies),
// If the upstream node is a Value node, do not nullify the context
false => upstream_metadata.context_dependencies = ContextDependencies::none(),
true => upstream_metadata.nullify = ContextDependencies::none(),
}
}
(context_dependencies.clone(), false)
@@ -575,10 +580,7 @@ impl NodeNetwork {
};
(deduplicated_protonode.callers, deduplicated_protonode.original_location)
} else {
(
std::mem::take(&mut protonode.callers),
std::mem::replace(&mut protonode.original_location, OriginalLocation::Node(Vec::new())),
)
(std::mem::take(&mut protonode.callers), protonode.original_location.clone())
};
// Map the callers inputs to the generated stable node id
@@ -592,7 +594,7 @@ impl NodeNetwork {
assert!(caller_index > current_protonode_index, "Caller index must be higher than current index");
nodes.inputs[input_index] = Some(UpstreamInputMetadata {
input_sni: stable_node_id,
context_dependencies: protonode_context_dependencies.clone(),
nullify: protonode_context_dependencies.clone(),
is_value: upstream_is_value,
})
}
@@ -726,7 +728,7 @@ impl NodeNetwork {
log::error!("The node which was supposed to be flattened does not exist in the network, id {upstream_node_id}");
return;
};
let cache_output = upstream_document_node.cache_output;
match &upstream_document_node.implementation {
DocumentNodeImplementation::Network(_node_network) => {
let traversal_input = AbsoluteInputConnector {
@@ -766,6 +768,7 @@ impl NodeNetwork {
identifier,
inputs: vec![None; number_of_inputs],
context_dependencies,
cache_output,
});
let protonode = ProtoNode {
construction_args,

View File

@@ -13,6 +13,7 @@ use graphene_core::uuid::NodeId;
use graphene_core::vector::style::Fill;
use graphene_core::{Color, MemoHash, Node, Type};
use graphene_svg_renderer::{GraphicElementRendered, RenderMetadata};
use std::cell::Cell;
use std::fmt::Display;
use std::hash::Hash;
use std::marker::PhantomData;
@@ -396,6 +397,7 @@ impl Display for TaggedValue {
pub struct UpcastNode {
value: MemoHash<TaggedValue>,
inspected: Cell<bool>,
}
impl<'input> Node<'input, DAny<'input>> for UpcastNode {
type Output = FutureAny<'input>;
@@ -403,10 +405,15 @@ impl<'input> Node<'input, DAny<'input>> for UpcastNode {
fn eval(&'input self, _: DAny<'input>) -> Self::Output {
Box::pin(async move { self.value.clone().into_inner().to_dynany() })
}
fn introspect(&self) -> graphene_core::memo::MonitorIntrospectResult {
let inspected = self.inspected.replace(true);
graphene_core::memo::MonitorIntrospectResult::Evaluated((Arc::new(self.value.clone().into_inner()) as Arc<dyn std::any::Any + Send + Sync>, !inspected))
}
}
impl UpcastNode {
pub fn new(value: MemoHash<TaggedValue>) -> Self {
Self { value }
Self { value, inspected: Cell::new(false) }
}
}
#[derive(Default, Debug, Clone, Copy)]
@@ -426,8 +433,6 @@ impl<T: AsRef<U> + Sync + Send, U: Sync + Send> UpcastAsRefNode<T, U> {
}
}
#[derive(Debug, Clone, PartialEq, dyn_any::DynAny, serde::Serialize, serde::Deserialize)]
pub struct RenderOutput {
pub data: RenderOutputType,
@@ -540,25 +545,13 @@ mod fake_hash {
macro_rules! thumbnail_render {
( $( $ty:ty ),* $(,)? ) => {
pub fn render_thumbnail_if_change(new_value: &Arc<dyn std::any::Any + Send + Sync>, old_value: Option<&Arc<dyn std::any::Any + Send + Sync>>) -> ThumbnailRenderResult {
pub fn render_thumbnail(new_value: &Arc<dyn std::any::Any + Send + Sync>) -> Option<String> {
$(
if let Some(new_value) = new_value.downcast_ref::<$ty>() {
match old_value {
None => return ThumbnailRenderResult::UpdateThumbnail(new_value.render_thumbnail()),
Some(old_value) => {
if let Some(old_value) = old_value.downcast_ref::<$ty>() {
match new_value == old_value {
true => return ThumbnailRenderResult::NoChange,
false => return ThumbnailRenderResult::UpdateThumbnail(new_value.render_thumbnail())
}
} else {
return ThumbnailRenderResult::UpdateThumbnail(new_value.render_thumbnail())
}
},
}
return Some(new_value.render_thumbnail());
}
)*
return ThumbnailRenderResult::ClearThumbnail;
return None;
}
};
}

View File

@@ -96,7 +96,7 @@ impl ProtoNetwork {
pub struct UpstreamInputMetadata {
pub input_sni: SNI,
// Context dependencies are accumulated during compilation, then replaced with the difference between the node's dependencies and the inputs dependencies
pub context_dependencies: ContextDependencies,
pub nullify: ContextDependencies,
// If the upstream node is a value node, then do not nullify since the value nodes do not have a cache inserted after them
pub is_value: bool,
}
@@ -112,6 +112,7 @@ pub struct NodeConstructionArgs {
pub inputs: Vec<Option<UpstreamInputMetadata>>,
// The union of all input context dependencies and the nodes context dependency. Used to generate the context nullification for the editor entry point
pub context_dependencies: ContextDependencies,
pub cache_output: bool,
}
#[derive(Debug, Clone)]
@@ -258,7 +259,7 @@ impl Debug for GraphErrorType {
}
}
#[derive(Clone, PartialEq, serde::Serialize, serde::Deserialize)]
#[derive(Clone, PartialEq, Debug, serde::Serialize, serde::Deserialize)]
pub struct GraphError {
pub original_location: OriginalLocation,
pub identifier: Cow<'static, str>,
@@ -279,11 +280,7 @@ impl GraphError {
}
}
}
impl Debug for GraphError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("NodeGraphError").field("identifier", &self.identifier.to_string()).field("error", &self.error).finish()
}
}
pub type GraphErrors = Vec<GraphError>;
/// The `TypingContext` is used to store the types of the nodes indexed by their stable node id.