Support bare reference parameters and lending outputs in the node macro and make the clone node the clone-out adapter

This commit is contained in:
Dennis Kobert
2026-08-03 18:29:16 +00:00
parent 9b45af854a
commit 62b7ccf40c
10 changed files with 254 additions and 172 deletions
+1 -1
View File
@@ -923,7 +923,7 @@ fn ref_adapter(proposed: &Type, wanted: &Type) -> Option<ProtoNodeIdentifier> {
return None; return None;
}; };
match (proposed_output.as_ref(), wanted_output.as_ref()) { match (proposed_output.as_ref(), wanted_output.as_ref()) {
(Type::Ref(inner), wanted_output @ Type::Concrete(_)) if valid_type(inner, wanted_output) => Some(ProtoNodeIdentifier::new("graphene_core::memo::CloneOutNode")), (Type::Ref(inner), wanted_output @ Type::Concrete(_)) if valid_type(inner, wanted_output) => Some(ProtoNodeIdentifier::new("graphene_core::debug::CloneNode")),
(proposed_output @ Type::Concrete(_), Type::Ref(inner)) if valid_type(proposed_output, inner) => Some(ProtoNodeIdentifier::new("graphene_core::memo::LendNode")), (proposed_output @ Type::Concrete(_), Type::Ref(inner)) if valid_type(proposed_output, inner) => Some(ProtoNodeIdentifier::new("graphene_core::memo::LendNode")),
_ => None, _ => None,
} }
@@ -566,7 +566,7 @@ mod test {
output: NodeId(1), output: NodeId(1),
nodes: vec![ nodes: vec![
(NodeId(0), string_value("lent")), (NodeId(0), string_value("lent")),
(NodeId(1), proto_node("graphene_core::memo::CloneOutNode", vec![NodeId(0)])), (NodeId(1), proto_node("graphene_core::debug::CloneNode", vec![NodeId(0)])),
], ],
}; };
@@ -574,6 +574,28 @@ mod test {
assert_eq!((&executor).execute(()).unwrap(), GPoll::Final(TaggedValue::String("lent".to_string()))); assert_eq!((&executor).execute(()).unwrap(), GPoll::Final(TaggedValue::String("lent".to_string())));
} }
#[test]
fn a_flipped_ref_parameter_gets_its_producer_lend_spliced() {
let raster_list = TaggedValue::from_type(&core_types::concrete!(graphene_std::list::List<graphene_std::raster_types::Raster<graphene_std::raster_types::CPU>>)).unwrap();
let network = ProtoNetwork {
inputs: vec![],
output: NodeId(2),
nodes: vec![
(NodeId(0), ProtoNode::value(ConstructionArgs::Value(raster_list.into()), vec![])),
(NodeId(1), ProtoNode::value(ConstructionArgs::Value(TaggedValue::U32(4).into()), vec![])),
(NodeId(2), proto_node("raster_nodes::image_color_palette::ImageColorPaletteNode", vec![NodeId(0), NodeId(1)])),
],
};
let executor = DynamicExecutor::new(network).unwrap();
let arena = Arena::new(1 << 12).unwrap();
let generations = [];
let scope = EvalScope::new(None, None, None, &generations, &arena);
let ctx = ContextImpl::root(&scope);
let result: Option<GPoll<graphene_std::list::List<graphene_std::raster::color::Color>>> = executor.tree().eval(NodeId(2), &ctx);
assert!(matches!(result, Some(GPoll::Final(_))), "the palette must evaluate through the spliced lend, got {result:?}");
}
#[test] #[test]
fn a_clone_out_adapter_is_spliced_between_a_lending_producer_and_an_owned_consumer() { fn a_clone_out_adapter_is_spliced_between_a_lending_producer_and_an_owned_consumer() {
let network = ProtoNetwork { let network = ProtoNetwork {
@@ -583,7 +605,7 @@ mod test {
(NodeId(0), string_value("memoized")), (NodeId(0), string_value("memoized")),
(NodeId(1), proto_node("graphene_core::memo::FrameMemoNode", vec![NodeId(0)])), (NodeId(1), proto_node("graphene_core::memo::FrameMemoNode", vec![NodeId(0)])),
(NodeId(2), proto_node("graphene_core::memo::FrameMemoNode", vec![NodeId(1)])), (NodeId(2), proto_node("graphene_core::memo::FrameMemoNode", vec![NodeId(1)])),
(NodeId(3), proto_node("graphene_core::memo::CloneOutNode", vec![NodeId(2)])), (NodeId(3), proto_node("graphene_core::debug::CloneNode", vec![NodeId(2)])),
], ],
}; };
@@ -20,7 +20,7 @@ use graphene_std::transform::Footprint;
use graphene_std::uuid::NodeId; use graphene_std::uuid::NodeId;
use graphene_std::vector::Vector; use graphene_std::vector::Vector;
use graphene_std::{Artboard, Context, Graphic, ProtoNodeIdentifier, SourceId, concrete, fn_type}; use graphene_std::{Artboard, Context, Graphic, ProtoNodeIdentifier, SourceId, concrete, fn_type};
use node_registry_macros::{async_node, clone_out_node, convert_node, frame_memo_node, into_node, lend_node}; use node_registry_macros::{async_node, clone_node, convert_node, frame_memo_node, into_node, lend_node};
use std::collections::HashMap; use std::collections::HashMap;
#[cfg(feature = "gpu")] #[cfg(feature = "gpu")]
use wgpu_executor::WgpuExecutorHandle; use wgpu_executor::WgpuExecutorHandle;
@@ -299,265 +299,265 @@ fn node_registry() -> HashMap<ProtoNodeIdentifier, Vec<RegistryEntry>> {
// REF ADAPTERS // REF ADAPTERS
// ============ // ============
lend_node!(()), lend_node!(()),
clone_out_node!(()), clone_node!(()),
frame_memo_node!(()), frame_memo_node!(()),
lend_node!(RuntimeHandle), lend_node!(RuntimeHandle),
clone_out_node!(RuntimeHandle), clone_node!(RuntimeHandle),
frame_memo_node!(RuntimeHandle), frame_memo_node!(RuntimeHandle),
lend_node!(SourceId), lend_node!(SourceId),
clone_out_node!(SourceId), clone_node!(SourceId),
frame_memo_node!(SourceId), frame_memo_node!(SourceId),
lend_node!(bool), lend_node!(bool),
clone_out_node!(bool), clone_node!(bool),
frame_memo_node!(bool), frame_memo_node!(bool),
lend_node!(List<Artboard>), lend_node!(List<Artboard>),
clone_out_node!(List<Artboard>), clone_node!(List<Artboard>),
frame_memo_node!(List<Artboard>), frame_memo_node!(List<Artboard>),
lend_node!(List<Graphic>), lend_node!(List<Graphic>),
clone_out_node!(List<Graphic>), clone_node!(List<Graphic>),
frame_memo_node!(List<Graphic>), frame_memo_node!(List<Graphic>),
lend_node!(List<Vector>), lend_node!(List<Vector>),
clone_out_node!(List<Vector>), clone_node!(List<Vector>),
frame_memo_node!(List<Vector>), frame_memo_node!(List<Vector>),
lend_node!(List<Raster<CPU>>), lend_node!(List<Raster<CPU>>),
clone_out_node!(List<Raster<CPU>>), clone_node!(List<Raster<CPU>>),
frame_memo_node!(List<Raster<CPU>>), frame_memo_node!(List<Raster<CPU>>),
lend_node!(List<Color>), lend_node!(List<Color>),
clone_out_node!(List<Color>), clone_node!(List<Color>),
frame_memo_node!(List<Color>), frame_memo_node!(List<Color>),
lend_node!(Image<Color>), lend_node!(Image<Color>),
clone_out_node!(Image<Color>), clone_node!(Image<Color>),
frame_memo_node!(Image<Color>), frame_memo_node!(Image<Color>),
lend_node!(List<GradientStops>), lend_node!(List<GradientStops>),
clone_out_node!(List<GradientStops>), clone_node!(List<GradientStops>),
frame_memo_node!(List<GradientStops>), frame_memo_node!(List<GradientStops>),
lend_node!(List<String>), lend_node!(List<String>),
clone_out_node!(List<String>), clone_node!(List<String>),
frame_memo_node!(List<String>), frame_memo_node!(List<String>),
lend_node!(List<NodeId>), lend_node!(List<NodeId>),
clone_out_node!(List<NodeId>), clone_node!(List<NodeId>),
frame_memo_node!(List<NodeId>), frame_memo_node!(List<NodeId>),
lend_node!(List<f64>), lend_node!(List<f64>),
clone_out_node!(List<f64>), clone_node!(List<f64>),
frame_memo_node!(List<f64>), frame_memo_node!(List<f64>),
lend_node!(List<u8>), lend_node!(List<u8>),
clone_out_node!(List<u8>), clone_node!(List<u8>),
frame_memo_node!(List<u8>), frame_memo_node!(List<u8>),
lend_node!(List<bool>), lend_node!(List<bool>),
clone_out_node!(List<bool>), clone_node!(List<bool>),
frame_memo_node!(List<bool>), frame_memo_node!(List<bool>),
lend_node!(List<DAffine2>), lend_node!(List<DAffine2>),
clone_out_node!(List<DAffine2>), clone_node!(List<DAffine2>),
frame_memo_node!(List<DAffine2>), frame_memo_node!(List<DAffine2>),
lend_node!(List<BlendMode>), lend_node!(List<BlendMode>),
clone_out_node!(List<BlendMode>), clone_node!(List<BlendMode>),
frame_memo_node!(List<BlendMode>), frame_memo_node!(List<BlendMode>),
lend_node!(List<graphene_std::vector::style::GradientType>), lend_node!(List<graphene_std::vector::style::GradientType>),
clone_out_node!(List<graphene_std::vector::style::GradientType>), clone_node!(List<graphene_std::vector::style::GradientType>),
frame_memo_node!(List<graphene_std::vector::style::GradientType>), frame_memo_node!(List<graphene_std::vector::style::GradientType>),
lend_node!(List<graphene_std::vector::style::GradientSpreadMethod>), lend_node!(List<graphene_std::vector::style::GradientSpreadMethod>),
clone_out_node!(List<graphene_std::vector::style::GradientSpreadMethod>), clone_node!(List<graphene_std::vector::style::GradientSpreadMethod>),
frame_memo_node!(List<graphene_std::vector::style::GradientSpreadMethod>), frame_memo_node!(List<graphene_std::vector::style::GradientSpreadMethod>),
lend_node!(AttributeDyn), lend_node!(AttributeDyn),
clone_out_node!(AttributeDyn), clone_node!(AttributeDyn),
frame_memo_node!(AttributeDyn), frame_memo_node!(AttributeDyn),
lend_node!(AttributeValueDyn), lend_node!(AttributeValueDyn),
clone_out_node!(AttributeValueDyn), clone_node!(AttributeValueDyn),
frame_memo_node!(AttributeValueDyn), frame_memo_node!(AttributeValueDyn),
lend_node!(ListDyn), lend_node!(ListDyn),
clone_out_node!(ListDyn), clone_node!(ListDyn),
frame_memo_node!(ListDyn), frame_memo_node!(ListDyn),
#[cfg(target_family = "wasm")] #[cfg(target_family = "wasm")]
lend_node!(CanvasHandle), lend_node!(CanvasHandle),
#[cfg(target_family = "wasm")] #[cfg(target_family = "wasm")]
clone_out_node!(CanvasHandle), clone_node!(CanvasHandle),
#[cfg(target_family = "wasm")] #[cfg(target_family = "wasm")]
frame_memo_node!(CanvasHandle), frame_memo_node!(CanvasHandle),
lend_node!(f64), lend_node!(f64),
clone_out_node!(f64), clone_node!(f64),
frame_memo_node!(f64), frame_memo_node!(f64),
lend_node!(f32), lend_node!(f32),
clone_out_node!(f32), clone_node!(f32),
frame_memo_node!(f32), frame_memo_node!(f32),
lend_node!(u32), lend_node!(u32),
clone_out_node!(u32), clone_node!(u32),
frame_memo_node!(u32), frame_memo_node!(u32),
lend_node!(u64), lend_node!(u64),
clone_out_node!(u64), clone_node!(u64),
frame_memo_node!(u64), frame_memo_node!(u64),
lend_node!(DVec2), lend_node!(DVec2),
clone_out_node!(DVec2), clone_node!(DVec2),
frame_memo_node!(DVec2), frame_memo_node!(DVec2),
lend_node!(String), lend_node!(String),
clone_out_node!(String), clone_node!(String),
frame_memo_node!(String), frame_memo_node!(String),
lend_node!(DAffine2), lend_node!(DAffine2),
clone_out_node!(DAffine2), clone_node!(DAffine2),
frame_memo_node!(DAffine2), frame_memo_node!(DAffine2),
lend_node!(Footprint), lend_node!(Footprint),
clone_out_node!(Footprint), clone_node!(Footprint),
frame_memo_node!(Footprint), frame_memo_node!(Footprint),
lend_node!(RenderOutput), lend_node!(RenderOutput),
clone_out_node!(RenderOutput), clone_node!(RenderOutput),
frame_memo_node!(RenderOutput), frame_memo_node!(RenderOutput),
lend_node!(std::sync::Arc<PlatformEditorApi>), lend_node!(std::sync::Arc<PlatformEditorApi>),
clone_out_node!(std::sync::Arc<PlatformEditorApi>), clone_node!(std::sync::Arc<PlatformEditorApi>),
frame_memo_node!(std::sync::Arc<PlatformEditorApi>), frame_memo_node!(std::sync::Arc<PlatformEditorApi>),
#[cfg(feature = "gpu")] #[cfg(feature = "gpu")]
lend_node!(List<Raster<GPU>>), lend_node!(List<Raster<GPU>>),
#[cfg(feature = "gpu")] #[cfg(feature = "gpu")]
clone_out_node!(List<Raster<GPU>>), clone_node!(List<Raster<GPU>>),
#[cfg(feature = "gpu")] #[cfg(feature = "gpu")]
frame_memo_node!(List<Raster<GPU>>), frame_memo_node!(List<Raster<GPU>>),
lend_node!(Option<f64>), lend_node!(Option<f64>),
clone_out_node!(Option<f64>), clone_node!(Option<f64>),
frame_memo_node!(Option<f64>), frame_memo_node!(Option<f64>),
lend_node!(Option<Color>), lend_node!(Option<Color>),
clone_out_node!(Option<Color>), clone_node!(Option<Color>),
frame_memo_node!(Option<Color>), frame_memo_node!(Option<Color>),
lend_node!(Graphic), lend_node!(Graphic),
clone_out_node!(Graphic), clone_node!(Graphic),
frame_memo_node!(Graphic), frame_memo_node!(Graphic),
lend_node!(glam::f32::Vec2), lend_node!(glam::f32::Vec2),
clone_out_node!(glam::f32::Vec2), clone_node!(glam::f32::Vec2),
frame_memo_node!(glam::f32::Vec2), frame_memo_node!(glam::f32::Vec2),
lend_node!(glam::f32::Affine2), lend_node!(glam::f32::Affine2),
clone_out_node!(glam::f32::Affine2), clone_node!(glam::f32::Affine2),
frame_memo_node!(glam::f32::Affine2), frame_memo_node!(glam::f32::Affine2),
lend_node!(graphene_std::vector::style::Stroke), lend_node!(graphene_std::vector::style::Stroke),
clone_out_node!(graphene_std::vector::style::Stroke), clone_node!(graphene_std::vector::style::Stroke),
frame_memo_node!(graphene_std::vector::style::Stroke), frame_memo_node!(graphene_std::vector::style::Stroke),
lend_node!(graphene_std::text::Font), lend_node!(graphene_std::text::Font),
clone_out_node!(graphene_std::text::Font), clone_node!(graphene_std::text::Font),
frame_memo_node!(graphene_std::text::Font), frame_memo_node!(graphene_std::text::Font),
lend_node!(List<BrushStroke>), lend_node!(List<BrushStroke>),
clone_out_node!(List<BrushStroke>), clone_node!(List<BrushStroke>),
frame_memo_node!(List<BrushStroke>), frame_memo_node!(List<BrushStroke>),
lend_node!(DocumentNode), lend_node!(DocumentNode),
clone_out_node!(DocumentNode), clone_node!(DocumentNode),
frame_memo_node!(DocumentNode), frame_memo_node!(DocumentNode),
lend_node!(graphene_std::ContextModification), lend_node!(graphene_std::ContextModification),
clone_out_node!(graphene_std::ContextModification), clone_node!(graphene_std::ContextModification),
frame_memo_node!(graphene_std::ContextModification), frame_memo_node!(graphene_std::ContextModification),
lend_node!(graphene_std::transform::Footprint), lend_node!(graphene_std::transform::Footprint),
clone_out_node!(graphene_std::transform::Footprint), clone_node!(graphene_std::transform::Footprint),
frame_memo_node!(graphene_std::transform::Footprint), frame_memo_node!(graphene_std::transform::Footprint),
lend_node!(Box<graphene_std::vector::VectorModification>), lend_node!(Box<graphene_std::vector::VectorModification>),
clone_out_node!(Box<graphene_std::vector::VectorModification>), clone_node!(Box<graphene_std::vector::VectorModification>),
frame_memo_node!(Box<graphene_std::vector::VectorModification>), frame_memo_node!(Box<graphene_std::vector::VectorModification>),
lend_node!(graphene_std::blending::BlendMode), lend_node!(graphene_std::blending::BlendMode),
clone_out_node!(graphene_std::blending::BlendMode), clone_node!(graphene_std::blending::BlendMode),
frame_memo_node!(graphene_std::blending::BlendMode), frame_memo_node!(graphene_std::blending::BlendMode),
lend_node!(graphene_std::raster::LuminanceCalculation), lend_node!(graphene_std::raster::LuminanceCalculation),
clone_out_node!(graphene_std::raster::LuminanceCalculation), clone_node!(graphene_std::raster::LuminanceCalculation),
frame_memo_node!(graphene_std::raster::LuminanceCalculation), frame_memo_node!(graphene_std::raster::LuminanceCalculation),
lend_node!(graphene_std::vector::QRCodeErrorCorrectionLevel), lend_node!(graphene_std::vector::QRCodeErrorCorrectionLevel),
clone_out_node!(graphene_std::vector::QRCodeErrorCorrectionLevel), clone_node!(graphene_std::vector::QRCodeErrorCorrectionLevel),
frame_memo_node!(graphene_std::vector::QRCodeErrorCorrectionLevel), frame_memo_node!(graphene_std::vector::QRCodeErrorCorrectionLevel),
lend_node!(graphene_std::extract_xy::XY), lend_node!(graphene_std::extract_xy::XY),
clone_out_node!(graphene_std::extract_xy::XY), clone_node!(graphene_std::extract_xy::XY),
frame_memo_node!(graphene_std::extract_xy::XY), frame_memo_node!(graphene_std::extract_xy::XY),
lend_node!(graphene_std::text_nodes::StringCapitalization), lend_node!(graphene_std::text_nodes::StringCapitalization),
clone_out_node!(graphene_std::text_nodes::StringCapitalization), clone_node!(graphene_std::text_nodes::StringCapitalization),
frame_memo_node!(graphene_std::text_nodes::StringCapitalization), frame_memo_node!(graphene_std::text_nodes::StringCapitalization),
lend_node!(graphene_std::raster::RedGreenBlue), lend_node!(graphene_std::raster::RedGreenBlue),
clone_out_node!(graphene_std::raster::RedGreenBlue), clone_node!(graphene_std::raster::RedGreenBlue),
frame_memo_node!(graphene_std::raster::RedGreenBlue), frame_memo_node!(graphene_std::raster::RedGreenBlue),
lend_node!(graphene_std::raster::RedGreenBlueAlpha), lend_node!(graphene_std::raster::RedGreenBlueAlpha),
clone_out_node!(graphene_std::raster::RedGreenBlueAlpha), clone_node!(graphene_std::raster::RedGreenBlueAlpha),
frame_memo_node!(graphene_std::raster::RedGreenBlueAlpha), frame_memo_node!(graphene_std::raster::RedGreenBlueAlpha),
lend_node!(graphene_std::animation::RealTimeMode), lend_node!(graphene_std::animation::RealTimeMode),
clone_out_node!(graphene_std::animation::RealTimeMode), clone_node!(graphene_std::animation::RealTimeMode),
frame_memo_node!(graphene_std::animation::RealTimeMode), frame_memo_node!(graphene_std::animation::RealTimeMode),
lend_node!(graphene_std::raster::NoiseType), lend_node!(graphene_std::raster::NoiseType),
clone_out_node!(graphene_std::raster::NoiseType), clone_node!(graphene_std::raster::NoiseType),
frame_memo_node!(graphene_std::raster::NoiseType), frame_memo_node!(graphene_std::raster::NoiseType),
lend_node!(graphene_std::raster::FractalType), lend_node!(graphene_std::raster::FractalType),
clone_out_node!(graphene_std::raster::FractalType), clone_node!(graphene_std::raster::FractalType),
frame_memo_node!(graphene_std::raster::FractalType), frame_memo_node!(graphene_std::raster::FractalType),
lend_node!(graphene_std::raster::CellularDistanceFunction), lend_node!(graphene_std::raster::CellularDistanceFunction),
clone_out_node!(graphene_std::raster::CellularDistanceFunction), clone_node!(graphene_std::raster::CellularDistanceFunction),
frame_memo_node!(graphene_std::raster::CellularDistanceFunction), frame_memo_node!(graphene_std::raster::CellularDistanceFunction),
lend_node!(graphene_std::raster::CellularReturnType), lend_node!(graphene_std::raster::CellularReturnType),
clone_out_node!(graphene_std::raster::CellularReturnType), clone_node!(graphene_std::raster::CellularReturnType),
frame_memo_node!(graphene_std::raster::CellularReturnType), frame_memo_node!(graphene_std::raster::CellularReturnType),
lend_node!(graphene_std::raster::DomainWarpType), lend_node!(graphene_std::raster::DomainWarpType),
clone_out_node!(graphene_std::raster::DomainWarpType), clone_node!(graphene_std::raster::DomainWarpType),
frame_memo_node!(graphene_std::raster::DomainWarpType), frame_memo_node!(graphene_std::raster::DomainWarpType),
lend_node!(graphene_std::raster::RelativeAbsolute), lend_node!(graphene_std::raster::RelativeAbsolute),
clone_out_node!(graphene_std::raster::RelativeAbsolute), clone_node!(graphene_std::raster::RelativeAbsolute),
frame_memo_node!(graphene_std::raster::RelativeAbsolute), frame_memo_node!(graphene_std::raster::RelativeAbsolute),
lend_node!(graphene_std::raster::SelectiveColorChoice), lend_node!(graphene_std::raster::SelectiveColorChoice),
clone_out_node!(graphene_std::raster::SelectiveColorChoice), clone_node!(graphene_std::raster::SelectiveColorChoice),
frame_memo_node!(graphene_std::raster::SelectiveColorChoice), frame_memo_node!(graphene_std::raster::SelectiveColorChoice),
lend_node!(graphene_std::vector::misc::GridType), lend_node!(graphene_std::vector::misc::GridType),
clone_out_node!(graphene_std::vector::misc::GridType), clone_node!(graphene_std::vector::misc::GridType),
frame_memo_node!(graphene_std::vector::misc::GridType), frame_memo_node!(graphene_std::vector::misc::GridType),
lend_node!(graphene_std::vector::misc::ArcType), lend_node!(graphene_std::vector::misc::ArcType),
clone_out_node!(graphene_std::vector::misc::ArcType), clone_node!(graphene_std::vector::misc::ArcType),
frame_memo_node!(graphene_std::vector::misc::ArcType), frame_memo_node!(graphene_std::vector::misc::ArcType),
lend_node!(graphene_std::vector::misc::RowsOrColumns), lend_node!(graphene_std::vector::misc::RowsOrColumns),
clone_out_node!(graphene_std::vector::misc::RowsOrColumns), clone_node!(graphene_std::vector::misc::RowsOrColumns),
frame_memo_node!(graphene_std::vector::misc::RowsOrColumns), frame_memo_node!(graphene_std::vector::misc::RowsOrColumns),
lend_node!(graphene_std::vector::misc::MergeByDistanceAlgorithm), lend_node!(graphene_std::vector::misc::MergeByDistanceAlgorithm),
clone_out_node!(graphene_std::vector::misc::MergeByDistanceAlgorithm), clone_node!(graphene_std::vector::misc::MergeByDistanceAlgorithm),
frame_memo_node!(graphene_std::vector::misc::MergeByDistanceAlgorithm), frame_memo_node!(graphene_std::vector::misc::MergeByDistanceAlgorithm),
lend_node!(graphene_std::vector::misc::ExtrudeJoiningAlgorithm), lend_node!(graphene_std::vector::misc::ExtrudeJoiningAlgorithm),
clone_out_node!(graphene_std::vector::misc::ExtrudeJoiningAlgorithm), clone_node!(graphene_std::vector::misc::ExtrudeJoiningAlgorithm),
frame_memo_node!(graphene_std::vector::misc::ExtrudeJoiningAlgorithm), frame_memo_node!(graphene_std::vector::misc::ExtrudeJoiningAlgorithm),
lend_node!(graphene_std::vector::misc::PointSpacingType), lend_node!(graphene_std::vector::misc::PointSpacingType),
clone_out_node!(graphene_std::vector::misc::PointSpacingType), clone_node!(graphene_std::vector::misc::PointSpacingType),
frame_memo_node!(graphene_std::vector::misc::PointSpacingType), frame_memo_node!(graphene_std::vector::misc::PointSpacingType),
lend_node!(graphene_std::vector::style::StrokeCap), lend_node!(graphene_std::vector::style::StrokeCap),
clone_out_node!(graphene_std::vector::style::StrokeCap), clone_node!(graphene_std::vector::style::StrokeCap),
frame_memo_node!(graphene_std::vector::style::StrokeCap), frame_memo_node!(graphene_std::vector::style::StrokeCap),
lend_node!(graphene_std::vector::style::StrokeJoin), lend_node!(graphene_std::vector::style::StrokeJoin),
clone_out_node!(graphene_std::vector::style::StrokeJoin), clone_node!(graphene_std::vector::style::StrokeJoin),
frame_memo_node!(graphene_std::vector::style::StrokeJoin), frame_memo_node!(graphene_std::vector::style::StrokeJoin),
lend_node!(graphene_std::vector::style::StrokeAlign), lend_node!(graphene_std::vector::style::StrokeAlign),
clone_out_node!(graphene_std::vector::style::StrokeAlign), clone_node!(graphene_std::vector::style::StrokeAlign),
frame_memo_node!(graphene_std::vector::style::StrokeAlign), frame_memo_node!(graphene_std::vector::style::StrokeAlign),
lend_node!(graphene_std::vector::style::PaintOrder), lend_node!(graphene_std::vector::style::PaintOrder),
clone_out_node!(graphene_std::vector::style::PaintOrder), clone_node!(graphene_std::vector::style::PaintOrder),
frame_memo_node!(graphene_std::vector::style::PaintOrder), frame_memo_node!(graphene_std::vector::style::PaintOrder),
lend_node!(graphene_std::vector::style::GradientType), lend_node!(graphene_std::vector::style::GradientType),
clone_out_node!(graphene_std::vector::style::GradientType), clone_node!(graphene_std::vector::style::GradientType),
frame_memo_node!(graphene_std::vector::style::GradientType), frame_memo_node!(graphene_std::vector::style::GradientType),
lend_node!(graphene_std::vector::style::GradientSpreadMethod), lend_node!(graphene_std::vector::style::GradientSpreadMethod),
clone_out_node!(graphene_std::vector::style::GradientSpreadMethod), clone_node!(graphene_std::vector::style::GradientSpreadMethod),
frame_memo_node!(graphene_std::vector::style::GradientSpreadMethod), frame_memo_node!(graphene_std::vector::style::GradientSpreadMethod),
lend_node!(Option<DAffine2>), lend_node!(Option<DAffine2>),
clone_out_node!(Option<DAffine2>), clone_node!(Option<DAffine2>),
frame_memo_node!(Option<DAffine2>), frame_memo_node!(Option<DAffine2>),
lend_node!(graphene_std::transform::ReferencePoint), lend_node!(graphene_std::transform::ReferencePoint),
clone_out_node!(graphene_std::transform::ReferencePoint), clone_node!(graphene_std::transform::ReferencePoint),
frame_memo_node!(graphene_std::transform::ReferencePoint), frame_memo_node!(graphene_std::transform::ReferencePoint),
lend_node!(graphene_std::vector::misc::CentroidType), lend_node!(graphene_std::vector::misc::CentroidType),
clone_out_node!(graphene_std::vector::misc::CentroidType), clone_node!(graphene_std::vector::misc::CentroidType),
frame_memo_node!(graphene_std::vector::misc::CentroidType), frame_memo_node!(graphene_std::vector::misc::CentroidType),
lend_node!(graphene_std::vector::misc::BooleanOperation), lend_node!(graphene_std::vector::misc::BooleanOperation),
clone_out_node!(graphene_std::vector::misc::BooleanOperation), clone_node!(graphene_std::vector::misc::BooleanOperation),
frame_memo_node!(graphene_std::vector::misc::BooleanOperation), frame_memo_node!(graphene_std::vector::misc::BooleanOperation),
lend_node!(graphene_std::text::TextAlign), lend_node!(graphene_std::text::TextAlign),
clone_out_node!(graphene_std::text::TextAlign), clone_node!(graphene_std::text::TextAlign),
frame_memo_node!(graphene_std::text::TextAlign), frame_memo_node!(graphene_std::text::TextAlign),
lend_node!(graphene_std::transform::ScaleType), lend_node!(graphene_std::transform::ScaleType),
clone_out_node!(graphene_std::transform::ScaleType), clone_node!(graphene_std::transform::ScaleType),
frame_memo_node!(graphene_std::transform::ScaleType), frame_memo_node!(graphene_std::transform::ScaleType),
lend_node!(graphene_std::vector::misc::InterpolationDistribution), lend_node!(graphene_std::vector::misc::InterpolationDistribution),
clone_out_node!(graphene_std::vector::misc::InterpolationDistribution), clone_node!(graphene_std::vector::misc::InterpolationDistribution),
frame_memo_node!(graphene_std::vector::misc::InterpolationDistribution), frame_memo_node!(graphene_std::vector::misc::InterpolationDistribution),
lend_node!(RenderIntermediate), lend_node!(RenderIntermediate),
clone_out_node!(RenderIntermediate), clone_node!(RenderIntermediate),
frame_memo_node!(RenderIntermediate), frame_memo_node!(RenderIntermediate),
lend_node!(wgpu_executor::WgpuExecutorHandle), lend_node!(wgpu_executor::WgpuExecutorHandle),
clone_out_node!(wgpu_executor::WgpuExecutorHandle), clone_node!(wgpu_executor::WgpuExecutorHandle),
frame_memo_node!(wgpu_executor::WgpuExecutorHandle), frame_memo_node!(wgpu_executor::WgpuExecutorHandle),
lend_node!(Option<wgpu_executor::WgpuExecutorHandle>), lend_node!(Option<wgpu_executor::WgpuExecutorHandle>),
clone_out_node!(Option<wgpu_executor::WgpuExecutorHandle>), clone_node!(Option<wgpu_executor::WgpuExecutorHandle>),
frame_memo_node!(Option<wgpu_executor::WgpuExecutorHandle>), frame_memo_node!(Option<wgpu_executor::WgpuExecutorHandle>),
lend_node!(wgpu_executor::WgpuPipelineCache), lend_node!(wgpu_executor::WgpuPipelineCache),
clone_out_node!(wgpu_executor::WgpuPipelineCache), clone_node!(wgpu_executor::WgpuPipelineCache),
frame_memo_node!(wgpu_executor::WgpuPipelineCache), frame_memo_node!(wgpu_executor::WgpuPipelineCache),
]; ];
// ============= // =============
@@ -761,7 +761,7 @@ mod node_registry_macros {
return Err(ConstructionError::Arity { expected: 1, got: inputs.len() }); return Err(ConstructionError::Arity { expected: 1, got: inputs.len() });
} }
let mut inputs = inputs.into_iter(); let mut inputs = inputs.into_iter();
let node = graphene_core::memo::LendNode::<$type, _>::new(inputs.next().unwrap().downcast::<$type>()?); let node = graphene_core::memo::LendNode::new(inputs.next().unwrap().downcast::<$type>()?);
Ok(EdgeHandle::new_ref(std::sync::Arc::new(node) as std::sync::Arc<ErasedLendNode<$type>>)) Ok(EdgeHandle::new_ref(std::sync::Arc::new(node) as std::sync::Arc<ErasedLendNode<$type>>))
}, },
}, },
@@ -769,10 +769,10 @@ mod node_registry_macros {
}; };
} }
macro_rules! clone_out_node { macro_rules! clone_node {
($type:ty) => { ($type:ty) => {
( (
ProtoNodeIdentifier::new("graphene_core::memo::CloneOutNode"), ProtoNodeIdentifier::new("graphene_core::debug::CloneNode"),
RegistryEntry { RegistryEntry {
io: NodeIOTypes::new(concrete!(Context), concrete!($type), vec![lend_edge_type::<$type>()]), io: NodeIOTypes::new(concrete!(Context), concrete!($type), vec![lend_edge_type::<$type>()]),
constructor: |inputs| { constructor: |inputs| {
@@ -780,7 +780,7 @@ mod node_registry_macros {
return Err(ConstructionError::Arity { expected: 1, got: inputs.len() }); return Err(ConstructionError::Arity { expected: 1, got: inputs.len() });
} }
let mut inputs = inputs.into_iter(); let mut inputs = inputs.into_iter();
let node = graphene_core::memo::CloneOutNode::<$type, _>::new(inputs.next().unwrap().downcast_lend::<$type>()?); let node = graphene_core::debug::CloneNode::new(inputs.next().unwrap().downcast_lend::<$type>()?);
Ok(EdgeHandle::new(std::sync::Arc::new(node) as std::sync::Arc<ErasedNode<$type>>)) Ok(EdgeHandle::new(std::sync::Arc::new(node) as std::sync::Arc<ErasedNode<$type>>))
}, },
}, },
@@ -808,7 +808,7 @@ mod node_registry_macros {
} }
pub(crate) use async_node; pub(crate) use async_node;
pub(crate) use clone_out_node; pub(crate) use clone_node;
pub(crate) use convert_node; pub(crate) use convert_node;
pub(crate) use frame_memo_node; pub(crate) use frame_memo_node;
pub(crate) use into_node; pub(crate) use into_node;
+91 -6
View File
@@ -633,6 +633,27 @@ pub(crate) fn generate_node_impl(crate_ident: &CrateIdent, parsed: &ParsedNodeFn
]); ]);
} }
let has_lend = parsed.fields.iter().any(|field| matches!(&field.ty, ParsedFieldType::Regular(RegularParsedField { lend: Some(_), .. })));
let declared_arena_lifetime = ctx_param.and_then(|ctx_param| {
ctx_param.bounds.iter().find_map(|bound| {
let TypeParamBound::Trait(trait_bound) = bound else { return None };
let segment = trait_bound.path.segments.last()?;
if segment.ident != "ExtractArena" {
return None;
}
let PathArguments::AngleBracketed(args) = &segment.arguments else { return None };
match args.args.first() {
Some(GenericArgument::Lifetime(lifetime)) => Some(lifetime.clone()),
_ => None,
}
})
});
let introduced_lend_lifetime = (has_lend && declared_arena_lifetime.is_none()).then(|| Lifetime::new("'__lend", proc_macro2::Span::call_site()));
let lend_lifetime = declared_arena_lifetime.or_else(|| introduced_lend_lifetime.clone());
if let Some(lifetime) = &introduced_lend_lifetime {
ctx_bounds.push(quote!(#core_types::context::ExtractArena<ArenaRef = &#lifetime #core_types::arena::Arena>));
}
let derives = ctx_param.is_some_and(|ctx_param| { let derives = ctx_param.is_some_and(|ctx_param| {
ctx_param.bounds.iter().any(|bound| match bound { ctx_param.bounds.iter().any(|bound| match bound {
TypeParamBound::Trait(trait_bound) => trait_bound.path.segments.last().is_some_and(|segment| segment.ident == "DeriveCtx"), TypeParamBound::Trait(trait_bound) => trait_bound.path.segments.last().is_some_and(|segment| segment.ident == "DeriveCtx"),
@@ -655,6 +676,9 @@ pub(crate) fn generate_node_impl(crate_ident: &CrateIdent, parsed: &ParsedNodeFn
if ctx_param.is_none() { if ctx_param.is_none() {
generics.push(ctx_generic); generics.push(ctx_generic);
} }
if let Some(lifetime) = &introduced_lend_lifetime {
generics.insert(0, quote!(#lifetime));
}
let fn_name = &parsed.fn_name; let fn_name = &parsed.fn_name;
let mod_name = format_ident!("_{}_mod", parsed.mod_name); let mod_name = format_ident!("_{}_mod", parsed.mod_name);
@@ -702,6 +726,7 @@ pub(crate) fn generate_node_impl(crate_ident: &CrateIdent, parsed: &ParsedNodeFn
let kernel_params = regular_fields.iter().filter(|field| !injected_name(&field.pat_ident.ident)).map(|field| { let kernel_params = regular_fields.iter().filter(|field| !injected_name(&field.pat_ident.ident)).map(|field| {
let pat = &field.pat_ident; let pat = &field.pat_ident;
match &field.ty { match &field.ty {
ParsedFieldType::Regular(RegularParsedField { ty, lend: Some(_), .. }) => quote!(#pat: &#ty),
ParsedFieldType::Regular(RegularParsedField { ty, .. }) => quote!(#pat: #ty), ParsedFieldType::Regular(RegularParsedField { ty, .. }) => quote!(#pat: #ty),
ParsedFieldType::Node(NodeParsedField { output_type, .. }) if raw_lazy => { ParsedFieldType::Node(NodeParsedField { output_type, .. }) if raw_lazy => {
let bound = lazy_bound(output_type); let bound = lazy_bound(output_type);
@@ -715,6 +740,10 @@ pub(crate) fn generate_node_impl(crate_ident: &CrateIdent, parsed: &ParsedNodeFn
}); });
let node_bounds = regular_fields.iter().zip(&node_generics).map(|(field, node_generic)| match &field.ty { let node_bounds = regular_fields.iter().zip(&node_generics).map(|(field, node_generic)| match &field.ty {
ParsedFieldType::Regular(RegularParsedField { ty, lend: Some(_), .. }) => {
let lifetime = lend_lifetime.as_ref().expect("lend fields imply the lend lifetime");
quote!(#node_generic: #core_types::node::Node<#ctx_ident, Output = &#lifetime #ty>)
}
ParsedFieldType::Regular(RegularParsedField { ty, .. }) => quote!(#node_generic: #core_types::node::Node<#ctx_ident, Output = #ty>), ParsedFieldType::Regular(RegularParsedField { ty, .. }) => quote!(#node_generic: #core_types::node::Node<#ctx_ident, Output = #ty>),
ParsedFieldType::Node(NodeParsedField { output_type, .. }) => { ParsedFieldType::Node(NodeParsedField { output_type, .. }) => {
let bound = lazy_bound(output_type); let bound = lazy_bound(output_type);
@@ -722,6 +751,23 @@ pub(crate) fn generate_node_impl(crate_ident: &CrateIdent, parsed: &ParsedNodeFn
} }
}); });
let mut lend_outlives: Vec<TokenStream2> = regular_fields
.iter()
.filter_map(|field| match &field.ty {
ParsedFieldType::Regular(RegularParsedField { ty, lend: Some(_), .. }) => {
let lifetime = lend_lifetime.as_ref().expect("lend fields imply the lend lifetime");
Some(quote!(#ty: #lifetime))
}
_ => None,
})
.collect();
if let Type::Reference(reference) = &trait_output
&& let Some(lifetime) = &reference.lifetime
{
let inner = &reference.elem;
lend_outlives.push(quote!(#inner: #lifetime));
}
let mut async_bounds = match (async_fn, future_kernel) { let mut async_bounds = match (async_fn, future_kernel) {
(false, false) => Vec::new(), (false, false) => Vec::new(),
(false, true) => vec![quote!(#trait_output: Clone)], (false, true) => vec![quote!(#trait_output: Clone)],
@@ -1007,6 +1053,7 @@ pub(crate) fn generate_node_impl(crate_ident: &CrateIdent, parsed: &ParsedNodeFn
impl<#(#generics,)* #(#node_generics,)*> #core_types::node::Node<#ctx_ident> for #mod_name::#struct_name<#(#struct_type_params,)*> impl<#(#generics,)* #(#node_generics,)*> #core_types::node::Node<#ctx_ident> for #mod_name::#struct_name<#(#struct_type_params,)*>
where where
#(#node_bounds,)* #(#node_bounds,)*
#(#lend_outlives,)*
#(#clampable_bounds,)* #(#clampable_bounds,)*
#(#async_bounds,)* #(#async_bounds,)*
#(#where_predicates,)* #(#where_predicates,)*
@@ -1181,22 +1228,60 @@ fn entries_tokens(parsed: &ParsedNodeFn, struct_name: &Ident, data_field_generic
return quote!(); return quote!();
} }
let ref_output_inner = match slot_value_type(&parsed.output_type) {
Type::Reference(reference) => Some((*reference.elem).clone()),
_ => None,
};
if let Some(inner) = &ref_output_inner {
let ctx_ident = context_param(parsed).map(|ctx| ctx.ident.clone());
let open_generics = parsed.fn_generics.iter().filter_map(|param| match param {
GenericParam::Type(type_param) if Some(&type_param.ident) != ctx_ident.as_ref() => Some(&type_param.ident),
_ => None,
});
if open_generics.into_iter().any(|generic| type_contains_ident(inner, generic)) {
return quote!();
}
}
let fn_name = &parsed.fn_name; let fn_name = &parsed.fn_name;
let entries_name = format_ident!("{}_entries", fn_name); let entries_name = format_ident!("{}_entries", fn_name);
let arity = regular_fields.len(); let arity = regular_fields.len();
let names: Vec<&Ident> = regular_fields.iter().map(|field| &field.pat_ident.ident).collect(); let names: Vec<&Ident> = regular_fields.iter().map(|field| &field.pat_ident.ident).collect();
let lend_flags: Vec<bool> = regular_fields
.iter()
.map(|field| matches!(&field.ty, ParsedFieldType::Regular(RegularParsedField { lend: Some(_), .. })))
.collect();
let entries = rows.iter().map(|row| { let entries = rows.iter().map(|row| {
let types = row.iter(); let input_types = row.iter().zip(&lend_flags).map(|(ty, lend)| match lend {
let edge_types = row.iter().map(|ty| quote!(gcore::registry::SharedEdge<gcore::registry::ErasedNode<#ty>>)); true => quote!(gcore::registry::lend_edge_type::<#ty>()),
false => quote!(gcore::registry::edge_type::<#ty>()),
});
let edge_types = row.iter().zip(&lend_flags).map(|(ty, lend)| match lend {
true => quote!(gcore::registry::SharedEdge<gcore::registry::ErasedLendNode<#ty>>),
false => quote!(gcore::registry::SharedEdge<gcore::registry::ErasedNode<#ty>>),
});
let output = quote!(<#struct_name<#(#edge_types),*> as gcore::node::Node<gcore::context::ContextImpl<'static>>>::Output); let output = quote!(<#struct_name<#(#edge_types),*> as gcore::node::Node<gcore::context::ContextImpl<'static>>>::Output);
let downcasts = names.iter().zip(row.iter()).map(|(name, ty)| quote!(let #name = inputs.next().unwrap().downcast::<#ty>()?;)); let (io_output, construct) = match &ref_output_inner {
Some(inner) => (
quote!(gcore::registry::ref_type::<#inner>()),
quote!(Ok(gcore::registry::EdgeHandle::new_ref(::std::sync::Arc::new(#struct_name::new(#(#names),*)) as ::std::sync::Arc<gcore::registry::ErasedLendNode<#inner>>))),
),
None => (
quote!(gcore::concrete!(#output)),
quote!(Ok(gcore::registry::EdgeHandle::new(::std::sync::Arc::new(#struct_name::new(#(#names),*)) as ::std::sync::Arc<gcore::registry::ErasedNode<#output>>))),
),
};
let downcasts = names.iter().zip(row.iter()).zip(&lend_flags).map(|((name, ty), lend)| match lend {
true => quote!(let #name = inputs.next().unwrap().downcast_lend::<#ty>()?;),
false => quote!(let #name = inputs.next().unwrap().downcast::<#ty>()?;),
});
quote! { quote! {
gcore::registry::RegistryEntry { gcore::registry::RegistryEntry {
io: gcore::registry::NodeIOTypes::new( io: gcore::registry::NodeIOTypes::new(
gcore::concrete!(gcore::context::ContextImpl<'static>), gcore::concrete!(gcore::context::ContextImpl<'static>),
gcore::concrete!(#output), #io_output,
vec![#(gcore::registry::edge_type::<#types>()),*], vec![#(#input_types),*],
), ),
constructor: |inputs| { constructor: |inputs| {
if inputs.len() != #arity { if inputs.len() != #arity {
@@ -1204,7 +1289,7 @@ fn entries_tokens(parsed: &ParsedNodeFn, struct_name: &Ident, data_field_generic
} }
let mut inputs = inputs.into_iter(); let mut inputs = inputs.into_iter();
#(#downcasts)* #(#downcasts)*
Ok(gcore::registry::EdgeHandle::new(::std::sync::Arc::new(#struct_name::new(#(#names),*)) as ::std::sync::Arc<gcore::registry::ErasedNode<#output>>)) #construct
}, },
} }
} }
+15
View File
@@ -240,6 +240,8 @@ impl Parse for NumberRange {
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct RegularParsedField { pub struct RegularParsedField {
pub ty: Type, pub ty: Type,
/// The original reference tokens when the parameter was written `&T`; `ty` holds the peeled inner type.
pub lend: Option<syn::TypeReference>,
pub exposed: bool, pub exposed: bool,
pub value_source: ParsedValueSource, pub value_source: ParsedValueSource,
pub number_soft_min: Option<NumberBound>, pub number_soft_min: Option<NumberBound>,
@@ -904,6 +906,11 @@ fn parse_field(pat_ident: PatIdent, ty: Type, attrs: &[Attribute]) -> syn::Resul
.transpose()? .transpose()?
.unwrap_or_default(); .unwrap_or_default();
let (ty, lend) = match ty {
Type::Reference(reference) => ((*reference.elem).clone(), Some(reference)),
ty => (ty, None),
};
// Error if a float literal is given for a bound on an integer-typed field // Error if a float literal is given for a bound on an integer-typed field
if is_integer_type(&ty) { if is_integer_type(&ty) {
let bound_attrs = [ let bound_attrs = [
@@ -936,6 +943,7 @@ fn parse_field(pat_ident: PatIdent, ty: Type, attrs: &[Attribute]) -> syn::Resul
number_hard_max, number_hard_max,
number_mode_range, number_mode_range,
ty, ty,
lend,
value_source, value_source,
implementations, implementations,
gpu_image, gpu_image,
@@ -1048,6 +1056,7 @@ impl ParsedNodeFn {
widget_override: ParsedWidgetOverride::Hidden, widget_override: ParsedWidgetOverride::Hidden,
ty: ParsedFieldType::Regular(RegularParsedField { ty: ParsedFieldType::Regular(RegularParsedField {
ty, ty,
lend: None,
exposed: false, exposed: false,
value_source, value_source,
number_soft_min: None, number_soft_min: None,
@@ -1218,6 +1227,7 @@ mod tests {
description: String::new(), description: String::new(),
widget_override: ParsedWidgetOverride::None, widget_override: ParsedWidgetOverride::None,
ty: ParsedFieldType::Regular(RegularParsedField { ty: ParsedFieldType::Regular(RegularParsedField {
lend: None,
ty: parse_quote!(f64), ty: parse_quote!(f64),
exposed: false, exposed: false,
value_source: ParsedValueSource::None, value_source: ParsedValueSource::None,
@@ -1308,6 +1318,7 @@ mod tests {
description: String::new(), description: String::new(),
widget_override: ParsedWidgetOverride::None, widget_override: ParsedWidgetOverride::None,
ty: ParsedFieldType::Regular(RegularParsedField { ty: ParsedFieldType::Regular(RegularParsedField {
lend: None,
ty: parse_quote!(DVec2), ty: parse_quote!(DVec2),
exposed: false, exposed: false,
value_source: ParsedValueSource::None, value_source: ParsedValueSource::None,
@@ -1380,6 +1391,7 @@ mod tests {
description: String::new(), description: String::new(),
widget_override: ParsedWidgetOverride::None, widget_override: ParsedWidgetOverride::None,
ty: ParsedFieldType::Regular(RegularParsedField { ty: ParsedFieldType::Regular(RegularParsedField {
lend: None,
ty: parse_quote!(f64), ty: parse_quote!(f64),
exposed: false, exposed: false,
value_source: ParsedValueSource::Default(quote!(50.)), value_source: ParsedValueSource::Default(quote!(50.)),
@@ -1450,6 +1462,7 @@ mod tests {
description: String::new(), description: String::new(),
widget_override: ParsedWidgetOverride::None, widget_override: ParsedWidgetOverride::None,
ty: ParsedFieldType::Regular(RegularParsedField { ty: ParsedFieldType::Regular(RegularParsedField {
lend: None,
ty: parse_quote!(f64), ty: parse_quote!(f64),
exposed: false, exposed: false,
value_source: ParsedValueSource::None, value_source: ParsedValueSource::None,
@@ -1532,6 +1545,7 @@ mod tests {
description: String::from("b"), description: String::from("b"),
widget_override: ParsedWidgetOverride::None, widget_override: ParsedWidgetOverride::None,
ty: ParsedFieldType::Regular(RegularParsedField { ty: ParsedFieldType::Regular(RegularParsedField {
lend: None,
ty: parse_quote!(f64), ty: parse_quote!(f64),
exposed: false, exposed: false,
value_source: ParsedValueSource::None, value_source: ParsedValueSource::None,
@@ -1617,6 +1631,7 @@ mod tests {
description: String::new(), description: String::new(),
widget_override: ParsedWidgetOverride::None, widget_override: ParsedWidgetOverride::None,
ty: ParsedFieldType::Regular(RegularParsedField { ty: ParsedFieldType::Regular(RegularParsedField {
lend: None,
ty: parse_quote!(String), ty: parse_quote!(String),
exposed: true, exposed: true,
value_source: ParsedValueSource::None, value_source: ParsedValueSource::None,
@@ -232,6 +232,7 @@ impl PerPixelAdjustCodegen<'_> {
widget_override: Default::default(), widget_override: Default::default(),
ty: ParsedFieldType::Regular(RegularParsedField { ty: ParsedFieldType::Regular(RegularParsedField {
ty: parse_quote!(#wgpu_executor::WgpuExecutorHandle), ty: parse_quote!(#wgpu_executor::WgpuExecutorHandle),
lend: None,
exposed: true, exposed: true,
value_source: ParsedValueSource::Scope(Box::new(parse_quote!("graphene_std::platform_application_io::WgpuExecutorNode"))), value_source: ParsedValueSource::Scope(Box::new(parse_quote!("graphene_std::platform_application_io::WgpuExecutorNode"))),
number_soft_min: None, number_soft_min: None,
+25
View File
@@ -12,6 +12,7 @@ pub fn validate_node_fn(parsed: &ParsedNodeFn) -> syn::Result<()> {
validate_min_max, validate_min_max,
validate_range_slider_bounds, validate_range_slider_bounds,
validate_async_source, validate_async_source,
validate_lend_fields,
]; ];
for validator in validators { for validator in validators {
@@ -63,6 +64,30 @@ fn validate_async_source(parsed: &ParsedNodeFn) {
} }
} }
fn validate_lend_fields(parsed: &ParsedNodeFn) {
let future_kernel = crate::codegen::is_source_kernel(&parsed.output_type);
for field in &parsed.fields {
let ParsedFieldType::Regular(RegularParsedField { lend: Some(reference), .. }) = &field.ty else {
continue;
};
if let Some(mutability) = &reference.mutability {
emit_error!(mutability.span(), "reference parameters are read-only lends; `&mut` is not supported");
}
if let Some(lifetime) = &reference.lifetime {
emit_error!(lifetime.span(), "reference parameters use the eval lifetime implicitly; write a bare `&T`");
}
if field.is_data_field {
emit_error!(field.pat_ident.span(), "`#[data]` fields are node-resident state and cannot be references");
}
if parsed.is_async || future_kernel {
emit_error!(
field.pat_ident.span(),
"source kernels move their inputs into the spawned task, so they cannot take reference parameters"
);
}
}
}
fn validate_min_max(parsed: &ParsedNodeFn) { fn validate_min_max(parsed: &ParsedNodeFn) {
for field in &parsed.fields { for field in &parsed.fields {
if let ParsedField { if let ParsedField {
+2 -2
View File
@@ -29,8 +29,8 @@ fn unwrap_option<T: Default>(_: impl Ctx, #[implementations(Option<f64>, Option<
input.unwrap_or_default() input.unwrap_or_default()
} }
/// Meant for debugging purposes, not general use. Clones the input value. /// Clones the value borrowed from a lending edge. Doubles as the checker-inserted clone-out adapter.
#[node_macro::node(category("Debug"))] #[node_macro::node(category("Debug"))]
fn clone<'i, T: Clone + 'i>(_: impl Ctx, #[implementations(&List<Raster<CPU>>)] value: &'i T) -> T { fn clone<T: Clone>(_: impl Ctx, #[implementations(List<Raster<CPU>>)] value: &T) -> T {
value.clone() value.clone()
} }
+3 -69
View File
@@ -103,75 +103,9 @@ pub fn park<T: Send + Sync>(arena: &Arena, result: GPoll<T>) -> GPoll<&T> {
} }
/// Adapts an owned edge to a lending one by parking each result in the eval arena. /// Adapts an owned edge to a lending one by parking each result in the eval arena.
pub struct LendNode<T, NodeContent> { #[node_macro::node(category(""), path(graphene_core::memo), skip_impl)]
content: NodeContent, fn lend<'e, T: Send + Sync>(ctx: impl Ctx + ExtractArena<'e>, value: T) -> GPoll<&'e T> {
_value: std::marker::PhantomData<fn() -> T>, park(ctx.arena(), GPoll::Final(value))
}
impl<T, NodeContent> LendNode<T, NodeContent> {
pub fn new(content: NodeContent) -> Self {
Self {
content,
_value: std::marker::PhantomData,
}
}
}
impl<'e, Input, T, NodeContent> Node<Input> for LendNode<T, NodeContent>
where
Input: Ctx + ExtractArena<ArenaRef = &'e Arena>,
T: Send + Sync + 'e,
NodeContent: Node<Input, Output = T>,
{
type Output = &'e T;
fn eval(&self, input: &Input) -> GPoll<&'e T> {
park(input.arena(), self.content.eval(input))
}
fn extent(&self, input: &Input) -> GPoll<Extent> {
self.content.extent(input)
}
fn serialize(&self) -> Option<Arc<dyn std::any::Any + Send + Sync>> {
self.content.serialize()
}
}
/// Adapts a lending edge to an owned one by cloning the borrowed value out.
pub struct CloneOutNode<T, NodeContent> {
content: NodeContent,
_value: std::marker::PhantomData<fn() -> T>,
}
impl<T, NodeContent> CloneOutNode<T, NodeContent> {
pub fn new(content: NodeContent) -> Self {
Self {
content,
_value: std::marker::PhantomData,
}
}
}
impl<'e, Input, T, NodeContent> Node<Input> for CloneOutNode<T, NodeContent>
where
Input: Ctx + ExtractArena<ArenaRef = &'e Arena>,
T: Clone + 'e,
NodeContent: Node<Input, Output = &'e T>,
{
type Output = T;
fn eval(&self, input: &Input) -> GPoll<T> {
self.content.eval(input).map(Clone::clone)
}
fn extent(&self, input: &Input) -> GPoll<Extent> {
self.content.extent(input)
}
fn serialize(&self) -> Option<Arc<dyn std::any::Any + Send + Sync>> {
self.content.serialize()
}
} }
type MonitorValue<T> = Arc<Mutex<Option<Arc<IORecord<CtxSnapshot, T>>>>>; type MonitorValue<T> = Arc<Mutex<Option<Arc<IORecord<CtxSnapshot, T>>>>>;
@@ -6,7 +6,7 @@ use raster_types::{CPU, Raster};
#[node_macro::node(category("Color"))] #[node_macro::node(category("Color"))]
fn image_color_palette( fn image_color_palette(
_: impl Ctx, _: impl Ctx,
image: List<Raster<CPU>>, image: &List<Raster<CPU>>,
#[default(4)] #[default(4)]
#[hard(1..)] #[hard(1..)]
count: u32, count: u32,