Skip to content

Commit c7e9d79

Browse files
Implement async refactor (#4731)
* Add editor task module, drop NodeGraphUpdateSender * Remove Node::reset and unused value nodes * Remove unneeded async from vector nodes * Remove unneeded async from graphic nodes * Remove unneeded async from raster nodes * Remove unneeded async from the remaining nodes * Make most node fns private * Make node construction sync * Remove block_on around node construction * Move GPU readback into ConvertAsync * Make Convert sync * Make wgpu pipelines and vello rendering sync * Make convert, brush and render nodes sync * Pass editor API as owned Arc * Take scope inputs by value * Move Spawn and Task into core-types * Add Poll type and status tracking * Add source generations to context * Stop storing context features in the document * Track source ids in context modification * Add async runtime and memo table * Pass runtime through scope, expose source ids * Set up async runtime in editor, CLI and benches * Give async nodes runtime and source id inputs * Classify node return types in the macro * Make Node eval sync and return Poll * Make lazy input nodes, tests and benches sync * Rerender when a spawned task finishes * Rerender in CLI until all tasks are done * Abort pending tasks when node is dropped * Trace errors through Poll node inputs * Let node errors carry upstream errors * Resolve error traces to the origin node * Show evaluation errors in the node graph * Default unmapped item primaries to a value * Add debug nodes * Memoize fallback results too * Show multiple node graph errors * Report node errors in CLI * Attach input fallback error as upstream * Keep input fallback error when node fails * Keep Poll status in render cache * Keep last frame while graph is pending * Treat partial export output as pending * Move environment from global to dispatcher * Add editor builder, drop global node runtime * Add Wake message, rerun graph when tasks finish * Add source generation tests * Add test for node error origin * Add render status overlay * Replace error artwork with render status * Poll node graph through portfolio message * Minor review fixups * Remove future type and macro --------- Co-authored-by: Dennis Kobert <dennis@kobert.dev>
1 parent 713b50c commit c7e9d79

141 files changed

Lines changed: 4066 additions & 2463 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎Cargo.lock‎

Lines changed: 2 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎desktop/src/app.rs‎

Lines changed: 39 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,11 @@ use crate::input::InputState;
2222
use crate::persist;
2323
use crate::preferences;
2424
use crate::render::{RenderError, RenderState};
25+
use crate::spawner::TokioSpawner;
2526
use crate::ui::{InputEvent, UiCommand, UiInstance};
2627
use crate::window::Window;
2728
use crate::wrapper::messages::{DesktopFrontendMessage, DesktopWrapperMessage, IngestAction, Preferences};
28-
use crate::wrapper::{DesktopWrapper, FolderStore, MmapResourceStorage, NodeGraphExecutionResult, WgpuContext, serialize_frontend_messages};
29+
use crate::wrapper::{DesktopWrapper, FolderStore, MmapResourceStorage, WgpuContext, serialize_frontend_messages};
2930

3031
pub(crate) struct App {
3132
render_state: Option<RenderState>,
@@ -77,36 +78,47 @@ impl App {
7778

7879
let exiting = Arc::new(AtomicBool::new(false));
7980

80-
let rendering_app_event_scheduler = app_event_scheduler.clone();
8181
let (start_render_sender, start_render_receiver) = std::sync::mpsc::sync_channel(1);
82-
let exiting_clone = exiting.clone();
83-
std::thread::spawn(move || {
84-
let runtime = tokio::runtime::Runtime::new().unwrap();
85-
loop {
86-
let result = runtime.block_on(DesktopWrapper::execute_node_graph());
87-
rendering_app_event_scheduler.schedule(AppEvent::NodeGraphExecutionResult(result));
88-
let _ = start_render_receiver.recv_timeout(Duration::from_millis(10));
89-
if exiting_clone.load(Ordering::Relaxed) {
90-
break;
91-
}
92-
}
93-
});
82+
83+
let async_runtime = tokio::runtime::Builder::new_multi_thread()
84+
.thread_name("graphite-async")
85+
.enable_all()
86+
.build()
87+
.expect("failed to construct tokio runtime");
9488

9589
let resource_storage = MmapResourceStorage::new(dirs::app_resources_dir()).expect("Failed to initialize on-disk resource storage");
9690

97-
// Wake the winit event loop when an editor future completes.
9891
let wake_scheduler = app_event_scheduler.clone();
99-
let wake = Arc::new(move || {
92+
let editor_wake = move || {
10093
wake_scheduler.schedule(AppEvent::DesktopWrapperMessage(DesktopWrapperMessage::Wake));
101-
});
102-
let desktop_wrapper = DesktopWrapper::new(
94+
};
95+
let runtime_wake_sender = start_render_sender.clone();
96+
let runtime_wake = move || {
97+
let _ = runtime_wake_sender.try_send(());
98+
};
99+
let (desktop_wrapper, mut node_runtime) = DesktopWrapper::new(
103100
rand::rng().random(),
104101
Arc::new(resource_storage),
105102
Arc::new(FolderStore::new(dirs::app_autosave_documents_dir())),
106103
wgpu_context.clone(),
107-
wake,
104+
TokioSpawner::new(async_runtime.handle().clone()),
105+
editor_wake,
106+
runtime_wake,
108107
);
109108

109+
let rendering_app_event_scheduler = app_event_scheduler.clone();
110+
let exiting_clone = exiting.clone();
111+
std::thread::spawn(move || {
112+
loop {
113+
let texture = async_runtime.block_on(node_runtime.run());
114+
rendering_app_event_scheduler.schedule(AppEvent::ViewportTexture(texture));
115+
let _ = start_render_receiver.recv_timeout(Duration::from_millis(10));
116+
if exiting_clone.load(Ordering::Relaxed) {
117+
break;
118+
}
119+
}
120+
});
121+
110122
Self {
111123
render_state: None,
112124
wgpu_context,
@@ -436,19 +448,15 @@ impl App {
436448
}
437449
}
438450
AppEvent::DesktopWrapperMessage(message) => self.dispatch_desktop_wrapper_message(message),
439-
AppEvent::NodeGraphExecutionResult(result) => match result {
440-
NodeGraphExecutionResult::HasRun(texture) => {
441-
self.dispatch_desktop_wrapper_message(DesktopWrapperMessage::PollNodeGraphEvaluation);
442-
if let Some(texture) = texture
443-
&& let Some(render_state) = self.render_state.as_mut()
444-
&& let Some(window) = self.window.as_ref()
445-
{
446-
render_state.bind_viewport_texture(texture);
447-
window.request_redraw();
448-
}
451+
AppEvent::ViewportTexture(texture) => {
452+
if let Some(texture) = texture
453+
&& let Some(render_state) = self.render_state.as_mut()
454+
&& let Some(window) = self.window.as_ref()
455+
{
456+
render_state.bind_viewport_texture(texture);
457+
window.request_redraw();
449458
}
450-
NodeGraphExecutionResult::NotRun => {}
451-
},
459+
}
452460
AppEvent::UiUpdate(texture) => {
453461
if let Some(render_state) = self.render_state.as_mut() {
454462
render_state.bind_ui_texture(texture);

‎desktop/src/event.rs‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
use crate::ui::Cursor;
2-
use crate::wrapper::NodeGraphExecutionResult;
2+
use crate::wrapper::Texture;
33
use crate::wrapper::messages::DesktopWrapperMessage;
44

55
pub(crate) enum AppEvent {
66
UiUpdate(wgpu::Texture),
77
CursorChange(Cursor),
88
WebCommunicationInitialized,
99
DesktopWrapperMessage(DesktopWrapperMessage),
10-
NodeGraphExecutionResult(NodeGraphExecutionResult),
10+
ViewportTexture(Option<Texture>),
1111
ClipboardRead,
1212
ClipboardWrite {
1313
content: String,

‎desktop/src/lib.rs‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ mod persist;
2222
mod preferences;
2323
mod render;
2424
mod socket;
25+
mod spawner;
2526
mod window;
2627

2728
pub(crate) mod consts;

‎desktop/src/render/state.rs‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -228,7 +228,7 @@ impl RenderState {
228228
return;
229229
};
230230
let size = glam::UVec2::new(viewport_texture.width(), viewport_texture.height());
231-
let result = futures::executor::block_on(self.executor.render_vello_scene(&scene, size, &Default::default(), None));
231+
let result = self.executor.render_vello_scene(&scene, size, &Default::default(), None);
232232
match result {
233233
Ok(texture) => {
234234
self.overlays_texture = Some(texture);

‎desktop/src/spawner.rs‎

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
use crate::wrapper::{Spawn, Task};
2+
3+
pub(crate) struct TokioSpawner(tokio::runtime::Handle);
4+
impl TokioSpawner {
5+
pub(crate) fn new(handle: tokio::runtime::Handle) -> Self {
6+
Self(handle)
7+
}
8+
}
9+
impl Spawn for TokioSpawner {
10+
fn spawn(&self, task: Task) {
11+
self.0.spawn(task);
12+
}
13+
}

‎desktop/wrapper/src/handle_desktop_wrapper_message.rs‎

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ pub(super) fn handle_desktop_wrapper_message(dispatcher: &mut DesktopWrapperMess
1010
dispatcher.queue_editor_message(*message);
1111
}
1212
DesktopWrapperMessage::Wake => {
13-
dispatcher.queue_editor_message(EditorMessage::Future(FutureMessage::Wake));
13+
dispatcher.queue_editor_message(EditorMessage::Wake);
1414
}
1515
DesktopWrapperMessage::Input(message) => {
1616
dispatcher.queue_editor_message(EditorMessage::InputPreprocessor(message));
@@ -35,7 +35,6 @@ pub(super) fn handle_desktop_wrapper_message(dispatcher: &mut DesktopWrapperMess
3535
dispatcher.respond(DesktopFrontendMessage::WriteFile { path, content });
3636
}
3737
},
38-
DesktopWrapperMessage::PollNodeGraphEvaluation => dispatcher.poll_node_graph_evaluation(),
3938
DesktopWrapperMessage::UpdateMaximized { maximized } => {
4039
let message = FrontendMessage::UpdateMaximized { maximized };
4140
dispatcher.queue_editor_message(message);

‎desktop/wrapper/src/lib.rs‎

Lines changed: 26 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
use graph_craft::application_io::PlatformApplicationIo;
22
use graph_craft::application_io::resource::ResourceStorage;
33
use graphite_editor::application::{Editor, Environment, Host, Platform};
4+
use graphite_editor::builder::{Builder, EditorBuilder, RuntimeBuilder};
45
use graphite_editor::messages::frontend::FrontendMessage;
5-
use graphite_editor::messages::prelude::Wake;
6+
use graphite_editor::node_graph_executor::NodeRuntime;
67

78
use message_dispatcher::DesktopWrapperMessageDispatcher;
89
use messages::{DesktopFrontendMessage, DesktopWrapperMessage};
@@ -11,6 +12,7 @@ use std::sync::Arc;
1112
pub use graph_craft::application_io::resource::MmapResourceStorage;
1213
pub use graphite_editor::consts::{DOUBLE_CLICK_MILLISECONDS, FILE_EXTENSION};
1314
pub use graphite_editor::document_store::{DocumentStore, FolderStore};
15+
pub use graphite_editor::task::{Spawn, Task};
1416
pub use wgpu_executor::Texture;
1517
pub use wgpu_executor::WgpuBackends;
1618
pub use wgpu_executor::WgpuContext;
@@ -33,7 +35,15 @@ pub struct DesktopWrapper {
3335
}
3436

3537
impl DesktopWrapper {
36-
pub fn new(uuid_random_seed: u64, resource_storage: Arc<dyn ResourceStorage>, document_store: Arc<dyn DocumentStore>, wgpu_context: WgpuContext, schedule_wake: Wake) -> Self {
38+
pub fn new(
39+
random_seed: u64,
40+
resource_storage: Arc<dyn ResourceStorage>,
41+
document_store: Arc<dyn DocumentStore>,
42+
wgpu_context: WgpuContext,
43+
spawner: impl Spawn + Send + Sync + 'static,
44+
editor_wake: impl Fn() + Send + Sync + 'static,
45+
runtime_wake: impl Fn() + Send + Sync + 'static,
46+
) -> (Self, NodeRuntime) {
3747
#[cfg(target_os = "windows")]
3848
let host = Host::Windows;
3949
#[cfg(target_os = "macos")]
@@ -43,29 +53,27 @@ impl DesktopWrapper {
4353
let env = Environment { platform: Platform::Desktop, host };
4454
let application_io = PlatformApplicationIo::new_with_context(wgpu_context);
4555

46-
Self {
47-
editor: Editor::new(env, uuid_random_seed, resource_storage, document_store, application_io, schedule_wake),
48-
}
56+
let (editor, runtime) = Builder::new()
57+
.with_editor(
58+
EditorBuilder::new()
59+
.with_wake(editor_wake)
60+
.with_environment(env)
61+
.with_resource_storage(resource_storage)
62+
.with_document_store(document_store),
63+
)
64+
.with_runtime(RuntimeBuilder::new().with_wake(runtime_wake).with_application_io(application_io))
65+
.with_spawner(spawner)
66+
.with_random_seed(random_seed)
67+
.build();
68+
69+
(Self { editor }, runtime)
4970
}
5071

5172
pub fn dispatch(&mut self, message: DesktopWrapperMessage) -> Vec<DesktopFrontendMessage> {
5273
let mut executor = DesktopWrapperMessageDispatcher::new(&mut self.editor);
5374
executor.queue_desktop_wrapper_message(message);
5475
executor.execute()
5576
}
56-
57-
pub async fn execute_node_graph() -> NodeGraphExecutionResult {
58-
let result = graphite_editor::node_graph_executor::run_node_graph().await;
59-
match result {
60-
(true, texture) => NodeGraphExecutionResult::HasRun(texture),
61-
(false, _) => NodeGraphExecutionResult::NotRun,
62-
}
63-
}
64-
}
65-
66-
pub enum NodeGraphExecutionResult {
67-
HasRun(Option<Texture>),
68-
NotRun,
6977
}
7078

7179
pub fn deserialize_editor_message(data: &[u8]) -> Option<DesktopWrapperMessage> {

‎desktop/wrapper/src/message_dispatcher.rs‎

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -42,18 +42,6 @@ impl<'a> DesktopWrapperMessageDispatcher<'a> {
4242
self.responses.push(response);
4343
}
4444

45-
pub(super) fn poll_node_graph_evaluation(&mut self) {
46-
let mut responses = VecDeque::new();
47-
if let Err(e) = self.editor.poll_node_graph_evaluation(&mut responses)
48-
&& e != "No active document"
49-
{
50-
tracing::error!("Error polling node graph: {}", e);
51-
}
52-
while let Some(message) = responses.pop_front() {
53-
self.queue_editor_message(message);
54-
}
55-
}
56-
5745
fn process_queue(&mut self) {
5846
let mut frontend_messages = Vec::new();
5947

‎desktop/wrapper/src/messages.rs‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,6 @@ pub enum DesktopWrapperMessage {
7979
Input(InputMessage),
8080
IngestFile { path: PathBuf, content: Vec<u8>, action: IngestAction },
8181
SaveFileDialogResult { path: PathBuf, context: SaveFileDialogContext },
82-
PollNodeGraphEvaluation,
8382
UpdateMaximized { maximized: bool },
8483
UpdateFullscreen { fullscreen: bool },
8584
LoadPersistedState { state: PersistedState },

0 commit comments

Comments
 (0)