mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-29 05:18:12 +08:00
Measure a node's level delta from its deepest layout source
This commit is contained in:
@@ -326,6 +326,14 @@ pub(crate) fn gathered_subject(node: &Node) -> Option<usize> {
|
|||||||
if !node.output.gathers {
|
if !node.output.gathers {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
materialized_subject(node)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The subject a gather copies frames from or a fold consumes, if any. At most one
|
||||||
|
/// exists: materializing takes an eager subject of non-zero depth, and [`subject`]
|
||||||
|
/// admits an eager one only at index 0 or as a routing source, whose bare-ident
|
||||||
|
/// match rules out any `IList` nesting.
|
||||||
|
pub(crate) fn materialized_subject(node: &Node) -> Option<usize> {
|
||||||
node.inputs
|
node.inputs
|
||||||
.iter()
|
.iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
@@ -357,11 +365,7 @@ pub(crate) fn folded_subject(node: &Node) -> Option<(u8, u8)> {
|
|||||||
if node.output.gathers {
|
if node.output.gathers {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
node.inputs
|
materialized_subject(node).map(|index| (index as u8, materialized_levels(node, index)))
|
||||||
.iter()
|
|
||||||
.enumerate()
|
|
||||||
.find(|(index, input)| input.subject && materialized_levels(node, *index) > 0)
|
|
||||||
.map(|(index, _)| (index as u8, materialized_levels(node, index)))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn field_writes(attrs: &[LevelAttr], core_types: &TokenStream2) -> Vec<TokenStream2> {
|
fn field_writes(attrs: &[LevelAttr], core_types: &TokenStream2) -> Vec<TokenStream2> {
|
||||||
@@ -474,8 +478,9 @@ pub(crate) fn name_input(node: &Node, placeholder: &Type) -> Option<usize> {
|
|||||||
|
|
||||||
fn level_delta(node: &Node) -> i8 {
|
fn level_delta(node: &Node) -> i8 {
|
||||||
// A folded subject contributes no base layout, so the delta is relative to
|
// A folded subject contributes no base layout, so the delta is relative to
|
||||||
// the fresh (empty) base.
|
// the fresh (empty) base. Several sources union into that base, and the union
|
||||||
let base_depth = layout_sources(node).first().map_or(0, |&index| node.inputs[index].shape.depth as i8);
|
// lifts the shallower ones, so the deepest is what the delta measures from.
|
||||||
|
let base_depth = layout_sources(node).iter().map(|&index| node.inputs[index].shape.depth as i8).max().unwrap_or(0);
|
||||||
node.output.shape.depth as i8 - base_depth
|
node.output.shape.depth as i8 - base_depth
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -718,13 +723,12 @@ mod tests {
|
|||||||
Element::Generic(_) => node.monomorphizations.is_empty(),
|
Element::Generic(_) => node.monomorphizations.is_empty(),
|
||||||
Element::Concrete(_) => false,
|
Element::Concrete(_) => false,
|
||||||
};
|
};
|
||||||
let subject_depth = node.inputs.iter().find(|input| input.subject).map_or(0, |input| input.shape.depth as i8);
|
|
||||||
Facts {
|
Facts {
|
||||||
sources: layout_sources(node),
|
sources: layout_sources(node),
|
||||||
carried,
|
carried,
|
||||||
writes: markers(node.output.shape.attrs.iter().map(|attr| &attr.marker)),
|
writes: markers(node.output.shape.attrs.iter().map(|attr| &attr.marker)),
|
||||||
removes: markers(node.output.removes.iter().map(|attr| &attr.marker)),
|
removes: markers(node.output.removes.iter().map(|attr| &attr.marker)),
|
||||||
delta: node.output.shape.depth as i8 - subject_depth,
|
delta: level_delta(node),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1358,6 +1362,33 @@ mod tests {
|
|||||||
assert_eq!(node.output.shape.depth as i8 - subject.shape.depth as i8, -1, "the reducer collapses one level");
|
assert_eq!(node.output.shape.depth as i8 - subject.shape.depth as i8, -1, "the reducer collapses one level");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn the_deepest_layout_source_is_the_delta_base() {
|
||||||
|
let mut parsed = parse_node_fn(
|
||||||
|
quote!(category("")),
|
||||||
|
quote!(
|
||||||
|
fn pick<T>(ctx: impl Ctx + Copy, condition: bool, scalar: impl Node<Context<'_>, Output = T>, listed: impl Node<Context<'_>, Output = IList<T>>) -> Result<IList<T>, Interrupt> {
|
||||||
|
if condition { listed.eval(ctx) } else { emit(scalar.eval(ctx)?) }
|
||||||
|
}
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
parsed.replace_impl_trait_in_input();
|
||||||
|
let node = build(&parsed);
|
||||||
|
|
||||||
|
// Both branches are lazy, so neither materializes and both source the
|
||||||
|
// output's layout, at depths 0 and 1.
|
||||||
|
let sources = layout_sources(&node);
|
||||||
|
let depths: Vec<u8> = sources.iter().map(|&index| node.inputs[index].shape.depth).collect();
|
||||||
|
assert_eq!(depths, vec![0, 1], "the two branches source the layout at differing depths");
|
||||||
|
|
||||||
|
// The union lifts the scalar side to the listed side's depth, so the
|
||||||
|
// output sits at the base rather than one level above it. Measuring from
|
||||||
|
// the first source instead would read a spurious +1 push.
|
||||||
|
assert_eq!(node.output.shape.depth, 1);
|
||||||
|
assert_eq!(level_delta(&node), 0, "the delta measures from the deepest source, not the first");
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn monomorphizations_key_by_generic() {
|
fn monomorphizations_key_by_generic() {
|
||||||
let node = assert_bridge(
|
let node = assert_bridge(
|
||||||
|
|||||||
Reference in New Issue
Block a user