mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-15 22:28:10 +08:00
Derive pushed index levels per input and pop them when lifting requirements
This commit is contained in:
@@ -523,10 +523,13 @@ impl ProtoNetwork {
|
||||
};
|
||||
|
||||
// Compute the dependencies for each branch and combine all of them
|
||||
for &node in &inputs {
|
||||
let pushed_levels = self.nodes[node_index].1.context_features.pushed_levels.clone();
|
||||
for (input, &node) in inputs.iter().enumerate() {
|
||||
let branch = self.find_context_dependencies(node);
|
||||
|
||||
combined_deps |= &branch.0;
|
||||
let mut lifted = branch.0.clone();
|
||||
lifted.index_levels = lifted.index_levels.popped(pushed_levels.get(input).copied().unwrap_or(0));
|
||||
combined_deps |= &lifted;
|
||||
branch_dependencies.push(branch);
|
||||
}
|
||||
let mut new_deps = combined_deps.clone();
|
||||
|
||||
@@ -57,10 +57,9 @@ pub trait ExtractIndices {
|
||||
}
|
||||
}
|
||||
|
||||
/// `LEVEL` is the level of the index chain a node reads, counted outwards from
|
||||
/// the innermost. Declaring it narrows the cache key to that level, and reading
|
||||
/// through `index` keeps the declaration and the read from drifting apart.
|
||||
/// Repeat the bound to declare several, then spell the level at each read.
|
||||
/// `LEVEL` is the index-chain level a node reads, counted outwards from the
|
||||
/// innermost; only declared levels survive in the cache key. Repeat the bound
|
||||
/// to declare several, spelling the level at each `index` call.
|
||||
pub trait ExtractIndex<const LEVEL: u8 = 0>: ExtractIndices {
|
||||
fn index(&self) -> u64 {
|
||||
self.try_index().and_then(|mut levels| levels.nth(LEVEL as usize)).unwrap_or(0) as u64
|
||||
@@ -245,6 +244,17 @@ impl IndexLevels {
|
||||
self.0 == u32::MAX
|
||||
}
|
||||
|
||||
/// The same requirement seen from outside `levels` pushed levels. An
|
||||
/// all-levels mask stays saturated, since its reader's level is not known at
|
||||
/// compile time.
|
||||
pub const fn popped(self, levels: u8) -> Self {
|
||||
match self.0 {
|
||||
u32::MAX => self,
|
||||
_ if levels as u32 >= u32::BITS => Self(0),
|
||||
mask => Self(mask >> levels),
|
||||
}
|
||||
}
|
||||
|
||||
/// Levels at or past `u32::BITS` are only readable through the all-levels
|
||||
/// sentinel, so they count as declared whenever the mask is saturated.
|
||||
pub const fn contains_level(self, level: usize) -> bool {
|
||||
@@ -319,6 +329,9 @@ pub struct ContextDependencies {
|
||||
/// written before the field existed default to the whole chain.
|
||||
#[cfg_attr(feature = "serde", serde(default = "IndexLevels::innermost"))]
|
||||
pub index_levels: IndexLevels,
|
||||
/// Index levels pushed per input, in input order; a missing entry is 0.
|
||||
#[cfg_attr(feature = "serde", serde(default))]
|
||||
pub pushed_levels: Vec<u8>,
|
||||
#[cfg_attr(feature = "serde", serde(default, deserialize_with = "deserialize_sorted_sources"))]
|
||||
sources: Vec<SourceId>,
|
||||
}
|
||||
@@ -333,6 +346,7 @@ impl ContextDependencies {
|
||||
extract,
|
||||
inject,
|
||||
index_levels,
|
||||
pushed_levels: Vec::new(),
|
||||
sources: Vec::new(),
|
||||
}
|
||||
}
|
||||
@@ -342,6 +356,11 @@ impl ContextDependencies {
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_pushed_levels(mut self, pushed_levels: Vec<u8>) -> Self {
|
||||
self.pushed_levels = pushed_levels;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn sources(&self) -> &[SourceId] {
|
||||
&self.sources
|
||||
}
|
||||
@@ -496,6 +515,7 @@ impl From<&[ContextFeature]> for ContextDependencies {
|
||||
extract,
|
||||
inject,
|
||||
index_levels,
|
||||
pushed_levels: Vec::new(),
|
||||
sources: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,6 +29,8 @@ pub struct NodeMetadata {
|
||||
pub struct FieldMetadata {
|
||||
pub name: &'static str,
|
||||
pub description: &'static str,
|
||||
/// Index levels the node pushes when evaluating this input.
|
||||
pub pushed_levels: u8,
|
||||
pub hidden: bool,
|
||||
pub exposed: bool,
|
||||
pub widget_override: RegistryWidgetOverride,
|
||||
|
||||
@@ -178,6 +178,17 @@ pub(crate) fn generate_node_code(crate_ident: &CrateIdent, parsed: &ParsedNodeFn
|
||||
|
||||
let input_descriptions: Vec<_> = regular_fields.iter().map(|f| &f.description).collect();
|
||||
|
||||
let subject_depth = node.inputs.iter().find(|input| input.subject).map_or(0, |input| input.shape.depth);
|
||||
let pushed_levels = (node.output.shape.depth as i8 - subject_depth as i8).max(0) as u8;
|
||||
let field_pushed_levels: Vec<u8> = regular_fields
|
||||
.iter()
|
||||
.map(|field| match &field.ty {
|
||||
ParsedFieldType::Regular(RegularParsedField { list_levels, .. }) if *list_levels > 0 => *list_levels as u8,
|
||||
ParsedFieldType::Node(_) => pushed_levels,
|
||||
_ => 0,
|
||||
})
|
||||
.collect();
|
||||
|
||||
// Generate struct fields: data fields (concrete types) + regular fields (generic types)
|
||||
let data_field_defs = data_fields.iter().map(|field| {
|
||||
let name = &field.pat_ident.ident;
|
||||
@@ -633,6 +644,7 @@ pub(crate) fn generate_node_code(crate_ident: &CrateIdent, parsed: &ParsedNodeFn
|
||||
name: #input_names,
|
||||
widget_override: #widget_override,
|
||||
description: #input_descriptions,
|
||||
pushed_levels: #field_pushed_levels,
|
||||
hidden: #input_hidden,
|
||||
exposed: #exposed,
|
||||
value_source: #value_sources,
|
||||
|
||||
@@ -98,7 +98,7 @@ fn boolean_core<'e>(
|
||||
/// Combines the geometric forms of one or more closed paths into a new vector path that results from cutting or joining the paths by the chosen method.
|
||||
#[node_macro::node(category("Vector: Modifier"), memoize)]
|
||||
fn boolean_operation<'e>(
|
||||
ctx: impl Ctx + ExtractArena<'e> + core_types::ExtractIndex + core_types::InjectIndex + Copy,
|
||||
ctx: impl Ctx + ExtractArena<'e> + core_types::InjectIndex + Copy,
|
||||
/// The wire of vector paths to perform the boolean operation on. Nested groups are automatically flattened.
|
||||
content: IList<Graphic>,
|
||||
/// Which boolean operation to perform on the paths.
|
||||
@@ -134,7 +134,7 @@ fn boolean_operation<'e>(
|
||||
/// The boolean operation over a plain vector level, as [`boolean_operation`].
|
||||
#[node_macro::node(category(""))]
|
||||
fn boolean_operation_vector<'e>(
|
||||
ctx: impl Ctx + ExtractArena<'e> + core_types::ExtractIndex + core_types::InjectIndex + Copy,
|
||||
ctx: impl Ctx + ExtractArena<'e> + core_types::InjectIndex + Copy,
|
||||
content: IList<Vector>,
|
||||
operation: BooleanOperation,
|
||||
) -> Result<
|
||||
|
||||
@@ -237,9 +237,8 @@ fn assign_colors_graphic<'e>(
|
||||
Ok((element, transform, layer_path))
|
||||
}
|
||||
|
||||
/// Where each lane's colors start in the level's flattened vector run, so the
|
||||
/// level is counted once per evaluation rather than once per lane. `offsets`
|
||||
/// holds one entry per lane plus the total.
|
||||
/// Where each lane's colors start in the level's flattened vector run, valid
|
||||
/// for one key and generation. `offsets` holds one entry per lane plus the total.
|
||||
#[derive(Debug)]
|
||||
pub struct LaneOffsets {
|
||||
key: u64,
|
||||
|
||||
@@ -205,7 +205,7 @@ impl Preprocessor {
|
||||
implementation: DocumentNodeImplementation::ProtoNode(id.clone()),
|
||||
visible: true,
|
||||
skip_deduplication: false,
|
||||
context_features: ContextDependencies::from(metadata.context_features.as_slice()),
|
||||
context_features: ContextDependencies::from(metadata.context_features.as_slice()).with_pushed_levels(metadata.fields.iter().map(|field| field.pushed_levels).collect()),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
@@ -256,7 +256,7 @@ 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()),
|
||||
context_features: ContextDependencies::from(metadata.context_features.as_slice()).with_pushed_levels(metadata.fields.iter().map(|field| field.pushed_levels).collect()),
|
||||
..Default::default()
|
||||
};
|
||||
inject_scopes.insert(id.clone(), (template, node_io.return_value.clone()));
|
||||
|
||||
Reference in New Issue
Block a user