mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-25 23:28:11 +08:00
Emit declarative LayoutMeta from the node macro into registry entries
This commit is contained in:
@@ -179,6 +179,7 @@ mod node_registry_macros {
|
|||||||
(
|
(
|
||||||
ProtoNodeIdentifier::new(concat!["graphene_core::ops::IntoNode<", stringify!($to), ">"]),
|
ProtoNodeIdentifier::new(concat!["graphene_core::ops::IntoNode<", stringify!($to), ">"]),
|
||||||
RegistryEntry {
|
RegistryEntry {
|
||||||
|
layout_meta: None,
|
||||||
io: NodeIOTypes::new(
|
io: NodeIOTypes::new(
|
||||||
concrete!(Context),
|
concrete!(Context),
|
||||||
core_types::registry::record_type::<$to>(),
|
core_types::registry::record_type::<$to>(),
|
||||||
@@ -246,6 +247,7 @@ mod node_registry_macros {
|
|||||||
(
|
(
|
||||||
ProtoNodeIdentifier::new(concat!["graphene_core::ops::ConvertNode<", stringify!($to), ">"]),
|
ProtoNodeIdentifier::new(concat!["graphene_core::ops::ConvertNode<", stringify!($to), ">"]),
|
||||||
RegistryEntry {
|
RegistryEntry {
|
||||||
|
layout_meta: None,
|
||||||
io: NodeIOTypes::new(
|
io: NodeIOTypes::new(
|
||||||
concrete!(Context),
|
concrete!(Context),
|
||||||
core_types::registry::record_type::<$to>(),
|
core_types::registry::record_type::<$to>(),
|
||||||
@@ -291,6 +293,7 @@ mod node_registry_macros {
|
|||||||
(
|
(
|
||||||
ProtoNodeIdentifier::new(concat!["graphene_core::ops::ConvertNode<", stringify!($to), ">"]),
|
ProtoNodeIdentifier::new(concat!["graphene_core::ops::ConvertNode<", stringify!($to), ">"]),
|
||||||
RegistryEntry {
|
RegistryEntry {
|
||||||
|
layout_meta: None,
|
||||||
io: NodeIOTypes::new(
|
io: NodeIOTypes::new(
|
||||||
concrete!(Context),
|
concrete!(Context),
|
||||||
core_types::registry::record_type::<$to>(),
|
core_types::registry::record_type::<$to>(),
|
||||||
|
|||||||
@@ -242,6 +242,52 @@ pub fn empty_layout() -> &'static Layout {
|
|||||||
EMPTY.get_or_init(Layout::default)
|
EMPTY.get_or_init(Layout::default)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Declarative record-io metadata for a node type, emitted by the macro into
|
||||||
|
/// its registry entry so the compiler can fold each wire's layout without
|
||||||
|
/// running the node's constructor. [`fold`](LayoutMeta::fold) reproduces the
|
||||||
|
/// layout the constructor derives at wiring today; the compiler layout pass
|
||||||
|
/// calls it over the proto graph instead.
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct LayoutMeta {
|
||||||
|
/// Whether the node derives its layout from a carrier input; `false` writes
|
||||||
|
/// a fresh record from `Layout::default`.
|
||||||
|
pub carrier: bool,
|
||||||
|
/// The output element: a concrete write, or carried through from the carrier.
|
||||||
|
pub element: ElementSpec,
|
||||||
|
/// The attributes the node writes at its acting level.
|
||||||
|
pub writes: Vec<FieldWrite>,
|
||||||
|
/// The attributes removed from the carrier's layout, as `(name, level)`.
|
||||||
|
pub removes: Vec<(&'static str, u8)>,
|
||||||
|
/// The depth change the node applies to its carrier: `0` for elementwise and
|
||||||
|
/// flip nodes, `+1` for a creator, `-1` for a reducer.
|
||||||
|
pub level_delta: i8,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Where a node's output element comes from, for [`LayoutMeta`].
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub enum ElementSpec {
|
||||||
|
/// The node writes this concrete element.
|
||||||
|
Concrete(ElementWrite),
|
||||||
|
/// The node carries the carrier's element through unchanged.
|
||||||
|
Carried,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LayoutMeta {
|
||||||
|
/// Folds the node's output layout from its carrier's, reproducing what the
|
||||||
|
/// node's constructor derives at wiring. `carrier` is the carrier input's
|
||||||
|
/// layout, or `None` when the node writes a fresh record.
|
||||||
|
pub fn fold(&self, carrier: Option<&Layout>) -> Layout {
|
||||||
|
let carrier = carrier.filter(|_| self.carrier);
|
||||||
|
let base = carrier.map_or_else(Layout::default, |c| c.without(&self.removes));
|
||||||
|
let depth = (carrier.map_or(0, |c| c.depth) as i8 + self.level_delta).max(0) as u8;
|
||||||
|
let element = match &self.element {
|
||||||
|
ElementSpec::Concrete(element) => *element,
|
||||||
|
ElementSpec::Carried => carrier.map_or_else(ElementWrite::default, |c| c.element),
|
||||||
|
};
|
||||||
|
base.with_writes(depth, element, &self.writes)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// A view of one record: a pointer whose layout is proven at wiring.
|
/// A view of one record: a pointer whose layout is proven at wiring.
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
pub struct Rec(*const u8);
|
pub struct Rec(*const u8);
|
||||||
|
|||||||
@@ -170,6 +170,11 @@ where
|
|||||||
unsafe { self.ptr.as_ref() }.extent(input)
|
unsafe { self.ptr.as_ref() }.extent(input)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn extent_at(&self, input: &Input, level: u8) -> crate::gpoll::GPoll<crate::gpoll::Extent> {
|
||||||
|
// SAFETY: as in eval.
|
||||||
|
unsafe { self.ptr.as_ref() }.extent_at(input, level)
|
||||||
|
}
|
||||||
|
|
||||||
fn serialize(&self) -> Option<std::sync::Arc<dyn std::any::Any + Send + Sync>> {
|
fn serialize(&self) -> Option<std::sync::Arc<dyn std::any::Any + Send + Sync>> {
|
||||||
// SAFETY: as in eval.
|
// SAFETY: as in eval.
|
||||||
unsafe { self.ptr.as_ref() }.serialize()
|
unsafe { self.ptr.as_ref() }.serialize()
|
||||||
@@ -278,6 +283,10 @@ pub type NodeConstructor = fn(Vec<EdgeHandle>) -> Result<EdgeHandle, Constructio
|
|||||||
pub struct RegistryEntry {
|
pub struct RegistryEntry {
|
||||||
pub io: NodeIOTypes,
|
pub io: NodeIOTypes,
|
||||||
pub constructor: NodeConstructor,
|
pub constructor: NodeConstructor,
|
||||||
|
/// Declarative record-io metadata for the compiler layout pass; `None` for
|
||||||
|
/// nodes whose layout the pass does not yet fold (routing/opaque, hand-written
|
||||||
|
/// rows), which keep the construction-time path.
|
||||||
|
pub layout_meta: Option<crate::record::LayoutMeta>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn construct(entry: &RegistryEntry, inputs: Vec<EdgeHandle>) -> Result<EdgeHandle, ConstructionError> {
|
pub fn construct(entry: &RegistryEntry, inputs: Vec<EdgeHandle>) -> Result<EdgeHandle, ConstructionError> {
|
||||||
@@ -502,6 +511,7 @@ mod tests {
|
|||||||
Ok(EdgeHandle::new(Arc::new(ValueNode(0u32)) as Arc<ErasedNode<u32>>))
|
Ok(EdgeHandle::new(Arc::new(ValueNode(0u32)) as Arc<ErasedNode<u32>>))
|
||||||
}
|
}
|
||||||
let entry = RegistryEntry {
|
let entry = RegistryEntry {
|
||||||
|
layout_meta: None,
|
||||||
io: NodeIOTypes::new(concrete!(Context), concrete!(u32), vec![edge_type::<String>()]),
|
io: NodeIOTypes::new(concrete!(Context), concrete!(u32), vec![edge_type::<String>()]),
|
||||||
constructor: construct_strlen,
|
constructor: construct_strlen,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1741,6 +1741,23 @@ pub(crate) fn generate_node_impl(crate_ident: &CrateIdent, parsed: &ParsedNodeFn
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
let layout_meta_fn = format_ident!("{}_layout_meta", fn_name);
|
||||||
|
let carrier_present = !shape.skips_carrier();
|
||||||
|
let element_spec = match &shape.element_write {
|
||||||
|
Some(ty) => quote!(#core_types::record::ElementSpec::Concrete(#core_types::record::element_write::<#ty>())),
|
||||||
|
None => quote!(#core_types::record::ElementSpec::Carried),
|
||||||
|
};
|
||||||
|
let layout_meta_def = quote! {
|
||||||
|
#vis fn #layout_meta_fn() -> #core_types::record::LayoutMeta {
|
||||||
|
#core_types::record::LayoutMeta {
|
||||||
|
carrier: #carrier_present,
|
||||||
|
element: #element_spec,
|
||||||
|
writes: ::std::vec![#(#write_descs),*],
|
||||||
|
removes: ::std::vec![#(#remove_pairs),*],
|
||||||
|
level_delta: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
let reading_secondaries = reading_secondary_indices(®ular_fields, shape);
|
let reading_secondaries = reading_secondary_indices(®ular_fields, shape);
|
||||||
let edge_args = regular_fields.iter().zip(&node_generics).map(|(field, generic)| {
|
let edge_args = regular_fields.iter().zip(&node_generics).map(|(field, generic)| {
|
||||||
let name = &field.pat_ident.ident;
|
let name = &field.pat_ident.ident;
|
||||||
@@ -1790,6 +1807,7 @@ pub(crate) fn generate_node_impl(crate_ident: &CrateIdent, parsed: &ParsedNodeFn
|
|||||||
let write_names = (0..shape.write_markers.len()).map(|index| format_ident!("__write_{index}")).map(|slot| quote!(#slot,));
|
let write_names = (0..shape.write_markers.len()).map(|index| format_ident!("__write_{index}")).map(|slot| quote!(#slot,));
|
||||||
quote! {
|
quote! {
|
||||||
#layout_def
|
#layout_def
|
||||||
|
#layout_meta_def
|
||||||
|
|
||||||
#[automatically_derived]
|
#[automatically_derived]
|
||||||
impl<#(#data_field_generic_idents,)* #(#node_generics,)*> #mod_name::#struct_name<#(#struct_type_params,)*> {
|
impl<#(#data_field_generic_idents,)* #(#node_generics,)*> #mod_name::#struct_name<#(#struct_type_params,)*> {
|
||||||
|
|||||||
@@ -56,6 +56,7 @@ fn flip_entries_tokens(parsed: &ParsedNodeFn, struct_name: &Ident, regular_field
|
|||||||
let arity = regular_fields.len();
|
let arity = regular_fields.len();
|
||||||
let names: Vec<&Ident> = regular_fields.iter().map(|field| &field.pat_ident.ident).collect();
|
let names: Vec<&Ident> = regular_fields.iter().map(|field| &field.pat_ident.ident).collect();
|
||||||
let node_underscores: Vec<TokenStream2> = regular_fields.iter().map(|_| quote!(_)).collect();
|
let node_underscores: Vec<TokenStream2> = regular_fields.iter().map(|_| quote!(_)).collect();
|
||||||
|
let carrier_present = flip_carrier(parsed);
|
||||||
|
|
||||||
// Shorthand associated types in the output only resolve against the
|
// Shorthand associated types in the output only resolve against the
|
||||||
// generics' bounds, so rows name the output through a bounded alias. Only
|
// generics' bounds, so rows name the output through a bounded alias. Only
|
||||||
@@ -144,6 +145,13 @@ fn flip_entries_tokens(parsed: &ParsedNodeFn, struct_name: &Ident, regular_field
|
|||||||
});
|
});
|
||||||
Some(quote! {
|
Some(quote! {
|
||||||
gcore::registry::RegistryEntry {
|
gcore::registry::RegistryEntry {
|
||||||
|
layout_meta: Some(gcore::record::LayoutMeta {
|
||||||
|
carrier: #carrier_present,
|
||||||
|
element: gcore::record::ElementSpec::Concrete(gcore::record::element_write::<#row_output>()),
|
||||||
|
writes: ::std::vec::Vec::new(),
|
||||||
|
removes: ::std::vec::Vec::new(),
|
||||||
|
level_delta: 0,
|
||||||
|
}),
|
||||||
io: gcore::registry::NodeIOTypes::new(
|
io: gcore::registry::NodeIOTypes::new(
|
||||||
gcore::concrete!(gcore::context::ContextImpl<'static>),
|
gcore::concrete!(gcore::context::ContextImpl<'static>),
|
||||||
gcore::registry::record_type::<#row_output>(),
|
gcore::registry::record_type::<#row_output>(),
|
||||||
@@ -273,6 +281,7 @@ fn routing_entries_tokens(parsed: &ParsedNodeFn, struct_name: &Ident, regular_fi
|
|||||||
quote! {
|
quote! {
|
||||||
pub fn #entries_name() -> ::std::vec::Vec<gcore::registry::RegistryEntry> {
|
pub fn #entries_name() -> ::std::vec::Vec<gcore::registry::RegistryEntry> {
|
||||||
vec![gcore::registry::RegistryEntry {
|
vec![gcore::registry::RegistryEntry {
|
||||||
|
layout_meta: None,
|
||||||
io: gcore::registry::NodeIOTypes::new(
|
io: gcore::registry::NodeIOTypes::new(
|
||||||
gcore::concrete!(gcore::context::ContextImpl<'static>),
|
gcore::concrete!(gcore::context::ContextImpl<'static>),
|
||||||
gcore::Type::Record(Box::new(gcore::Type::Generic(::std::borrow::Cow::Borrowed(#token_name)))),
|
gcore::Type::Record(Box::new(gcore::Type::Generic(::std::borrow::Cow::Borrowed(#token_name)))),
|
||||||
@@ -353,6 +362,7 @@ fn record_opaque_entries_tokens(parsed: &ParsedNodeFn, struct_name: &Ident, regu
|
|||||||
quote! {
|
quote! {
|
||||||
pub fn #entries_name() -> ::std::vec::Vec<gcore::registry::RegistryEntry> {
|
pub fn #entries_name() -> ::std::vec::Vec<gcore::registry::RegistryEntry> {
|
||||||
vec![gcore::registry::RegistryEntry {
|
vec![gcore::registry::RegistryEntry {
|
||||||
|
layout_meta: None,
|
||||||
io: gcore::registry::NodeIOTypes::new(
|
io: gcore::registry::NodeIOTypes::new(
|
||||||
gcore::concrete!(gcore::context::ContextImpl<'static>),
|
gcore::concrete!(gcore::context::ContextImpl<'static>),
|
||||||
gcore::Type::Record(Box::new(gcore::Type::Generic(::std::borrow::Cow::Borrowed("T")))),
|
gcore::Type::Record(Box::new(gcore::Type::Generic(::std::borrow::Cow::Borrowed("T")))),
|
||||||
@@ -464,9 +474,11 @@ fn record_entries_tokens(parsed: &ParsedNodeFn, struct_name: &Ident, regular_fie
|
|||||||
(_, None) => unreachable!("non-token record nodes write an element"),
|
(_, None) => unreachable!("non-token record nodes write an element"),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let layout_meta_fn = format_ident!("{}_layout_meta", fn_name);
|
||||||
quote! {
|
quote! {
|
||||||
pub fn #entries_name() -> ::std::vec::Vec<gcore::registry::RegistryEntry> {
|
pub fn #entries_name() -> ::std::vec::Vec<gcore::registry::RegistryEntry> {
|
||||||
vec![gcore::registry::RegistryEntry {
|
vec![gcore::registry::RegistryEntry {
|
||||||
|
layout_meta: Some(self::#layout_meta_fn()),
|
||||||
io: gcore::registry::NodeIOTypes::new(
|
io: gcore::registry::NodeIOTypes::new(
|
||||||
gcore::concrete!(gcore::context::ContextImpl<'static>),
|
gcore::concrete!(gcore::context::ContextImpl<'static>),
|
||||||
#io_output,
|
#io_output,
|
||||||
|
|||||||
Reference in New Issue
Block a user