Wire source ids through flattening, the nullification pass, and slot keying

This commit is contained in:
Dennis Kobert
2026-07-30 15:46:10 +00:00
parent c7fef30cf1
commit f4247986f0
14 changed files with 166 additions and 70 deletions

View File

@@ -8,7 +8,6 @@ pub use core_types::uuid::generate_uuid;
use core_types::{Context, ContextDependencies, Cow, MemoHash, ProtoNodeIdentifier, Type};
use dyn_any::DynAny;
use glam::IVec2;
use log::Metadata;
use rustc_hash::FxHashMap;
use std::collections::HashMap;
use std::collections::hash_map::DefaultHasher;
@@ -215,12 +214,14 @@ impl InlineRust {
#[derive(Debug, Clone, PartialEq, Hash, core_types::CacheHash, DynAny, serde::Serialize, serde::Deserialize)]
pub enum DocumentNodeMetadata {
DocumentNodePath,
SourceId,
}
impl DocumentNodeMetadata {
pub fn ty(&self) -> Type {
match self {
DocumentNodeMetadata::DocumentNodePath => concrete!(core_types::list::List<NodeId>),
DocumentNodeMetadata::SourceId => concrete!(u64),
}
}
}
@@ -273,7 +274,7 @@ impl NodeInput {
NodeInput::Import { import_type, .. } => import_type.clone(),
NodeInput::Inline(_) => panic!("ty() called on NodeInput::Inline"),
NodeInput::Scope(_) => panic!("ty() called on NodeInput::Scope"),
NodeInput::Reflection(_) => concrete!(Metadata),
NodeInput::Reflection(metadata) => metadata.ty(),
}
}
@@ -879,7 +880,7 @@ impl NodeNetwork {
// Replace value inputs with dedicated value nodes
if node.implementation != DocumentNodeImplementation::ProtoNode(ProtoNodeIdentifier::new("core_types::value::ClonedNode")) {
Self::replace_value_inputs_with_nodes(&mut node.inputs, &mut self.nodes, &path, gen_id, map_ids, id);
Self::replace_value_inputs_with_nodes(&mut node.inputs, &mut self.nodes, &path, gen_id, map_ids, id, Some(&mut node.context_features));
}
let DocumentNodeImplementation::Network(mut inner_network) = node.implementation else {
@@ -898,6 +899,7 @@ impl NodeNetwork {
gen_id,
map_ids,
id,
None,
);
// Connect all network inputs to either the parent network nodes, or newly created value nodes for the parent node.
@@ -978,6 +980,12 @@ impl NodeNetwork {
}
}
fn source_id_for_path(path: &[NodeId]) -> u64 {
let mut hasher = DefaultHasher::new();
path.hash(&mut hasher);
hasher.finish()
}
#[inline(never)]
fn replace_value_inputs_with_nodes(
inputs: &mut [NodeInput],
@@ -986,6 +994,7 @@ impl NodeNetwork {
gen_id: impl Fn() -> NodeId + Copy,
map_ids: impl Fn(NodeId, NodeId) -> NodeId + Copy,
id: NodeId,
mut context_features: Option<&mut ContextDependencies>,
) {
// Replace value exports and imports with value nodes, added inside the nested network
for export in inputs {
@@ -996,6 +1005,13 @@ impl NodeNetwork {
NodeInput::Value { tagged_value, exposed } => (tagged_value, exposed),
NodeInput::Reflection(reflect) => match reflect {
DocumentNodeMetadata::DocumentNodePath => (TaggedValue::NodeIdPath(path.to_vec()).into(), false),
DocumentNodeMetadata::SourceId => {
let source_id = Self::source_id_for_path(path);
if let Some(context_features) = context_features.as_deref_mut() {
core_types::context::merge_sorted_sources(&mut context_features.sources, &[source_id]);
}
(TaggedValue::U64(source_id).into(), false)
}
},
previous_export => {
*export = previous_export;

View File

@@ -12,7 +12,7 @@ use core_types::gnode::GNode;
use core_types::gpoll::GPoll;
use core_types::registry::{EdgeHandle, edge_type};
use core_types::value::value_edge;
use core_types::{CacheHash, Color, ContextFeatures, MemoHash, Node, Type, TypeDescriptor};
use core_types::{CacheHash, Color, ContextModification, MemoHash, Node, Type, TypeDescriptor};
use dyn_any::DynAny;
pub use dyn_any::StaticType;
pub use glam::{DAffine2, DVec2, IVec2, UVec2};
@@ -95,7 +95,7 @@ macro_rules! tagged_value {
DocumentNode(DocumentNode),
/// Carried by context nullification proto nodes constructed at proto node compilation time in `insert_context_nullification_nodes`.
#[serde(skip)]
ContextFeatures(ContextFeatures),
ContextModification(ContextModification),
#[serde(skip)]
EditorApi(Arc<PlatformEditorApi>),
/// Only used by the `resource` node, should never be serialized
@@ -125,7 +125,7 @@ macro_rules! tagged_value {
// =======================
Self::NodeIdPath(path) => path.hash(state),
Self::DocumentNode(node) => node.cache_hash(state),
Self::ContextFeatures(features) => features.cache_hash(state),
Self::ContextModification(modification) => modification.cache_hash(state),
Self::RenderOutput(x) => x.cache_hash(state),
Self::EditorApi(x) => x.cache_hash(state),
Self::ResourceHash(x) => x.cache_hash(state),
@@ -180,7 +180,7 @@ macro_rules! tagged_value {
Box::new(list)
}
Self::DocumentNode(node) => Box::new(node),
Self::ContextFeatures(features) => Box::new(features),
Self::ContextModification(modification) => Box::new(modification),
Self::EditorApi(x) => Box::new(x),
Self::ResourceHash(x) => Box::new(x),
}
@@ -230,7 +230,7 @@ macro_rules! tagged_value {
Arc::new(list)
}
Self::DocumentNode(node) => Arc::new(node),
Self::ContextFeatures(features) => Arc::new(features),
Self::ContextModification(modification) => Arc::new(modification),
Self::EditorApi(x) => Arc::new(x),
Self::ResourceHash(x) => Arc::new(x),
}
@@ -258,7 +258,7 @@ macro_rules! tagged_value {
Self::RenderOutput(_) => concrete!(RenderOutput),
Self::NodeIdPath(_) => concrete!(List<NodeId>),
Self::DocumentNode(_) => concrete!(DocumentNode),
Self::ContextFeatures(_) => concrete!(ContextFeatures),
Self::ContextModification(_) => concrete!(ContextModification),
Self::EditorApi(_) => concrete!(Arc<PlatformEditorApi>),
Self::ResourceHash(_) => concrete!(ResourceHash),
}
@@ -308,7 +308,7 @@ macro_rules! tagged_value {
Ok(value_edge(list))
}
Self::DocumentNode(node) => Ok(value_edge(node)),
Self::ContextFeatures(features) => Ok(value_edge(features)),
Self::ContextModification(modification) => Ok(value_edge(modification)),
Self::EditorApi(x) => Ok(value_edge(x)),
Self::ResourceHash(x) => Ok(value_edge(x)),
}
@@ -441,7 +441,7 @@ macro_rules! tagged_value {
Self::RenderOutput(_) => "RenderOutput".to_string(),
Self::NodeIdPath(path) => format!("NodeIdPath({path:?})"),
Self::DocumentNode(node) => format!("DocumentNode({node:?})"),
Self::ContextFeatures(features) => format!("ContextFeatures({features:?})"),
Self::ContextModification(modification) => format!("ContextModification({modification:?})"),
Self::EditorApi(_) => "PlatformEditorApi".to_string(),
Self::ResourceHash(hash) => format!("ResourceHash({hash:?})"),
}

View File

@@ -308,7 +308,7 @@ impl ProtoNetwork {
Ok(())
}
fn insert_context_nullification_node(&mut self, node_id: NodeId, context_deps: ContextFeatures) -> NodeId {
fn insert_context_nullification_node(&mut self, node_id: NodeId, context_deps: ContextModification) -> NodeId {
let (_, node) = &self.nodes[node_id.0 as usize];
let mut path = node.original_location.path.clone();
@@ -338,7 +338,7 @@ impl ProtoNetwork {
self.nodes.push((
nullification_value_node_id,
ProtoNode {
construction_args: ConstructionArgs::Value(MemoHash::new(TaggedValue::ContextFeatures(context_deps))),
construction_args: ConstructionArgs::Value(MemoHash::new(TaggedValue::ContextModification(context_deps))),
call_argument: concrete!(Context),
identifier: ProtoNodeIdentifier::new("core_types::value::ClonedNode"),
original_location: OriginalLocation {
@@ -365,36 +365,40 @@ impl ProtoNetwork {
nullification_node_id
}
fn find_context_dependencies(&mut self, id: NodeId) -> (ContextFeatures, Option<NodeId>) {
fn find_context_dependencies(&mut self, id: NodeId) -> (ContextModification, Option<NodeId>) {
let mut branch_dependencies = Vec::new();
let mut combined_deps = ContextFeatures::default();
let mut combined_deps = ContextModification::default();
let node_index = id.0 as usize;
let context_features = self.nodes[node_index].1.context_features;
let context_features = self.nodes[node_index].1.context_features.clone();
let own_deps = ContextModification {
features: context_features.extract,
sources: context_features.sources.clone(),
};
let mut inputs = match &self.nodes[node_index].1.construction_args {
// We pretend like we have already placed context modification nodes after ourselves because value nodes don't need to be cached
ConstructionArgs::Value(_) => return (context_features.extract, Some(id)),
ConstructionArgs::Value(_) => return (own_deps, Some(id)),
ConstructionArgs::Nodes(items) => items.clone(),
ConstructionArgs::Inline(_) => return (context_features.extract, Some(id)),
ConstructionArgs::Inline(_) => return (own_deps, Some(id)),
};
// Compute the dependencies for each branch and combine all of them
for &node in &inputs {
let branch = self.find_context_dependencies(node);
combined_deps |= &branch.0;
branch_dependencies.push(branch);
combined_deps |= branch.0;
}
let mut new_deps = combined_deps;
let mut new_deps = combined_deps.clone();
// Remove requirements which this node provides
new_deps &= !context_features.inject;
// Add requirements we have
new_deps |= context_features.extract;
new_deps |= own_deps;
// If we either introduce new dependencies, we can cache all children which don't yet need that dependency
let we_introduce_new_deps = !combined_deps.contains(new_deps);
let we_introduce_new_deps = !combined_deps.contains(&new_deps);
// For diverging branches, we can add a cache node for all branches which don't reqire all dependencies
for (child_node, (deps, new_id)) in inputs.iter_mut().zip(branch_dependencies.into_iter()) {
@@ -410,15 +414,15 @@ impl ProtoNetwork {
let net_injections = context_features.inject.difference(context_features.extract);
// Which dependencies still need to be met after this node?
let remaining_deps_from_children = combined_deps.difference(net_injections);
let remaining_deps_from_children = combined_deps.features.difference(net_injections);
// Do we satisfy any existing dependencies?
let we_supply_existing_deps = !combined_deps.difference(remaining_deps_from_children).is_empty();
let we_supply_existing_deps = !combined_deps.features.difference(remaining_deps_from_children).is_empty();
let mut new_id = None;
if we_supply_existing_deps {
// Our set of context dependencies has shrunk so we can add a cache node after the current node
new_id = Some(self.insert_context_nullification_node(id, new_deps));
new_id = Some(self.insert_context_nullification_node(id, new_deps.clone()));
}
(new_deps, new_id)