mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-15 22:28:10 +08:00
Add node macro support for injecting nodes into global scope (#4221)
* Macro support * Preprocessor support * Allow Cow str as scope name * Use Passthrough node to work around compiler auto wrapping the producer node and invalidating the node id * Stable NodeIds * Less hacky but with bad performace * Remove Cow from RegistryValueSource::Scope * Connect scope injections to the inner network export --------- Co-authored-by: Dennis Kobert <dennis@kobert.dev>
This commit is contained in:
@@ -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),
|
||||
@@ -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);
|
||||
@@ -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 {
|
||||
|
||||
@@ -54,6 +54,8 @@ 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)]
|
||||
@@ -61,7 +63,7 @@ pub enum ParsedValueSource {
|
||||
#[default]
|
||||
None,
|
||||
Default(TokenStream2),
|
||||
Scope(LitStr),
|
||||
Scope(Expr),
|
||||
}
|
||||
|
||||
// #[widget(ParsedWidgetOverride::Hidden)]
|
||||
@@ -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;
|
||||
@@ -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"), ...)]
|
||||
"#
|
||||
@@ -430,6 +445,7 @@ impl Parse for NodeFnAttributes {
|
||||
shader_node,
|
||||
serialize,
|
||||
memoize,
|
||||
inject_scope,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -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"),
|
||||
}
|
||||
@@ -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()),
|
||||
@@ -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()),
|
||||
@@ -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()),
|
||||
@@ -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()),
|
||||
@@ -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()),
|
||||
@@ -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()),
|
||||
@@ -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()),
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user