mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-10-04 03:08:12 +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>() {
|
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
|
return Ok(handle
|
||||||
.downcast_record::<$ty>()
|
.downcast_record::<$ty>()
|
||||||
.map_err(|e| format!("{e:?}"))?
|
.map_err(|e| format!("{e:?}"))?
|
||||||
@@ -381,7 +381,7 @@ macro_rules! tagged_value {
|
|||||||
}
|
}
|
||||||
)*
|
)*
|
||||||
if ty == core_types::registry::record_edge_type::<RenderOutput>() {
|
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
|
return Ok(handle
|
||||||
.downcast_record::<RenderOutput>()
|
.downcast_record::<RenderOutput>()
|
||||||
.map_err(|e| format!("{e:?}"))?
|
.map_err(|e| format!("{e:?}"))?
|
||||||
|
|||||||
@@ -128,7 +128,7 @@ impl ConstructionArgs {
|
|||||||
pub(crate) struct Resolved {
|
pub(crate) struct Resolved {
|
||||||
pub io: Option<NodeIOTypes>,
|
pub io: Option<NodeIOTypes>,
|
||||||
pub layout_meta: Option<core_types::record::LayoutMeta>,
|
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 {
|
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()
|
self.resolved.layout.as_ref()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -372,10 +372,18 @@ impl ProtoNetwork {
|
|||||||
let layout = {
|
let layout = {
|
||||||
let node = &self.nodes[index].1;
|
let node = &self.nodes[index].1;
|
||||||
match &node.construction_args {
|
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| {
|
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();
|
let input_layouts: Vec<Option<&core_types::record::Layout>> =
|
||||||
meta.sources.iter().all(|&source| input_layouts[source as usize].is_some()).then(|| meta.fold(&input_layouts))
|
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,
|
ConstructionArgs::Inline(_) => None,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -476,15 +476,16 @@ impl BorrowTree {
|
|||||||
.clone()
|
.clone()
|
||||||
.to_edge()
|
.to_edge()
|
||||||
.map_err(|error| vec![GraphError::new(&proto_node, GraphErrorType::ConstructionFailed(error))])?;
|
.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());
|
self.store_node(node, id, path.into());
|
||||||
}
|
}
|
||||||
ConstructionArgs::Inline(_) => unimplemented!("Inline nodes are not supported yet"),
|
ConstructionArgs::Inline(_) => unimplemented!("Inline nodes are not supported yet"),
|
||||||
ConstructionArgs::Nodes(ids) => {
|
ConstructionArgs::Nodes(ids) => {
|
||||||
let construction_nodes = self.node_deps(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 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:?}")))])?;
|
let mut 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());
|
if let Some(layout) = proto_node.resolved_layout() {
|
||||||
|
node.set_layout(layout.clone());
|
||||||
|
}
|
||||||
self.store_node(node, id, path.into());
|
self.store_node(node, id, path.into());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -503,7 +504,7 @@ impl BorrowTree {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn frame_bytes(&self, id: NodeId) -> usize {
|
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 mut inputs = inputs.into_iter();
|
||||||
let handle = inputs.next().unwrap();
|
let handle = inputs.next().unwrap();
|
||||||
let Some(layout) = handle.layout().cloned() else {
|
let layout = handle.layout().clone();
|
||||||
return Err(ConstructionError::MissingLayout);
|
|
||||||
};
|
|
||||||
let node = graphene_std::ops::IntoNode::<$to, _, $from>::new(handle.downcast_record::<$from>()?, &layout);
|
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>))
|
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 inputs = inputs.into_iter();
|
||||||
let mut claim = || {
|
let mut claim = || {
|
||||||
let handle = inputs.next().unwrap();
|
let handle = inputs.next().unwrap();
|
||||||
let Some(layout) = handle.layout().cloned() else {
|
let layout = handle.layout().clone();
|
||||||
return Err(ConstructionError::MissingLayout);
|
Ok::<_, ConstructionError>((handle, layout))
|
||||||
};
|
|
||||||
Ok((handle, layout))
|
|
||||||
};
|
};
|
||||||
let (value, value_layout) = claim()?;
|
let (value, value_layout) = claim()?;
|
||||||
let (converter, converter_layout) = claim()?;
|
let (converter, converter_layout) = claim()?;
|
||||||
@@ -305,13 +301,9 @@ mod node_registry_macros {
|
|||||||
}
|
}
|
||||||
let mut inputs = inputs.into_iter();
|
let mut inputs = inputs.into_iter();
|
||||||
let value = inputs.next().unwrap();
|
let value = inputs.next().unwrap();
|
||||||
let Some(value_layout) = value.layout().cloned() else {
|
let value_layout = value.layout().clone();
|
||||||
return Err(ConstructionError::MissingLayout);
|
|
||||||
};
|
|
||||||
let converter = inputs.next().unwrap();
|
let converter = inputs.next().unwrap();
|
||||||
let Some(converter_layout) = converter.layout().cloned() else {
|
let converter_layout = converter.layout().clone();
|
||||||
return Err(ConstructionError::MissingLayout);
|
|
||||||
};
|
|
||||||
let node = graphene_std::ops::ConvertNode::<$to, _, _, $from, $convert>::new(
|
let node = graphene_std::ops::ConvertNode::<$to, _, _, $from, $convert>::new(
|
||||||
value.downcast_record::<$from>()?,
|
value.downcast_record::<$from>()?,
|
||||||
converter.downcast_record::<$convert>()?,
|
converter.downcast_record::<$convert>()?,
|
||||||
|
|||||||
@@ -278,6 +278,9 @@ pub trait Node<Input> {
|
|||||||
crate::record::empty_layout()
|
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>
|
fn eval_batch<'a>(&'a self, input: &'a Input, range: Range<u64>, scratch: Option<&'a mut [MaybeUninit<Self::Output>]>) -> BatchStatus<'a, Self::Output>
|
||||||
where
|
where
|
||||||
Input: InjectIndex + Copy,
|
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
|
/// 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
|
/// its registry entry so the compiler can fold each wire's layout without
|
||||||
/// running the node's constructor. [`fold`](LayoutMeta::fold) reproduces the
|
/// running the node's constructor. [`fold`](LayoutMeta::fold) reproduces the
|
||||||
/// layout the constructor derives at wiring today; the compiler layout pass
|
/// layout the constructor derives at wiring today; the compiler layout pass
|
||||||
@@ -287,8 +295,7 @@ pub enum ElementSpec {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl LayoutMeta {
|
impl LayoutMeta {
|
||||||
/// The meta of an elementwise carrier flip that retypes input 0's element,
|
/// Keeps input 0's layout but replaces its element.
|
||||||
/// preserving its depth and attributes: what an `Into`/`Convert` coercion derives.
|
|
||||||
pub fn retype(element: ElementWrite) -> Self {
|
pub fn retype(element: ElementWrite) -> Self {
|
||||||
Self {
|
Self {
|
||||||
sources: vec![0],
|
sources: vec![0],
|
||||||
@@ -318,6 +325,23 @@ impl LayoutMeta {
|
|||||||
};
|
};
|
||||||
base.with_writes(depth, element, &self.writes)
|
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.
|
/// 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 {
|
pub enum ConstructionError {
|
||||||
Arity { expected: usize, got: usize },
|
Arity { expected: usize, got: usize },
|
||||||
Type { expected: Box<Type>, found: Box<Type> },
|
Type { expected: Box<Type>, found: Box<Type> },
|
||||||
MissingLayout,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct SharedEdge<N: ?Sized> {
|
pub struct SharedEdge<N: ?Sized> {
|
||||||
@@ -193,7 +192,8 @@ pub struct EdgeHandle {
|
|||||||
node: Box<DynEdge>,
|
node: Box<DynEdge>,
|
||||||
share: fn(&DynEdge) -> Box<DynEdge>,
|
share: fn(&DynEdge) -> Box<DynEdge>,
|
||||||
serialize: fn(&DynEdge) -> Option<std::sync::Arc<dyn std::any::Any + Send + Sync>>,
|
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,
|
ty: Type,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -228,7 +228,12 @@ impl EdgeHandle {
|
|||||||
node: Box::new(SharedEdge::new(node)),
|
node: Box::new(SharedEdge::new(node)),
|
||||||
share: |edge| Box::new(edge.downcast_ref::<SharedEdge<N>>().expect("share hook matches the stored edge type").share()),
|
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")),
|
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,
|
ty,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -243,6 +248,7 @@ impl EdgeHandle {
|
|||||||
share: self.share,
|
share: self.share,
|
||||||
serialize: self.serialize,
|
serialize: self.serialize,
|
||||||
layout: self.layout,
|
layout: self.layout,
|
||||||
|
set_layout: self.set_layout,
|
||||||
ty: self.ty.clone(),
|
ty: self.ty.clone(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -251,10 +257,14 @@ impl EdgeHandle {
|
|||||||
(self.serialize)(&*self.node)
|
(self.serialize)(&*self.node)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn layout(&self) -> Option<&crate::record::Layout> {
|
pub fn layout(&self) -> &crate::record::Layout {
|
||||||
(self.layout)(&*self.node)
|
(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> {
|
pub fn downcast<T: 'static>(self) -> Result<SharedEdge<ErasedNode<T>>, ConstructionError> {
|
||||||
self.downcast_erased(edge_type::<T>())
|
self.downcast_erased(edge_type::<T>())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -403,19 +403,7 @@ pub(crate) fn generate_node_code(crate_ident: &CrateIdent, parsed: &ParsedNodeFn
|
|||||||
})
|
})
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.flatten();
|
.flatten();
|
||||||
let flip_prelude = flip
|
// The output layout, frame size, and copy plan are installed by `set_layout`.
|
||||||
.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();
|
|
||||||
let flip_read_bindings = flip
|
let flip_read_bindings = flip
|
||||||
.then(|| {
|
.then(|| {
|
||||||
lazy_read_fields(&struct_regular_fields).into_iter().map(|(index, field)| {
|
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();
|
.flatten();
|
||||||
let flip_output_inits = flip
|
let flip_output_inits = flip
|
||||||
.then(|| {
|
.then(|| {
|
||||||
let plan = carrier_flip.then(|| quote!(__plan,));
|
let plan = carrier_flip.then(|| quote!(__plan: ::std::vec::Vec::new(),));
|
||||||
match flip_generic_idents.is_empty() {
|
let marker = (!flip_generic_idents.is_empty()).then(|| quote!(__marker: ::core::marker::PhantomData,));
|
||||||
true => quote!(__layout, __frame_bytes, #plan),
|
quote!(__layout: ::core::default::Default::default(), __frame_bytes: 0, #plan #marker)
|
||||||
false => quote!(__layout, __frame_bytes, #plan __marker: ::core::marker::PhantomData,),
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
.into_iter();
|
.into_iter();
|
||||||
// The flip prelude's `element_write` instantiates the erased glue at the
|
// `new` carries the bounds the erased glue needs at the output type.
|
||||||
// output type, so `new` carries the bounds the glue needs.
|
|
||||||
let new_where = flip
|
let new_where = flip
|
||||||
.then(|| {
|
.then(|| {
|
||||||
let existing = parsed.where_clause.iter().flat_map(|clause| clause.predicates.iter());
|
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)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
pub fn new(#(#new_args,)* #(#routing_layout_param)* #(#routing_in_params)* #(#flip_layout_params)*) -> Self {
|
pub fn new(#(#new_args,)* #(#routing_layout_param)* #(#routing_in_params)* #(#flip_layout_params)*) -> Self {
|
||||||
#(#flip_prelude)*
|
|
||||||
#(#flip_read_bindings)*
|
#(#flip_read_bindings)*
|
||||||
Self {
|
Self {
|
||||||
#(#all_field_inits,)*
|
#(#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),
|
crate::codegen::ir::Element::Concrete(ty) => Some(ty),
|
||||||
_ => None,
|
_ => 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 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 level_delta = node.output.shape.depth as i8 - subject_depth as i8;
|
||||||
let pushed_levels = level_delta.max(0) as u8;
|
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(),
|
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 {
|
let record_layout_impl = match record_io || routing_generic.is_some() || flip || opaque {
|
||||||
true => quote! {
|
true => quote! {
|
||||||
fn layout(&self) -> &#core_types::record::Layout {
|
fn layout(&self) -> &#core_types::record::Layout {
|
||||||
&self.__layout
|
&self.__layout
|
||||||
}
|
}
|
||||||
|
#set_layout_method
|
||||||
},
|
},
|
||||||
false => quote!(),
|
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 entries = entries_tokens(parsed, &struct_name, &data_field_generic_idents, ®ular_fields);
|
||||||
let cfg = crate::shader_nodes::modify_cfg(&parsed.attributes);
|
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}");
|
let slot = format_ident!("__in_{index}");
|
||||||
quote!(#slot: &#core_types::record::Layout,)
|
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 read_inits = flat_reads.iter().enumerate().map(|(slot, (owner, read))| {
|
||||||
let marker = &read.marker;
|
let marker = &read.marker;
|
||||||
let slot = format_ident!("__read_{slot}");
|
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);)
|
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 data_inits = data_names.iter().map(|name| quote!(#name: ::core::default::Default::default(),));
|
||||||
let edge_inits = regular_fields.iter().map(|field| {
|
let edge_inits = regular_fields.iter().map(|field| {
|
||||||
let name = &field.pat_ident.ident;
|
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}");
|
let slot = format_ident!("__in_{index}");
|
||||||
quote!(#slot: #slot.clone(),)
|
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 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! {
|
quote! {
|
||||||
#layout_def
|
#layout_def
|
||||||
#layout_meta_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,)*> {
|
impl<#(#data_field_generic_idents,)* #(#node_generics,)*> #mod_name::#struct_name<#(#struct_type_params,)*> {
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
#vis fn new(#(#edge_args,)* #(#carrier_layout_param)* #(#input_layout_params)*) -> Self {
|
#vis fn new(#(#edge_args,)* #(#carrier_layout_param)* #(#input_layout_params)*) -> Self {
|
||||||
#layout_binding
|
|
||||||
#plan_binding
|
|
||||||
#(#read_inits)*
|
#(#read_inits)*
|
||||||
#(#write_inits)*
|
|
||||||
let __frame_bytes = __layout.frame_bytes();
|
|
||||||
Self {
|
Self {
|
||||||
#(#data_inits)*
|
#(#data_inits)*
|
||||||
#(#edge_inits)*
|
#(#edge_inits)*
|
||||||
#(#carrier_init)*
|
#(#carrier_init)*
|
||||||
#(#input_layout_inits)*
|
#(#input_layout_inits)*
|
||||||
__layout,
|
__layout: ::core::default::Default::default(),
|
||||||
#(#plan_init)*
|
#(#plan_default)*
|
||||||
__frame_bytes,
|
__frame_bytes: 0,
|
||||||
#(#read_names)*
|
#(#read_names)*
|
||||||
#(#write_names)*
|
#(#write_defaults)*
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1968,7 +1977,7 @@ pub(crate) fn generate_node_impl(crate_ident: &CrateIdent, parsed: &ParsedNodeFn
|
|||||||
Ok(NodePlan {
|
Ok(NodePlan {
|
||||||
kernel,
|
kernel,
|
||||||
lazy_read_fns: quote!(#(#lazy_read_fns)*),
|
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,
|
node_impl: top_level,
|
||||||
entries,
|
entries,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
|
|||||||
@@ -132,9 +132,7 @@ fn flip_entries_tokens(parsed: &ParsedNodeFn, struct_name: &Ident, regular_field
|
|||||||
let layout = format_ident!("__layout_{index}");
|
let layout = format_ident!("__layout_{index}");
|
||||||
quote! {
|
quote! {
|
||||||
let #handle = inputs.next().unwrap();
|
let #handle = inputs.next().unwrap();
|
||||||
let Some(#layout) = #handle.layout().cloned() else {
|
let #layout = #handle.layout().clone();
|
||||||
return Err(gcore::registry::ConstructionError::MissingLayout);
|
|
||||||
};
|
|
||||||
let #name = #handle.downcast_record::<#ty>()?;
|
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! {
|
SlotKind::BaseGeneric(_) | SlotKind::BaseConcrete(_) => quote! {
|
||||||
let #handle = inputs.next().unwrap();
|
let #handle = inputs.next().unwrap();
|
||||||
let #ty = #handle.ty().clone();
|
let #ty = #handle.ty().clone();
|
||||||
let Some(#layout) = #handle.layout().cloned() else {
|
let #layout = #handle.layout().clone();
|
||||||
return Err(gcore::registry::ConstructionError::MissingLayout);
|
|
||||||
};
|
|
||||||
let #name = #handle.downcast_erased::<gcore::registry::ErasedRecordNode>(#ty.clone())?;
|
let #name = #handle.downcast_erased::<gcore::registry::ErasedRecordNode>(#ty.clone())?;
|
||||||
},
|
},
|
||||||
SlotKind::Value(value_ty) => quote! {
|
SlotKind::Value(value_ty) => quote! {
|
||||||
let #handle = inputs.next().unwrap();
|
let #handle = inputs.next().unwrap();
|
||||||
let Some(#layout) = #handle.layout().cloned() else {
|
let #layout = #handle.layout().clone();
|
||||||
return Err(gcore::registry::ConstructionError::MissingLayout);
|
|
||||||
};
|
|
||||||
let #name = #handle.downcast_record::<#value_ty>()?;
|
let #name = #handle.downcast_record::<#value_ty>()?;
|
||||||
},
|
},
|
||||||
SlotKind::Plain(value_ty) | SlotKind::Lazy(value_ty) => quote!(let #name = inputs.next().unwrap().downcast::<#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));
|
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) {
|
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 lift = core_types::record::RecordLift::<T, _>::new(ValueNode(value));
|
||||||
let layout = Node::<ContextImpl>::layout(&lift).clone();
|
let layout = Node::<ContextImpl>::layout(&lift).clone();
|
||||||
@@ -268,7 +283,7 @@ mod tests {
|
|||||||
let leveled = repeat_opacity_layout(&base);
|
let leveled = repeat_opacity_layout(&base);
|
||||||
reserve_for(&[&base, &leveled]);
|
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);
|
assert_eq!(node.layout(), &leveled);
|
||||||
let GPoll::Final(value) = node.eval(&indexed) else {
|
let GPoll::Final(value) = node.eval(&indexed) else {
|
||||||
panic!("expected a final record");
|
panic!("expected a final record");
|
||||||
@@ -288,7 +303,7 @@ mod tests {
|
|||||||
let base = f64_layout(&[]);
|
let base = f64_layout(&[]);
|
||||||
reserve_for(&[&base]);
|
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.
|
// 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)));
|
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(&[]);
|
let out = f64_layout(&[]);
|
||||||
reserve_for(&[&base, &leveled, &out]);
|
reserve_for(&[&base, &leveled, &out]);
|
||||||
|
|
||||||
let repeat = RepeatOpacityNode::new(bare_source(&base, 7.), ValueNode(3u32), &base);
|
let repeat = install(RepeatOpacityNode::new(bare_source(&base, 7.), ValueNode(3u32), &base), repeat_opacity_layout_meta(), &[Some(&base)]);
|
||||||
let node = SumNode::new(repeat, &leveled);
|
let node = install_flip(SumNode::new(repeat, &leveled), &out);
|
||||||
assert_eq!(node.layout().depth, 0, "the reducer collapsed the rank level");
|
assert_eq!(node.layout().depth, 0, "the reducer collapsed the rank level");
|
||||||
|
|
||||||
let GPoll::Final(value) = node.eval(&ctx) else {
|
let GPoll::Final(value) = node.eval(&ctx) else {
|
||||||
@@ -342,7 +357,15 @@ mod tests {
|
|||||||
let stacked = multiply_opacity_layout(&modified);
|
let stacked = multiply_opacity_layout(&modified);
|
||||||
reserve_for(&[&source_layout, &modified, &stacked]);
|
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);
|
assert_eq!(chain.layout(), &stacked);
|
||||||
let GPoll::Final(value) = chain.eval(&ctx) else {
|
let GPoll::Final(value) = chain.eval(&ctx) else {
|
||||||
panic!("expected a final record");
|
panic!("expected a final record");
|
||||||
@@ -363,7 +386,7 @@ mod tests {
|
|||||||
let measured = measure_layout(&source_layout);
|
let measured = measure_layout(&source_layout);
|
||||||
reserve_for(&[&source_layout, &measured]);
|
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 {
|
let GPoll::Final(value) = chain.eval(&ctx) else {
|
||||||
panic!("expected a final record");
|
panic!("expected a final record");
|
||||||
};
|
};
|
||||||
@@ -384,7 +407,14 @@ mod tests {
|
|||||||
let measured = measure_layout(&modified);
|
let measured = measure_layout(&modified);
|
||||||
reserve_for(&[&source_layout, &modified, &measured]);
|
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 {
|
let GPoll::Final(value) = chain.eval(&ctx) else {
|
||||||
panic!("expected a final record");
|
panic!("expected a final record");
|
||||||
};
|
};
|
||||||
@@ -405,13 +435,20 @@ mod tests {
|
|||||||
let shaded = shade_layout(&modified);
|
let shaded = shade_layout(&modified);
|
||||||
reserve_for(&[&source_layout, &modified, &shaded]);
|
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 {
|
let GPoll::Final(value) = bare.eval(&ctx) else {
|
||||||
panic!("expected a final record");
|
panic!("expected a final record");
|
||||||
};
|
};
|
||||||
assert_eq!(unsafe { source_layout.rec(&value).element::<f64>() }, 4.);
|
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 {
|
let GPoll::Final(value) = chain.eval(&ctx) else {
|
||||||
panic!("expected a final record");
|
panic!("expected a final record");
|
||||||
};
|
};
|
||||||
@@ -433,7 +470,7 @@ mod tests {
|
|||||||
let u32_faded = fade_layout(&u32_source);
|
let u32_faded = fade_layout(&u32_source);
|
||||||
reserve_for(&[&f64_source, &f64_faded, &u32_source, &u32_faded]);
|
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 {
|
let GPoll::Final(value) = wide.eval(&ctx) else {
|
||||||
panic!("expected a final record");
|
panic!("expected a final record");
|
||||||
};
|
};
|
||||||
@@ -441,15 +478,19 @@ mod tests {
|
|||||||
assert_eq!(unsafe { rec.element::<f64>() }, 8.);
|
assert_eq!(unsafe { rec.element::<f64>() }, 8.);
|
||||||
assert_eq!(unsafe { rec.read::<f64>(f64_faded.offset_of(Opacity::NAME, 0).unwrap()) }, 0.5);
|
assert_eq!(unsafe { rec.read::<f64>(f64_faded.offset_of(Opacity::NAME, 0).unwrap()) }, 0.5);
|
||||||
|
|
||||||
let narrow = FadeNode::new(
|
let narrow = install(
|
||||||
RecordSourceNode {
|
FadeNode::new(
|
||||||
layout: u32_source.clone(),
|
RecordSourceNode {
|
||||||
element: 7u32,
|
layout: u32_source.clone(),
|
||||||
fields: vec![],
|
element: 7u32,
|
||||||
partial: false,
|
fields: vec![],
|
||||||
},
|
partial: false,
|
||||||
ValueNode(0.25),
|
},
|
||||||
&u32_source,
|
ValueNode(0.25),
|
||||||
|
&u32_source,
|
||||||
|
),
|
||||||
|
fade_layout_meta(),
|
||||||
|
&[Some(&u32_source)],
|
||||||
);
|
);
|
||||||
let GPoll::Final(value) = narrow.eval(&ctx) else {
|
let GPoll::Final(value) = narrow.eval(&ctx) else {
|
||||||
panic!("expected a final record");
|
panic!("expected a final record");
|
||||||
@@ -469,7 +510,7 @@ mod tests {
|
|||||||
let layout = source_opacity_layout();
|
let layout = source_opacity_layout();
|
||||||
reserve_for(&[&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);
|
assert_eq!(Node::<ContextImpl>::layout(&node), &layout);
|
||||||
let GPoll::Final(value) = node.eval(&ctx) else {
|
let GPoll::Final(value) = node.eval(&ctx) else {
|
||||||
panic!("expected a final record");
|
panic!("expected a final record");
|
||||||
@@ -490,15 +531,19 @@ mod tests {
|
|||||||
let modified = multiply_opacity_layout(&source_layout);
|
let modified = multiply_opacity_layout(&source_layout);
|
||||||
reserve_for(&[&source_layout, &modified]);
|
reserve_for(&[&source_layout, &modified]);
|
||||||
|
|
||||||
let chain = MultiplyOpacityNode::new(
|
let chain = install(
|
||||||
RecordSourceNode {
|
MultiplyOpacityNode::new(
|
||||||
layout: source_layout.clone(),
|
RecordSourceNode {
|
||||||
element: 1.,
|
layout: source_layout.clone(),
|
||||||
fields: vec![],
|
element: 1.,
|
||||||
partial: true,
|
fields: vec![],
|
||||||
},
|
partial: true,
|
||||||
ValueNode(0.5),
|
},
|
||||||
&source_layout,
|
ValueNode(0.5),
|
||||||
|
&source_layout,
|
||||||
|
),
|
||||||
|
multiply_opacity_layout_meta(),
|
||||||
|
&[Some(&source_layout)],
|
||||||
);
|
);
|
||||||
let GPoll::Partial(value) = chain.eval(&ctx) else {
|
let GPoll::Partial(value) = chain.eval(&ctx) else {
|
||||||
panic!("expected a partial record");
|
panic!("expected a partial record");
|
||||||
@@ -517,13 +562,13 @@ mod tests {
|
|||||||
let modified = checked_multiply_opacity_layout(&source_layout);
|
let modified = checked_multiply_opacity_layout(&source_layout);
|
||||||
reserve_for(&[&source_layout, &modified]);
|
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 {
|
let GPoll::Final(value) = ok.eval(&ctx) else {
|
||||||
panic!("expected a final record");
|
panic!("expected a final record");
|
||||||
};
|
};
|
||||||
assert_eq!(unsafe { modified.rec(&value).read::<f64>(modified.offset_of(Opacity::NAME, 0).unwrap()) }, 0.5);
|
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 {
|
let GPoll::Error(error) = failing.eval(&ctx) else {
|
||||||
panic!("expected an error");
|
panic!("expected an error");
|
||||||
};
|
};
|
||||||
@@ -542,7 +587,15 @@ mod tests {
|
|||||||
let scaled = scale_layout(&modified);
|
let scaled = scale_layout(&modified);
|
||||||
reserve_for(&[&source_layout, &modified, &scaled]);
|
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 {
|
let GPoll::Final(value) = chain.eval(&ctx) else {
|
||||||
panic!("expected a final record");
|
panic!("expected a final record");
|
||||||
};
|
};
|
||||||
@@ -563,11 +616,15 @@ mod tests {
|
|||||||
let transferred = transfer_opacity_layout(&carrier_layout);
|
let transferred = transfer_opacity_layout(&carrier_layout);
|
||||||
reserve_for(&[&carrier_layout, &secondary_layout, &transferred]);
|
reserve_for(&[&carrier_layout, &secondary_layout, &transferred]);
|
||||||
|
|
||||||
let chain = TransferOpacityNode::new(
|
let chain = install(
|
||||||
f64_record_source(&carrier_layout, 2., vec![(carrier_layout.offset_of("opacity", 0).unwrap(), 0.5)]),
|
TransferOpacityNode::new(
|
||||||
f64_record_source(&secondary_layout, 3., vec![(secondary_layout.offset_of("opacity", 0).unwrap(), 0.25)]),
|
f64_record_source(&carrier_layout, 2., vec![(carrier_layout.offset_of("opacity", 0).unwrap(), 0.5)]),
|
||||||
&carrier_layout,
|
f64_record_source(&secondary_layout, 3., vec![(secondary_layout.offset_of("opacity", 0).unwrap(), 0.25)]),
|
||||||
&secondary_layout,
|
&carrier_layout,
|
||||||
|
&secondary_layout,
|
||||||
|
),
|
||||||
|
transfer_opacity_layout_meta(),
|
||||||
|
&[Some(&carrier_layout), None],
|
||||||
);
|
);
|
||||||
let GPoll::Final(value) = chain.eval(&ctx) else {
|
let GPoll::Final(value) = chain.eval(&ctx) else {
|
||||||
panic!("expected a final record");
|
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);
|
assert_eq!(unsafe { rec.read::<f64>(transferred.offset_of(Opacity::NAME, 0).unwrap()) }, 0.125);
|
||||||
|
|
||||||
let bare_secondary = f64_layout(&[]);
|
let bare_secondary = f64_layout(&[]);
|
||||||
let defaulted = TransferOpacityNode::new(
|
let defaulted = install(
|
||||||
f64_record_source(&carrier_layout, 2., vec![(carrier_layout.offset_of("opacity", 0).unwrap(), 0.5)]),
|
TransferOpacityNode::new(
|
||||||
bare_source(&bare_secondary, 3.),
|
f64_record_source(&carrier_layout, 2., vec![(carrier_layout.offset_of("opacity", 0).unwrap(), 0.5)]),
|
||||||
&carrier_layout,
|
bare_source(&bare_secondary, 3.),
|
||||||
&bare_secondary,
|
&carrier_layout,
|
||||||
|
&bare_secondary,
|
||||||
|
),
|
||||||
|
transfer_opacity_layout_meta(),
|
||||||
|
&[Some(&carrier_layout), None],
|
||||||
);
|
);
|
||||||
let GPoll::Final(value) = defaulted.eval(&ctx) else {
|
let GPoll::Final(value) = defaulted.eval(&ctx) else {
|
||||||
panic!("expected a final record");
|
panic!("expected a final record");
|
||||||
@@ -602,11 +663,15 @@ mod tests {
|
|||||||
let factor_layout = Node::<ContextImpl>::layout(&factor).clone();
|
let factor_layout = Node::<ContextImpl>::layout(&factor).clone();
|
||||||
reserve_for(&[&source_layout]);
|
reserve_for(&[&source_layout]);
|
||||||
|
|
||||||
let node = BoostNode::new(
|
let node = install(
|
||||||
f64_record_source(&source_layout, 2., vec![(source_layout.offset_of("opacity", 0).unwrap(), 0.25)]),
|
BoostNode::new(
|
||||||
factor,
|
f64_record_source(&source_layout, 2., vec![(source_layout.offset_of("opacity", 0).unwrap(), 0.25)]),
|
||||||
&source_layout,
|
factor,
|
||||||
&factor_layout,
|
&source_layout,
|
||||||
|
&factor_layout,
|
||||||
|
),
|
||||||
|
boost_layout_meta(),
|
||||||
|
&[Some(&source_layout)],
|
||||||
);
|
);
|
||||||
let out_layout = Node::<ContextImpl>::layout(&node).clone();
|
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");
|
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.);
|
let (factor, factor_layout) = lifted_value(3.);
|
||||||
reserve_for(&[&source_layout]);
|
reserve_for(&[&source_layout]);
|
||||||
|
|
||||||
let node = BoostPollNode::new(
|
let node = install(
|
||||||
f64_record_source(&source_layout, 2., vec![(source_layout.offset_of("opacity", 0).unwrap(), 0.25)]),
|
BoostPollNode::new(
|
||||||
factor,
|
f64_record_source(&source_layout, 2., vec![(source_layout.offset_of("opacity", 0).unwrap(), 0.25)]),
|
||||||
&source_layout,
|
factor,
|
||||||
&factor_layout,
|
&source_layout,
|
||||||
|
&factor_layout,
|
||||||
|
),
|
||||||
|
boost_poll_layout_meta(),
|
||||||
|
&[Some(&source_layout)],
|
||||||
);
|
);
|
||||||
let out_layout = Node::<ContextImpl>::layout(&node).clone();
|
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");
|
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");
|
assert!(by_layout.frame_bytes() != 0, "the borrow must point into a spilled frame to exercise the park");
|
||||||
reserve_for(&[&carrier_layout, &by_layout]);
|
reserve_for(&[&carrier_layout, &by_layout]);
|
||||||
|
|
||||||
let node = OffsetNode::new(
|
let node = install(
|
||||||
f64_record_source(&carrier_layout, 2., vec![(carrier_layout.offset_of("opacity", 0).unwrap(), 0.25)]),
|
OffsetNode::new(
|
||||||
f64_record_source(&by_layout, 40., vec![]),
|
f64_record_source(&carrier_layout, 2., vec![(carrier_layout.offset_of("opacity", 0).unwrap(), 0.25)]),
|
||||||
&carrier_layout,
|
f64_record_source(&by_layout, 40., vec![]),
|
||||||
&by_layout,
|
&carrier_layout,
|
||||||
|
&by_layout,
|
||||||
|
),
|
||||||
|
offset_layout_meta(),
|
||||||
|
&[Some(&carrier_layout)],
|
||||||
);
|
);
|
||||||
let out_layout = Node::<ContextImpl>::layout(&node).clone();
|
let out_layout = Node::<ContextImpl>::layout(&node).clone();
|
||||||
let GPoll::Final(value) = node.eval(&ctx) else {
|
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);
|
let (source_id, source_id_layout) = lifted_value(7 as SourceId);
|
||||||
reserve_for(&[&source_layout]);
|
reserve_for(&[&source_layout]);
|
||||||
|
|
||||||
let node = DoubleAsyncNode::new(
|
let node = install(
|
||||||
f64_record_source(&source_layout, 3., vec![(source_layout.offset_of("opacity", 0).unwrap(), 0.25)]),
|
DoubleAsyncNode::new(
|
||||||
runtime,
|
f64_record_source(&source_layout, 3., vec![(source_layout.offset_of("opacity", 0).unwrap(), 0.25)]),
|
||||||
source_id,
|
runtime,
|
||||||
&source_layout,
|
source_id,
|
||||||
&runtime_layout,
|
&source_layout,
|
||||||
&source_id_layout,
|
&runtime_layout,
|
||||||
|
&source_id_layout,
|
||||||
|
),
|
||||||
|
double_async_layout_meta(),
|
||||||
|
&[Some(&source_layout)],
|
||||||
);
|
);
|
||||||
let out_layout = Node::<ContextImpl>::layout(&node).clone();
|
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");
|
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);
|
let shaded = shade_layout(&stripped);
|
||||||
reserve_for(&[&source_layout, &modified, &stripped, &shaded]);
|
reserve_for(&[&source_layout, &modified, &stripped, &shaded]);
|
||||||
|
|
||||||
let chain = ShadeNode::new(
|
let chain = install(
|
||||||
StripOpacityNode::new(MultiplyOpacityNode::new(bare_source(&source_layout, 4.), ValueNode(0.5), &source_layout), &modified),
|
ShadeNode::new(
|
||||||
&stripped,
|
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 {
|
let GPoll::Final(value) = chain.eval(&ctx) else {
|
||||||
panic!("expected a final record");
|
panic!("expected a final record");
|
||||||
@@ -796,13 +884,17 @@ mod tests {
|
|||||||
assert!(relengthed.offset_of(Opacity::NAME, 0).is_none());
|
assert!(relengthed.offset_of(Opacity::NAME, 0).is_none());
|
||||||
reserve_for(&[&source_layout, &relengthed]);
|
reserve_for(&[&source_layout, &relengthed]);
|
||||||
|
|
||||||
let chain = RelengthNode::new(
|
let chain = install(
|
||||||
f64_record_source(
|
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,
|
&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 {
|
let GPoll::Final(value) = chain.eval(&ctx) else {
|
||||||
panic!("expected a final record");
|
panic!("expected a final record");
|
||||||
@@ -824,10 +916,14 @@ mod tests {
|
|||||||
let relabeled = label_layout(&labeled);
|
let relabeled = label_layout(&labeled);
|
||||||
reserve_for(&[&source_layout, &labeled, &relabeled]);
|
reserve_for(&[&source_layout, &labeled, &relabeled]);
|
||||||
|
|
||||||
let chain = LabelNode::new(
|
let chain = install(
|
||||||
LabelNode::new(bare_source(&source_layout, 1.), ValueNode(String::from("a")), &source_layout),
|
LabelNode::new(
|
||||||
ValueNode(String::from("b")),
|
install(LabelNode::new(bare_source(&source_layout, 1.), ValueNode(String::from("a")), &source_layout), label_layout_meta(), &[Some(&source_layout)]),
|
||||||
&labeled,
|
ValueNode(String::from("b")),
|
||||||
|
&labeled,
|
||||||
|
),
|
||||||
|
label_layout_meta(),
|
||||||
|
&[Some(&labeled)],
|
||||||
);
|
);
|
||||||
let GPoll::Final(value) = chain.eval(&ctx) else {
|
let GPoll::Final(value) = chain.eval(&ctx) else {
|
||||||
panic!("expected a final record");
|
panic!("expected a final record");
|
||||||
@@ -1187,7 +1283,7 @@ mod tests {
|
|||||||
let labeled = label_layout(&source_layout);
|
let labeled = label_layout(&source_layout);
|
||||||
reserve_for(&[&labeled, &labeled]);
|
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 memo = crate::memo::MemoizeNode::new(chain, &labeled);
|
||||||
|
|
||||||
let first_arena = Arena::new(1024).unwrap();
|
let first_arena = Arena::new(1024).unwrap();
|
||||||
|
|||||||
Reference in New Issue
Block a user