Skip to content

Commit 481a718

Browse files
committed
Desktop: Execute editor and node graph natively
1 parent 35ab266 commit 481a718

16 files changed

Lines changed: 241 additions & 78 deletions

File tree

Cargo.lock

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

desktop/Cargo.toml

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,17 +9,20 @@ edition = "2024"
99
rust-version = "1.87"
1010

1111
[features]
12-
# default = ["gpu"]
13-
# gpu = ["graphite-editor/gpu"]
12+
default = ["gpu"]
13+
gpu = ["graphite-editor/gpu"]
1414

1515
[dependencies]
16-
# Local dependencies
17-
# graphite-editor = { path = "../editor", features = [
18-
# "gpu",
19-
# "ron",
20-
# "vello",
21-
# "decouple-execution",
22-
# ] }
16+
# # Local dependencies
17+
graphite-editor = { path = "../editor", features = [
18+
"gpu",
19+
"ron",
20+
"vello",
21+
"decouple-execution",
22+
] }
23+
graph-craft = { workspace = true }
24+
wgpu-executor = { workspace = true }
25+
2326
wgpu = { workspace = true }
2427
winit = { workspace = true, features = ["serde"] }
2528
thiserror = { workspace = true }
@@ -29,4 +32,6 @@ include_dir = { workspace = true }
2932
tracing-subscriber = { workspace = true }
3033
tracing = { workspace = true }
3134
dirs = { workspace = true }
35+
ron = { workspace = true}
3236
bytemuck = { workspace = true }
37+
glam = { workspace = true }

desktop/src/app.rs

Lines changed: 130 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@ use crate::CustomEvent;
22
use crate::WindowSize;
33
use crate::render::GraphicsState;
44
use crate::render::WgpuContext;
5+
use graph_craft::wasm_application_io::WasmApplicationIo;
6+
use graphite_editor::application::Editor;
7+
use graphite_editor::messages::prelude::*;
58
use std::sync::Arc;
69
use std::sync::mpsc::Sender;
710
use std::time::Duration;
@@ -21,11 +24,13 @@ pub(crate) struct WinitApp {
2124
pub(crate) cef_context: cef::Context<cef::Initialized>,
2225
pub(crate) window: Option<Arc<Window>>,
2326
cef_schedule: Option<Instant>,
27+
// Cached frame buffer from CEF, used to check if mouse is on a transparent pixel
2428
_ui_frame_buffer: Option<wgpu::Texture>,
2529
window_size_sender: Sender<WindowSize>,
2630
_viewport_frame_buffer: Option<wgpu::Texture>,
2731
graphics_state: Option<GraphicsState>,
2832
wgpu_context: WgpuContext,
33+
pub(crate) editor: Editor,
2934
}
3035

3136
impl WinitApp {
@@ -39,8 +44,25 @@ impl WinitApp {
3944
graphics_state: None,
4045
window_size_sender,
4146
wgpu_context,
47+
editor: Editor::new(),
4248
}
4349
}
50+
51+
fn dispatch_message(&mut self, message: Message) {
52+
let responses = self.editor.handle_message(message);
53+
self.send_messages_to_editor(responses);
54+
}
55+
56+
fn send_messages_to_editor(&mut self, responses: Vec<FrontendMessage>) {
57+
if responses.is_empty() {
58+
return;
59+
}
60+
let Ok(message) = ron::to_string(&responses) else {
61+
tracing::error!("Failed to serialize Messages");
62+
return;
63+
};
64+
self.cef_context.send_web_message(message.as_bytes());
65+
}
4466
}
4567

4668
impl ApplicationHandler<CustomEvent> for WinitApp {
@@ -49,16 +71,41 @@ impl ApplicationHandler<CustomEvent> for WinitApp {
4971
let timeout = Instant::now() + Duration::from_millis(10);
5072
let wait_until = timeout.min(self.cef_schedule.unwrap_or(timeout));
5173
self.cef_context.work();
74+
75+
let (_has_run, texture) = futures::executor::block_on(graphite_editor::node_graph_executor::run_node_graph());
76+
if _has_run {
77+
let mut responses = VecDeque::new();
78+
let err = self.editor.poll_node_graph_evaluation(&mut responses);
79+
if let Err(e) = err {
80+
tracing::error!("Error poling node graph: {}", e);
81+
}
82+
let frontend_messages = responses
83+
.into_iter()
84+
.flat_map(|response| if let Message::Frontend(frontend) = response { Some(frontend) } else { None })
85+
.collect();
86+
self.send_messages_to_editor(frontend_messages);
87+
}
88+
if let Some(texture) = texture
89+
&& let Some(graphics_state) = &mut self.graphics_state
90+
{
91+
graphics_state.bind_viewport_texture(texture.texture.as_ref());
92+
}
93+
5294
event_loop.set_control_flow(ControlFlow::WaitUntil(wait_until));
5395
}
5496

55-
fn new_events(&mut self, _event_loop: &ActiveEventLoop, _cause: StartCause) {
97+
fn new_events(&mut self, _event_loop: &ActiveEventLoop, cause: StartCause) {
5698
if let Some(schedule) = self.cef_schedule
5799
&& schedule < Instant::now()
58100
{
59101
self.cef_schedule = None;
60102
self.cef_context.work();
61103
}
104+
if let StartCause::ResumeTimeReached { .. } = cause {
105+
if let Some(window) = &self.window {
106+
window.request_redraw();
107+
}
108+
}
62109
}
63110

64111
fn resumed(&mut self, event_loop: &ActiveEventLoop) {
@@ -71,12 +118,67 @@ impl ApplicationHandler<CustomEvent> for WinitApp {
71118
)
72119
.unwrap(),
73120
);
74-
let graphics_state = GraphicsState::new(window.clone(), self.wgpu_context.clone());
121+
let mut graphics_state = GraphicsState::new(window.clone(), self.wgpu_context.clone());
122+
123+
let mut test_data = vec![0u8; 800 * 600 * 4];
124+
125+
for y in 0..600 {
126+
for x in 0..800 {
127+
let idx = (y * 800 + x) * 4;
128+
test_data[idx + 1] = (x * 255 / 800) as u8; // Blue
129+
test_data[idx + 2] = (y * 255 / 600) as u8; // Green
130+
test_data[idx] = 255; // Red
131+
test_data[idx + 3] = 255; // Alpha
132+
}
133+
}
134+
135+
let texture = self.wgpu_context.device.create_texture(&wgpu::TextureDescriptor {
136+
label: Some("Viewport Texture"),
137+
size: wgpu::Extent3d {
138+
width: 800,
139+
height: 600,
140+
depth_or_array_layers: 1,
141+
},
142+
mip_level_count: 1,
143+
sample_count: 1,
144+
dimension: wgpu::TextureDimension::D2,
145+
format: wgpu::TextureFormat::Bgra8UnormSrgb,
146+
usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST,
147+
view_formats: &[],
148+
});
149+
150+
self.wgpu_context.queue.write_texture(
151+
wgpu::TexelCopyTextureInfo {
152+
texture: &texture,
153+
mip_level: 0,
154+
origin: wgpu::Origin3d::ZERO,
155+
aspect: wgpu::TextureAspect::All,
156+
},
157+
test_data.as_slice(),
158+
wgpu::TexelCopyBufferLayout {
159+
offset: 0,
160+
bytes_per_row: Some(4 * 800),
161+
rows_per_image: Some(600),
162+
},
163+
wgpu::Extent3d {
164+
width: 800,
165+
height: 600,
166+
depth_or_array_layers: 1,
167+
},
168+
);
169+
170+
graphics_state.bind_viewport_texture(&texture);
75171

76172
self.window = Some(window);
77173
self.graphics_state = Some(graphics_state);
78174

79175
tracing::info!("Winit window created and ready");
176+
177+
graphite_editor::application::set_uuid_seed(42);
178+
179+
let application_io = WasmApplicationIo::new_with_context(self.wgpu_context.clone());
180+
181+
futures::executor::block_on(graphite_editor::node_graph_executor::replace_application_io(application_io));
80182
}
81183

82184
fn user_event(&mut self, _: &ActiveEventLoop, event: CustomEvent) {
@@ -97,6 +199,32 @@ impl ApplicationHandler<CustomEvent> for WinitApp {
97199
self.cef_schedule = Some(instant);
98200
}
99201
}
202+
CustomEvent::MessageReceived { message } => {
203+
if let Message::InputPreprocessor(ipp_message) = &message {
204+
if let Some(window) = &self.window {
205+
window.request_redraw();
206+
}
207+
if let InputPreprocessorMessage::CurrentTime { .. } | InputPreprocessorMessage::PointerMove { .. } = &ipp_message {
208+
} else {
209+
// println!("got ipp message: {:?}", &ipp_message.to_discriminant());
210+
}
211+
}
212+
if let Message::InputPreprocessor(InputPreprocessorMessage::BoundsOfViewports { bounds_of_viewports }) = &message {
213+
if let Some(graphic_state) = &mut self.graphics_state {
214+
let window_size = self.window.as_ref().unwrap().inner_size();
215+
let window_size = glam::Vec2::new(window_size.width as f32, window_size.height as f32);
216+
let top_left = bounds_of_viewports[0].top_left.as_vec2() / window_size;
217+
let bottom_right = bounds_of_viewports[0].bottom_right.as_vec2() / window_size;
218+
let offset = top_left.to_array();
219+
let scale = (bottom_right - top_left).recip();
220+
graphic_state.set_viewport_offset(offset);
221+
graphic_state.set_viewport_scale(scale.to_array());
222+
} else {
223+
panic!("graphics state not intialized, viewport offset might be lost");
224+
}
225+
}
226+
self.dispatch_message(message);
227+
}
100228
}
101229
}
102230

desktop/src/cef.rs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,5 +120,15 @@ impl CefEventHandler for CefHandler {
120120
let _ = self.event_loop_proxy.send_event(CustomEvent::ScheduleBrowserWork(scheduled_time));
121121
}
122122

123-
fn receive_web_message(&self, message: &[u8]) {}
123+
fn receive_web_message(&self, message: &[u8]) {
124+
let str = std::str::from_utf8(message).unwrap();
125+
match ron::from_str(str) {
126+
Ok(message) => {
127+
let _ = self.event_loop_proxy.send_event(CustomEvent::MessageReceived { message });
128+
}
129+
Err(e) => {
130+
tracing::error!("Failed to deserialize message {:?}", e)
131+
}
132+
}
133+
}
124134
}

desktop/src/cef/internal/render_process_handler.rs

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,6 @@
11
use cef::rc::{ConvertReturnValue, Rc, RcImpl};
22
use cef::sys::{_cef_render_process_handler_t, cef_base_ref_counted_t, cef_render_process_handler_t, cef_v8_propertyattribute_t, cef_v8_value_create_array_buffer_with_copy};
3-
use cef::{
4-
CefString, ImplFrame, ImplRenderProcessHandler, ImplV8Context, ImplV8Value, V8Handler, V8Propertyattribute, V8Value, WrapRenderProcessHandler, v8_context_get_entered_context,
5-
v8_value_create_function,
6-
};
3+
use cef::{CefString, ImplFrame, ImplRenderProcessHandler, ImplV8Context, ImplV8Value, V8Handler, V8Propertyattribute, V8Value, WrapRenderProcessHandler, v8_value_create_function};
74

85
use crate::cef::ipc::{MessageType, UnpackMessage, UnpackedMessage};
96

@@ -61,7 +58,9 @@ impl ImplRenderProcessHandler for RenderProcessHandlerImpl {
6158
cef_v8_propertyattribute_t::V8_PROPERTY_ATTRIBUTE_READONLY.wrap_result(),
6259
);
6360

64-
frame.execute_java_script(Some(&CefString::from(function_call.as_str())), None, 0);
61+
if global.value_bykey(Some(&CefString::from(function_name))).is_some() {
62+
frame.execute_java_script(Some(&CefString::from(function_call.as_str())), None, 0);
63+
}
6564

6665
if context.exit() == 0 {
6766
tracing::error!("Failed to exit V8 context");

desktop/src/cef/ipc.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use cef::{CefString, Frame, ImplBinaryValue, ImplBrowser, ImplFrame, ImplListValue, ImplProcessMessage, ImplV8Context, ProcessId, V8Context, rc::ConvertParam, sys::cef_process_id_t};
1+
use cef::{CefString, Frame, ImplBinaryValue, ImplBrowser, ImplFrame, ImplListValue, ImplProcessMessage, ImplV8Context, ProcessId, V8Context, sys::cef_process_id_t};
22

33
use super::{Context, Initialized};
44

desktop/src/main.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ use std::fmt::Debug;
22
use std::process::exit;
33
use std::time::Instant;
44

5+
use graphite_editor::messages::prelude::Message;
56
use tracing_subscriber::EnvFilter;
67
use winit::event_loop::EventLoop;
78

@@ -20,6 +21,7 @@ mod dirs;
2021
pub(crate) enum CustomEvent {
2122
UiUpdate(wgpu::Texture),
2223
ScheduleBrowserWork(Instant),
24+
MessageReceived { message: Message },
2325
}
2426

2527
fn main() {
@@ -38,7 +40,7 @@ fn main() {
3840

3941
let (window_size_sender, window_size_receiver) = std::sync::mpsc::channel();
4042

41-
let wgpu_context = futures::executor::block_on(WgpuContext::new());
43+
let wgpu_context = futures::executor::block_on(WgpuContext::new()).unwrap();
4244
let cef_context = match cef_context.init(cef::CefHandler::new(window_size_receiver, event_loop.create_proxy(), wgpu_context.clone())) {
4345
Ok(c) => c,
4446
Err(cef::InitError::AlreadyRunning) => {
@@ -51,6 +53,8 @@ fn main() {
5153
}
5254
};
5355

56+
tracing::info!("Cef initialized successfully");
57+
5458
let mut winit_app = WinitApp::new(cef_context, window_size_sender, wgpu_context);
5559

5660
event_loop.run_app(&mut winit_app).unwrap();

desktop/src/render.rs

Lines changed: 1 addition & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -56,46 +56,7 @@ pub(crate) enum FrameBufferError {
5656
InvalidSize { buffer_size: usize, expected_size: usize, width: usize, height: usize },
5757
}
5858

59-
#[derive(Debug, Clone)]
60-
pub(crate) struct WgpuContext {
61-
pub(crate) device: wgpu::Device,
62-
pub(crate) queue: wgpu::Queue,
63-
adapter: wgpu::Adapter,
64-
instance: wgpu::Instance,
65-
}
66-
67-
impl WgpuContext {
68-
pub(crate) async fn new() -> Self {
69-
let instance = wgpu::Instance::new(&wgpu::InstanceDescriptor {
70-
backends: wgpu::Backends::PRIMARY,
71-
..Default::default()
72-
});
73-
74-
let adapter = instance
75-
.request_adapter(&wgpu::RequestAdapterOptions {
76-
power_preference: wgpu::PowerPreference::default(),
77-
compatible_surface: None,
78-
force_fallback_adapter: false,
79-
})
80-
.await
81-
.unwrap();
82-
83-
let required_limits = adapter.limits();
84-
85-
let (device, queue) = adapter
86-
.request_device(&wgpu::DeviceDescriptor {
87-
label: None,
88-
required_features: wgpu::Features::PUSH_CONSTANTS,
89-
required_limits,
90-
memory_hints: Default::default(),
91-
trace: wgpu::Trace::Off,
92-
})
93-
.await
94-
.unwrap();
95-
96-
Self { device, queue, adapter, instance }
97-
}
98-
}
59+
pub use wgpu_executor::Context as WgpuContext;
9960

10061
#[derive(Debug)]
10162
pub(crate) struct GraphicsState {

editor/src/messages/portfolio/portfolio_message_handler.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -883,6 +883,7 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageContext<'_>> for Portfolio
883883
responses.add(FrontendMessage::UpdateOpenDocumentsList { open_documents });
884884
}
885885
PortfolioMessage::UpdateVelloPreference => {
886+
responses.add(FrontendMessage::UpdateViewportHolePunch { active: preferences.use_vello });
886887
responses.add(NodeGraphMessage::RunDocumentGraph);
887888
self.persistent_data.use_vello = preferences.use_vello;
888889
}

0 commit comments

Comments
 (0)