mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-15 14:18:04 +08:00
Graphene: Fine-grained context caching (#2500)
* RFC: Fine Grained Context Caching * Fix typos * Fix label * Add description of inject traits * Explicitly support context modification * Start implementation of context invalidation * Add inject trait variants * Route Extract / Inject traits to the proto nodes * Implement context dependency analysis * Implement context modification node insertion * Fix erronous force graph run message * Fix Extract* Inject* annotations in the nodes * Require Hash implementation for VarArgs * Fix nullification node insertion * Cross of done items unresolved questions section * Update Cargo.lock * Fix context features propagation * Update demo artwork * Remove BondlessFootprint and FreezeRealTime nodes * Fix migration * Add migrations for adding context features to old networks * Always update real time regardless of animation state * Cargo fmt * Fix tests * Readd sed command to hopefully fix profile result parsing * Add debug output to profiling pr * Use new totals instead of summaries for for iai results * Even more debugging * Use correct debug metrics (hopefully) * Add more MemoNode implementations * Add context features annotation to shader node macro * Cleanup * Time -> RealTime * Code review --------- Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
@@ -41,6 +41,8 @@ pub(crate) fn generate_node_code(parsed: &ParsedNodeFn) -> syn::Result<TokenStre
|
||||
let struct_generics: Vec<Ident> = fields.iter().enumerate().map(|(i, _)| format_ident!("Node{}", i)).collect();
|
||||
let input_ident = &input.pat_ident;
|
||||
|
||||
let context_features = &input.context_features;
|
||||
|
||||
let field_idents: Vec<_> = fields.iter().map(|f| &f.pat_ident).collect();
|
||||
let field_names: Vec<_> = field_idents.iter().map(|pat_ident| &pat_ident.ident).collect();
|
||||
|
||||
@@ -242,7 +244,7 @@ pub(crate) fn generate_node_code(parsed: &ParsedNodeFn) -> syn::Result<TokenStre
|
||||
#name: #graphene_core::Node<'n, #input_type, Output = #fut_ident > + #graphene_core::WasmNotSync
|
||||
)
|
||||
}
|
||||
(ParsedFieldType::Node { .. }, false) => unreachable!(),
|
||||
(ParsedFieldType::Node { .. }, false) => unreachable!("Found node which takes an impl Node<> input but is not async"),
|
||||
});
|
||||
}
|
||||
let where_clause = where_clause.clone().unwrap_or(WhereClause {
|
||||
@@ -329,7 +331,7 @@ pub(crate) fn generate_node_code(parsed: &ParsedNodeFn) -> syn::Result<TokenStre
|
||||
mod #mod_name {
|
||||
use super::*;
|
||||
use #graphene_core as gcore;
|
||||
use gcore::{Node, NodeIOTypes, concrete, fn_type, fn_type_fut, future, ProtoNodeIdentifier, WasmNotSync, NodeIO};
|
||||
use gcore::{Node, NodeIOTypes, concrete, fn_type, fn_type_fut, future, ProtoNodeIdentifier, WasmNotSync, NodeIO, ContextFeature};
|
||||
use gcore::value::ClonedNode;
|
||||
use gcore::ops::TypeNode;
|
||||
use gcore::registry::{NodeMetadata, FieldMetadata, NODE_REGISTRY, NODE_METADATA, DynAnyNode, DowncastBothNode, DynFuture, TypeErasedBox, PanicNode, RegistryValueSource, RegistryWidgetOverride};
|
||||
@@ -364,6 +366,7 @@ pub(crate) fn generate_node_code(parsed: &ParsedNodeFn) -> syn::Result<TokenStre
|
||||
category: #category,
|
||||
description: #description,
|
||||
properties: #properties,
|
||||
context_features: vec![#(ContextFeature::#context_features,)*],
|
||||
fields: vec![
|
||||
#(
|
||||
FieldMetadata {
|
||||
|
||||
@@ -7,8 +7,8 @@ use syn::punctuated::Punctuated;
|
||||
use syn::spanned::Spanned;
|
||||
use syn::token::{Comma, RArrow};
|
||||
use syn::{
|
||||
AttrStyle, Attribute, Error, Expr, ExprTuple, FnArg, GenericParam, Ident, ItemFn, Lit, LitFloat, LitInt, LitStr, Meta, Pat, PatIdent, PatType, Path, ReturnType, Type, TypeParam, Visibility,
|
||||
WhereClause, parse_quote,
|
||||
AttrStyle, Attribute, Error, Expr, ExprTuple, FnArg, GenericParam, Ident, ItemFn, Lit, LitFloat, LitInt, LitStr, Meta, Pat, PatIdent, PatType, Path, ReturnType, TraitBound, Type, TypeImplTrait,
|
||||
TypeParam, TypeParamBound, Visibility, WhereClause, parse_quote,
|
||||
};
|
||||
|
||||
use crate::codegen::generate_node_code;
|
||||
@@ -149,6 +149,7 @@ pub(crate) struct Input {
|
||||
pub(crate) pat_ident: PatIdent,
|
||||
pub(crate) ty: Type,
|
||||
pub(crate) implementations: Punctuated<Type, Comma>,
|
||||
pub(crate) context_features: Vec<Ident>,
|
||||
}
|
||||
|
||||
impl Parse for Implementation {
|
||||
@@ -384,10 +385,12 @@ fn parse_inputs(inputs: &Punctuated<FnArg, Comma>) -> syn::Result<(Input, Vec<Pa
|
||||
.map(|attr| parse_implementations(attr, &pat_ident.ident))
|
||||
.transpose()?
|
||||
.unwrap_or_default();
|
||||
let context_features = parse_context_feature_idents(ty);
|
||||
input = Some(Input {
|
||||
pat_ident,
|
||||
ty: (**ty).clone(),
|
||||
implementations,
|
||||
context_features,
|
||||
});
|
||||
} else if let Pat::Ident(pat_ident) = &**pat {
|
||||
let field = parse_field(pat_ident.clone(), (**ty).clone(), attrs).map_err(|e| Error::new_spanned(pat_ident, format!("Failed to parse argument '{}': {}", pat_ident.ident, e)))?;
|
||||
@@ -404,6 +407,41 @@ fn parse_inputs(inputs: &Punctuated<FnArg, Comma>) -> syn::Result<(Input, Vec<Pa
|
||||
Ok((input, fields))
|
||||
}
|
||||
|
||||
/// Parse context feature identifiers from the trait bounds of a context parameter.
|
||||
fn parse_context_feature_idents(ty: &Type) -> Vec<Ident> {
|
||||
let mut features = Vec::new();
|
||||
|
||||
// Check if this is an impl trait (impl Ctx + ...)
|
||||
if let Type::ImplTrait(TypeImplTrait { bounds, .. }) = ty {
|
||||
for bound in bounds {
|
||||
if let TypeParamBound::Trait(TraitBound { path, .. }) = bound {
|
||||
// Extract the last segment of the trait path
|
||||
if let Some(segment) = path.segments.last() {
|
||||
match segment.ident.to_string().as_str() {
|
||||
"ExtractFootprint"
|
||||
| "ExtractRealTime"
|
||||
| "ExtractAnimationTime"
|
||||
| "ExtractIndex"
|
||||
| "ExtractVarArgs"
|
||||
| "InjectFootprint"
|
||||
| "InjectRealTime"
|
||||
| "InjectAnimationTime"
|
||||
| "InjectIndex"
|
||||
| "InjectVarArgs" => {
|
||||
features.push(segment.ident.clone());
|
||||
}
|
||||
// Skip Modify* traits as they don't affect usage tracking
|
||||
// Also ignore other traits like Ctx, ExtractAll, etc.
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
features
|
||||
}
|
||||
|
||||
fn parse_implementations(attr: &Attribute, name: &Ident) -> syn::Result<Punctuated<Type, Comma>> {
|
||||
let content: TokenStream2 = attr.parse_args()?;
|
||||
let parser = Punctuated::<Type, Comma>::parse_terminated;
|
||||
@@ -817,6 +855,7 @@ mod tests {
|
||||
pat_ident: pat_ident("a"),
|
||||
ty: parse_quote!(f64),
|
||||
implementations: Punctuated::new(),
|
||||
context_features: vec![],
|
||||
},
|
||||
output_type: parse_quote!(f64),
|
||||
is_async: false,
|
||||
@@ -883,6 +922,7 @@ mod tests {
|
||||
pat_ident: pat_ident("footprint"),
|
||||
ty: parse_quote!(Footprint),
|
||||
implementations: Punctuated::new(),
|
||||
context_features: vec![],
|
||||
},
|
||||
output_type: parse_quote!(T),
|
||||
is_async: false,
|
||||
@@ -936,7 +976,7 @@ mod tests {
|
||||
let attr = quote!(category("Vector: Shape"));
|
||||
let input = quote!(
|
||||
/// Test
|
||||
fn circle(_: impl Ctx, #[default(50.)] radius: f64) -> Vector {
|
||||
fn circle(_: impl Ctx + ExtractFootprint, #[default(50.)] radius: f64) -> Vector {
|
||||
// Implementation details...
|
||||
}
|
||||
);
|
||||
@@ -960,8 +1000,9 @@ mod tests {
|
||||
where_clause: None,
|
||||
input: Input {
|
||||
pat_ident: pat_ident("_"),
|
||||
ty: parse_quote!(impl Ctx),
|
||||
ty: parse_quote!(impl Ctx + ExtractFootprint),
|
||||
implementations: Punctuated::new(),
|
||||
context_features: vec![format_ident!("ExtractFootprint")],
|
||||
},
|
||||
output_type: parse_quote!(Vector),
|
||||
is_async: false,
|
||||
@@ -1024,6 +1065,7 @@ mod tests {
|
||||
pat_ident: pat_ident("image"),
|
||||
ty: parse_quote!(Table<Raster<P>>),
|
||||
implementations: Punctuated::new(),
|
||||
context_features: vec![],
|
||||
},
|
||||
output_type: parse_quote!(Table<Raster<P>>),
|
||||
is_async: false,
|
||||
@@ -1098,6 +1140,7 @@ mod tests {
|
||||
pat_ident: pat_ident("a"),
|
||||
ty: parse_quote!(f64),
|
||||
implementations: Punctuated::new(),
|
||||
context_features: vec![],
|
||||
},
|
||||
output_type: parse_quote!(f64),
|
||||
is_async: false,
|
||||
@@ -1160,6 +1203,7 @@ mod tests {
|
||||
pat_ident: pat_ident("api"),
|
||||
ty: parse_quote!(&WasmEditorApi),
|
||||
implementations: Punctuated::new(),
|
||||
context_features: vec![],
|
||||
},
|
||||
output_type: parse_quote!(Table<Raster<CPU>>),
|
||||
is_async: true,
|
||||
@@ -1222,6 +1266,7 @@ mod tests {
|
||||
pat_ident: pat_ident("input"),
|
||||
ty: parse_quote!(i32),
|
||||
implementations: Punctuated::new(),
|
||||
context_features: vec![],
|
||||
},
|
||||
output_type: parse_quote!(i32),
|
||||
is_async: false,
|
||||
|
||||
@@ -295,6 +295,7 @@ impl PerPixelAdjustCodegen<'_> {
|
||||
pat_ident: self.parsed.input.pat_ident.clone(),
|
||||
ty: parse_quote!(impl #gcore::context::Ctx),
|
||||
implementations: Default::default(),
|
||||
context_features: self.parsed.input.context_features.clone(),
|
||||
},
|
||||
output_type: raster_gpu,
|
||||
is_async: true,
|
||||
|
||||
Reference in New Issue
Block a user