Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ pub(super) fn post_process_nodes(custom: Vec<DocumentNodeDefinition>) -> HashMap
description,
properties,
context_features,
memoize: _,
..
} = metadata;

let implementations = node_registry.get(id).unwrap_or(&empty_implementations);
Expand Down
9 changes: 4 additions & 5 deletions editor/src/node_graph_executor/runtime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@ use crate::messages::frontend::utility_types::{ExportBounds, FileType};
use glam::{DAffine2, DVec2, UVec2};
use graph_craft::application_io::resource::ResourceRegistry;
use graph_craft::application_io::{PlatformApplicationIo, PlatformEditorApi};
use graph_craft::concrete;
use graph_craft::document::value::{RenderOutput, RenderOutputType, TaggedValue};
use graph_craft::document::{NodeId, NodeNetwork};
use graph_craft::graphene_compiler::Compiler;
use graph_craft::proto::GraphErrors;
use graph_craft::{ProtoNodeIdentifier, concrete};
use graphene_std::application_io::{ApplicationIo, ExportFormat, ImageTexture, NodeGraphUpdateMessage, NodeGraphUpdateSender, RenderConfig};
use graphene_std::bounds::{BoundingBox, RenderBoundingBox};
use graphene_std::list::List;
Expand Down Expand Up @@ -49,8 +49,7 @@ pub struct NodeRuntime {
/// Which node is inspected and which monitor node is used (if any) for the current execution.
inspect_state: Option<InspectState>,

/// Mapping of the fully-qualified node paths to their preprocessor substitutions.
substitutions: HashMap<ProtoNodeIdentifier, DocumentNode>,
preprocessor: preprocessor::Preprocessor,

// TODO: Remove, it doesn't need to be persisted anymore
/// The current renders of the thumbnails for layer nodes.
Expand Down Expand Up @@ -146,7 +145,7 @@ impl NodeRuntime {
node_graph_errors: Vec::new(),
monitor_nodes: Vec::new(),

substitutions: preprocessor::generate_node_substitutions(),
preprocessor: preprocessor::Preprocessor::new(),

thumbnail_renders: Default::default(),
vector_modify: Default::default(),
Expand Down Expand Up @@ -347,7 +346,7 @@ impl NodeRuntime {
}

async fn update_network(&mut self, mut graph: NodeNetwork) -> Result<ResolvedDocumentNodeTypesDelta, (ResolvedDocumentNodeTypesDelta, String)> {
if let Err(e) = preprocessor::expand_network(&mut graph, &self.substitutions, &self.resources) {
if let Err(e) = self.preprocessor.expand_network(&mut graph, &self.resources) {
return Err((ResolvedDocumentNodeTypesDelta::default(), e.to_string()));
}

Expand Down
37 changes: 27 additions & 10 deletions node-graph/graph-craft/src/document.rs
Original file line number Diff line number Diff line change
Expand Up @@ -731,9 +731,9 @@ impl NodeNetwork {
}

/// Replace all references in any node of `old_output` with `new_output`
fn replace_network_outputs(&mut self, old_output: NodeInput, new_output: NodeInput) {
fn replace_network_outputs(&mut self, old_output: &NodeInput, new_output: &NodeInput) {
for output in self.exports.iter_mut() {
if *output == old_output {
if *output == *old_output {
*output = new_output.clone();
}
}
Expand Down Expand Up @@ -899,27 +899,44 @@ impl NodeNetwork {
}
// TODO: Add support for flattening exports that are NodeInput::Import (https://github.com/GraphiteEditor/Graphite/issues/1762)

self.replace_node_with_its_exports(id, &node.original_location, &inner_network.exports);

for node_id in new_nodes {
self.flatten_with_fns(node_id, map_ids, gen_id);
}
}

fn replace_node_with_its_exports(&mut self, id: NodeId, original_location: &OriginalLocation, exports: &[NodeInput]) {
// Connect scope injections to the inner network export
self.scope_injections.values_mut().for_each(|(node_id, _ty)| {
if node_id == &id {
let Some(export) = exports.first() else {
log::error!("Inner network should have at least one export");
return;
};
if let NodeInput::Node { node_id: export_id, output_index: _ } = export {
*node_id = *export_id;
}
}
});

// Connect all nodes that were previously connected to this node to the nodes of the inner network
for (i, export) in inner_network.exports.into_iter().enumerate() {
for (i, export) in exports.iter().enumerate() {
if let NodeInput::Node { node_id, output_index, .. } = &export {
for deps in &node.original_location.dependants {
for deps in &original_location.dependants {
for dep in deps {
self.replace_node_inputs(*dep, (id, i), (*node_id, *output_index));
}
}

if let Some(new_output_node) = self.nodes.get_mut(node_id) {
for dep in &node.original_location.dependants[i] {
for dep in &original_location.dependants[i] {
new_output_node.original_location.dependants[*output_index].push(*dep);
}
}
}

self.replace_network_outputs(NodeInput::node(id, i), export);
}

for node_id in new_nodes {
self.flatten_with_fns(node_id, map_ids, gen_id);
self.replace_network_outputs(&NodeInput::node(id, i), export);
}
}

Expand Down
4 changes: 2 additions & 2 deletions node-graph/graphene-cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -242,8 +242,8 @@ fn compile_graph(document_string: String, editor_api: Arc<PlatformEditorApi>) ->
let mut network = load_network(&document_string);
fix_nodes(&mut network);

let substitutions = preprocessor::generate_node_substitutions();
preprocessor::expand_network(&mut network, &substitutions, &ResourceRegistry::default()).expect("Failed to expand network"); // TODO: actually load the resources from the document
let preprocessor = preprocessor::Preprocessor::new();
preprocessor.expand_network(&mut network, &ResourceRegistry::default()).expect("Failed to expand network"); // TODO: actually load the resources from the document

let wrapped_network = wrap_network_in_scope(network.clone(), editor_api);

Expand Down
4 changes: 2 additions & 2 deletions node-graph/interpreted-executor/benches/benchmark_util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ use interpreted_executor::util::wrap_network_in_scope;
pub fn setup_network(name: &str) -> (DynamicExecutor, ProtoNetwork) {
let mut network = load_from_name(name);
let editor_api = std::sync::Arc::new(EditorApi::default());
let substitutions = preprocessor::generate_node_substitutions();
preprocessor::expand_network(&mut network, &substitutions, &ResourceRegistry::default()).unwrap();
let preprocessor = preprocessor::Preprocessor::new();
preprocessor.expand_network(&mut network, &ResourceRegistry::default()).unwrap();
let network = wrap_network_in_scope(network, editor_api);
let proto_network = compile(network);
let executor = block_on(DynamicExecutor::new(proto_network.clone())).unwrap();
Expand Down
1 change: 1 addition & 0 deletions node-graph/libraries/core-types/src/registry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ pub struct NodeMetadata {
pub properties: Option<&'static str>,
pub context_features: Vec<ContextFeature>,
pub memoize: bool,
pub inject_scope: bool,
}

// Translation struct between macro and definition
Expand Down
7 changes: 7 additions & 0 deletions node-graph/libraries/core-types/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,13 @@ impl ProtoNodeIdentifier {
pub fn as_str(&self) -> &str {
self.name.as_ref()
}

pub const fn as_static_str(&self) -> &'static str {
Comment thread
TrueDoctor marked this conversation as resolved.
match self.name {
Cow::Borrowed(name) => name,
Cow::Owned(_) => panic!("`as_static_str` called on a `ProtoNodeIdentifier` backed by an owned string"),
}
}
}

impl Display for ProtoNodeIdentifier {
Expand Down
10 changes: 9 additions & 1 deletion node-graph/node-macro/src/codegen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,13 @@ pub(crate) fn generate_node_code(crate_ident: &CrateIdent, parsed: &ParsedNodeFn
quote!(RegistryValueSource::Default(stringify!(#data)))
}
}
ParsedValueSource::Scope(data) => quote!(RegistryValueSource::Scope(#data)),
ParsedValueSource::Scope(data) => {
if let syn::Expr::Lit(syn::ExprLit { lit: syn::Lit::Str(_), .. }) = data {
quote!(RegistryValueSource::Scope(#data))
} else {
quote!(RegistryValueSource::Scope(#data.as_static_str()))
}
}
_ => quote!(RegistryValueSource::None),
},
_ => quote!(RegistryValueSource::None),
Expand Down Expand Up @@ -402,6 +408,7 @@ pub(crate) fn generate_node_code(crate_ident: &CrateIdent, parsed: &ParsedNodeFn

let properties = &attributes.properties_string.as_ref().map(|value| quote!(Some(#value))).unwrap_or(quote!(None));
let memoize_flag = attributes.memoize;
let inject_scope_flag = attributes.inject_scope;

let cfg = crate::shader_nodes::modify_cfg(attributes);
let node_input_accessor = generate_node_input_references(parsed, fn_generics, &field_idents, core_types, &identifier, &cfg);
Expand Down Expand Up @@ -500,6 +507,7 @@ pub(crate) fn generate_node_code(crate_ident: &CrateIdent, parsed: &ParsedNodeFn
properties: #properties,
context_features: vec![#(ContextFeature::#context_features,)*],
memoize: #memoize_flag,
inject_scope: #inject_scope_flag,
fields: vec![
#(
FieldMetadata {
Expand Down
29 changes: 26 additions & 3 deletions node-graph/node-macro/src/parsing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,14 +54,16 @@ pub(crate) struct NodeFnAttributes {
pub(crate) serialize: Option<Path>,
/// Whether the preprocessor should add a Memoize node after this node in the generated subnetwork
pub(crate) memoize: bool,
/// Whether this node provides a scope
pub(crate) inject_scope: bool,
}

#[derive(Clone, Debug, Default)]
pub enum ParsedValueSource {
#[default]
None,
Default(TokenStream2),
Scope(LitStr),
Scope(Expr),
}

// #[widget(ParsedWidgetOverride::Hidden)]
Expand Down Expand Up @@ -261,6 +263,7 @@ impl Parse for NodeFnAttributes {
let mut shader_node = None;
let mut serialize = None;
let mut memoize = false;
let mut inject_scope = false;

let content = input;
// let content;
Expand Down Expand Up @@ -391,13 +394,25 @@ impl Parse for NodeFnAttributes {
}
memoize = true;
}
// Instructs the preprocessor to make this node available as a scope.
// Other nodes can then access it with `#[scope(node::IDENTIFIER)]`.
//
// Example usage:
// #[node_macro::node(..., inject_scope, ...)]
"inject_scope" => {
let path = meta.require_path_only()?;
if inject_scope {
return Err(Error::new_spanned(path, "Multiple 'inject_scope' attributes are not allowed"));
}
inject_scope = true;
}
_ => {
return Err(Error::new_spanned(
meta,
indoc!(
r#"
Unsupported attribute in `node`.
Supported attributes are 'category', 'name', 'path', 'skip_impl', 'properties', 'cfg', 'shader_node', 'serialize', and 'memoize'.
Supported attributes are 'category', 'name', 'path', 'skip_impl', 'properties', 'cfg', 'shader_node', 'serialize', 'memoize', and 'inject_scope'.
Example usage:
#[node_macro::node(..., name("Test Node"), ...)]
"#
Expand Down Expand Up @@ -430,6 +445,7 @@ impl Parse for NodeFnAttributes {
shader_node,
serialize,
memoize,
inject_scope,
})
}
}
Expand Down Expand Up @@ -979,7 +995,7 @@ mod tests {
assert_eq!(p.to_token_stream().to_string(), e.to_token_stream().to_string());
}
(ParsedValueSource::Scope(p), ParsedValueSource::Scope(e)) => {
assert_eq!(p.value(), e.value());
assert_eq!(p.to_token_stream().to_string(), e.to_token_stream().to_string());
}
_ => panic!("Mismatched default values"),
}
Expand Down Expand Up @@ -1038,6 +1054,7 @@ mod tests {
shader_node: None,
serialize: None,
memoize: false,
inject_scope: false,
},
fn_name: Ident::new("add", Span::call_site()),
struct_name: Ident::new("Add", Span::call_site()),
Expand Down Expand Up @@ -1107,6 +1124,7 @@ mod tests {
shader_node: None,
serialize: None,
memoize: false,
inject_scope: false,
},
fn_name: Ident::new("transform", Span::call_site()),
struct_name: Ident::new("Transform", Span::call_site()),
Expand Down Expand Up @@ -1190,6 +1208,7 @@ mod tests {
shader_node: None,
serialize: None,
memoize: false,
inject_scope: false,
},
fn_name: Ident::new("circle", Span::call_site()),
struct_name: Ident::new("Circle", Span::call_site()),
Expand Down Expand Up @@ -1255,6 +1274,7 @@ mod tests {
shader_node: None,
serialize: None,
memoize: false,
inject_scope: false,
},
fn_name: Ident::new("levels", Span::call_site()),
struct_name: Ident::new("Levels", Span::call_site()),
Expand Down Expand Up @@ -1332,6 +1352,7 @@ mod tests {
shader_node: None,
serialize: None,
memoize: false,
inject_scope: false,
},
fn_name: Ident::new("add", Span::call_site()),
struct_name: Ident::new("Add", Span::call_site()),
Expand Down Expand Up @@ -1397,6 +1418,7 @@ mod tests {
shader_node: None,
serialize: None,
memoize: false,
inject_scope: false,
},
fn_name: Ident::new("load_image", Span::call_site()),
struct_name: Ident::new("LoadImage", Span::call_site()),
Expand Down Expand Up @@ -1462,6 +1484,7 @@ mod tests {
shader_node: None,
serialize: None,
memoize: false,
inject_scope: false,
},
fn_name: Ident::new("custom_node", Span::call_site()),
struct_name: Ident::new("CustomNode", Span::call_site()),
Expand Down
2 changes: 1 addition & 1 deletion node-graph/node-macro/src/shader_nodes/per_pixel_adjust.rs
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,7 @@ impl PerPixelAdjustCodegen<'_> {
ty: ParsedFieldType::Regular(RegularParsedField {
ty: parse_quote!(&'a WgpuExecutor),
exposed: true,
value_source: ParsedValueSource::Scope(LitStr::new("wgpu-executor", Span::call_site())),
value_source: ParsedValueSource::Scope(parse_quote!("wgpu-executor")),
number_soft_min: None,
number_soft_max: None,
number_hard_min: None,
Expand Down
10 changes: 10 additions & 0 deletions node-graph/nodes/gcore/src/debug.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,13 @@ fn unwrap_option<T: Default>(_: impl Ctx, #[implementations(Option<f64>, Option<
fn clone<'i, T: Clone + 'i>(_: impl Ctx, #[implementations(&List<Raster<CPU>>)] value: &'i T) -> T {
value.clone()
}

#[node_macro::node(category("Debug"), inject_scope)]
fn inject_scope_test_producer(_: impl Ctx) -> bool {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3: Test-only inject-scope nodes are being added as regular production Debug nodes, which can mislead users and clutter the node surface with no-op scaffolding.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At node-graph/nodes/gcore/src/debug.rs, line 39:

<comment>Test-only inject-scope nodes are being added as regular production Debug nodes, which can mislead users and clutter the node surface with no-op scaffolding.</comment>

<file context>
@@ -34,3 +34,13 @@ fn unwrap_option<T: Default>(_: impl Ctx, #[implementations(Option<f64>, Option<
 }
+
+#[node_macro::node(category("Debug"), inject_scope)]
+fn inject_scope_test_producer(_: impl Ctx) -> bool {
+	true
+}
</file context>

true
}

#[node_macro::node(category("Debug"))]
fn inject_scope_test_consumer(_: impl Ctx, _primary: (), #[scope(inject_scope_test_producer::IDENTIFIER)] injected: bool) -> bool {
injected
}
Loading
Loading