mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-27 02:58:11 +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
|
// 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);
|
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);
|
branch_dependencies.push(branch);
|
||||||
}
|
}
|
||||||
let mut new_deps = combined_deps.clone();
|
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
|
/// `LEVEL` is the index-chain level a node reads, counted outwards from the
|
||||||
/// the innermost. Declaring it narrows the cache key to that level, and reading
|
/// innermost; only declared levels survive in the cache key. Repeat the bound
|
||||||
/// through `index` keeps the declaration and the read from drifting apart.
|
/// to declare several, spelling the level at each `index` call.
|
||||||
/// Repeat the bound to declare several, then spell the level at each read.
|
|
||||||
pub trait ExtractIndex<const LEVEL: u8 = 0>: ExtractIndices {
|
pub trait ExtractIndex<const LEVEL: u8 = 0>: ExtractIndices {
|
||||||
fn index(&self) -> u64 {
|
fn index(&self) -> u64 {
|
||||||
self.try_index().and_then(|mut levels| levels.nth(LEVEL as usize)).unwrap_or(0) as 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
|
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
|
/// Levels at or past `u32::BITS` are only readable through the all-levels
|
||||||
/// sentinel, so they count as declared whenever the mask is saturated.
|
/// sentinel, so they count as declared whenever the mask is saturated.
|
||||||
pub const fn contains_level(self, level: usize) -> bool {
|
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.
|
/// written before the field existed default to the whole chain.
|
||||||
#[cfg_attr(feature = "serde", serde(default = "IndexLevels::innermost"))]
|
#[cfg_attr(feature = "serde", serde(default = "IndexLevels::innermost"))]
|
||||||
pub index_levels: IndexLevels,
|
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"))]
|
#[cfg_attr(feature = "serde", serde(default, deserialize_with = "deserialize_sorted_sources"))]
|
||||||
sources: Vec<SourceId>,
|
sources: Vec<SourceId>,
|
||||||
}
|
}
|
||||||
@@ -333,6 +346,7 @@ impl ContextDependencies {
|
|||||||
extract,
|
extract,
|
||||||
inject,
|
inject,
|
||||||
index_levels,
|
index_levels,
|
||||||
|
pushed_levels: Vec::new(),
|
||||||
sources: Vec::new(),
|
sources: Vec::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -342,6 +356,11 @@ impl ContextDependencies {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn with_pushed_levels(mut self, pushed_levels: Vec<u8>) -> Self {
|
||||||
|
self.pushed_levels = pushed_levels;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
pub fn sources(&self) -> &[SourceId] {
|
pub fn sources(&self) -> &[SourceId] {
|
||||||
&self.sources
|
&self.sources
|
||||||
}
|
}
|
||||||
@@ -496,6 +515,7 @@ impl From<&[ContextFeature]> for ContextDependencies {
|
|||||||
extract,
|
extract,
|
||||||
inject,
|
inject,
|
||||||
index_levels,
|
index_levels,
|
||||||
|
pushed_levels: Vec::new(),
|
||||||
sources: Vec::new(),
|
sources: Vec::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,6 +29,8 @@ pub struct NodeMetadata {
|
|||||||
pub struct FieldMetadata {
|
pub struct FieldMetadata {
|
||||||
pub name: &'static str,
|
pub name: &'static str,
|
||||||
pub description: &'static str,
|
pub description: &'static str,
|
||||||
|
/// Index levels the node pushes when evaluating this input.
|
||||||
|
pub pushed_levels: u8,
|
||||||
pub hidden: bool,
|
pub hidden: bool,
|
||||||
pub exposed: bool,
|
pub exposed: bool,
|
||||||
pub widget_override: RegistryWidgetOverride,
|
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 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)
|
// Generate struct fields: data fields (concrete types) + regular fields (generic types)
|
||||||
let data_field_defs = data_fields.iter().map(|field| {
|
let data_field_defs = data_fields.iter().map(|field| {
|
||||||
let name = &field.pat_ident.ident;
|
let name = &field.pat_ident.ident;
|
||||||
@@ -633,6 +644,7 @@ pub(crate) fn generate_node_code(crate_ident: &CrateIdent, parsed: &ParsedNodeFn
|
|||||||
name: #input_names,
|
name: #input_names,
|
||||||
widget_override: #widget_override,
|
widget_override: #widget_override,
|
||||||
description: #input_descriptions,
|
description: #input_descriptions,
|
||||||
|
pushed_levels: #field_pushed_levels,
|
||||||
hidden: #input_hidden,
|
hidden: #input_hidden,
|
||||||
exposed: #exposed,
|
exposed: #exposed,
|
||||||
value_source: #value_sources,
|
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.
|
/// 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)]
|
#[node_macro::node(category("Vector: Modifier"), memoize)]
|
||||||
fn boolean_operation<'e>(
|
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.
|
/// The wire of vector paths to perform the boolean operation on. Nested groups are automatically flattened.
|
||||||
content: IList<Graphic>,
|
content: IList<Graphic>,
|
||||||
/// Which boolean operation to perform on the paths.
|
/// 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`].
|
/// The boolean operation over a plain vector level, as [`boolean_operation`].
|
||||||
#[node_macro::node(category(""))]
|
#[node_macro::node(category(""))]
|
||||||
fn boolean_operation_vector<'e>(
|
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>,
|
content: IList<Vector>,
|
||||||
operation: BooleanOperation,
|
operation: BooleanOperation,
|
||||||
) -> Result<
|
) -> Result<
|
||||||
|
|||||||
@@ -237,9 +237,8 @@ fn assign_colors_graphic<'e>(
|
|||||||
Ok((element, transform, layer_path))
|
Ok((element, transform, layer_path))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Where each lane's colors start in the level's flattened vector run, so the
|
/// Where each lane's colors start in the level's flattened vector run, valid
|
||||||
/// level is counted once per evaluation rather than once per lane. `offsets`
|
/// for one key and generation. `offsets` holds one entry per lane plus the total.
|
||||||
/// holds one entry per lane plus the total.
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct LaneOffsets {
|
pub struct LaneOffsets {
|
||||||
key: u64,
|
key: u64,
|
||||||
|
|||||||
@@ -205,7 +205,7 @@ impl Preprocessor {
|
|||||||
implementation: DocumentNodeImplementation::ProtoNode(id.clone()),
|
implementation: DocumentNodeImplementation::ProtoNode(id.clone()),
|
||||||
visible: true,
|
visible: true,
|
||||||
skip_deduplication: false,
|
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()
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -256,7 +256,7 @@ impl Preprocessor {
|
|||||||
call_argument: node_io.call_argument.clone(),
|
call_argument: node_io.call_argument.clone(),
|
||||||
implementation: DocumentNodeImplementation::ProtoNode(id.clone()),
|
implementation: DocumentNodeImplementation::ProtoNode(id.clone()),
|
||||||
visible: true,
|
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()
|
..Default::default()
|
||||||
};
|
};
|
||||||
inject_scopes.insert(id.clone(), (template, node_io.return_value.clone()));
|
inject_scopes.insert(id.clone(), (template, node_io.return_value.clone()));
|
||||||
|
|||||||
Reference in New Issue
Block a user