mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-27 03:58:12 +08:00
Cut over to the graphene execution model
This commit is contained in:
@@ -14,7 +14,7 @@ use graphene_std::memo::IORecord;
|
||||
use graphene_std::raster_types::{CPU, GPU, Raster};
|
||||
use graphene_std::vector::Vector;
|
||||
use graphene_std::vector::style::{FillChoice, FillChoiceUI, GradientSpreadMethod, GradientType};
|
||||
use graphene_std::{Artboard, Color, Context, Graphic};
|
||||
use graphene_std::{Artboard, Color, CtxSnapshot, Graphic};
|
||||
use std::any::Any;
|
||||
use std::sync::Arc;
|
||||
|
||||
@@ -167,7 +167,7 @@ macro_rules! generate_layout_downcast {
|
||||
($introspected_data:expr, $data:expr, [ $($ty:ty),* $(,)? ]) => {
|
||||
if false { None }
|
||||
$(
|
||||
else if let Some(io) = $introspected_data.downcast_ref::<IORecord<Context, $ty>>() {
|
||||
else if let Some(io) = $introspected_data.downcast_ref::<IORecord<CtxSnapshot, $ty>>() {
|
||||
Some(io.output.layout_with_breadcrumb($data))
|
||||
}
|
||||
)*
|
||||
@@ -178,7 +178,7 @@ macro_rules! generate_layout_downcast {
|
||||
fn generate_layout(introspected_data: &Arc<dyn std::any::Any + Send + Sync + 'static>, data: &mut LayoutData) -> Option<Vec<LayoutGroup>> {
|
||||
// `List<NodeId>` is interpreted as a path (e.g. the value produced by `path_of_subgraph`), shown as a
|
||||
// `List` where each item's NodeId resolves against the prefix made up of the items above it.
|
||||
if let Some(io) = introspected_data.downcast_ref::<IORecord<Context, List<NodeId>>>() {
|
||||
if let Some(io) = introspected_data.downcast_ref::<IORecord<CtxSnapshot, List<NodeId>>>() {
|
||||
return Some(table_node_id_path_layout_with_breadcrumb(&io.output, data));
|
||||
}
|
||||
generate_layout_downcast!(introspected_data, data, [
|
||||
|
||||
@@ -919,7 +919,12 @@ fn document_node_definitions() -> HashMap<DefinitionIdentifier, DocumentNodeDefi
|
||||
exports: vec![NodeInput::node(NodeId(1), 0)],
|
||||
nodes: [
|
||||
DocumentNode {
|
||||
inputs: vec![NodeInput::value(TaggedValue::None, false), NodeInput::import(concrete!(String), 1)],
|
||||
inputs: vec![
|
||||
NodeInput::value(TaggedValue::None, false),
|
||||
NodeInput::import(concrete!(String), 1),
|
||||
NodeInput::scope("graphene_std::runtime::RuntimeNode"),
|
||||
NodeInput::Reflection(graph_craft::document::DocumentNodeMetadata::SourceId),
|
||||
],
|
||||
implementation: DocumentNodeImplementation::ProtoNode(platform_application_io::load_resource::IDENTIFIER),
|
||||
..Default::default()
|
||||
},
|
||||
@@ -994,7 +999,13 @@ fn document_node_definitions() -> HashMap<DefinitionIdentifier, DocumentNodeDefi
|
||||
..Default::default()
|
||||
},
|
||||
DocumentNode {
|
||||
inputs: vec![NodeInput::import(generic!(T), 0), NodeInput::import(concrete!(Footprint), 1), NodeInput::node(NodeId(1), 0)],
|
||||
inputs: vec![
|
||||
NodeInput::import(generic!(T), 0),
|
||||
NodeInput::import(concrete!(Footprint), 1),
|
||||
NodeInput::node(NodeId(1), 0),
|
||||
NodeInput::scope("graphene_std::runtime::RuntimeNode"),
|
||||
NodeInput::Reflection(graph_craft::document::DocumentNodeMetadata::SourceId),
|
||||
],
|
||||
implementation: DocumentNodeImplementation::ProtoNode(platform_application_io::rasterize::IDENTIFIER),
|
||||
..Default::default()
|
||||
},
|
||||
|
||||
+4
-4
@@ -29,7 +29,7 @@ pub(super) fn post_process_nodes(custom: Vec<DocumentNodeDefinition>) -> HashMap
|
||||
// fallback when deriving `call_argument` so it reflects the impls actually registered, which will usually be `Context`.
|
||||
let extended_node_registry = &*interpreted_executor::node_registry::NODE_REGISTRY;
|
||||
let node_registry = NODE_REGISTRY.lock().unwrap();
|
||||
let empty_implementations: Vec<(NodeConstructor, NodeIOTypes)> = Vec::new();
|
||||
let empty_implementations: Vec<RegistryEntry> = Vec::new();
|
||||
let context_type = concrete!(Context);
|
||||
for (id, metadata) in NODE_METADATA.lock().unwrap().iter() {
|
||||
let identifier = DefinitionIdentifier::ProtoNode(id.clone());
|
||||
@@ -48,12 +48,12 @@ pub(super) fn post_process_nodes(custom: Vec<DocumentNodeDefinition>) -> HashMap
|
||||
|
||||
let implementations = node_registry.get(id).unwrap_or(&empty_implementations);
|
||||
|
||||
let first_node_io = implementations.first().map(|(_, node_io)| node_io).unwrap_or(const { &NodeIOTypes::empty() });
|
||||
let first_node_io = implementations.first().map(|entry| &entry.io).unwrap_or(const { &NodeIOTypes::empty() });
|
||||
|
||||
let call_arguments: Vec<&Type> = if !implementations.is_empty() {
|
||||
implementations.iter().map(|(_, io)| &io.call_argument).collect()
|
||||
implementations.iter().map(|entry| &entry.io.call_argument).collect()
|
||||
} else if let Some(impls) = extended_node_registry.get(id) {
|
||||
impls.keys().map(|io| &io.call_argument).collect()
|
||||
impls.iter().map(|entry| &entry.io.call_argument).collect()
|
||||
} else {
|
||||
Vec::new()
|
||||
};
|
||||
|
||||
@@ -2360,7 +2360,7 @@ pub(crate) fn generate_node_properties(node_id: NodeId, context: &mut NodeProper
|
||||
return Vec::new();
|
||||
};
|
||||
|
||||
let mut input_types = implementations.keys().filter_map(|item| item.inputs.get(input_index)).collect::<Vec<_>>();
|
||||
let mut input_types = implementations.iter().filter_map(|entry| entry.io.inputs.get(input_index)).collect::<Vec<_>>();
|
||||
input_types.sort_by_key(|ty| ty.type_name());
|
||||
let input_type = input_types.first().cloned();
|
||||
|
||||
|
||||
@@ -95,6 +95,34 @@ impl NodeNetworkInterface {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Append the hidden runtime and source-id inputs to async-source protonodes saved before their injection.
|
||||
/// Runs after the identifier replacement pass, so it matches only current identifier spellings.
|
||||
pub fn migrate_async_source_inputs(&mut self) {
|
||||
const PRE_INJECTION_ARITIES: [(&str, usize); 5] = [
|
||||
("graphene_std::platform_application_io::GetRequestNode", 4),
|
||||
("graphene_std::platform_application_io::PostRequestNode", 5),
|
||||
("graphene_std::platform_application_io::LoadResourceNode", 2),
|
||||
("graphene_std::platform_application_io::RasterizeNode", 3),
|
||||
("graphene_std::platform_application_io::ResourceNode", 2),
|
||||
];
|
||||
fix_network(self.document_network_mut());
|
||||
fn fix_network(network: &mut NodeNetwork) {
|
||||
for node in network.nodes.values_mut() {
|
||||
if let Some(network) = node.implementation.get_network_mut() {
|
||||
fix_network(network);
|
||||
}
|
||||
if let DocumentNodeImplementation::ProtoNode(protonode) = &node.implementation
|
||||
&& let Some(base) = protonode.as_str().split('<').next()
|
||||
&& let Some((_, arity)) = PRE_INJECTION_ARITIES.iter().find(|(identifier, _)| *identifier == base)
|
||||
&& node.inputs.len() == *arity
|
||||
{
|
||||
node.inputs.push(NodeInput::scope("graphene_std::runtime::RuntimeNode"));
|
||||
node.inputs.push(NodeInput::Reflection(graph_craft::document::DocumentNodeMetadata::SourceId));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Public immutable getters for the network interface
|
||||
|
||||
+7
-5
@@ -253,8 +253,9 @@ impl NodeNetworkInterface {
|
||||
};
|
||||
let number_of_inputs = self.number_of_inputs(node_id, network_path);
|
||||
implementations
|
||||
.keys()
|
||||
.filter_map(|node_io| {
|
||||
.iter()
|
||||
.filter_map(|entry| {
|
||||
let node_io = &entry.io;
|
||||
// Check if this NodeIOTypes implementation is valid for the other inputs
|
||||
let valid_implementation = (0..number_of_inputs).filter(|iterator_index| iterator_index != input_index).all(|iterator_index| {
|
||||
let input_type = self.input_type_not_invalid(&InputConnector::node(*node_id, iterator_index), network_path);
|
||||
@@ -293,8 +294,9 @@ impl NodeNetworkInterface {
|
||||
let valid_output_types = self.valid_output_types(&OutputConnector::node(*node_id, 0), network_path);
|
||||
|
||||
implementations
|
||||
.keys()
|
||||
.filter_map(|node_io| {
|
||||
.iter()
|
||||
.filter_map(|entry| {
|
||||
let node_io = &entry.io;
|
||||
if !valid_output_types.iter().any(|output_type| output_type.nested_type() == node_io.return_value.nested_type()) {
|
||||
return None;
|
||||
}
|
||||
@@ -323,7 +325,7 @@ impl NodeNetworkInterface {
|
||||
log::error!("Protonode {render_node:?} not found in registry");
|
||||
return Vec::new();
|
||||
};
|
||||
implementations.keys().map(|types| types.inputs[1].clone()).collect()
|
||||
implementations.iter().map(|entry| entry.io.inputs[1].clone()).collect()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1148,6 +1148,8 @@ pub fn document_migration_upgrades(document: &mut DocumentMessageHandler, reset_
|
||||
}
|
||||
}
|
||||
|
||||
document.network_interface.migrate_async_source_inputs();
|
||||
|
||||
// The "Brush" wrapper network was replaced with the `brush` proto node directly. Convert old `Network("Brush")` instances to the proto node, forwarding all 3 inputs (Background, Trace, Cache) one-to-one.
|
||||
// This must run as a pre-pass before the recursive iteration below: replacing the outer Brush's network impl orphans its child paths, and the recursive iteration would log errors for those stale paths.
|
||||
let brush_layers: Vec<(NodeId, Vec<NodeId>)> = document
|
||||
|
||||
@@ -15,7 +15,7 @@ use graphene_std::raster::{CPU, Raster};
|
||||
use graphene_std::renderer::{RenderMetadata, graphic_list_bounding_box};
|
||||
use graphene_std::transform::Footprint;
|
||||
use graphene_std::vector::{Vector, graphic_types};
|
||||
use graphene_std::{ATTR_TRANSFORM, Context, Graphic, NodeInputDecleration};
|
||||
use graphene_std::{ATTR_TRANSFORM, CtxSnapshot, Graphic, NodeInputDecleration};
|
||||
use interpreted_executor::dynamic_executor::ResolvedDocumentNodeTypesDelta;
|
||||
use std::any::Any;
|
||||
use std::sync::Arc;
|
||||
@@ -26,7 +26,7 @@ pub use runtime_io::NodeRuntimeIO;
|
||||
mod runtime;
|
||||
pub use runtime::*;
|
||||
|
||||
#[derive(Debug, serde::Serialize, serde::Deserialize)]
|
||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
||||
pub struct ExecutionRequest {
|
||||
execution_id: u64,
|
||||
render_config: RenderConfig,
|
||||
@@ -375,10 +375,12 @@ impl NodeGraphExecutor {
|
||||
}
|
||||
}
|
||||
|
||||
let Some((queued_execution_id, execution_context)) = self.futures.pop_front() else {
|
||||
let execution_context = if self.futures.front().is_some_and(|&(queued_execution_id, _)| queued_execution_id == execution_id) {
|
||||
let (_, execution_context) = self.futures.pop_front().expect("front was just matched");
|
||||
execution_context
|
||||
} else {
|
||||
panic!("InvalidGenerationId")
|
||||
};
|
||||
assert_eq!(queued_execution_id, execution_id, "Missmatch in execution id");
|
||||
|
||||
// TODO: Eventually remove this document upgrade code
|
||||
// Gradient-migration measurement runs only read back the fill's evaluated geometry; they never render to the artwork.
|
||||
@@ -892,7 +894,7 @@ fn introspected_output<T: Clone + Send + Sync + 'static>(data: &Arc<dyn Any + Se
|
||||
if let Some(io) = data.downcast_ref::<IORecord<Footprint, T>>() {
|
||||
return Some(io.output.clone());
|
||||
}
|
||||
if let Some(io) = data.downcast_ref::<IORecord<Context, T>>() {
|
||||
if let Some(io) = data.downcast_ref::<IORecord<CtxSnapshot, T>>() {
|
||||
return Some(io.output.clone());
|
||||
}
|
||||
None
|
||||
@@ -911,7 +913,7 @@ mod test {
|
||||
use crate::test_utils::test_prelude::{self, NodeGraphLayer};
|
||||
use graph_craft::ProtoNodeIdentifier;
|
||||
use graph_craft::document::NodeNetwork;
|
||||
use graphene_std::Context;
|
||||
use graphene_std::CtxSnapshot;
|
||||
use graphene_std::NodeInputDecleration;
|
||||
use graphene_std::memo::IORecord;
|
||||
use test_prelude::LayerNodeIdentifier;
|
||||
@@ -979,7 +981,7 @@ mod test {
|
||||
Some(x.output.clone())
|
||||
} else if let Some(x) = dynamic.downcast_ref::<IORecord<Footprint, Input::Result>>() {
|
||||
Some(x.output.clone())
|
||||
} else if let Some(x) = dynamic.downcast_ref::<IORecord<Context, Input::Result>>() {
|
||||
} else if let Some(x) = dynamic.downcast_ref::<IORecord<CtxSnapshot, Input::Result>>() {
|
||||
Some(x.output.clone())
|
||||
} else {
|
||||
warn!("cannot downcast type for introspection");
|
||||
|
||||
@@ -3,25 +3,26 @@ use crate::messages::frontend::utility_types::{ExportBounds, FileType};
|
||||
use glam::{DAffine2, DVec2, UVec2};
|
||||
use graph_craft::application_io::resource::ResourceRegistry;
|
||||
use graph_craft::application_io::{PlatformApplicationIo, PlatformEditorApi};
|
||||
use graph_craft::concrete;
|
||||
use graph_craft::document::value::{RenderOutput, RenderOutputType, TaggedValue};
|
||||
use graph_craft::document::{NodeId, NodeNetwork};
|
||||
use graph_craft::graphene_compiler::Compiler;
|
||||
use graph_craft::proto::GraphErrors;
|
||||
use graphene_std::application_io::{ApplicationIo, ExportFormat, NodeGraphUpdateMessage, NodeGraphUpdateSender, RenderConfig, Texture};
|
||||
use graphene_std::bounds::RenderBoundingBox;
|
||||
use graphene_std::core_types::gpoll::GPoll;
|
||||
use graphene_std::list::List;
|
||||
use graphene_std::memo::IORecord;
|
||||
use graphene_std::ops::Convert;
|
||||
use graphene_std::ops::ConvertAsync;
|
||||
#[cfg(all(target_family = "wasm", feature = "gpu", feature = "wasm"))]
|
||||
use graphene_std::platform_application_io::canvas_utils::{Canvas, CanvasSurface, CanvasSurfaceHandle};
|
||||
use graphene_std::raster_types::Raster;
|
||||
use graphene_std::renderer::{Render, RenderParams, RenderSvgSegmentList, SvgRender, SvgSegment};
|
||||
use graphene_std::runtime::{DynGraphRuntime, DynSpawner, GraphRuntime, NoopSpawner, RuntimeHandle};
|
||||
use graphene_std::transform::RenderQuality;
|
||||
use graphene_std::vector::Vector;
|
||||
use graphene_std::vector::style::RenderMode;
|
||||
use graphene_std::{Artboard, Context, Graphic};
|
||||
use interpreted_executor::dynamic_executor::{DynamicExecutor, IntrospectError, ResolvedDocumentNodeTypesDelta};
|
||||
use graphene_std::{Artboard, CtxSnapshot, Graphic};
|
||||
use interpreted_executor::dynamic_executor::{DynamicExecutor, ResolvedDocumentNodeTypesDelta};
|
||||
use interpreted_executor::util::wrap_network_in_scope;
|
||||
use spin::Mutex;
|
||||
use std::sync::Arc;
|
||||
@@ -40,6 +41,7 @@ pub struct NodeRuntime {
|
||||
editor_preferences: EditorPreferences,
|
||||
old_graph: Option<NodeNetwork>,
|
||||
update_thumbnails: bool,
|
||||
graph_runtime: Arc<DynGraphRuntime>,
|
||||
|
||||
editor_api: Arc<PlatformEditorApi>,
|
||||
resources: ResourceRegistry,
|
||||
@@ -121,18 +123,25 @@ pub static NODE_RUNTIME: once_cell::sync::Lazy<Mutex<Option<NodeRuntime>>> = onc
|
||||
|
||||
impl NodeRuntime {
|
||||
pub fn new(receiver: Receiver<GraphRuntimeRequest>, sender: Sender<NodeGraphUpdate>) -> Self {
|
||||
let spawner: Box<DynSpawner> = Box::new(NoopSpawner);
|
||||
let graph_runtime: Arc<DynGraphRuntime> = Arc::new(GraphRuntime::new(spawner));
|
||||
let mut executor = DynamicExecutor::default();
|
||||
executor.set_runtime(Arc::clone(&graph_runtime));
|
||||
|
||||
Self {
|
||||
executor: DynamicExecutor::default(),
|
||||
executor,
|
||||
receiver,
|
||||
sender: InternalNodeGraphUpdateSender(sender.clone()),
|
||||
editor_preferences: EditorPreferences::default(),
|
||||
old_graph: None,
|
||||
resources: ResourceRegistry::default(),
|
||||
update_thumbnails: true,
|
||||
graph_runtime: Arc::clone(&graph_runtime),
|
||||
|
||||
editor_api: PlatformEditorApi {
|
||||
editor_preferences: Box::new(EditorPreferences::default()),
|
||||
node_graph_message_sender: Box::new(InternalNodeGraphUpdateSender(sender)),
|
||||
runtime: RuntimeHandle(graph_runtime),
|
||||
|
||||
#[cfg(not(test))]
|
||||
application_io: None,
|
||||
@@ -173,7 +182,6 @@ impl NodeRuntime {
|
||||
}
|
||||
|
||||
let for_export = execution_request.render_config.for_export;
|
||||
|
||||
execution = Some(request);
|
||||
|
||||
// If we get an export request we always execute it immedeatly otherwise it could get deduplicated
|
||||
@@ -203,11 +211,12 @@ impl NodeRuntime {
|
||||
application_io: self.editor_api.application_io.clone(),
|
||||
node_graph_message_sender: Box::new(self.sender.clone()),
|
||||
editor_preferences: Box::new(preferences),
|
||||
runtime: self.editor_api.runtime.clone(),
|
||||
}
|
||||
.into();
|
||||
if let Some(graph) = self.old_graph.clone() {
|
||||
// We ignore this result as compilation errors should have been reported in an earlier iteration
|
||||
let _ = self.update_network(graph).await;
|
||||
let _ = self.update_network(graph);
|
||||
}
|
||||
}
|
||||
GraphRuntimeRequest::GraphUpdate(GraphUpdate {
|
||||
@@ -222,7 +231,7 @@ impl NodeRuntime {
|
||||
self.resources = resources;
|
||||
|
||||
self.node_graph_errors.clear();
|
||||
let result = self.update_network(network).await;
|
||||
let result = self.update_network(network);
|
||||
let node_graph_errors = self.node_graph_errors.clone();
|
||||
|
||||
self.update_thumbnails = true;
|
||||
@@ -237,7 +246,7 @@ impl NodeRuntime {
|
||||
render_config.export_format = ExportFormat::Svg;
|
||||
}
|
||||
|
||||
let result = self.execute_network(render_config).await;
|
||||
let result = self.execute_network(render_config);
|
||||
let mut responses = VecDeque::new();
|
||||
// TODO: Only process monitor nodes if the graph has changed, not when only the Footprint changes
|
||||
if !render_config.for_eyedropper {
|
||||
@@ -258,10 +267,10 @@ impl NodeRuntime {
|
||||
.application_io
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.gpu_executor()
|
||||
.gpu_executor_arc()
|
||||
.expect("GPU executor should be available when we receive a texture");
|
||||
|
||||
let raster_cpu = Raster::new_gpu(texture).convert(Footprint::BOUNDLESS, executor).await;
|
||||
let raster_cpu = Raster::new_gpu(texture).convert(Footprint::BOUNDLESS, wgpu_executor::WgpuExecutorHandle(executor)).await;
|
||||
|
||||
let (data, width, height) = raster_cpu.to_flat_u8();
|
||||
|
||||
@@ -282,10 +291,10 @@ impl NodeRuntime {
|
||||
.application_io
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.gpu_executor()
|
||||
.gpu_executor_arc()
|
||||
.expect("GPU executor should be available when we receive a texture");
|
||||
|
||||
let raster_cpu = Raster::new_gpu(texture).convert(Footprint::BOUNDLESS, executor).await;
|
||||
let raster_cpu = Raster::new_gpu(texture).convert(Footprint::BOUNDLESS, wgpu_executor::WgpuExecutorHandle(executor)).await;
|
||||
|
||||
self.sender.send_eyedropper_preview(raster_cpu);
|
||||
continue;
|
||||
@@ -345,7 +354,7 @@ impl NodeRuntime {
|
||||
None
|
||||
}
|
||||
|
||||
async fn update_network(&mut self, graph: NodeNetwork) -> Result<ResolvedDocumentNodeTypesDelta, (ResolvedDocumentNodeTypesDelta, String)> {
|
||||
fn update_network(&mut self, graph: NodeNetwork) -> Result<ResolvedDocumentNodeTypesDelta, (ResolvedDocumentNodeTypesDelta, String)> {
|
||||
let mut scoped_network = wrap_network_in_scope(graph, self.editor_api.clone());
|
||||
|
||||
if let Err(e) = self.preprocessor.preprocess(&mut scoped_network, &|resource_id| self.resources.hash(&resource_id)) {
|
||||
@@ -368,20 +377,24 @@ impl NodeRuntime {
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
assert_ne!(proto_network.nodes.len(), 0, "No proto nodes exist?");
|
||||
self.executor.update(proto_network).await.map_err(|(types, e)| {
|
||||
self.executor.update(proto_network).map_err(|(types, e)| {
|
||||
self.node_graph_errors.clone_from(&e);
|
||||
(types, format!("{e:?}"))
|
||||
})
|
||||
}
|
||||
|
||||
async fn execute_network(&mut self, render_config: RenderConfig) -> Result<TaggedValue, String> {
|
||||
fn execute_network(&mut self, render_config: RenderConfig) -> Result<TaggedValue, String> {
|
||||
use graph_craft::graphene_compiler::Executor;
|
||||
|
||||
match self.executor.input_type() {
|
||||
Some(t) if t == concrete!(RenderConfig) => (&self.executor).execute(render_config).await.map_err(|e| e.to_string()),
|
||||
Some(t) if t == concrete!(()) => (&self.executor).execute(()).await.map_err(|e| e.to_string()),
|
||||
Some(t) => Err(format!("Invalid input type {t:?}")),
|
||||
_ => Err(format!("No input type:\n{:?}", self.node_graph_errors)),
|
||||
match (&self.executor).execute(render_config).map_err(|e| e.to_string())? {
|
||||
GPoll::Final(value) | GPoll::Partial(value) => Ok(value),
|
||||
GPoll::Fallback(boxed) => {
|
||||
let (value, error) = *boxed;
|
||||
error!("Node graph evaluation reported an error alongside its fallback output: {error:?}");
|
||||
Ok(value)
|
||||
}
|
||||
GPoll::Pending => Err("Node graph evaluation is pending".to_string()),
|
||||
GPoll::Error(error) => Err(format!("Node graph evaluation failed: {error:?}")),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -415,7 +428,7 @@ impl NodeRuntime {
|
||||
};
|
||||
|
||||
// Graphic list: thumbnail (text-aware bounds, since the `BoundingBox` trait can't lay out `Graphic::Text` content)
|
||||
if let Some(io) = introspected_data.downcast_ref::<IORecord<Context, List<Graphic>>>() {
|
||||
if let Some(io) = introspected_data.downcast_ref::<IORecord<CtxSnapshot, List<Graphic>>>() {
|
||||
if update_thumbnails {
|
||||
let bounds = graphene_std::renderer::graphic_list_bounding_box(&io.output, DAffine2::IDENTITY);
|
||||
Self::render_thumbnail(&mut self.thumbnail_renders, parent_network_node_id, &io.output, bounds, responses)
|
||||
@@ -423,19 +436,19 @@ impl NodeRuntime {
|
||||
}
|
||||
// Artboard thumbnail bounds come from the clipping rectangles, not the content union, since the renderer
|
||||
// clips content to those rectangles so anything outside isn't visible
|
||||
else if let Some(io) = introspected_data.downcast_ref::<IORecord<Context, List<Artboard>>>() {
|
||||
else if let Some(io) = introspected_data.downcast_ref::<IORecord<CtxSnapshot, List<Artboard>>>() {
|
||||
if update_thumbnails {
|
||||
let bounds = artboard_clip_bounds(&io.output);
|
||||
Self::render_thumbnail(&mut self.thumbnail_renders, parent_network_node_id, &io.output, bounds, responses)
|
||||
}
|
||||
}
|
||||
// Vector list: vector modifications
|
||||
else if let Some(io) = introspected_data.downcast_ref::<IORecord<Context, List<Vector>>>() {
|
||||
else if let Some(io) = introspected_data.downcast_ref::<IORecord<CtxSnapshot, List<Vector>>>() {
|
||||
// Insert the vector modify
|
||||
self.vector_modify.insert(parent_network_node_id, io.output.element(0).cloned().unwrap_or_default());
|
||||
}
|
||||
// String list: thumbnail (bounds need text layout, which the `BoundingBox` trait can't do for a bare `String`)
|
||||
else if let Some(io) = introspected_data.downcast_ref::<IORecord<Context, List<String>>>() {
|
||||
else if let Some(io) = introspected_data.downcast_ref::<IORecord<CtxSnapshot, List<String>>>() {
|
||||
if update_thumbnails {
|
||||
let bounds = graphene_std::renderer::text_list_bounding_box(&io.output, DAffine2::IDENTITY);
|
||||
Self::render_thumbnail(&mut self.thumbnail_renders, parent_network_node_id, &io.output, bounds, responses)
|
||||
@@ -544,14 +557,6 @@ fn expand_to_thumbnail_aspect(bounds: [DVec2; 2]) -> [DVec2; 2] {
|
||||
[center - half, center + half]
|
||||
}
|
||||
|
||||
pub async fn introspect_node(path: &[NodeId]) -> Result<Arc<dyn std::any::Any + Send + Sync + 'static>, IntrospectError> {
|
||||
let runtime = NODE_RUNTIME.lock();
|
||||
if let Some(ref mut runtime) = runtime.as_ref() {
|
||||
return runtime.executor.introspect(path);
|
||||
}
|
||||
Err(IntrospectError::RuntimeNotReady)
|
||||
}
|
||||
|
||||
pub async fn run_node_graph() -> (bool, Option<Texture>) {
|
||||
let Some(mut runtime) = NODE_RUNTIME.try_lock() else { return (false, None) };
|
||||
if let Some(ref mut runtime) = runtime.as_mut() {
|
||||
@@ -577,6 +582,7 @@ impl NodeRuntime {
|
||||
application_io: Some(application_io.into()),
|
||||
node_graph_message_sender: Box::new(self.sender.clone()),
|
||||
editor_preferences: Box::new(self.editor_preferences.clone()),
|
||||
runtime: self.editor_api.runtime.clone(),
|
||||
}
|
||||
.into();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user