mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-15 22:28:10 +08:00
Install record layouts from the pass via set_layout
This commit is contained in:
@@ -372,7 +372,7 @@ macro_rules! tagged_value {
|
||||
}
|
||||
$(
|
||||
if ty == core_types::registry::record_edge_type::<$ty>() {
|
||||
let layout = handle.layout().ok_or_else(|| "a record edge must carry its layout".to_string())?.clone();
|
||||
let layout = handle.layout().clone();
|
||||
return Ok(handle
|
||||
.downcast_record::<$ty>()
|
||||
.map_err(|e| format!("{e:?}"))?
|
||||
@@ -381,7 +381,7 @@ macro_rules! tagged_value {
|
||||
}
|
||||
)*
|
||||
if ty == core_types::registry::record_edge_type::<RenderOutput>() {
|
||||
let layout = handle.layout().ok_or_else(|| "a record edge must carry its layout".to_string())?.clone();
|
||||
let layout = handle.layout().clone();
|
||||
return Ok(handle
|
||||
.downcast_record::<RenderOutput>()
|
||||
.map_err(|e| format!("{e:?}"))?
|
||||
|
||||
@@ -128,7 +128,7 @@ impl ConstructionArgs {
|
||||
pub(crate) struct Resolved {
|
||||
pub io: Option<NodeIOTypes>,
|
||||
pub layout_meta: Option<core_types::record::LayoutMeta>,
|
||||
pub layout: Option<core_types::record::Layout>,
|
||||
pub layout: Option<core_types::record::RecordLayout>,
|
||||
}
|
||||
|
||||
impl PartialEq for Resolved {
|
||||
@@ -221,7 +221,7 @@ impl ProtoNode {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn resolved_layout(&self) -> Option<&core_types::record::Layout> {
|
||||
pub fn resolved_layout(&self) -> Option<&core_types::record::RecordLayout> {
|
||||
self.resolved.layout.as_ref()
|
||||
}
|
||||
}
|
||||
@@ -372,10 +372,18 @@ impl ProtoNetwork {
|
||||
let layout = {
|
||||
let node = &self.nodes[index].1;
|
||||
match &node.construction_args {
|
||||
ConstructionArgs::Value(value) => value.element_write().map(|element| core_types::record::Layout::default().with_writes(0, element, &[])),
|
||||
ConstructionArgs::Value(value) => value.element_write().map(|element| {
|
||||
let layout = core_types::record::Layout::default().with_writes(0, element, &[]);
|
||||
core_types::record::RecordLayout {
|
||||
frame_bytes: layout.frame_bytes(),
|
||||
plan: Vec::new(),
|
||||
layout,
|
||||
}
|
||||
}),
|
||||
ConstructionArgs::Nodes(inputs) => node.resolved.layout_meta.as_ref().and_then(|meta| {
|
||||
let input_layouts: Vec<Option<&core_types::record::Layout>> = inputs.iter().map(|input| self.nodes[input.0 as usize].1.resolved.layout.as_ref()).collect();
|
||||
meta.sources.iter().all(|&source| input_layouts[source as usize].is_some()).then(|| meta.fold(&input_layouts))
|
||||
let input_layouts: Vec<Option<&core_types::record::Layout>> =
|
||||
inputs.iter().map(|input| self.nodes[input.0 as usize].1.resolved.layout.as_ref().map(|resolved| &resolved.layout)).collect();
|
||||
meta.sources.iter().all(|&source| input_layouts[source as usize].is_some()).then(|| meta.resolve(&input_layouts))
|
||||
}),
|
||||
ConstructionArgs::Inline(_) => None,
|
||||
}
|
||||
|
||||
@@ -476,15 +476,16 @@ impl BorrowTree {
|
||||
.clone()
|
||||
.to_edge()
|
||||
.map_err(|error| vec![GraphError::new(&proto_node, GraphErrorType::ConstructionFailed(error))])?;
|
||||
debug_assert!(proto_node.resolved_layout().map_or(true, |expected| node.layout() == Some(expected)), "layout pass disagrees with construction for {}", proto_node.identifier.as_str());
|
||||
self.store_node(node, id, path.into());
|
||||
}
|
||||
ConstructionArgs::Inline(_) => unimplemented!("Inline nodes are not supported yet"),
|
||||
ConstructionArgs::Nodes(ids) => {
|
||||
let construction_nodes = self.node_deps(ids);
|
||||
let constructor = typing_context.constructor(id).ok_or_else(|| vec![GraphError::new(&proto_node, GraphErrorType::NoConstructor)])?;
|
||||
let node = constructor(construction_nodes).map_err(|error| vec![GraphError::new(&proto_node, GraphErrorType::ConstructionFailed(format!("{error:?}")))])?;
|
||||
debug_assert!(proto_node.resolved_layout().map_or(true, |expected| node.layout() == Some(expected)), "layout pass disagrees with construction for {}", proto_node.identifier.as_str());
|
||||
let mut node = constructor(construction_nodes).map_err(|error| vec![GraphError::new(&proto_node, GraphErrorType::ConstructionFailed(format!("{error:?}")))])?;
|
||||
if let Some(layout) = proto_node.resolved_layout() {
|
||||
node.set_layout(layout.clone());
|
||||
}
|
||||
self.store_node(node, id, path.into());
|
||||
}
|
||||
};
|
||||
@@ -503,7 +504,7 @@ impl BorrowTree {
|
||||
}
|
||||
|
||||
fn frame_bytes(&self, id: NodeId) -> usize {
|
||||
self.nodes.get(&id).and_then(|(handle, _)| handle.layout()).map_or(0, |layout| layout.frame_bytes())
|
||||
self.nodes.get(&id).map_or(0, |(handle, _)| handle.layout().frame_bytes())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -191,9 +191,7 @@ mod node_registry_macros {
|
||||
}
|
||||
let mut inputs = inputs.into_iter();
|
||||
let handle = inputs.next().unwrap();
|
||||
let Some(layout) = handle.layout().cloned() else {
|
||||
return Err(ConstructionError::MissingLayout);
|
||||
};
|
||||
let layout = handle.layout().clone();
|
||||
let node = graphene_std::ops::IntoNode::<$to, _, $from>::new(handle.downcast_record::<$from>()?, &layout);
|
||||
Ok(EdgeHandle::new_record::<$to>(std::sync::Arc::new(node) as std::sync::Arc<core_types::registry::ErasedRecordNode>))
|
||||
},
|
||||
@@ -265,10 +263,8 @@ mod node_registry_macros {
|
||||
let mut inputs = inputs.into_iter();
|
||||
let mut claim = || {
|
||||
let handle = inputs.next().unwrap();
|
||||
let Some(layout) = handle.layout().cloned() else {
|
||||
return Err(ConstructionError::MissingLayout);
|
||||
};
|
||||
Ok((handle, layout))
|
||||
let layout = handle.layout().clone();
|
||||
Ok::<_, ConstructionError>((handle, layout))
|
||||
};
|
||||
let (value, value_layout) = claim()?;
|
||||
let (converter, converter_layout) = claim()?;
|
||||
@@ -305,13 +301,9 @@ mod node_registry_macros {
|
||||
}
|
||||
let mut inputs = inputs.into_iter();
|
||||
let value = inputs.next().unwrap();
|
||||
let Some(value_layout) = value.layout().cloned() else {
|
||||
return Err(ConstructionError::MissingLayout);
|
||||
};
|
||||
let value_layout = value.layout().clone();
|
||||
let converter = inputs.next().unwrap();
|
||||
let Some(converter_layout) = converter.layout().cloned() else {
|
||||
return Err(ConstructionError::MissingLayout);
|
||||
};
|
||||
let converter_layout = converter.layout().clone();
|
||||
let node = graphene_std::ops::ConvertNode::<$to, _, _, $from, $convert>::new(
|
||||
value.downcast_record::<$from>()?,
|
||||
converter.downcast_record::<$convert>()?,
|
||||
|
||||
@@ -278,6 +278,9 @@ pub trait Node<Input> {
|
||||
crate::record::empty_layout()
|
||||
}
|
||||
|
||||
/// Installs this node's resolved record layout; a no-op unless it produces records.
|
||||
fn set_layout(&mut self, _layout: crate::record::RecordLayout) {}
|
||||
|
||||
fn eval_batch<'a>(&'a self, input: &'a Input, range: Range<u64>, scratch: Option<&'a mut [MaybeUninit<Self::Output>]>) -> BatchStatus<'a, Self::Output>
|
||||
where
|
||||
Input: InjectIndex + Copy,
|
||||
|
||||
@@ -243,6 +243,14 @@ pub fn empty_layout() -> &'static Layout {
|
||||
}
|
||||
|
||||
/// Declarative record-io metadata for a node type, emitted by the macro into
|
||||
/// A record node's output layout with the frame size and carrier copy plan derived from it.
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct RecordLayout {
|
||||
pub layout: Layout,
|
||||
pub frame_bytes: usize,
|
||||
pub plan: Vec<(usize, usize, usize)>,
|
||||
}
|
||||
|
||||
/// 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
|
||||
@@ -287,8 +295,7 @@ pub enum ElementSpec {
|
||||
}
|
||||
|
||||
impl LayoutMeta {
|
||||
/// The meta of an elementwise carrier flip that retypes input 0's element,
|
||||
/// preserving its depth and attributes: what an `Into`/`Convert` coercion derives.
|
||||
/// Keeps input 0's layout but replaces its element.
|
||||
pub fn retype(element: ElementWrite) -> Self {
|
||||
Self {
|
||||
sources: vec![0],
|
||||
@@ -318,6 +325,23 @@ impl LayoutMeta {
|
||||
};
|
||||
base.with_writes(depth, element, &self.writes)
|
||||
}
|
||||
|
||||
/// [`fold`](LayoutMeta::fold) with the frame size and carrier copy plan derived from it.
|
||||
pub fn resolve(&self, inputs: &[Option<&Layout>]) -> RecordLayout {
|
||||
let layout = self.fold(inputs);
|
||||
let frame_bytes = layout.frame_bytes();
|
||||
let plan = match self.sources.first() {
|
||||
// A reducer collapses its carrier's levels, so it writes a fresh record rather than copying fields down.
|
||||
Some(&source) if self.level_delta >= 0 => {
|
||||
let from = inputs[source as usize].expect("layout resolve source input has no layout");
|
||||
let carry_element = matches!(self.element, ElementSpec::Carried);
|
||||
let removes: Vec<(&str, u8)> = self.removes.clone();
|
||||
copy_plan(from, &layout, carry_element, &removes)
|
||||
}
|
||||
_ => Vec::new(),
|
||||
};
|
||||
RecordLayout { layout, frame_bytes, plan }
|
||||
}
|
||||
}
|
||||
|
||||
/// A view of one record: a pointer whose layout is proven at wiring.
|
||||
|
||||
@@ -113,7 +113,6 @@ pub fn cache_key<C: CacheHash + ?Sized>(ctx: &C) -> u64 {
|
||||
pub enum ConstructionError {
|
||||
Arity { expected: usize, got: usize },
|
||||
Type { expected: Box<Type>, found: Box<Type> },
|
||||
MissingLayout,
|
||||
}
|
||||
|
||||
pub struct SharedEdge<N: ?Sized> {
|
||||
@@ -193,7 +192,8 @@ pub struct EdgeHandle {
|
||||
node: Box<DynEdge>,
|
||||
share: fn(&DynEdge) -> Box<DynEdge>,
|
||||
serialize: fn(&DynEdge) -> Option<std::sync::Arc<dyn std::any::Any + Send + Sync>>,
|
||||
layout: fn(&DynEdge) -> Option<&crate::record::Layout>,
|
||||
layout: fn(&DynEdge) -> &crate::record::Layout,
|
||||
set_layout: fn(&mut DynEdge, crate::record::RecordLayout),
|
||||
ty: Type,
|
||||
}
|
||||
|
||||
@@ -228,7 +228,12 @@ impl EdgeHandle {
|
||||
node: Box::new(SharedEdge::new(node)),
|
||||
share: |edge| Box::new(edge.downcast_ref::<SharedEdge<N>>().expect("share hook matches the stored edge type").share()),
|
||||
serialize: |edge| Node::<ContextImpl>::serialize(edge.downcast_ref::<SharedEdge<N>>().expect("serialize hook matches the stored edge type")),
|
||||
layout: |edge| Some(Node::<ContextImpl>::layout(edge.downcast_ref::<SharedEdge<N>>().expect("layout hook matches the stored edge type"))),
|
||||
layout: |edge| Node::<ContextImpl>::layout(edge.downcast_ref::<SharedEdge<N>>().expect("layout hook matches the stored edge type")),
|
||||
set_layout: |edge, layout| {
|
||||
let shared = edge.downcast_mut::<SharedEdge<N>>().expect("set_layout hook matches the stored edge type");
|
||||
let node = std::sync::Arc::get_mut(&mut shared.own).expect("layout is installed before the node is shared");
|
||||
Node::<ContextImpl>::set_layout(node, layout);
|
||||
},
|
||||
ty,
|
||||
}
|
||||
}
|
||||
@@ -243,6 +248,7 @@ impl EdgeHandle {
|
||||
share: self.share,
|
||||
serialize: self.serialize,
|
||||
layout: self.layout,
|
||||
set_layout: self.set_layout,
|
||||
ty: self.ty.clone(),
|
||||
}
|
||||
}
|
||||
@@ -251,10 +257,14 @@ impl EdgeHandle {
|
||||
(self.serialize)(&*self.node)
|
||||
}
|
||||
|
||||
pub fn layout(&self) -> Option<&crate::record::Layout> {
|
||||
pub fn layout(&self) -> &crate::record::Layout {
|
||||
(self.layout)(&*self.node)
|
||||
}
|
||||
|
||||
pub fn set_layout(&mut self, layout: crate::record::RecordLayout) {
|
||||
(self.set_layout)(&mut *self.node, layout);
|
||||
}
|
||||
|
||||
pub fn downcast<T: 'static>(self) -> Result<SharedEdge<ErasedNode<T>>, ConstructionError> {
|
||||
self.downcast_erased(edge_type::<T>())
|
||||
}
|
||||
|
||||
@@ -403,19 +403,7 @@ pub(crate) fn generate_node_code(crate_ident: &CrateIdent, parsed: &ParsedNodeFn
|
||||
})
|
||||
.into_iter()
|
||||
.flatten();
|
||||
let flip_prelude = flip
|
||||
.then(|| match carrier_flip {
|
||||
true => quote! {
|
||||
let __layout = __in_0.with_writes(__in_0.depth, gcore::record::element_write::<#slot_value_type>(), &[]);
|
||||
let __plan = gcore::record::copy_plan(__in_0, &__layout, false, &[]);
|
||||
let __frame_bytes = __layout.frame_bytes();
|
||||
},
|
||||
false => quote! {
|
||||
let __layout = gcore::record::Layout::default().with_writes(0, gcore::record::element_write::<#slot_value_type>(), &[]);
|
||||
let __frame_bytes = __layout.frame_bytes();
|
||||
},
|
||||
})
|
||||
.into_iter();
|
||||
// The output layout, frame size, and copy plan are installed by `set_layout`.
|
||||
let flip_read_bindings = flip
|
||||
.then(|| {
|
||||
lazy_read_fields(&struct_regular_fields).into_iter().map(|(index, field)| {
|
||||
@@ -441,15 +429,12 @@ pub(crate) fn generate_node_code(crate_ident: &CrateIdent, parsed: &ParsedNodeFn
|
||||
.flatten();
|
||||
let flip_output_inits = flip
|
||||
.then(|| {
|
||||
let plan = carrier_flip.then(|| quote!(__plan,));
|
||||
match flip_generic_idents.is_empty() {
|
||||
true => quote!(__layout, __frame_bytes, #plan),
|
||||
false => quote!(__layout, __frame_bytes, #plan __marker: ::core::marker::PhantomData,),
|
||||
}
|
||||
let plan = carrier_flip.then(|| quote!(__plan: ::std::vec::Vec::new(),));
|
||||
let marker = (!flip_generic_idents.is_empty()).then(|| quote!(__marker: ::core::marker::PhantomData,));
|
||||
quote!(__layout: ::core::default::Default::default(), __frame_bytes: 0, #plan #marker)
|
||||
})
|
||||
.into_iter();
|
||||
// The flip prelude's `element_write` instantiates the erased glue at the
|
||||
// output type, so `new` carries the bounds the glue needs.
|
||||
// `new` carries the bounds the erased glue needs at the output type.
|
||||
let new_where = flip
|
||||
.then(|| {
|
||||
let existing = parsed.where_clause.iter().flat_map(|clause| clause.predicates.iter());
|
||||
@@ -463,7 +448,6 @@ pub(crate) fn generate_node_code(crate_ident: &CrateIdent, parsed: &ParsedNodeFn
|
||||
{
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn new(#(#new_args,)* #(#routing_layout_param)* #(#routing_in_params)* #(#flip_layout_params)*) -> Self {
|
||||
#(#flip_prelude)*
|
||||
#(#flip_read_bindings)*
|
||||
Self {
|
||||
#(#all_field_inits,)*
|
||||
@@ -747,8 +731,6 @@ pub(crate) fn generate_node_impl(crate_ident: &CrateIdent, parsed: &ParsedNodeFn
|
||||
crate::codegen::ir::Element::Concrete(ty) => Some(ty),
|
||||
_ => None,
|
||||
});
|
||||
// A creator pushes rank levels: its `IList` return raises the output depth
|
||||
// above its carrier's, so the layout writes one level deeper.
|
||||
let subject_depth = node.inputs.iter().find(|input| input.subject).map_or(0, |input| input.shape.depth);
|
||||
let level_delta = node.output.shape.depth as i8 - subject_depth as i8;
|
||||
let pushed_levels = level_delta.max(0) as u8;
|
||||
@@ -1709,14 +1691,60 @@ pub(crate) fn generate_node_impl(crate_ident: &CrateIdent, parsed: &ParsedNodeFn
|
||||
false => Vec::new(),
|
||||
};
|
||||
|
||||
let set_layout_body = if flip {
|
||||
let plan = carrier_flip.then(|| quote!(self.__plan = __resolved.plan;));
|
||||
Some(quote! {
|
||||
self.__frame_bytes = __resolved.frame_bytes;
|
||||
#plan
|
||||
self.__layout = __resolved.layout;
|
||||
})
|
||||
} else if record_io {
|
||||
let write_installs = write_markers.iter().enumerate().map(|(index, marker)| {
|
||||
let slot = format_ident!("__write_{index}");
|
||||
quote! {
|
||||
self.#slot = __resolved.layout.offset_of(<#marker as #core_types::attribute::Attribute>::NAME, 0).expect("a written attribute is always part of the wired layout");
|
||||
}
|
||||
});
|
||||
let plan = (!skips_carrier).then(|| quote!(self.__plan = __resolved.plan;));
|
||||
Some(quote! {
|
||||
#(#write_installs)*
|
||||
self.__frame_bytes = __resolved.frame_bytes;
|
||||
#plan
|
||||
self.__layout = __resolved.layout;
|
||||
})
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let set_layout_method = set_layout_body.map(|body| {
|
||||
quote! {
|
||||
fn set_layout(&mut self, __resolved: #core_types::record::RecordLayout) {
|
||||
#body
|
||||
}
|
||||
}
|
||||
});
|
||||
let record_layout_impl = match record_io || routing_generic.is_some() || flip || opaque {
|
||||
true => quote! {
|
||||
fn layout(&self) -> &#core_types::record::Layout {
|
||||
&self.__layout
|
||||
}
|
||||
#set_layout_method
|
||||
},
|
||||
false => quote!(),
|
||||
};
|
||||
let flip_meta_concrete = flip && !element_write.is_some_and(|ty| crate::codegen::classify::contains_open_generic(parsed, ty));
|
||||
let flip_layout_meta_fn = flip_meta_concrete.then(|| {
|
||||
let layout_meta_fn = format_ident!("{}_layout_meta", fn_name);
|
||||
let element_spec = match 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 = crate::codegen::ir::layout_meta_tokens(&node, element_spec, core_types);
|
||||
quote! {
|
||||
#vis fn #layout_meta_fn() -> #core_types::record::LayoutMeta {
|
||||
#layout_meta
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let entries = entries_tokens(parsed, &struct_name, &data_field_generic_idents, ®ular_fields);
|
||||
let cfg = crate::shader_nodes::modify_cfg(&parsed.attributes);
|
||||
@@ -1769,13 +1797,6 @@ pub(crate) fn generate_node_impl(crate_ident: &CrateIdent, parsed: &ParsedNodeFn
|
||||
let slot = format_ident!("__in_{index}");
|
||||
quote!(#slot: &#core_types::record::Layout,)
|
||||
});
|
||||
let layout_binding = match skips_carrier {
|
||||
true => quote!(let __layout = self::#layout_fn();),
|
||||
false => quote!(let __layout = self::#layout_fn(__carrier_layout);),
|
||||
};
|
||||
let carry_element = element_write.is_none();
|
||||
let plan_binding =
|
||||
(!skips_carrier).then(|| quote!(let __plan = #core_types::record::copy_plan(__carrier_layout, &__layout, #carry_element, &[#(#remove_pairs),*]);));
|
||||
let read_inits = flat_reads.iter().enumerate().map(|(slot, (owner, read))| {
|
||||
let marker = &read.marker;
|
||||
let slot = format_ident!("__read_{slot}");
|
||||
@@ -1785,14 +1806,6 @@ pub(crate) fn generate_node_impl(crate_ident: &CrateIdent, parsed: &ParsedNodeFn
|
||||
};
|
||||
quote!(let #slot = #source.offset_of(<#marker as #core_types::attribute::Attribute>::NAME, 0);)
|
||||
});
|
||||
let write_inits = write_markers.iter().enumerate().map(|(index, marker)| {
|
||||
let slot = format_ident!("__write_{index}");
|
||||
quote! {
|
||||
let #slot = __layout
|
||||
.offset_of(<#marker as #core_types::attribute::Attribute>::NAME, 0)
|
||||
.expect("a written attribute is always part of the wired layout");
|
||||
}
|
||||
});
|
||||
let data_inits = data_names.iter().map(|name| quote!(#name: ::core::default::Default::default(),));
|
||||
let edge_inits = regular_fields.iter().map(|field| {
|
||||
let name = &field.pat_ident.ident;
|
||||
@@ -1803,9 +1816,9 @@ pub(crate) fn generate_node_impl(crate_ident: &CrateIdent, parsed: &ParsedNodeFn
|
||||
let slot = format_ident!("__in_{index}");
|
||||
quote!(#slot: #slot.clone(),)
|
||||
});
|
||||
let plan_init = (!skips_carrier).then(|| quote!(__plan,)).into_iter();
|
||||
let plan_default = (!skips_carrier).then(|| quote!(__plan: ::std::vec::Vec::new(),)).into_iter();
|
||||
let read_names = (0..flat_reads.len()).map(|index| format_ident!("__read_{index}")).map(|slot| quote!(#slot,));
|
||||
let write_names = (0..write_markers.len()).map(|index| format_ident!("__write_{index}")).map(|slot| quote!(#slot,));
|
||||
let write_defaults = (0..write_markers.len()).map(|index| format_ident!("__write_{index}")).map(|slot| quote!(#slot: 0,));
|
||||
quote! {
|
||||
#layout_def
|
||||
#layout_meta_def
|
||||
@@ -1814,21 +1827,17 @@ pub(crate) fn generate_node_impl(crate_ident: &CrateIdent, parsed: &ParsedNodeFn
|
||||
impl<#(#data_field_generic_idents,)* #(#node_generics,)*> #mod_name::#struct_name<#(#struct_type_params,)*> {
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
#vis fn new(#(#edge_args,)* #(#carrier_layout_param)* #(#input_layout_params)*) -> Self {
|
||||
#layout_binding
|
||||
#plan_binding
|
||||
#(#read_inits)*
|
||||
#(#write_inits)*
|
||||
let __frame_bytes = __layout.frame_bytes();
|
||||
Self {
|
||||
#(#data_inits)*
|
||||
#(#edge_inits)*
|
||||
#(#carrier_init)*
|
||||
#(#input_layout_inits)*
|
||||
__layout,
|
||||
#(#plan_init)*
|
||||
__frame_bytes,
|
||||
__layout: ::core::default::Default::default(),
|
||||
#(#plan_default)*
|
||||
__frame_bytes: 0,
|
||||
#(#read_names)*
|
||||
#(#write_names)*
|
||||
#(#write_defaults)*
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1968,7 +1977,7 @@ pub(crate) fn generate_node_impl(crate_ident: &CrateIdent, parsed: &ParsedNodeFn
|
||||
Ok(NodePlan {
|
||||
kernel,
|
||||
lazy_read_fns: quote!(#(#lazy_read_fns)*),
|
||||
record_ctor: quote!(#record_wiring),
|
||||
record_ctor: quote!(#record_wiring #flip_layout_meta_fn),
|
||||
node_impl: top_level,
|
||||
entries,
|
||||
..Default::default()
|
||||
|
||||
@@ -132,9 +132,7 @@ fn flip_entries_tokens(parsed: &ParsedNodeFn, struct_name: &Ident, regular_field
|
||||
let layout = format_ident!("__layout_{index}");
|
||||
quote! {
|
||||
let #handle = inputs.next().unwrap();
|
||||
let Some(#layout) = #handle.layout().cloned() else {
|
||||
return Err(gcore::registry::ConstructionError::MissingLayout);
|
||||
};
|
||||
let #layout = #handle.layout().clone();
|
||||
let #name = #handle.downcast_record::<#ty>()?;
|
||||
}
|
||||
});
|
||||
@@ -261,16 +259,12 @@ fn single_row_entries(parsed: &ParsedNodeFn, struct_name: &Ident, regular_fields
|
||||
SlotKind::BaseGeneric(_) | SlotKind::BaseConcrete(_) => quote! {
|
||||
let #handle = inputs.next().unwrap();
|
||||
let #ty = #handle.ty().clone();
|
||||
let Some(#layout) = #handle.layout().cloned() else {
|
||||
return Err(gcore::registry::ConstructionError::MissingLayout);
|
||||
};
|
||||
let #layout = #handle.layout().clone();
|
||||
let #name = #handle.downcast_erased::<gcore::registry::ErasedRecordNode>(#ty.clone())?;
|
||||
},
|
||||
SlotKind::Value(value_ty) => quote! {
|
||||
let #handle = inputs.next().unwrap();
|
||||
let Some(#layout) = #handle.layout().cloned() else {
|
||||
return Err(gcore::registry::ConstructionError::MissingLayout);
|
||||
};
|
||||
let #layout = #handle.layout().clone();
|
||||
let #name = #handle.downcast_record::<#value_ty>()?;
|
||||
},
|
||||
SlotKind::Plain(value_ty) | SlotKind::Lazy(value_ty) => quote!(let #name = inputs.next().unwrap().downcast::<#value_ty>()?;),
|
||||
|
||||
@@ -232,6 +232,21 @@ mod tests {
|
||||
stack::reserve(layouts.iter().map(|layout| layout.frame_bytes()).sum::<usize>().max(1 << 12));
|
||||
}
|
||||
|
||||
fn install<N: Node<ContextImpl<'static>>>(mut node: N, meta: core_types::record::LayoutMeta, inputs: &[Option<&Layout>]) -> N {
|
||||
<N as Node<ContextImpl<'static>>>::set_layout(&mut node, meta.resolve(inputs));
|
||||
node
|
||||
}
|
||||
|
||||
fn install_flip<N: Node<ContextImpl<'static>>>(mut node: N, layout: &Layout) -> N {
|
||||
let bundle = core_types::record::RecordLayout {
|
||||
frame_bytes: layout.frame_bytes(),
|
||||
plan: Vec::new(),
|
||||
layout: layout.clone(),
|
||||
};
|
||||
<N as Node<ContextImpl<'static>>>::set_layout(&mut node, bundle);
|
||||
node
|
||||
}
|
||||
|
||||
fn lifted_value<T: Clone + Send + Sync + 'static>(value: T) -> (core_types::record::RecordLift<T, ValueNode<T>>, Layout) {
|
||||
let lift = core_types::record::RecordLift::<T, _>::new(ValueNode(value));
|
||||
let layout = Node::<ContextImpl>::layout(&lift).clone();
|
||||
@@ -268,7 +283,7 @@ mod tests {
|
||||
let leveled = repeat_opacity_layout(&base);
|
||||
reserve_for(&[&base, &leveled]);
|
||||
|
||||
let node = RepeatOpacityNode::new(bare_source(&base, 7.), ValueNode(3u32), &base);
|
||||
let node = install(RepeatOpacityNode::new(bare_source(&base, 7.), ValueNode(3u32), &base), repeat_opacity_layout_meta(), &[Some(&base)]);
|
||||
assert_eq!(node.layout(), &leveled);
|
||||
let GPoll::Final(value) = node.eval(&indexed) else {
|
||||
panic!("expected a final record");
|
||||
@@ -288,7 +303,7 @@ mod tests {
|
||||
let base = f64_layout(&[]);
|
||||
reserve_for(&[&base]);
|
||||
|
||||
let node = RepeatOpacityNode::new(bare_source(&base, 7.), ValueNode(3u32), &base);
|
||||
let node = install(RepeatOpacityNode::new(bare_source(&base, 7.), ValueNode(3u32), &base), repeat_opacity_layout_meta(), &[Some(&base)]);
|
||||
// The pushed level (0, the only level) reports the copy count.
|
||||
assert_eq!(node.extent_at(&ctx, 0), core_types::gpoll::GPoll::Final(core_types::gpoll::Extent::Exactly(3)));
|
||||
}
|
||||
@@ -305,8 +320,8 @@ mod tests {
|
||||
let out = f64_layout(&[]);
|
||||
reserve_for(&[&base, &leveled, &out]);
|
||||
|
||||
let repeat = RepeatOpacityNode::new(bare_source(&base, 7.), ValueNode(3u32), &base);
|
||||
let node = SumNode::new(repeat, &leveled);
|
||||
let repeat = install(RepeatOpacityNode::new(bare_source(&base, 7.), ValueNode(3u32), &base), repeat_opacity_layout_meta(), &[Some(&base)]);
|
||||
let node = install_flip(SumNode::new(repeat, &leveled), &out);
|
||||
assert_eq!(node.layout().depth, 0, "the reducer collapsed the rank level");
|
||||
|
||||
let GPoll::Final(value) = node.eval(&ctx) else {
|
||||
@@ -342,7 +357,15 @@ mod tests {
|
||||
let stacked = multiply_opacity_layout(&modified);
|
||||
reserve_for(&[&source_layout, &modified, &stacked]);
|
||||
|
||||
let chain = MultiplyOpacityNode::new(MultiplyOpacityNode::new(bare_source(&source_layout, 2.), ValueNode(0.5), &source_layout), ValueNode(0.5), &modified);
|
||||
let chain = install(
|
||||
MultiplyOpacityNode::new(
|
||||
install(MultiplyOpacityNode::new(bare_source(&source_layout, 2.), ValueNode(0.5), &source_layout), multiply_opacity_layout_meta(), &[Some(&source_layout)]),
|
||||
ValueNode(0.5),
|
||||
&modified,
|
||||
),
|
||||
multiply_opacity_layout_meta(),
|
||||
&[Some(&modified)],
|
||||
);
|
||||
assert_eq!(chain.layout(), &stacked);
|
||||
let GPoll::Final(value) = chain.eval(&ctx) else {
|
||||
panic!("expected a final record");
|
||||
@@ -363,7 +386,7 @@ mod tests {
|
||||
let measured = measure_layout(&source_layout);
|
||||
reserve_for(&[&source_layout, &measured]);
|
||||
|
||||
let chain = MeasureNode::new(bare_source(&source_layout, -2.), &source_layout);
|
||||
let chain = install(MeasureNode::new(bare_source(&source_layout, -2.), &source_layout), measure_layout_meta(), &[Some(&source_layout)]);
|
||||
let GPoll::Final(value) = chain.eval(&ctx) else {
|
||||
panic!("expected a final record");
|
||||
};
|
||||
@@ -384,7 +407,14 @@ mod tests {
|
||||
let measured = measure_layout(&modified);
|
||||
reserve_for(&[&source_layout, &modified, &measured]);
|
||||
|
||||
let chain = MeasureNode::new(MultiplyOpacityNode::new(bare_source(&source_layout, -2.), ValueNode(0.5), &source_layout), &modified);
|
||||
let chain = install(
|
||||
MeasureNode::new(
|
||||
install(MultiplyOpacityNode::new(bare_source(&source_layout, -2.), ValueNode(0.5), &source_layout), multiply_opacity_layout_meta(), &[Some(&source_layout)]),
|
||||
&modified,
|
||||
),
|
||||
measure_layout_meta(),
|
||||
&[Some(&modified)],
|
||||
);
|
||||
let GPoll::Final(value) = chain.eval(&ctx) else {
|
||||
panic!("expected a final record");
|
||||
};
|
||||
@@ -405,13 +435,20 @@ mod tests {
|
||||
let shaded = shade_layout(&modified);
|
||||
reserve_for(&[&source_layout, &modified, &shaded]);
|
||||
|
||||
let bare = ShadeNode::new(bare_source(&source_layout, 4.), &source_layout);
|
||||
let bare = install(ShadeNode::new(bare_source(&source_layout, 4.), &source_layout), shade_layout_meta(), &[Some(&source_layout)]);
|
||||
let GPoll::Final(value) = bare.eval(&ctx) else {
|
||||
panic!("expected a final record");
|
||||
};
|
||||
assert_eq!(unsafe { source_layout.rec(&value).element::<f64>() }, 4.);
|
||||
|
||||
let chain = ShadeNode::new(MultiplyOpacityNode::new(bare_source(&source_layout, 4.), ValueNode(0.5), &source_layout), &modified);
|
||||
let chain = install(
|
||||
ShadeNode::new(
|
||||
install(MultiplyOpacityNode::new(bare_source(&source_layout, 4.), ValueNode(0.5), &source_layout), multiply_opacity_layout_meta(), &[Some(&source_layout)]),
|
||||
&modified,
|
||||
),
|
||||
shade_layout_meta(),
|
||||
&[Some(&modified)],
|
||||
);
|
||||
let GPoll::Final(value) = chain.eval(&ctx) else {
|
||||
panic!("expected a final record");
|
||||
};
|
||||
@@ -433,7 +470,7 @@ mod tests {
|
||||
let u32_faded = fade_layout(&u32_source);
|
||||
reserve_for(&[&f64_source, &f64_faded, &u32_source, &u32_faded]);
|
||||
|
||||
let wide = FadeNode::new(bare_source(&f64_source, 8.), ValueNode(0.5), &f64_source);
|
||||
let wide = install(FadeNode::new(bare_source(&f64_source, 8.), ValueNode(0.5), &f64_source), fade_layout_meta(), &[Some(&f64_source)]);
|
||||
let GPoll::Final(value) = wide.eval(&ctx) else {
|
||||
panic!("expected a final record");
|
||||
};
|
||||
@@ -441,15 +478,19 @@ mod tests {
|
||||
assert_eq!(unsafe { rec.element::<f64>() }, 8.);
|
||||
assert_eq!(unsafe { rec.read::<f64>(f64_faded.offset_of(Opacity::NAME, 0).unwrap()) }, 0.5);
|
||||
|
||||
let narrow = FadeNode::new(
|
||||
RecordSourceNode {
|
||||
layout: u32_source.clone(),
|
||||
element: 7u32,
|
||||
fields: vec![],
|
||||
partial: false,
|
||||
},
|
||||
ValueNode(0.25),
|
||||
&u32_source,
|
||||
let narrow = install(
|
||||
FadeNode::new(
|
||||
RecordSourceNode {
|
||||
layout: u32_source.clone(),
|
||||
element: 7u32,
|
||||
fields: vec![],
|
||||
partial: false,
|
||||
},
|
||||
ValueNode(0.25),
|
||||
&u32_source,
|
||||
),
|
||||
fade_layout_meta(),
|
||||
&[Some(&u32_source)],
|
||||
);
|
||||
let GPoll::Final(value) = narrow.eval(&ctx) else {
|
||||
panic!("expected a final record");
|
||||
@@ -469,7 +510,7 @@ mod tests {
|
||||
let layout = source_opacity_layout();
|
||||
reserve_for(&[&layout]);
|
||||
|
||||
let node = SourceOpacityNode::new(ValueNode(3.), ValueNode(0.25));
|
||||
let node = install(SourceOpacityNode::new(ValueNode(3.), ValueNode(0.25)), source_opacity_layout_meta(), &[]);
|
||||
assert_eq!(Node::<ContextImpl>::layout(&node), &layout);
|
||||
let GPoll::Final(value) = node.eval(&ctx) else {
|
||||
panic!("expected a final record");
|
||||
@@ -490,15 +531,19 @@ mod tests {
|
||||
let modified = multiply_opacity_layout(&source_layout);
|
||||
reserve_for(&[&source_layout, &modified]);
|
||||
|
||||
let chain = MultiplyOpacityNode::new(
|
||||
RecordSourceNode {
|
||||
layout: source_layout.clone(),
|
||||
element: 1.,
|
||||
fields: vec![],
|
||||
partial: true,
|
||||
},
|
||||
ValueNode(0.5),
|
||||
&source_layout,
|
||||
let chain = install(
|
||||
MultiplyOpacityNode::new(
|
||||
RecordSourceNode {
|
||||
layout: source_layout.clone(),
|
||||
element: 1.,
|
||||
fields: vec![],
|
||||
partial: true,
|
||||
},
|
||||
ValueNode(0.5),
|
||||
&source_layout,
|
||||
),
|
||||
multiply_opacity_layout_meta(),
|
||||
&[Some(&source_layout)],
|
||||
);
|
||||
let GPoll::Partial(value) = chain.eval(&ctx) else {
|
||||
panic!("expected a partial record");
|
||||
@@ -517,13 +562,13 @@ mod tests {
|
||||
let modified = checked_multiply_opacity_layout(&source_layout);
|
||||
reserve_for(&[&source_layout, &modified]);
|
||||
|
||||
let ok = CheckedMultiplyOpacityNode::new(bare_source(&source_layout, 1.), ValueNode(0.5), &source_layout);
|
||||
let ok = install(CheckedMultiplyOpacityNode::new(bare_source(&source_layout, 1.), ValueNode(0.5), &source_layout), checked_multiply_opacity_layout_meta(), &[Some(&source_layout)]);
|
||||
let GPoll::Final(value) = ok.eval(&ctx) else {
|
||||
panic!("expected a final record");
|
||||
};
|
||||
assert_eq!(unsafe { modified.rec(&value).read::<f64>(modified.offset_of(Opacity::NAME, 0).unwrap()) }, 0.5);
|
||||
|
||||
let failing = CheckedMultiplyOpacityNode::new(bare_source(&source_layout, 1.), ValueNode(-1.), &source_layout);
|
||||
let failing = install(CheckedMultiplyOpacityNode::new(bare_source(&source_layout, 1.), ValueNode(-1.), &source_layout), checked_multiply_opacity_layout_meta(), &[Some(&source_layout)]);
|
||||
let GPoll::Error(error) = failing.eval(&ctx) else {
|
||||
panic!("expected an error");
|
||||
};
|
||||
@@ -542,7 +587,15 @@ mod tests {
|
||||
let scaled = scale_layout(&modified);
|
||||
reserve_for(&[&source_layout, &modified, &scaled]);
|
||||
|
||||
let chain = ScaleNode::new(MultiplyOpacityNode::new(bare_source(&source_layout, 2.), ValueNode(0.5), &source_layout), ValueNode(3.), &modified);
|
||||
let chain = install(
|
||||
ScaleNode::new(
|
||||
install(MultiplyOpacityNode::new(bare_source(&source_layout, 2.), ValueNode(0.5), &source_layout), multiply_opacity_layout_meta(), &[Some(&source_layout)]),
|
||||
ValueNode(3.),
|
||||
&modified,
|
||||
),
|
||||
scale_layout_meta(),
|
||||
&[Some(&modified)],
|
||||
);
|
||||
let GPoll::Final(value) = chain.eval(&ctx) else {
|
||||
panic!("expected a final record");
|
||||
};
|
||||
@@ -563,11 +616,15 @@ mod tests {
|
||||
let transferred = transfer_opacity_layout(&carrier_layout);
|
||||
reserve_for(&[&carrier_layout, &secondary_layout, &transferred]);
|
||||
|
||||
let chain = TransferOpacityNode::new(
|
||||
f64_record_source(&carrier_layout, 2., vec![(carrier_layout.offset_of("opacity", 0).unwrap(), 0.5)]),
|
||||
f64_record_source(&secondary_layout, 3., vec![(secondary_layout.offset_of("opacity", 0).unwrap(), 0.25)]),
|
||||
&carrier_layout,
|
||||
&secondary_layout,
|
||||
let chain = install(
|
||||
TransferOpacityNode::new(
|
||||
f64_record_source(&carrier_layout, 2., vec![(carrier_layout.offset_of("opacity", 0).unwrap(), 0.5)]),
|
||||
f64_record_source(&secondary_layout, 3., vec![(secondary_layout.offset_of("opacity", 0).unwrap(), 0.25)]),
|
||||
&carrier_layout,
|
||||
&secondary_layout,
|
||||
),
|
||||
transfer_opacity_layout_meta(),
|
||||
&[Some(&carrier_layout), None],
|
||||
);
|
||||
let GPoll::Final(value) = chain.eval(&ctx) else {
|
||||
panic!("expected a final record");
|
||||
@@ -577,11 +634,15 @@ mod tests {
|
||||
assert_eq!(unsafe { rec.read::<f64>(transferred.offset_of(Opacity::NAME, 0).unwrap()) }, 0.125);
|
||||
|
||||
let bare_secondary = f64_layout(&[]);
|
||||
let defaulted = TransferOpacityNode::new(
|
||||
f64_record_source(&carrier_layout, 2., vec![(carrier_layout.offset_of("opacity", 0).unwrap(), 0.5)]),
|
||||
bare_source(&bare_secondary, 3.),
|
||||
&carrier_layout,
|
||||
&bare_secondary,
|
||||
let defaulted = install(
|
||||
TransferOpacityNode::new(
|
||||
f64_record_source(&carrier_layout, 2., vec![(carrier_layout.offset_of("opacity", 0).unwrap(), 0.5)]),
|
||||
bare_source(&bare_secondary, 3.),
|
||||
&carrier_layout,
|
||||
&bare_secondary,
|
||||
),
|
||||
transfer_opacity_layout_meta(),
|
||||
&[Some(&carrier_layout), None],
|
||||
);
|
||||
let GPoll::Final(value) = defaulted.eval(&ctx) else {
|
||||
panic!("expected a final record");
|
||||
@@ -602,11 +663,15 @@ mod tests {
|
||||
let factor_layout = Node::<ContextImpl>::layout(&factor).clone();
|
||||
reserve_for(&[&source_layout]);
|
||||
|
||||
let node = BoostNode::new(
|
||||
f64_record_source(&source_layout, 2., vec![(source_layout.offset_of("opacity", 0).unwrap(), 0.25)]),
|
||||
factor,
|
||||
&source_layout,
|
||||
&factor_layout,
|
||||
let node = install(
|
||||
BoostNode::new(
|
||||
f64_record_source(&source_layout, 2., vec![(source_layout.offset_of("opacity", 0).unwrap(), 0.25)]),
|
||||
factor,
|
||||
&source_layout,
|
||||
&factor_layout,
|
||||
),
|
||||
boost_layout_meta(),
|
||||
&[Some(&source_layout)],
|
||||
);
|
||||
let out_layout = Node::<ContextImpl>::layout(&node).clone();
|
||||
let opacity_offset = out_layout.offset_of(Opacity::NAME, 0).expect("the primary input's fields pass through to the output");
|
||||
@@ -629,11 +694,15 @@ mod tests {
|
||||
let (factor, factor_layout) = lifted_value(3.);
|
||||
reserve_for(&[&source_layout]);
|
||||
|
||||
let node = BoostPollNode::new(
|
||||
f64_record_source(&source_layout, 2., vec![(source_layout.offset_of("opacity", 0).unwrap(), 0.25)]),
|
||||
factor,
|
||||
&source_layout,
|
||||
&factor_layout,
|
||||
let node = install(
|
||||
BoostPollNode::new(
|
||||
f64_record_source(&source_layout, 2., vec![(source_layout.offset_of("opacity", 0).unwrap(), 0.25)]),
|
||||
factor,
|
||||
&source_layout,
|
||||
&factor_layout,
|
||||
),
|
||||
boost_poll_layout_meta(),
|
||||
&[Some(&source_layout)],
|
||||
);
|
||||
let out_layout = Node::<ContextImpl>::layout(&node).clone();
|
||||
let opacity_offset = out_layout.offset_of(Opacity::NAME, 0).expect("the primary input's fields pass through the poll kernel");
|
||||
@@ -657,11 +726,15 @@ mod tests {
|
||||
assert!(by_layout.frame_bytes() != 0, "the borrow must point into a spilled frame to exercise the park");
|
||||
reserve_for(&[&carrier_layout, &by_layout]);
|
||||
|
||||
let node = OffsetNode::new(
|
||||
f64_record_source(&carrier_layout, 2., vec![(carrier_layout.offset_of("opacity", 0).unwrap(), 0.25)]),
|
||||
f64_record_source(&by_layout, 40., vec![]),
|
||||
&carrier_layout,
|
||||
&by_layout,
|
||||
let node = install(
|
||||
OffsetNode::new(
|
||||
f64_record_source(&carrier_layout, 2., vec![(carrier_layout.offset_of("opacity", 0).unwrap(), 0.25)]),
|
||||
f64_record_source(&by_layout, 40., vec![]),
|
||||
&carrier_layout,
|
||||
&by_layout,
|
||||
),
|
||||
offset_layout_meta(),
|
||||
&[Some(&carrier_layout)],
|
||||
);
|
||||
let out_layout = Node::<ContextImpl>::layout(&node).clone();
|
||||
let GPoll::Final(value) = node.eval(&ctx) else {
|
||||
@@ -694,13 +767,17 @@ mod tests {
|
||||
let (source_id, source_id_layout) = lifted_value(7 as SourceId);
|
||||
reserve_for(&[&source_layout]);
|
||||
|
||||
let node = DoubleAsyncNode::new(
|
||||
f64_record_source(&source_layout, 3., vec![(source_layout.offset_of("opacity", 0).unwrap(), 0.25)]),
|
||||
runtime,
|
||||
source_id,
|
||||
&source_layout,
|
||||
&runtime_layout,
|
||||
&source_id_layout,
|
||||
let node = install(
|
||||
DoubleAsyncNode::new(
|
||||
f64_record_source(&source_layout, 3., vec![(source_layout.offset_of("opacity", 0).unwrap(), 0.25)]),
|
||||
runtime,
|
||||
source_id,
|
||||
&source_layout,
|
||||
&runtime_layout,
|
||||
&source_id_layout,
|
||||
),
|
||||
double_async_layout_meta(),
|
||||
&[Some(&source_layout)],
|
||||
);
|
||||
let out_layout = Node::<ContextImpl>::layout(&node).clone();
|
||||
let opacity_offset = out_layout.offset_of(Opacity::NAME, 0).expect("the carrier's fields pass through the async source");
|
||||
@@ -774,9 +851,20 @@ mod tests {
|
||||
let shaded = shade_layout(&stripped);
|
||||
reserve_for(&[&source_layout, &modified, &stripped, &shaded]);
|
||||
|
||||
let chain = ShadeNode::new(
|
||||
StripOpacityNode::new(MultiplyOpacityNode::new(bare_source(&source_layout, 4.), ValueNode(0.5), &source_layout), &modified),
|
||||
&stripped,
|
||||
let chain = install(
|
||||
ShadeNode::new(
|
||||
install(
|
||||
StripOpacityNode::new(
|
||||
install(MultiplyOpacityNode::new(bare_source(&source_layout, 4.), ValueNode(0.5), &source_layout), multiply_opacity_layout_meta(), &[Some(&source_layout)]),
|
||||
&modified,
|
||||
),
|
||||
strip_opacity_layout_meta(),
|
||||
&[Some(&modified)],
|
||||
),
|
||||
&stripped,
|
||||
),
|
||||
shade_layout_meta(),
|
||||
&[Some(&stripped)],
|
||||
);
|
||||
let GPoll::Final(value) = chain.eval(&ctx) else {
|
||||
panic!("expected a final record");
|
||||
@@ -796,13 +884,17 @@ mod tests {
|
||||
assert!(relengthed.offset_of(Opacity::NAME, 0).is_none());
|
||||
reserve_for(&[&source_layout, &relengthed]);
|
||||
|
||||
let chain = RelengthNode::new(
|
||||
f64_record_source(
|
||||
let chain = install(
|
||||
RelengthNode::new(
|
||||
f64_record_source(
|
||||
&source_layout,
|
||||
3.,
|
||||
vec![(source_layout.offset_of("opacity", 0).unwrap(), 0.25), (source_layout.offset_of("length", 0).unwrap(), 9.)],
|
||||
),
|
||||
&source_layout,
|
||||
3.,
|
||||
vec![(source_layout.offset_of("opacity", 0).unwrap(), 0.25), (source_layout.offset_of("length", 0).unwrap(), 9.)],
|
||||
),
|
||||
&source_layout,
|
||||
relength_layout_meta(),
|
||||
&[Some(&source_layout)],
|
||||
);
|
||||
let GPoll::Final(value) = chain.eval(&ctx) else {
|
||||
panic!("expected a final record");
|
||||
@@ -824,10 +916,14 @@ mod tests {
|
||||
let relabeled = label_layout(&labeled);
|
||||
reserve_for(&[&source_layout, &labeled, &relabeled]);
|
||||
|
||||
let chain = LabelNode::new(
|
||||
LabelNode::new(bare_source(&source_layout, 1.), ValueNode(String::from("a")), &source_layout),
|
||||
ValueNode(String::from("b")),
|
||||
&labeled,
|
||||
let chain = install(
|
||||
LabelNode::new(
|
||||
install(LabelNode::new(bare_source(&source_layout, 1.), ValueNode(String::from("a")), &source_layout), label_layout_meta(), &[Some(&source_layout)]),
|
||||
ValueNode(String::from("b")),
|
||||
&labeled,
|
||||
),
|
||||
label_layout_meta(),
|
||||
&[Some(&labeled)],
|
||||
);
|
||||
let GPoll::Final(value) = chain.eval(&ctx) else {
|
||||
panic!("expected a final record");
|
||||
@@ -1187,7 +1283,7 @@ mod tests {
|
||||
let labeled = label_layout(&source_layout);
|
||||
reserve_for(&[&labeled, &labeled]);
|
||||
|
||||
let chain = LabelNode::new(bare_source(&source_layout, 1.), ValueNode(String::from("a")), &source_layout);
|
||||
let chain = install(LabelNode::new(bare_source(&source_layout, 1.), ValueNode(String::from("a")), &source_layout), label_layout_meta(), &[Some(&source_layout)]);
|
||||
let memo = crate::memo::MemoizeNode::new(chain, &labeled);
|
||||
|
||||
let first_arena = Arena::new(1024).unwrap();
|
||||
|
||||
Reference in New Issue
Block a user