Skip to content

Commit 077d11b

Browse files
timon-schellingKeavon
authored andcommitted
Resolve scopes before flattening network (#4235)
* Compiler changes * Run preprocessor on wrapping network * Remove test nodes * FIx * move generate_node_paths call to compiler * Make it more obvious what input is set to
1 parent 34e0fd7 commit 077d11b

8 files changed

Lines changed: 131 additions & 109 deletions

File tree

editor/src/node_graph_executor/runtime.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -345,13 +345,13 @@ impl NodeRuntime {
345345
None
346346
}
347347

348-
async fn update_network(&mut self, mut graph: NodeNetwork) -> Result<ResolvedDocumentNodeTypesDelta, (ResolvedDocumentNodeTypesDelta, String)> {
349-
if let Err(e) = self.preprocessor.expand_network(&mut graph, &self.resources) {
348+
async fn update_network(&mut self, graph: NodeNetwork) -> Result<ResolvedDocumentNodeTypesDelta, (ResolvedDocumentNodeTypesDelta, String)> {
349+
let mut scoped_network = wrap_network_in_scope(graph, self.editor_api.clone());
350+
351+
if let Err(e) = self.preprocessor.preprocess(&mut scoped_network, &self.resources) {
350352
return Err((ResolvedDocumentNodeTypesDelta::default(), e.to_string()));
351353
}
352354

353-
let scoped_network = wrap_network_in_scope(graph, self.editor_api.clone());
354-
355355
// We assume only one output
356356
assert_eq!(scoped_network.exports.len(), 1, "Graph with multiple outputs not yet handled");
357357

node-graph/graph-craft/src/document.rs

Lines changed: 69 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -655,6 +655,73 @@ impl NodeNetwork {
655655
}
656656
}
657657

658+
// Functions for resolving scope inputs
659+
impl NodeNetwork {
660+
pub fn resolve_scope_inputs(&mut self) {
661+
let mut leftover = Vec::new();
662+
self.resolve_scope_inputs_impl(None, &mut leftover);
663+
assert!(leftover.is_empty(), "Unresolved scope keys at top level: {leftover:?}");
664+
}
665+
666+
fn resolve_scope_inputs_impl(&mut self, parent: Option<&ScopeChain<'_>>, network_inputs: &mut Vec<NodeInput>) {
667+
let scope_chain = ScopeChain {
668+
scopes: &self.scope_injections,
669+
parent,
670+
};
671+
672+
for node in self.nodes.values_mut() {
673+
let DocumentNodeImplementation::Network(network) = &mut node.implementation else { continue };
674+
network.resolve_scope_inputs_impl(Some(&scope_chain), &mut node.inputs);
675+
}
676+
677+
let mut key_to_idx: FxHashMap<Cow<'static, str>, usize> = FxHashMap::default();
678+
for node in self.nodes.values_mut() {
679+
for input in node.inputs.iter_mut() {
680+
let NodeInput::Scope(key) = input else { continue };
681+
*input = match scope_chain.lookup(key) {
682+
ScopeChainLookup::Current(node_id, _ty) => NodeInput::node(*node_id, 0),
683+
ScopeChainLookup::Parent(_node_id, ty) => {
684+
let import_index = *key_to_idx.entry(key.clone()).or_insert_with(|| {
685+
let index = network_inputs.len();
686+
network_inputs.push(NodeInput::Scope(key.clone()));
687+
index
688+
});
689+
NodeInput::Import {
690+
import_type: ty.clone(),
691+
import_index,
692+
}
693+
}
694+
ScopeChainLookup::None => panic!("Scope key `{key}` not found in any ancestor scope_injections"),
695+
};
696+
}
697+
}
698+
}
699+
}
700+
701+
struct ScopeChain<'a> {
702+
scopes: &'a FxHashMap<String, (NodeId, Type)>,
703+
parent: Option<&'a ScopeChain<'a>>,
704+
}
705+
enum ScopeChainLookup<'a> {
706+
Current(&'a NodeId, &'a Type),
707+
Parent(&'a NodeId, &'a Type),
708+
None,
709+
}
710+
impl ScopeChain<'_> {
711+
fn lookup(&'_ self, key: &str) -> ScopeChainLookup<'_> {
712+
self.scopes
713+
.get(key)
714+
.map(|(id, ty)| ScopeChainLookup::Current(id, ty))
715+
.or_else(|| {
716+
self.parent.and_then(|parent| match parent.lookup(key) {
717+
ScopeChainLookup::Current(id, ty) | ScopeChainLookup::Parent(id, ty) => Some(ScopeChainLookup::Parent(id, ty)),
718+
ScopeChainLookup::None => None,
719+
})
720+
})
721+
.unwrap_or(ScopeChainLookup::None)
722+
}
723+
}
724+
658725
/// Functions for compiling the network
659726
impl NodeNetwork {
660727
/// Replace all references in the graph of a node ID with a new node ID defined by the function `f`.
@@ -784,18 +851,6 @@ impl NodeNetwork {
784851
are_inputs_used
785852
}
786853

787-
pub fn resolve_scope_inputs(&mut self) {
788-
for node in self.nodes.values_mut() {
789-
for input in node.inputs.iter_mut() {
790-
if let NodeInput::Scope(key) = input {
791-
let (import_id, _ty) = self.scope_injections.get(key.as_ref()).expect("Tried to import a non existent key from scope");
792-
// TODO use correct output index
793-
*input = NodeInput::node(*import_id, 0);
794-
}
795-
}
796-
}
797-
}
798-
799854
/// Remove all nodes that contain [`DocumentNodeImplementation::Network`] by moving the nested nodes into the parent network.
800855
pub fn flatten(&mut self, node_id: NodeId) {
801856
self.flatten_with_fns(node_id, merge_ids, NodeId::new)
@@ -886,11 +941,7 @@ impl NodeNetwork {
886941
}
887942
NodeInput::Value { .. } => unreachable!("Value inputs should have been replaced with value nodes"),
888943
NodeInput::Inline(_) => (),
889-
NodeInput::Scope(ref key) => {
890-
let (import_id, _ty) = self.scope_injections.get(key.as_ref()).expect("Tried to import a non existent key from scope");
891-
// TODO use correct output index
892-
nested_node.inputs[nested_input_index] = NodeInput::node(*import_id, 0);
893-
}
944+
NodeInput::Scope(_) => unreachable!("Scope inputs should have been resolved by resolve_scope_inputs_recursive before flattening"),
894945
NodeInput::Reflection(_) => unreachable!("Reflection inputs should have been replaced with value nodes"),
895946
}
896947
}
@@ -906,21 +957,8 @@ impl NodeNetwork {
906957
}
907958
}
908959

960+
/// Connect all nodes that were previously connected to this node to the nodes of the inner network
909961
fn replace_node_with_its_exports(&mut self, id: NodeId, original_location: &OriginalLocation, exports: &[NodeInput]) {
910-
// Connect scope injections to the inner network export
911-
self.scope_injections.values_mut().for_each(|(node_id, _ty)| {
912-
if node_id == &id {
913-
let Some(export) = exports.first() else {
914-
log::error!("Inner network should have at least one export");
915-
return;
916-
};
917-
if let NodeInput::Node { node_id: export_id, output_index: _ } = export {
918-
*node_id = *export_id;
919-
}
920-
}
921-
});
922-
923-
// Connect all nodes that were previously connected to this node to the nodes of the inner network
924962
for (i, export) in exports.iter().enumerate() {
925963
if let NodeInput::Node { node_id, output_index, .. } = &export {
926964
for deps in &original_location.dependants {

node-graph/graph-craft/src/graphene_compiler.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,13 @@ pub struct Compiler {}
66

77
impl Compiler {
88
pub fn compile(&self, mut network: NodeNetwork) -> impl Iterator<Item = Result<ProtoNetwork, String>> {
9+
network.resolve_scope_inputs();
10+
network.generate_node_paths(&[]);
911
let node_ids = network.nodes.keys().copied().collect::<Vec<_>>();
1012
network.populate_dependants();
1113
for id in node_ids {
1214
network.flatten(id);
1315
}
14-
network.resolve_scope_inputs();
1516
network.remove_redundant_passthrough_nodes();
1617
// network.remove_dead_nodes(0);
1718
let proto_networks = network.into_proto_networks();

node-graph/graphene-cli/src/main.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -242,10 +242,10 @@ fn compile_graph(document_string: String, editor_api: Arc<PlatformEditorApi>) ->
242242
let mut network = load_network(&document_string);
243243
fix_nodes(&mut network);
244244

245-
let preprocessor = preprocessor::Preprocessor::new();
246-
preprocessor.expand_network(&mut network, &ResourceRegistry::default()).expect("Failed to expand network"); // TODO: actually load the resources from the document
245+
let mut wrapped_network = wrap_network_in_scope(network, editor_api);
247246

248-
let wrapped_network = wrap_network_in_scope(network.clone(), editor_api);
247+
let preprocessor = preprocessor::Preprocessor::new();
248+
preprocessor.preprocess(&mut wrapped_network, &ResourceRegistry::default()).expect("Failed to expand network"); // TODO: actually load the resources from the document
249249

250250
let compiler = Compiler {};
251251
compiler.compile_single(wrapped_network).map_err(|x| x.into())

node-graph/interpreted-executor/benches/benchmark_util.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,11 @@ use interpreted_executor::dynamic_executor::DynamicExecutor;
99
use interpreted_executor::util::wrap_network_in_scope;
1010

1111
pub fn setup_network(name: &str) -> (DynamicExecutor, ProtoNetwork) {
12-
let mut network = load_from_name(name);
12+
let network = load_from_name(name);
1313
let editor_api = std::sync::Arc::new(EditorApi::default());
14+
let mut network = wrap_network_in_scope(network, editor_api);
1415
let preprocessor = preprocessor::Preprocessor::new();
15-
preprocessor.expand_network(&mut network, &ResourceRegistry::default()).unwrap();
16-
let network = wrap_network_in_scope(network, editor_api);
16+
preprocessor.preprocess(&mut network, &ResourceRegistry::default()).unwrap();
1717
let proto_network = compile(network);
1818
let executor = block_on(DynamicExecutor::new(proto_network.clone())).unwrap();
1919
(executor, proto_network)

node-graph/interpreted-executor/src/util.rs

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,7 @@ use graphene_std::uuid::NodeId;
1010
use std::sync::Arc;
1111
use wgpu_executor::WgpuExecutor;
1212

13-
pub fn wrap_network_in_scope(mut network: NodeNetwork, editor_api: Arc<PlatformEditorApi>) -> NodeNetwork {
14-
network.generate_node_paths(&[]);
15-
13+
pub fn wrap_network_in_scope(network: NodeNetwork, editor_api: Arc<PlatformEditorApi>) -> NodeNetwork {
1614
let inner_network = DocumentNode {
1715
implementation: DocumentNodeImplementation::Network(network),
1816
inputs: vec![],
@@ -126,7 +124,6 @@ pub fn wrap_network_in_scope(mut network: NodeNetwork, editor_api: Arc<PlatformE
126124
exports: vec![NodeInput::node(NodeId(1), 0)],
127125
nodes: nodes.into_iter().enumerate().map(|(id, node)| (NodeId(id as u64), node)).collect(),
128126
scope_injections: scope_injections.into_iter().collect(),
129-
// TODO(TrueDoctor): check if it makes sense to set `generated` to `true`
130-
generated: false,
127+
generated: true,
131128
}
132129
}

node-graph/nodes/gcore/src/debug.rs

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -34,13 +34,3 @@ fn unwrap_option<T: Default>(_: impl Ctx, #[implementations(Option<f64>, Option<
3434
fn clone<'i, T: Clone + 'i>(_: impl Ctx, #[implementations(&List<Raster<CPU>>)] value: &'i T) -> T {
3535
value.clone()
3636
}
37-
38-
#[node_macro::node(category("Debug"), inject_scope)]
39-
fn inject_scope_test_producer(_: impl Ctx) -> bool {
40-
true
41-
}
42-
43-
#[node_macro::node(category("Debug"))]
44-
fn inject_scope_test_consumer(_: impl Ctx, _primary: (), #[scope(inject_scope_test_producer::IDENTIFIER)] injected: bool) -> bool {
45-
injected
46-
}

node-graph/preprocessor/src/lib.rs

Lines changed: 48 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -19,59 +19,14 @@ pub struct Preprocessor {
1919
}
2020

2121
impl Preprocessor {
22-
pub fn expand_network(&self, network: &mut NodeNetwork, resources: &ResourceRegistry) -> Result<(), PreprocessorError> {
22+
pub fn preprocess(&self, network: &mut NodeNetwork, resources: &ResourceRegistry) -> Result<(), PreprocessorError> {
2323
self.insert_inject_scopes(network);
24-
replace_resource_inputs(network, resources)?;
25-
self.expand_network_inner(network);
24+
self.replace_resource_inputs(network, resources)?;
25+
self.expand_network(network);
2626
Ok(())
2727
}
2828
}
2929

30-
/// Replace every `TaggedValue::Resource(hash)` input with a reference to a freshly inserted `resource` proto node.
31-
fn replace_resource_inputs(network: &mut NodeNetwork, resources: &ResourceRegistry) -> Result<(), PreprocessorError> {
32-
let mut hash_to_node_id: HashMap<graph_craft::application_io::resource::ResourceHash, NodeId> = HashMap::new();
33-
let mut new_resource_nodes: Vec<(NodeId, DocumentNode)> = Vec::new();
34-
35-
for node in network.nodes.values_mut() {
36-
if let DocumentNodeImplementation::Network(nested) = &mut node.implementation {
37-
replace_resource_inputs(nested, resources)?;
38-
continue;
39-
}
40-
41-
if matches!(&node.implementation, DocumentNodeImplementation::ProtoNode(identifier) if *identifier == platform_application_io::resource::IDENTIFIER) {
42-
continue;
43-
}
44-
45-
for input in node.inputs.iter_mut() {
46-
let NodeInput::Value { tagged_value, .. } = input else { continue };
47-
let TaggedValue::Resource(resource_id) = **tagged_value else { continue };
48-
49-
let Some(hash) = resources.hash(&resource_id) else {
50-
return Err(PreprocessorError::ResourceNotFound(resource_id));
51-
};
52-
53-
let resource_id = *hash_to_node_id.entry(hash).or_insert_with(|| {
54-
let id = NodeId::new();
55-
let resource_node = DocumentNode {
56-
inputs: vec![NodeInput::value(TaggedValue::ResourceHash(hash), false), NodeInput::scope("editor-api")],
57-
implementation: DocumentNodeImplementation::ProtoNode(platform_application_io::resource::IDENTIFIER),
58-
..Default::default()
59-
};
60-
new_resource_nodes.push((id, resource_node));
61-
id
62-
});
63-
64-
*input = NodeInput::node(resource_id, 0);
65-
}
66-
}
67-
68-
for (id, node) in new_resource_nodes {
69-
network.nodes.insert(id, node);
70-
}
71-
72-
Ok(())
73-
}
74-
7530
impl Preprocessor {
7631
fn insert_inject_scopes(&self, network: &mut NodeNetwork) {
7732
for (identifier, (template, ty)) in self.inject_scopes.iter() {
@@ -85,14 +40,55 @@ impl Preprocessor {
8540
}
8641
}
8742

88-
fn expand_network_inner(&self, network: &mut NodeNetwork) {
89-
if network.generated {
90-
return;
43+
/// Replace every `TaggedValue::Resource(hash)` input with a reference to a freshly inserted `resource` proto node.
44+
fn replace_resource_inputs(&self, network: &mut NodeNetwork, resources: &ResourceRegistry) -> Result<(), PreprocessorError> {
45+
let mut hash_to_node_id: HashMap<graph_craft::application_io::resource::ResourceHash, NodeId> = HashMap::new();
46+
let mut new_resource_nodes: Vec<(NodeId, DocumentNode)> = Vec::new();
47+
48+
for node in network.nodes.values_mut() {
49+
if let DocumentNodeImplementation::Network(nested) = &mut node.implementation {
50+
self.replace_resource_inputs(nested, resources)?;
51+
continue;
52+
}
53+
54+
if matches!(&node.implementation, DocumentNodeImplementation::ProtoNode(identifier) if *identifier == platform_application_io::resource::IDENTIFIER) {
55+
continue;
56+
}
57+
58+
for input in node.inputs.iter_mut() {
59+
let NodeInput::Value { tagged_value, .. } = input else { continue };
60+
let TaggedValue::Resource(resource_id) = **tagged_value else { continue };
61+
62+
let Some(hash) = resources.hash(&resource_id) else {
63+
return Err(PreprocessorError::ResourceNotFound(resource_id));
64+
};
65+
66+
let resource_id = *hash_to_node_id.entry(hash).or_insert_with(|| {
67+
let id = NodeId::new();
68+
let resource_node = DocumentNode {
69+
inputs: vec![NodeInput::value(TaggedValue::ResourceHash(hash), false), NodeInput::scope("editor-api")],
70+
implementation: DocumentNodeImplementation::ProtoNode(platform_application_io::resource::IDENTIFIER),
71+
..Default::default()
72+
};
73+
new_resource_nodes.push((id, resource_node));
74+
id
75+
});
76+
77+
*input = NodeInput::node(resource_id, 0);
78+
}
79+
}
80+
81+
for (id, node) in new_resource_nodes {
82+
network.nodes.insert(id, node);
9183
}
9284

85+
Ok(())
86+
}
87+
88+
fn expand_network(&self, network: &mut NodeNetwork) {
9389
for node in network.nodes.values_mut() {
9490
match &mut node.implementation {
95-
DocumentNodeImplementation::Network(node_network) => self.expand_network_inner(node_network),
91+
DocumentNodeImplementation::Network(node_network) => self.expand_network(node_network),
9692
DocumentNodeImplementation::ProtoNode(proto_node_identifier) => {
9793
if let Some(new_node) = self.substitutions.get(proto_node_identifier) {
9894
// Reconcile the document node's inputs with what the current node definition expects,

0 commit comments

Comments
 (0)