mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-27 19:48:12 +08:00
Fix wireing of async sources
This commit is contained in:
@@ -167,6 +167,11 @@ impl NodeRuntime {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
pub fn take_dirty(&self) -> bool {
|
||||||
|
self.executor.take_dirty()
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn run(&mut self) -> Option<Texture> {
|
pub async fn run(&mut self) -> Option<Texture> {
|
||||||
let mut preferences = None;
|
let mut preferences = None;
|
||||||
let mut graph = None;
|
let mut graph = None;
|
||||||
|
|||||||
@@ -53,6 +53,22 @@ impl EditorTestUtils {
|
|||||||
}
|
}
|
||||||
runtime.run().await;
|
runtime.run().await;
|
||||||
|
|
||||||
|
// An async source reports `Pending` on the evaluation that starts it and marks the runtime dirty
|
||||||
|
// once it completes, so the value only reaches the render on a follow-up evaluation. That first
|
||||||
|
// response is superseded, so it is drained rather than asserted on.
|
||||||
|
while runtime.take_dirty() {
|
||||||
|
let _ = editor.poll_node_graph_evaluation(&mut VecDeque::new());
|
||||||
|
|
||||||
|
let portfolio = &mut editor.dispatcher.message_handlers.portfolio_message_handler;
|
||||||
|
let (executor, documents) = (&mut portfolio.executor, &mut portfolio.documents);
|
||||||
|
let document = documents.get_mut(&document_id).unwrap();
|
||||||
|
|
||||||
|
if let Err(e) = executor.submit_current_node_graph_evaluation(document, document_id, UVec2::ONE, 1., Default::default(), DVec2::ZERO) {
|
||||||
|
return Err(format!("submit_current_node_graph_evaluation failed\n\n{e}"));
|
||||||
|
}
|
||||||
|
runtime.run().await;
|
||||||
|
}
|
||||||
|
|
||||||
let mut messages = VecDeque::new();
|
let mut messages = VecDeque::new();
|
||||||
if let Err(e) = editor.poll_node_graph_evaluation(&mut messages) {
|
if let Err(e) = editor.poll_node_graph_evaluation(&mut messages) {
|
||||||
return Err(format!("Graph should render\n\n{e}"));
|
return Err(format!("Graph should render\n\n{e}"));
|
||||||
|
|||||||
@@ -189,11 +189,16 @@ fn node_registry() -> HashMap<ProtoNodeIdentifier, Vec<RegistryEntry>> {
|
|||||||
// Context nullification
|
// Context nullification
|
||||||
#[cfg(feature = "gpu")]
|
#[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 => 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 => 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 => 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 => 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 => 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 => 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")]
|
#[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 => 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]),
|
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
|
// MEMO NODES
|
||||||
// ==========
|
// ==========
|
||||||
async_node!(graphene_core::memo::MemoizeNode<_, _>, input: Context, fn_params: [Context => ()]),
|
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 => 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<Artboard>]),
|
||||||
async_node!(graphene_core::memo::MemoizeNode<_, _>, input: Context, fn_params: [Context => List<Graphic>]),
|
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::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::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::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::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::CentroidType]),
|
||||||
async_node!(graphene_core::memo::MemoizeNode<_, _>, input: Context, fn_params: [Context => graphene_std::vector::misc::BooleanOperation]),
|
async_node!(graphene_core::memo::MemoizeNode<_, _>, input: Context, fn_params: [Context => graphene_std::vector::misc::BooleanOperation]),
|
||||||
|
|||||||
@@ -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;
|
pub struct NoopSpawner;
|
||||||
|
|
||||||
impl Spawner for NoopSpawner {
|
impl Spawner for NoopSpawner {
|
||||||
fn spawn(&self, _task: SourceFuture) {
|
fn spawn(&self, mut task: SourceFuture) {
|
||||||
log::warn!("async source spawned before a host spawner is wired; the task is dropped");
|
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");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -131,16 +131,38 @@ impl Preprocessor {
|
|||||||
input_type = &const { generic!(D) };
|
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 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 passthrough_node = ops::passthrough::IDENTIFIER;
|
||||||
|
|
||||||
let mut generated_nodes = 0;
|
let mut generated_nodes = 0;
|
||||||
let mut nodes: HashMap<_, _, _> = node_io_types
|
let mut nodes: HashMap<_, _, _> = node_io_types
|
||||||
.iter()
|
.iter()
|
||||||
.take(input_count)
|
.take(wrapper_input_count)
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.map(|(i, inputs)| {
|
.map(|(i, inputs)| {
|
||||||
(
|
(
|
||||||
@@ -185,7 +207,7 @@ impl Preprocessor {
|
|||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
if generated_nodes == 0 && !memoize && !inject_scope {
|
if generated_nodes == 0 && !memoize && !inject_scope && injected_field_count == 0 {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -199,13 +221,14 @@ impl Preprocessor {
|
|||||||
..Default::default()
|
..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
|
// 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 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 {
|
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()),
|
implementation: DocumentNodeImplementation::ProtoNode(graphene_core::memo::memoize::IDENTIFIER.clone()),
|
||||||
visible: true,
|
visible: true,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
@@ -213,7 +236,7 @@ impl Preprocessor {
|
|||||||
nodes.insert(memoize_node_id, memoize_node);
|
nodes.insert(memoize_node_id, memoize_node);
|
||||||
memoize_node_id
|
memoize_node_id
|
||||||
} else {
|
} else {
|
||||||
NodeId(input_count as u64)
|
main_node_id
|
||||||
};
|
};
|
||||||
|
|
||||||
let node = DocumentNode {
|
let node = DocumentNode {
|
||||||
@@ -256,6 +279,14 @@ impl Preprocessor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn injected_field_input(field: ®istry::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> {
|
pub fn node_inputs(fields: &[registry::FieldMetadata], first_node_io: &NodeIOTypes) -> Vec<NodeInput> {
|
||||||
fields
|
fields
|
||||||
.iter()
|
.iter()
|
||||||
|
|||||||
@@ -1,22 +0,0 @@
|
|||||||
trait Attr {
|
|
||||||
fn name() -> &'static str
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Attr for bool {
|
|
||||||
fn name() {"condition"}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Opacity(f64);
|
|
||||||
|
|
||||||
impl Attr for Opacity {
|
|
||||||
fn name() {"opacity"}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#[node]
|
|
||||||
fn opacity<T>(_: impl Ctx, input: T, x: ReadAttr<bool>, opacity: WriteAttr<Opacity>) -> T {
|
|
||||||
if x {
|
|
||||||
*opacity = 1.;
|
|
||||||
}
|
|
||||||
input
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user