mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-10-05 21:38:12 +08:00
Share one graph runtime between the editor api scope and the executor
This commit is contained in:
@@ -228,7 +228,7 @@ impl RenderState {
|
|||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
let size = glam::UVec2::new(viewport_texture.width(), viewport_texture.height());
|
let size = glam::UVec2::new(viewport_texture.width(), viewport_texture.height());
|
||||||
let result = futures::executor::block_on(self.executor.render_vello_scene(&scene, size, &Default::default(), None));
|
let result = self.executor.render_vello_scene(&scene, size, &Default::default(), None);
|
||||||
match result {
|
match result {
|
||||||
Ok(texture) => {
|
Ok(texture) => {
|
||||||
self.overlays_texture = Some(texture.into());
|
self.overlays_texture = Some(texture.into());
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ use graphene_std::ops::{Convert, ConvertAsync};
|
|||||||
use graphene_std::platform_application_io::canvas_utils::{Canvas, CanvasSurface, CanvasSurfaceHandle};
|
use graphene_std::platform_application_io::canvas_utils::{Canvas, CanvasSurface, CanvasSurfaceHandle};
|
||||||
use graphene_std::raster_types::Raster;
|
use graphene_std::raster_types::Raster;
|
||||||
use graphene_std::renderer::{Render, RenderParams, RenderSvgSegmentList, SvgRender, SvgSegment};
|
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::transform::RenderQuality;
|
||||||
use graphene_std::vector::Vector;
|
use graphene_std::vector::Vector;
|
||||||
use graphene_std::vector::style::RenderMode;
|
use graphene_std::vector::style::RenderMode;
|
||||||
@@ -120,8 +121,12 @@ pub static NODE_RUNTIME: once_cell::sync::Lazy<Mutex<Option<NodeRuntime>>> = onc
|
|||||||
|
|
||||||
impl NodeRuntime {
|
impl NodeRuntime {
|
||||||
pub fn new(receiver: Receiver<GraphRuntimeRequest>, sender: Sender<NodeGraphUpdate>) -> Self {
|
pub fn new(receiver: Receiver<GraphRuntimeRequest>, sender: Sender<NodeGraphUpdate>) -> Self {
|
||||||
|
let graph_runtime: Arc<DynGraphRuntime> = Arc::new(GraphRuntime::new(Box::new(NoopSpawner) as Box<DynSpawner>));
|
||||||
|
let mut executor = DynamicExecutor::default();
|
||||||
|
executor.set_runtime(Arc::clone(&graph_runtime));
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
executor: DynamicExecutor::default(),
|
executor,
|
||||||
receiver,
|
receiver,
|
||||||
sender: InternalNodeGraphUpdateSender(sender.clone()),
|
sender: InternalNodeGraphUpdateSender(sender.clone()),
|
||||||
editor_preferences: EditorPreferences::default(),
|
editor_preferences: EditorPreferences::default(),
|
||||||
@@ -132,6 +137,7 @@ impl NodeRuntime {
|
|||||||
editor_api: PlatformEditorApi {
|
editor_api: PlatformEditorApi {
|
||||||
editor_preferences: Box::new(EditorPreferences::default()),
|
editor_preferences: Box::new(EditorPreferences::default()),
|
||||||
node_graph_message_sender: Box::new(InternalNodeGraphUpdateSender(sender)),
|
node_graph_message_sender: Box::new(InternalNodeGraphUpdateSender(sender)),
|
||||||
|
runtime: RuntimeHandle(graph_runtime),
|
||||||
|
|
||||||
#[cfg(not(test))]
|
#[cfg(not(test))]
|
||||||
application_io: None,
|
application_io: None,
|
||||||
@@ -202,6 +208,7 @@ impl NodeRuntime {
|
|||||||
application_io: self.editor_api.application_io.clone(),
|
application_io: self.editor_api.application_io.clone(),
|
||||||
node_graph_message_sender: Box::new(self.sender.clone()),
|
node_graph_message_sender: Box::new(self.sender.clone()),
|
||||||
editor_preferences: Box::new(preferences),
|
editor_preferences: Box::new(preferences),
|
||||||
|
runtime: self.editor_api.runtime.clone(),
|
||||||
}
|
}
|
||||||
.into();
|
.into();
|
||||||
if let Some(graph) = self.old_graph.clone() {
|
if let Some(graph) = self.old_graph.clone() {
|
||||||
@@ -575,6 +582,7 @@ impl NodeRuntime {
|
|||||||
application_io: Some(application_io.into()),
|
application_io: Some(application_io.into()),
|
||||||
node_graph_message_sender: Box::new(self.sender.clone()),
|
node_graph_message_sender: Box::new(self.sender.clone()),
|
||||||
editor_preferences: Box::new(self.editor_preferences.clone()),
|
editor_preferences: Box::new(self.editor_preferences.clone()),
|
||||||
|
runtime: self.editor_api.runtime.clone(),
|
||||||
}
|
}
|
||||||
.into();
|
.into();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -294,6 +294,10 @@ impl ProtoNetwork {
|
|||||||
(inwards_edges, id_map)
|
(inwards_edges, id_map)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn source_ids(&self) -> Vec<SourceId> {
|
||||||
|
self.nodes.iter().flat_map(|(_, node)| node.context_features.sources.iter().copied()).collect()
|
||||||
|
}
|
||||||
|
|
||||||
/// Inserts context nullification nodes to optimize caching.
|
/// Inserts context nullification nodes to optimize caching.
|
||||||
/// This analysis is performed after topological sorting to ensure proper dependency tracking.
|
/// This analysis is performed after topological sorting to ensure proper dependency tracking.
|
||||||
pub fn insert_context_nullification_nodes(&mut self) -> Result<(), String> {
|
pub fn insert_context_nullification_nodes(&mut self) -> Result<(), String> {
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ use graph_craft::graphene_compiler::Compiler;
|
|||||||
use graph_craft::proto::ProtoNetwork;
|
use graph_craft::proto::ProtoNetwork;
|
||||||
use graph_craft::util::load_network;
|
use graph_craft::util::load_network;
|
||||||
use graphene_std::application_io::{ApplicationIo, NodeGraphUpdateMessage, NodeGraphUpdateSender};
|
use graphene_std::application_io::{ApplicationIo, NodeGraphUpdateMessage, NodeGraphUpdateSender};
|
||||||
|
use graphene_std::runtime::{DynGraphRuntime, DynSpawner, GraphRuntime, NoopSpawner, RuntimeHandle};
|
||||||
use interpreted_executor::dynamic_executor::DynamicExecutor;
|
use interpreted_executor::dynamic_executor::DynamicExecutor;
|
||||||
use interpreted_executor::util::wrap_network_in_scope;
|
use interpreted_executor::util::wrap_network_in_scope;
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
@@ -182,10 +183,12 @@ async fn main() -> Result<(), Box<dyn Error>> {
|
|||||||
let preferences = EditorPreferences {
|
let preferences = EditorPreferences {
|
||||||
max_render_region_size: EditorPreferences::default().max_render_region_size,
|
max_render_region_size: EditorPreferences::default().max_render_region_size,
|
||||||
};
|
};
|
||||||
|
let graph_runtime: Arc<DynGraphRuntime> = Arc::new(GraphRuntime::new(Box::new(NoopSpawner) as Box<DynSpawner>));
|
||||||
let editor_api = Arc::new(PlatformEditorApi {
|
let editor_api = Arc::new(PlatformEditorApi {
|
||||||
application_io: Some(application_io_for_api),
|
application_io: Some(application_io_for_api),
|
||||||
node_graph_message_sender: Box::new(UpdateLogger {}),
|
node_graph_message_sender: Box::new(UpdateLogger {}),
|
||||||
editor_preferences: Box::new(preferences),
|
editor_preferences: Box::new(preferences),
|
||||||
|
runtime: RuntimeHandle(graph_runtime.clone()),
|
||||||
});
|
});
|
||||||
let proto_graph = compile_graph(node_network, editor_api, gdd.as_ref())?;
|
let proto_graph = compile_graph(node_network, editor_api, gdd.as_ref())?;
|
||||||
|
|
||||||
@@ -218,7 +221,7 @@ async fn main() -> Result<(), Box<dyn Error>> {
|
|||||||
let file_type = export::detect_file_type(&output)?;
|
let file_type = export::detect_file_type(&output)?;
|
||||||
|
|
||||||
// Create executor
|
// Create executor
|
||||||
let executor = create_executor(proto_graph)?;
|
let executor = create_executor(proto_graph, graph_runtime)?;
|
||||||
|
|
||||||
if fps <= 0. {
|
if fps <= 0. {
|
||||||
return Err("Fps number must be positive".into());
|
return Err("Fps number must be positive".into());
|
||||||
@@ -285,7 +288,8 @@ fn compile_graph(network: NodeNetwork, editor_api: Arc<PlatformEditorApi>, gdd:
|
|||||||
compiler.compile_single(network).map_err(|x| x.into())
|
compiler.compile_single(network).map_err(|x| x.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_executor(proto_network: ProtoNetwork) -> Result<DynamicExecutor, Box<dyn Error>> {
|
fn create_executor(proto_network: ProtoNetwork, runtime: Arc<DynGraphRuntime>) -> Result<DynamicExecutor, Box<dyn Error>> {
|
||||||
let executor = block_on(DynamicExecutor::new(proto_network)).map_err(|errors| errors.iter().map(|e| format!("{e:?}")).reduce(|acc, e| format!("{acc}\n{e}")).unwrap_or_default())?;
|
let mut executor = block_on(DynamicExecutor::new(proto_network)).map_err(|errors| errors.iter().map(|e| format!("{e:?}")).reduce(|acc, e| format!("{acc}\n{e}")).unwrap_or_default())?;
|
||||||
|
executor.set_runtime(runtime);
|
||||||
Ok(executor)
|
Ok(executor)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ use core_types::context::{ContextImpl, DynSlot, EvalScope, VarArg, VarArgLink, V
|
|||||||
use core_types::gnode::GNode;
|
use core_types::gnode::GNode;
|
||||||
use core_types::gpoll::GPoll;
|
use core_types::gpoll::GPoll;
|
||||||
use core_types::registry::{EdgeHandle, ErasedGNode};
|
use core_types::registry::{EdgeHandle, ErasedGNode};
|
||||||
use core_types::runtime::{GraphRuntime, SourceFuture, Spawner};
|
use core_types::runtime::{DynGraphRuntime, DynSpawner, GraphRuntime, NoopSpawner};
|
||||||
use graph_craft::Type;
|
use graph_craft::Type;
|
||||||
use graph_craft::document::NodeId;
|
use graph_craft::document::NodeId;
|
||||||
use graph_craft::document::value::TaggedValue;
|
use graph_craft::document::value::TaggedValue;
|
||||||
@@ -17,15 +17,6 @@ use std::sync::{Arc, Mutex, PoisonError};
|
|||||||
|
|
||||||
const ARENA_CAPACITY: usize = 1 << 20;
|
const ARENA_CAPACITY: usize = 1 << 20;
|
||||||
|
|
||||||
/// Dropped tasks 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");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// An executor of a node graph that does not require an online compilation server, and instead uses `Box<dyn ...>`.
|
/// An executor of a node graph that does not require an online compilation server, and instead uses `Box<dyn ...>`.
|
||||||
pub struct DynamicExecutor {
|
pub struct DynamicExecutor {
|
||||||
output: NodeId,
|
output: NodeId,
|
||||||
@@ -36,9 +27,15 @@ pub struct DynamicExecutor {
|
|||||||
// This allows us to keep the nodes around for one more frame which is used for introspection
|
// This allows us to keep the nodes around for one more frame which is used for introspection
|
||||||
orphaned_nodes: HashSet<NodeId>,
|
orphaned_nodes: HashSet<NodeId>,
|
||||||
arena: Mutex<Arena>,
|
arena: Mutex<Arena>,
|
||||||
runtime: Arc<GraphRuntime<NoopSpawner>>,
|
runtime: Arc<DynGraphRuntime>,
|
||||||
|
live_sources: Vec<core_types::SourceId>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn noop_runtime() -> Arc<DynGraphRuntime> {
|
||||||
|
Arc::new(GraphRuntime::new(Box::new(NoopSpawner) as Box<DynSpawner>))
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
impl Default for DynamicExecutor {
|
impl Default for DynamicExecutor {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
@@ -47,7 +44,8 @@ impl Default for DynamicExecutor {
|
|||||||
typing_context: TypingContext::new(&node_registry::NODE_REGISTRY),
|
typing_context: TypingContext::new(&node_registry::NODE_REGISTRY),
|
||||||
orphaned_nodes: HashSet::new(),
|
orphaned_nodes: HashSet::new(),
|
||||||
arena: Mutex::new(Arena::new(ARENA_CAPACITY)),
|
arena: Mutex::new(Arena::new(ARENA_CAPACITY)),
|
||||||
runtime: Arc::new(GraphRuntime::new(NoopSpawner)),
|
runtime: noop_runtime(),
|
||||||
|
live_sources: Vec::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -71,7 +69,10 @@ impl DynamicExecutor {
|
|||||||
let mut typing_context = TypingContext::new(&node_registry::NODE_REGISTRY);
|
let mut typing_context = TypingContext::new(&node_registry::NODE_REGISTRY);
|
||||||
typing_context.update(&proto_network)?;
|
typing_context.update(&proto_network)?;
|
||||||
let output = proto_network.output;
|
let output = proto_network.output;
|
||||||
|
let sources = proto_network.source_ids();
|
||||||
let tree = BorrowTree::new(proto_network, &typing_context).await?;
|
let tree = BorrowTree::new(proto_network, &typing_context).await?;
|
||||||
|
let runtime = noop_runtime();
|
||||||
|
runtime.retain_sources(&sources);
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
tree,
|
tree,
|
||||||
@@ -79,10 +80,20 @@ impl DynamicExecutor {
|
|||||||
typing_context,
|
typing_context,
|
||||||
orphaned_nodes: HashSet::new(),
|
orphaned_nodes: HashSet::new(),
|
||||||
arena: Mutex::new(Arena::new(ARENA_CAPACITY)),
|
arena: Mutex::new(Arena::new(ARENA_CAPACITY)),
|
||||||
runtime: Arc::new(GraphRuntime::new(NoopSpawner)),
|
runtime,
|
||||||
|
live_sources: sources,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn set_runtime(&mut self, runtime: Arc<DynGraphRuntime>) {
|
||||||
|
runtime.retain_sources(&self.live_sources);
|
||||||
|
self.runtime = runtime;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn take_dirty(&self) -> bool {
|
||||||
|
self.runtime.take_dirty()
|
||||||
|
}
|
||||||
|
|
||||||
/// Updates the existing [`BorrowTree`] to reflect the new [`ProtoNetwork`], reusing nodes where possible.
|
/// Updates the existing [`BorrowTree`] to reflect the new [`ProtoNetwork`], reusing nodes where possible.
|
||||||
#[cfg_attr(debug_assertions, inline(never))]
|
#[cfg_attr(debug_assertions, inline(never))]
|
||||||
pub async fn update(&mut self, proto_network: ProtoNetwork) -> Result<ResolvedDocumentNodeTypesDelta, (ResolvedDocumentNodeTypesDelta, GraphErrors)> {
|
pub async fn update(&mut self, proto_network: ProtoNetwork) -> Result<ResolvedDocumentNodeTypesDelta, (ResolvedDocumentNodeTypesDelta, GraphErrors)> {
|
||||||
@@ -108,11 +119,14 @@ impl DynamicExecutor {
|
|||||||
(ResolvedDocumentNodeTypesDelta { add, remove: Vec::new() }, e)
|
(ResolvedDocumentNodeTypesDelta { add, remove: Vec::new() }, e)
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
|
let sources = proto_network.source_ids();
|
||||||
let (add, orphaned) = self
|
let (add, orphaned) = self
|
||||||
.tree
|
.tree
|
||||||
.update(proto_network, &self.typing_context)
|
.update(proto_network, &self.typing_context)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| (ResolvedDocumentNodeTypesDelta::default(), e))?;
|
.map_err(|e| (ResolvedDocumentNodeTypesDelta::default(), e))?;
|
||||||
|
self.runtime.retain_sources(&sources);
|
||||||
|
self.live_sources = sources;
|
||||||
let old_to_remove = core::mem::replace(&mut self.orphaned_nodes, orphaned);
|
let old_to_remove = core::mem::replace(&mut self.orphaned_nodes, orphaned);
|
||||||
let mut remove = Vec::with_capacity(old_to_remove.len() - self.orphaned_nodes.len().min(old_to_remove.len()));
|
let mut remove = Vec::with_capacity(old_to_remove.len() - self.orphaned_nodes.len().min(old_to_remove.len()));
|
||||||
for node_id in old_to_remove {
|
for node_id in old_to_remove {
|
||||||
|
|||||||
@@ -112,6 +112,7 @@ pub struct EditorApi<Io> {
|
|||||||
pub node_graph_message_sender: Box<dyn NodeGraphUpdateSender + Send + Sync>,
|
pub node_graph_message_sender: Box<dyn NodeGraphUpdateSender + Send + Sync>,
|
||||||
/// Editor preferences made available to the graph through the `PlatformEditorApi`.
|
/// Editor preferences made available to the graph through the `PlatformEditorApi`.
|
||||||
pub editor_preferences: Box<dyn GetEditorPreferences + Send + Sync>,
|
pub editor_preferences: Box<dyn GetEditorPreferences + Send + Sync>,
|
||||||
|
pub runtime: core_types::runtime::RuntimeHandle,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Io> Eq for EditorApi<Io> {}
|
impl<Io> Eq for EditorApi<Io> {}
|
||||||
@@ -122,6 +123,7 @@ impl<Io: Default> Default for EditorApi<Io> {
|
|||||||
application_io: None,
|
application_io: None,
|
||||||
node_graph_message_sender: Box::new(Logger),
|
node_graph_message_sender: Box::new(Logger),
|
||||||
editor_preferences: Box::new(DummyPreferences),
|
editor_preferences: Box::new(DummyPreferences),
|
||||||
|
runtime: Default::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,6 +36,34 @@ pub trait Spawner {
|
|||||||
fn spawn(&self, task: SourceFuture);
|
fn spawn(&self, task: SourceFuture);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(target_family = "wasm"))]
|
||||||
|
pub type DynSpawner = dyn Spawner + Send + Sync;
|
||||||
|
#[cfg(target_family = "wasm")]
|
||||||
|
pub type DynSpawner = dyn Spawner;
|
||||||
|
|
||||||
|
impl<S: Spawner + ?Sized> Spawner for Box<S> {
|
||||||
|
fn spawn(&self, task: SourceFuture) {
|
||||||
|
(**self).spawn(task)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Dropped tasks 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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type DynGraphRuntime = GraphRuntime<Box<DynSpawner>>;
|
||||||
|
|
||||||
|
impl Default for RuntimeHandle {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self(Arc::new(GraphRuntime::new(Box::new(NoopSpawner) as Box<DynSpawner>)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct GraphRuntime<S> {
|
pub struct GraphRuntime<S> {
|
||||||
generations: Arc<Mutex<HashMap<SourceId, u64>>>,
|
generations: Arc<Mutex<HashMap<SourceId, u64>>>,
|
||||||
dirty: Arc<AtomicBool>,
|
dirty: Arc<AtomicBool>,
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ pub mod render_background;
|
|||||||
pub mod render_cache;
|
pub mod render_cache;
|
||||||
pub mod render_node;
|
pub mod render_node;
|
||||||
pub mod render_pixel_preview;
|
pub mod render_pixel_preview;
|
||||||
|
pub mod runtime;
|
||||||
pub mod text;
|
pub mod text;
|
||||||
pub use blending_nodes;
|
pub use blending_nodes;
|
||||||
pub use brush_nodes as brush;
|
pub use brush_nodes as brush;
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
pub use core_types::runtime::*;
|
||||||
|
|
||||||
|
use crate::platform_application_io::editor_api;
|
||||||
|
use core_types::Ctx;
|
||||||
|
use graph_craft::application_io::PlatformEditorApi;
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
#[node_macro::node(category(""), inject_scope)]
|
||||||
|
pub fn runtime(_: impl Ctx, #[scope(editor_api::IDENTIFIER)] editor_api: Arc<PlatformEditorApi>) -> RuntimeHandle {
|
||||||
|
editor_api.runtime.clone()
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user