mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-15 22:28:10 +08:00
Resolve context features from the registry at compile instead of persisting them
This commit is contained in:
@@ -1,6 +1,5 @@
|
||||
pub mod node {
|
||||
pub const CALL_ARGUMENT: &str = "call_argument";
|
||||
pub const CONTEXT_FEATURES: &str = "context_features";
|
||||
pub const VISIBLE: &str = "visible";
|
||||
pub const SKIP_DEDUPLICATION: &str = "skip_deduplication";
|
||||
pub const REFLECTION_METADATA: &str = "reflection_metadata";
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use core_types::Context;
|
||||
use core_types::context::ContextDependencies;
|
||||
use core_types::uuid::NodeId as RuntimeNodeId;
|
||||
use graph_craft::concrete;
|
||||
use graph_craft::document::value::TaggedValue;
|
||||
@@ -390,9 +389,6 @@ fn convert_node<M: NodeMetadataSource + ?Sized>(doc_node: &DocumentNode, locatio
|
||||
attributes
|
||||
.set_if_not_default(node::CALL_ARGUMENT, &doc_node.call_argument, &concrete!(Context), timestamp)
|
||||
.map_err(map_serialization_error(node::CALL_ARGUMENT))?;
|
||||
attributes
|
||||
.set_if_not_default(node::CONTEXT_FEATURES, &doc_node.context_features, &ContextDependencies::default(), timestamp)
|
||||
.map_err(map_serialization_error(node::CONTEXT_FEATURES))?;
|
||||
attributes
|
||||
.set_if_not_default(node::VISIBLE, &doc_node.visible, &true, timestamp)
|
||||
.map_err(map_serialization_error(node::VISIBLE))?;
|
||||
|
||||
@@ -248,11 +248,6 @@ fn test_nested_network_flattening() {
|
||||
#[test]
|
||||
fn test_metadata_preservation() {
|
||||
// Create a network with nodes that have non-default metadata
|
||||
let context_features = ContextDependencies::new(
|
||||
core_types::context::ContextFeatures::FOOTPRINT | core_types::context::ContextFeatures::REAL_TIME,
|
||||
core_types::context::ContextFeatures::empty(),
|
||||
);
|
||||
|
||||
let network = NodeNetwork {
|
||||
exports: vec![NodeInput::node(NodeId(1), 0)],
|
||||
nodes: [
|
||||
@@ -262,7 +257,6 @@ fn test_metadata_preservation() {
|
||||
inputs: vec![NodeInput::import(concrete!(f64), 0), NodeInput::import(Type::Generic(Cow::Borrowed("T")), 1)],
|
||||
implementation: DocumentNodeImplementation::ProtoNode(ProtoNodeIdentifier::new("test::NodeWithMetadata")),
|
||||
call_argument: concrete!(String),
|
||||
context_features,
|
||||
visible: false, // Non-default value
|
||||
skip_deduplication: true, // Non-default value
|
||||
..Default::default()
|
||||
@@ -296,8 +290,12 @@ fn test_metadata_preservation() {
|
||||
let conv_node_1 = converted.nodes.get(&NodeId(1)).unwrap();
|
||||
assert_eq!(orig_node_1.call_argument, conv_node_1.call_argument, "call_argument for node 1 should be preserved");
|
||||
|
||||
// Verify context_features is preserved
|
||||
assert_eq!(orig_node_0.context_features, conv_node_0.context_features, "context_features should be preserved");
|
||||
// Verify context_features is not stored
|
||||
assert_eq!(
|
||||
conv_node_0.context_features,
|
||||
ContextDependencies::default(),
|
||||
"context_features should resolve at compile, not round-trip"
|
||||
);
|
||||
|
||||
// Verify visible is preserved
|
||||
assert_eq!(orig_node_0.visible, conv_node_0.visible, "visible should be preserved");
|
||||
|
||||
@@ -310,8 +310,8 @@ fn convert_node(
|
||||
implementation: convert_implementation(context, &node.implementation, metadata_path, runtime_node_id, node_collector, network_collector)?,
|
||||
visible: node.attributes.get_or(node::VISIBLE, true),
|
||||
skip_deduplication: node.attributes.get_or(node::SKIP_DEDUPLICATION, false),
|
||||
context_features: node.attributes.get_or_default(node::CONTEXT_FEATURES),
|
||||
// Regenerated during compilation; not stored.
|
||||
context_features: Default::default(),
|
||||
original_location: Default::default(),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -42,7 +42,6 @@ pub(super) fn post_process_nodes(custom: Vec<DocumentNodeDefinition>) -> HashMap
|
||||
fields,
|
||||
description,
|
||||
properties,
|
||||
context_features,
|
||||
..
|
||||
} = metadata;
|
||||
|
||||
@@ -78,7 +77,7 @@ pub(super) fn post_process_nodes(custom: Vec<DocumentNodeDefinition>) -> HashMap
|
||||
implementation: DocumentNodeImplementation::ProtoNode(id.clone()),
|
||||
visible: true,
|
||||
skip_deduplication: false,
|
||||
context_features: ContextDependencies::from(context_features.as_slice()),
|
||||
context_features: ContextDependencies::default(),
|
||||
..Default::default()
|
||||
},
|
||||
persistent_node_metadata: DocumentNodePersistentMetadata {
|
||||
|
||||
@@ -24,7 +24,6 @@ use graph_craft::Type;
|
||||
use graph_craft::application_io::resource::ResourceId;
|
||||
use graph_craft::document::value::TaggedValue;
|
||||
use graph_craft::document::{DocumentNode, DocumentNodeImplementation, NodeId, NodeInput, NodeNetwork, OldDocumentNodeImplementation, OldNodeNetwork};
|
||||
use graphene_std::ContextDependencies;
|
||||
use graphene_std::Graphic;
|
||||
use graphene_std::list::List;
|
||||
use graphene_std::math::quad::Quad;
|
||||
@@ -4090,18 +4089,6 @@ impl NodeNetworkInterface {
|
||||
node.call_argument = call_argument;
|
||||
}
|
||||
|
||||
pub fn set_context_features(&mut self, node_id: &NodeId, network_path: &[NodeId], context_features: ContextDependencies) {
|
||||
let Some(network) = self.network_mut(network_path) else {
|
||||
log::error!("Could not get nested network in set_context_features");
|
||||
return;
|
||||
};
|
||||
let Some(node) = network.nodes.get_mut(node_id) else {
|
||||
log::error!("Could not get node in set_context_features");
|
||||
return;
|
||||
};
|
||||
node.context_features = context_features;
|
||||
}
|
||||
|
||||
/// Lightweight version of `set_input` for bulk import operations.
|
||||
/// Directly sets the input without `is_acyclic` checks, `load_structure`, position conversions,
|
||||
/// or per-node cache invalidation. Call `load_structure`, `unload_all_nodes_click_targets`, and
|
||||
|
||||
@@ -2441,15 +2441,6 @@ fn migrate_node(node_id: &NodeId, node: &DocumentNode, network_path: &[NodeId],
|
||||
}
|
||||
}
|
||||
|
||||
// Add context features to nodes that don't have them (fine-grained context caching migration)
|
||||
if node.context_features == graphene_std::ContextDependencies::default()
|
||||
&& let Some(reference) = document.network_interface.reference(node_id, network_path).clone()
|
||||
&& let Some(node_definition) = resolve_document_node_type(&reference)
|
||||
{
|
||||
let context_features = node_definition.node_template.document_node.context_features.clone();
|
||||
document.network_interface.set_context_features(node_id, network_path, context_features);
|
||||
}
|
||||
|
||||
// Add the "Scale Type" parameter to the "Decompose Scale" node
|
||||
if reference == DefinitionIdentifier::ProtoNode(graphene_std::transform_nodes::decompose_scale::IDENTIFIER) && inputs_count == 1 {
|
||||
let mut node_template = resolve_document_node_type(&reference)?.default_node_template();
|
||||
|
||||
@@ -57,7 +57,8 @@ pub struct DocumentNode {
|
||||
#[serde(default)]
|
||||
pub skip_deduplication: bool,
|
||||
/// List of Extract and Inject annotations for the Context.
|
||||
#[serde(default)]
|
||||
/// Resolved from the registry at compile time; only code-built wrapper nodes carry a declaration here.
|
||||
#[serde(skip)]
|
||||
pub context_features: ContextDependencies,
|
||||
/// The path to this node and its inputs and outputs as of when [`NodeNetwork::generate_node_paths`] was called.
|
||||
#[serde(skip)]
|
||||
|
||||
@@ -500,18 +500,18 @@ impl ProtoNetwork {
|
||||
nullification_node_id
|
||||
}
|
||||
|
||||
/// The node's declared index levels and per-input pushed levels, both read
|
||||
/// The node's declared dependencies and per-input pushed levels, both read
|
||||
/// from the registry since they follow the node's signature. A node with no
|
||||
/// registry entry pushes nothing and names no level.
|
||||
fn registry_index_levels(&self, node_index: usize) -> (core_types::context::IndexLevels, Vec<u8>) {
|
||||
/// registry entry declares nothing and pushes nothing.
|
||||
fn registry_dependencies(&self, node_index: usize) -> (ContextDependencies, Vec<u8>) {
|
||||
let identifier = &self.nodes[node_index].1.identifier;
|
||||
let metadata = core_types::registry::NODE_METADATA.lock().unwrap();
|
||||
match metadata.get(identifier) {
|
||||
Some(entry) => (
|
||||
ContextDependencies::from(entry.context_features.as_slice()).index_levels,
|
||||
ContextDependencies::from(entry.context_features.as_slice()),
|
||||
entry.fields.iter().map(|field| field.pushed_levels).collect(),
|
||||
),
|
||||
None => (core_types::context::IndexLevels::empty(), Vec::new()),
|
||||
None => (ContextDependencies::default(), Vec::new()),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -520,14 +520,21 @@ impl ProtoNetwork {
|
||||
let mut combined_deps = ContextModification::default();
|
||||
let node_index = id.0 as usize;
|
||||
|
||||
let (declared_levels, pushed_levels) = self.registry_index_levels(node_index);
|
||||
let (registry_deps, pushed_levels) = self.registry_dependencies(node_index);
|
||||
let (extract, inject, own_deps) = {
|
||||
let dependencies = &self.nodes[node_index].1.context_features;
|
||||
let carried = &self.nodes[node_index].1.context_features;
|
||||
// A code-built wrapper node declares dependencies its signature cannot
|
||||
// express; everything else resolves from the registry. Sources are
|
||||
// input wiring, not declarations, so they merge either way.
|
||||
let mut dependencies = match carried.extract.is_empty() && carried.inject.is_empty() {
|
||||
false => carried.clone(),
|
||||
true => registry_deps,
|
||||
};
|
||||
dependencies.add_sources(carried.sources());
|
||||
let index_levels = match dependencies.extract.contains(core_types::context::ContextFeatures::INDEX) {
|
||||
// A wrapper node declaring its dependencies by hand keeps `INDEX`
|
||||
// without the signature naming a level, and addresses the innermost.
|
||||
true if declared_levels.is_empty() => core_types::context::IndexLevels::innermost(),
|
||||
true => declared_levels,
|
||||
// A hand-declared `INDEX` names no level and addresses the innermost.
|
||||
true if dependencies.index_levels.is_empty() => core_types::context::IndexLevels::innermost(),
|
||||
true => dependencies.index_levels,
|
||||
false => core_types::context::IndexLevels::empty(),
|
||||
};
|
||||
let own_deps = ContextModification::from_sources(dependencies.extract, dependencies.sources()).with_index_levels(index_levels);
|
||||
@@ -536,7 +543,15 @@ impl ProtoNetwork {
|
||||
|
||||
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 (own_deps, Some(id)),
|
||||
ConstructionArgs::Value(value) => {
|
||||
let mut deps = own_deps;
|
||||
// A leveled value serves its lanes by the innermost index.
|
||||
if value.value_layout().is_some_and(|layout| layout.depth > 0) {
|
||||
deps |= core_types::context::ContextFeatures::INDEX;
|
||||
deps.index_levels |= core_types::context::IndexLevels::innermost();
|
||||
}
|
||||
return (deps, Some(id));
|
||||
}
|
||||
ConstructionArgs::Nodes(items) => items.clone(),
|
||||
ConstructionArgs::Inline(_) => return (own_deps, Some(id)),
|
||||
};
|
||||
@@ -571,6 +586,9 @@ impl ProtoNetwork {
|
||||
self.nodes[node_index].1.construction_args = ConstructionArgs::Nodes(inputs);
|
||||
|
||||
// Which dependencies do we supply (and don't need ourselves)?
|
||||
// TODO: a shrinking index-level mask does not count as supplying a
|
||||
// dependency, so a fold that consumes its subtree's only level inserts
|
||||
// no boundary here.
|
||||
let net_injections = inject.difference(extract);
|
||||
|
||||
// Which dependencies still need to be met after this node?
|
||||
|
||||
@@ -205,7 +205,6 @@ impl Preprocessor {
|
||||
implementation: DocumentNodeImplementation::ProtoNode(id.clone()),
|
||||
visible: true,
|
||||
skip_deduplication: false,
|
||||
context_features: ContextDependencies::from(metadata.context_features.as_slice()),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
@@ -256,7 +255,6 @@ impl Preprocessor {
|
||||
call_argument: node_io.call_argument.clone(),
|
||||
implementation: DocumentNodeImplementation::ProtoNode(id.clone()),
|
||||
visible: true,
|
||||
context_features: ContextDependencies::from(metadata.context_features.as_slice()),
|
||||
..Default::default()
|
||||
};
|
||||
inject_scopes.insert(id.clone(), (template, node_io.return_value.clone()));
|
||||
|
||||
Reference in New Issue
Block a user