Fix wireing of async sources

This commit is contained in:
Dennis Kobert
2026-08-02 13:55:53 +02:00
parent 77d439ec35
commit e3aa32bc83
6 changed files with 75 additions and 33 deletions

View File

@@ -189,11 +189,16 @@ fn node_registry() -> HashMap<ProtoNodeIdentifier, Vec<RegistryEntry>> {
// Context nullification
#[cfg(feature = "gpu")]
async_node!(graphene_core::context_modification::ContextModificationNode<_, _>, input: Context, fn_params: [Context => std::sync::Arc<PlatformEditorApi>, Context => graphene_std::ContextModification]),
async_node!(graphene_core::context_modification::ContextModificationNode<_, _>, input: Context, fn_params: [Context => RuntimeHandle, Context => graphene_std::ContextModification]),
async_node!(graphene_core::context_modification::ContextModificationNode<_, _>, input: Context, fn_params: [Context => SourceId, Context => graphene_std::ContextModification]),
async_node!(graphene_core::context_modification::ContextModificationNode<_, _>, input: Context, fn_params: [Context => RenderIntermediate, Context => graphene_std::ContextModification]),
async_node!(graphene_core::context_modification::ContextModificationNode<_, _>, input: Context, fn_params: [Context => RenderOutput, Context => graphene_std::ContextModification]),
async_node!(graphene_core::context_modification::ContextModificationNode<_, _>, input: Context, fn_params: [Context => AttributeDyn, Context => graphene_std::ContextModification]),
async_node!(graphene_core::context_modification::ContextModificationNode<_, _>, input: Context, fn_params: [Context => AttributeValueDyn, Context => graphene_std::ContextModification]),
async_node!(graphene_core::context_modification::ContextModificationNode<_, _>, input: Context, fn_params: [Context => ListDyn, Context => graphene_std::ContextModification]),
async_node!(graphene_core::context_modification::ContextModificationNode<_, _>, input: Context, fn_params: [Context => graphene_std::vector::style::GradientType, Context => graphene_std::ContextModification]),
async_node!(graphene_core::context_modification::ContextModificationNode<_, _>, input: Context, fn_params: [Context => graphene_std::vector::style::GradientSpreadMethod, Context => graphene_std::ContextModification]),
async_node!(graphene_core::context_modification::ContextModificationNode<_, _>, input: Context, fn_params: [Context => Option<DAffine2>, Context => graphene_std::ContextModification]),
#[cfg(target_family = "wasm")]
async_node!(graphene_core::context_modification::ContextModificationNode<_, _>, input: Context, fn_params: [Context => CanvasHandle, Context => graphene_std::ContextModification]),
async_node!(graphene_core::context_modification::ContextModificationNode<_, _>, input: Context, fn_params: [Context => std::sync::Arc<PlatformEditorApi>, Context => graphene_std::ContextModification]),
@@ -204,6 +209,8 @@ fn node_registry() -> HashMap<ProtoNodeIdentifier, Vec<RegistryEntry>> {
// MEMO NODES
// ==========
async_node!(graphene_core::memo::MemoizeNode<_, _>, input: Context, fn_params: [Context => ()]),
async_node!(graphene_core::memo::MemoizeNode<_, _>, input: Context, fn_params: [Context => RuntimeHandle]),
async_node!(graphene_core::memo::MemoizeNode<_, _>, input: Context, fn_params: [Context => SourceId]),
async_node!(graphene_core::memo::MemoizeNode<_, _>, input: Context, fn_params: [Context => bool]),
async_node!(graphene_core::memo::MemoizeNode<_, _>, input: Context, fn_params: [Context => List<Artboard>]),
async_node!(graphene_core::memo::MemoizeNode<_, _>, input: Context, fn_params: [Context => List<Graphic>]),
@@ -276,6 +283,8 @@ fn node_registry() -> HashMap<ProtoNodeIdentifier, Vec<RegistryEntry>> {
async_node!(graphene_core::memo::MemoizeNode<_, _>, input: Context, fn_params: [Context => graphene_std::vector::style::StrokeAlign]),
async_node!(graphene_core::memo::MemoizeNode<_, _>, input: Context, fn_params: [Context => graphene_std::vector::style::PaintOrder]),
async_node!(graphene_core::memo::MemoizeNode<_, _>, input: Context, fn_params: [Context => graphene_std::vector::style::GradientType]),
async_node!(graphene_core::memo::MemoizeNode<_, _>, input: Context, fn_params: [Context => graphene_std::vector::style::GradientSpreadMethod]),
async_node!(graphene_core::memo::MemoizeNode<_, _>, input: Context, fn_params: [Context => Option<DAffine2>]),
async_node!(graphene_core::memo::MemoizeNode<_, _>, input: Context, fn_params: [Context => graphene_std::transform::ReferencePoint]),
async_node!(graphene_core::memo::MemoizeNode<_, _>, input: Context, fn_params: [Context => graphene_std::vector::misc::CentroidType]),
async_node!(graphene_core::memo::MemoizeNode<_, _>, input: Context, fn_params: [Context => graphene_std::vector::misc::BooleanOperation]),

View File

@@ -59,12 +59,15 @@ impl<S: Spawner + ?Sized> Spawner for Box<S> {
}
}
/// Dropped tasks never complete.
/// Polls each task once inline. Tasks that are not immediately ready never complete.
pub struct NoopSpawner;
impl Spawner for NoopSpawner {
fn spawn(&self, _task: SourceFuture) {
log::warn!("async source spawned before a host spawner is wired; the task is dropped");
fn spawn(&self, mut task: SourceFuture) {
let mut context = std::task::Context::from_waker(std::task::Waker::noop());
if task.as_mut().poll(&mut context).is_pending() {
log::warn!("async source is not immediately ready and no host spawner is wired; the task is dropped");
}
}
}

View File

@@ -131,16 +131,38 @@ impl Preprocessor {
input_type = &const { generic!(D) };
}
let inputs: Vec<_> = node_inputs(fields, first_node_io);
let mut inputs: Vec<_> = node_inputs(fields, first_node_io);
let input_count = inputs.len();
let network_inputs = (0..input_count).map(|i| NodeInput::node(NodeId(i as u64), 0)).collect();
// The node macro appends a `RuntimeHandle` scope field and a `SourceId` reflection field to every
// async or source kernel. They are resolved inside the substitution network, so the wrapper neither
// exposes them nor carries their context modification.
let injected_field_count = match &fields[..] {
[.., runtime, source] if matches!(runtime.value_source, RegistryValueSource::Scope(_)) && matches!(source.value_source, RegistryValueSource::SourceId) => 2,
_ => 0,
};
let wrapper_input_count = input_count - injected_field_count;
inputs.truncate(wrapper_input_count);
// The injected fields go straight onto the inner node, so the source it reflects is recorded on the
// node that consumes it rather than on a forwarding node that later gets dissolved.
let network_inputs = (0..input_count)
.map(|i| {
if i < wrapper_input_count {
NodeInput::node(NodeId(i as u64), 0)
} else {
injected_field_input(&fields[i])
}
})
.collect();
let passthrough_node = ops::passthrough::IDENTIFIER;
let mut generated_nodes = 0;
let mut nodes: HashMap<_, _, _> = node_io_types
.iter()
.take(input_count)
.take(wrapper_input_count)
.enumerate()
.map(|(i, inputs)| {
(
@@ -185,7 +207,7 @@ impl Preprocessor {
})
.collect();
if generated_nodes == 0 && !memoize && !inject_scope {
if generated_nodes == 0 && !memoize && !inject_scope && injected_field_count == 0 {
continue;
}
@@ -199,13 +221,14 @@ impl Preprocessor {
..Default::default()
};
nodes.insert(NodeId(input_count as u64), document_node);
let main_node_id = NodeId(wrapper_input_count as u64);
nodes.insert(main_node_id, document_node);
// If memoize is requested, append a Memoize node after the main node and redirect the export through it
let export_node_id = if *memoize {
let memoize_node_id = NodeId(input_count as u64 + 1);
let memoize_node_id = NodeId(wrapper_input_count as u64 + 1);
let memoize_node = DocumentNode {
inputs: vec![NodeInput::node(NodeId(input_count as u64), 0)],
inputs: vec![NodeInput::node(main_node_id, 0)],
implementation: DocumentNodeImplementation::ProtoNode(graphene_core::memo::memoize::IDENTIFIER.clone()),
visible: true,
..Default::default()
@@ -213,7 +236,7 @@ impl Preprocessor {
nodes.insert(memoize_node_id, memoize_node);
memoize_node_id
} else {
NodeId(input_count as u64)
main_node_id
};
let node = DocumentNode {
@@ -256,6 +279,14 @@ impl Preprocessor {
}
}
fn injected_field_input(field: &registry::FieldMetadata) -> NodeInput {
match field.value_source {
RegistryValueSource::Scope(data) => NodeInput::scope(data),
RegistryValueSource::SourceId => NodeInput::Reflection(DocumentNodeMetadata::SourceId),
_ => NodeInput::value(TaggedValue::None, false),
}
}
pub fn node_inputs(fields: &[registry::FieldMetadata], first_node_io: &NodeIOTypes) -> Vec<NodeInput> {
fields
.iter()