Skip to content

Commit 118e239

Browse files
committed
Cleanup code and improve latency / frame pacing
1 parent a415e74 commit 118e239

File tree

14 files changed

+691
-557
lines changed

14 files changed

+691
-557
lines changed

desktop/Cargo.toml

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

1111
[features]
12-
default = ["gpu",]
12+
default = ["gpu"]
1313
gpu = ["graphite-editor/gpu"]
14-
accelerated_paint = ["ash", "libc", "windows", "objc2-io-surface", "objc2-metal", "core-foundation"]
14+
15+
# Hardware acceleration features
16+
accelerated_paint = ["ash"]
17+
accelerated_paint_dmabuf = ["accelerated_paint", "libc"]
18+
accelerated_paint_d3d11 = ["accelerated_paint", "windows"]
19+
accelerated_paint_iosurface = ["accelerated_paint", "objc2-io-surface", "objc2-metal", "core-foundation"]
20+
21+
# Platform-specific feature bundles
22+
accelerated_paint_linux = ["accelerated_paint_dmabuf"]
23+
accelerated_paint_windows = ["accelerated_paint_d3d11"]
24+
accelerated_paint_macos = ["accelerated_paint_iosurface"]
1525

1626
[dependencies]
1727
# # Local dependencies
@@ -44,7 +54,6 @@ image = { workspace = true }
4454

4555
# Hardware acceleration dependencies
4656
ash = { version = "0.38", optional = true }
47-
libc = { version = "0.2", optional = true }
4857

4958
# Windows-specific dependencies
5059
[target.'cfg(windows)'.dependencies]
@@ -60,3 +69,7 @@ windows = { version = "0.58", features = [
6069
objc2-io-surface = { version = "0.3", optional = true }
6170
objc2-metal = { version = "0.3", optional = true }
6271
core-foundation = { version = "0.9", optional = true }
72+
73+
# Linux-specific dependencies
74+
[target.'cfg(target_os = "linux")'.dependencies]
75+
libc = { version = "0.2", optional = true }

desktop/src/app.rs

Lines changed: 37 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,12 @@ use graphite_editor::messages::prelude::*;
1515
use std::fs;
1616
use std::sync::Arc;
1717
use std::sync::mpsc::Sender;
18+
use std::sync::mpsc::SyncSender;
1819
use std::thread;
1920
use std::time::Duration;
2021
use std::time::Instant;
2122
use winit::application::ApplicationHandler;
2223
use winit::dpi::PhysicalSize;
23-
use winit::event::StartCause;
2424
use winit::event::WindowEvent;
2525
use winit::event_loop::ActiveEventLoop;
2626
use winit::event_loop::ControlFlow;
@@ -41,10 +41,23 @@ pub(crate) struct WinitApp {
4141
editor: Editor,
4242
last_ui_update: Instant,
4343
avg_frame_time: f32,
44+
start_render_sender: SyncSender<()>,
4445
}
4546

4647
impl WinitApp {
4748
pub(crate) fn new(cef_context: cef::Context<cef::Initialized>, window_size_sender: Sender<WindowSize>, wgpu_context: WgpuContext, event_loop_proxy: EventLoopProxy<CustomEvent>) -> Self {
49+
let rendering_loop_proxy = event_loop_proxy.clone();
50+
let (start_render_sender, start_render_receiver) = std::sync::mpsc::sync_channel(1);
51+
std::thread::spawn(move || {
52+
loop {
53+
let (has_run, texture) = futures::executor::block_on(graphite_editor::node_graph_executor::run_node_graph());
54+
if has_run {
55+
let _ = rendering_loop_proxy.send_event(CustomEvent::NodeGraphRan(texture.map(|t| (*t.texture).clone())));
56+
}
57+
let _ = start_render_receiver.recv();
58+
}
59+
});
60+
4861
Self {
4962
cef_context,
5063
window: None,
@@ -56,6 +69,7 @@ impl WinitApp {
5669
editor: Editor::new(),
5770
last_ui_update: Instant::now(),
5871
avg_frame_time: 0.,
72+
start_render_sender,
5973
}
6074
}
6175

@@ -153,23 +167,20 @@ impl ApplicationHandler<CustomEvent> for WinitApp {
153167
// Set a timeout in case we miss any cef schedule requests
154168
let timeout = Instant::now() + Duration::from_millis(10);
155169
let wait_until = timeout.min(self.cef_schedule.unwrap_or(timeout));
156-
self.cef_context.work();
157-
158-
event_loop.set_control_flow(ControlFlow::WaitUntil(wait_until));
159-
}
160-
161-
fn new_events(&mut self, _event_loop: &ActiveEventLoop, cause: StartCause) {
162170
if let Some(schedule) = self.cef_schedule
163171
&& schedule < Instant::now()
164172
{
165173
self.cef_schedule = None;
166-
self.cef_context.work();
167-
}
168-
if let StartCause::ResumeTimeReached { .. } = cause {
169-
if let Some(window) = &self.window {
170-
window.request_redraw();
174+
// Poll cef message loop multiple times to avoid message loop starvation
175+
for _ in 0..10 {
176+
self.cef_context.work();
171177
}
172178
}
179+
if let Some(window) = &self.window.as_ref() {
180+
window.request_redraw();
181+
}
182+
183+
event_loop.set_control_flow(ControlFlow::WaitUntil(wait_until));
173184
}
174185

175186
fn resumed(&mut self, event_loop: &ActiveEventLoop) {
@@ -211,8 +222,9 @@ impl ApplicationHandler<CustomEvent> for WinitApp {
211222
graphics_state.bind_ui_texture(texture);
212223
let elapsed = self.last_ui_update.elapsed().as_secs_f32();
213224
self.last_ui_update = Instant::now();
214-
self.avg_frame_time = (self.avg_frame_time * 3. + elapsed) / 4.;
215-
println!("ui fps: {:.2}", 1. / self.avg_frame_time);
225+
if elapsed < 0.5 {
226+
self.avg_frame_time = (self.avg_frame_time * 3. + elapsed) / 4.;
227+
}
216228
}
217229
if let Some(window) = &self.window {
218230
window.request_redraw();
@@ -357,16 +369,18 @@ impl ApplicationHandler<CustomEvent> for WinitApp {
357369
WindowEvent::RedrawRequested => {
358370
let Some(ref mut graphics_state) = self.graphics_state else { return };
359371
// Only rerender once we have a new ui texture to display
360-
361-
match graphics_state.render() {
362-
Ok(_) => {}
363-
Err(wgpu::SurfaceError::Lost) => {
364-
tracing::warn!("lost surface");
365-
}
366-
Err(wgpu::SurfaceError::OutOfMemory) => {
367-
event_loop.exit();
372+
if let Some(window) = &self.window {
373+
match graphics_state.render(window.as_ref()) {
374+
Ok(_) => {}
375+
Err(wgpu::SurfaceError::Lost) => {
376+
tracing::warn!("lost surface");
377+
}
378+
Err(wgpu::SurfaceError::OutOfMemory) => {
379+
event_loop.exit();
380+
}
381+
Err(e) => tracing::error!("{:?}", e),
368382
}
369-
Err(e) => tracing::error!("{:?}", e),
383+
let _ = self.start_render_sender.try_send(());
370384
}
371385
}
372386
_ => {}

desktop/src/cef.rs

Lines changed: 37 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,33 @@
1+
//! CEF (Chromium Embedded Framework) integration for Graphite Desktop
2+
//!
3+
//! This module provides CEF browser integration with hardware-accelerated texture sharing.
4+
//!
5+
//! # Hardware Acceleration
6+
//!
7+
//! The texture import system supports platform-specific hardware acceleration:
8+
//!
9+
//! - **Linux**: DMA-BUF via Vulkan external memory (`accelerated_paint_dmabuf` feature)
10+
//! - **Windows**: D3D11 shared textures via Vulkan interop (`accelerated_paint_d3d11` feature)
11+
//! - **macOS**: IOSurface via Metal/Vulkan interop (`accelerated_paint_iosurface` feature)
12+
//!
13+
//! ## Feature Configuration
14+
//!
15+
//! Enable hardware acceleration with:
16+
//! ```toml
17+
//! [dependencies]
18+
//! graphite-desktop = { features = ["accelerated_paint"] }
19+
//! ```
20+
//!
21+
//! Or enable platform-specific support:
22+
//! ```toml
23+
//! [dependencies]
24+
//! graphite-desktop = { features = ["accelerated_paint_linux"] } # Linux only
25+
//! graphite-desktop = { features = ["accelerated_paint_windows"] } # Windows only
26+
//! graphite-desktop = { features = ["accelerated_paint_macos"] } # macOS only
27+
//! ```
28+
//!
29+
//! The system gracefully falls back to CPU textures when hardware acceleration is unavailable.
30+
131
use crate::{CustomEvent, WgpuContext, render::FrameBufferRef};
232
use std::{
333
sync::{Arc, Mutex, mpsc::Receiver},
@@ -6,26 +36,18 @@ use std::{
636

737
mod context;
838
mod dirs;
9-
mod dmabuf;
10-
#[cfg(target_os = "windows")]
11-
mod d3d11;
12-
#[cfg(target_os = "macos")]
13-
mod iosurface;
1439
mod input;
1540
mod internal;
1641
mod ipc;
1742
mod platform;
1843
mod scheme_handler;
44+
#[cfg(feature = "accelerated_paint")]
45+
mod texture_import;
1946
mod utility;
2047

2148
pub(crate) use context::{Context, InitError, Initialized, Setup, SetupError};
2249
use winit::event_loop::EventLoopProxy;
2350

24-
#[cfg(target_os = "windows")]
25-
use crate::cef::d3d11::D3D11SharedTexture;
26-
#[cfg(target_os = "macos")]
27-
use crate::cef::iosurface::IOSurfaceTexture;
28-
2951
pub(crate) trait CefEventHandler: Clone {
3052
fn window_size(&self) -> WindowSize;
3153
fn draw<'a>(&self, frame_buffer: FrameBufferRef<'a>);
@@ -147,71 +169,12 @@ impl CefEventHandler for CefHandler {
147169

148170
#[cfg(feature = "accelerated_paint")]
149171
fn on_accelerated_paint(&self, shared_handle: internal::render_handler::SharedTextureHandle) {
150-
#[cfg(target_os = "linux")]
151-
use crate::cef::dmabuf::DmaBufTexture;
152-
153-
match shared_handle {
154-
#[cfg(target_os = "linux")]
155-
internal::render_handler::SharedTextureHandle::DmaBuf {
156-
fds,
157-
format,
158-
modifier,
159-
width,
160-
height,
161-
strides,
162-
offsets,
163-
} => {
164-
let dmabuf_texture = DmaBufTexture {
165-
fds,
166-
format,
167-
modifier,
168-
width,
169-
height,
170-
strides,
171-
offsets,
172-
};
173-
match dmabuf_texture.import_to_wgpu(&self.wgpu_context.device) {
174-
Ok(texture) => {
175-
let _ = self.event_loop_proxy.send_event(CustomEvent::UiUpdate(texture));
176-
}
177-
Err(e) => {
178-
tracing::error!("Failed to import DMA-BUF texture: {}", e);
179-
}
180-
}
181-
}
182-
#[cfg(target_os = "windows")]
183-
internal::render_handler::SharedTextureHandle::D3D11 { handle, format, width, height } => {
184-
let d3d11_texture = D3D11SharedTexture {
185-
handle,
186-
width,
187-
height,
188-
format,
189-
};
190-
match d3d11_texture.import_to_wgpu(&self.wgpu_context.device) {
191-
Ok(texture) => {
192-
let _ = self.event_loop_proxy.send_event(CustomEvent::UiUpdate(texture));
193-
}
194-
Err(e) => {
195-
tracing::error!("Failed to import D3D11 shared texture: {}", e);
196-
}
197-
}
172+
match self::texture_import::import_texture(shared_handle, &self.wgpu_context.device) {
173+
Ok(texture) => {
174+
let _ = self.event_loop_proxy.send_event(CustomEvent::UiUpdate(texture));
198175
}
199-
#[cfg(target_os = "macos")]
200-
internal::render_handler::SharedTextureHandle::IOSurface { handle, format, width, height } => {
201-
let iosurface_texture = IOSurfaceTexture {
202-
handle,
203-
width,
204-
height,
205-
format,
206-
};
207-
match iosurface_texture.import_to_wgpu(&self.wgpu_context.device) {
208-
Ok(texture) => {
209-
let _ = self.event_loop_proxy.send_event(CustomEvent::UiUpdate(texture));
210-
}
211-
Err(e) => {
212-
tracing::error!("Failed to import IOSurface texture: {}", e);
213-
}
214-
}
176+
Err(e) => {
177+
tracing::error!("Failed to import shared texture: {}", e);
215178
}
216179
}
217180
}

desktop/src/cef/internal/browser_process_app.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ impl<H: CefEventHandler + Clone> ImplApp for BrowserProcessAppImpl<H> {
4040
cmd.append_switch(Some(&CefString::from("disable-gpu")));
4141
cmd.append_switch(Some(&CefString::from("disable-gpu-compositing")));
4242
}
43-
43+
4444
#[cfg(feature = "accelerated_paint")]
4545
{
4646
// Enable GPU acceleration switches for better performance

desktop/src/cef/internal/render_handler.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use cef::{Browser, ImplRenderHandler, PaintElementType, Rect, WrapRenderHandler}
55
use crate::cef::CefEventHandler;
66
use crate::render::FrameBufferRef;
77

8-
#[cfg(target_os = "linux")]
8+
#[cfg(all(target_os = "linux", feature = "accelerated_paint"))]
99
use std::os::fd::RawFd;
1010
#[cfg(all(feature = "accelerated_paint", any(target_os = "windows", target_os = "macos")))]
1111
use std::os::raw::c_void;

0 commit comments

Comments
 (0)