Skip to content

Commit a52ee70

Browse files
Desktop: Directly upload frame buffer (#2930)
* Upload frame buffer directly to gpu texture * Disable cef GPU acceleration to prevent crashes * Cleanup code * Address review comments --------- Co-authored-by: Timon Schelling <[email protected]>
1 parent 735d58a commit a52ee70

File tree

7 files changed

+149
-157
lines changed

7 files changed

+149
-157
lines changed

desktop/src/app.rs

Lines changed: 16 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use crate::CustomEvent;
2-
use crate::FrameBuffer;
32
use crate::WindowSize;
43
use crate::render::GraphicsState;
4+
use crate::render::WgpuContext;
55
use std::sync::Arc;
66
use std::sync::mpsc::Sender;
77
use std::time::Duration;
@@ -12,7 +12,6 @@ use winit::event::StartCause;
1212
use winit::event::WindowEvent;
1313
use winit::event_loop::ActiveEventLoop;
1414
use winit::event_loop::ControlFlow;
15-
use winit::event_loop::EventLoopProxy;
1615
use winit::window::Window;
1716
use winit::window::WindowId;
1817

@@ -22,33 +21,34 @@ pub(crate) struct WinitApp {
2221
pub(crate) cef_context: cef::Context<cef::Initialized>,
2322
pub(crate) window: Option<Arc<Window>>,
2423
cef_schedule: Option<Instant>,
25-
ui_frame_buffer: Option<FrameBuffer>,
24+
_ui_frame_buffer: Option<wgpu::Texture>,
2625
window_size_sender: Sender<WindowSize>,
27-
_viewport_frame_buffer: Option<FrameBuffer>,
26+
_viewport_frame_buffer: Option<wgpu::Texture>,
2827
graphics_state: Option<GraphicsState>,
29-
event_loop_proxy: EventLoopProxy<CustomEvent>,
28+
wgpu_context: WgpuContext,
3029
}
3130

3231
impl WinitApp {
33-
pub(crate) fn new(cef_context: cef::Context<cef::Initialized>, window_size_sender: Sender<WindowSize>, event_loop_proxy: EventLoopProxy<CustomEvent>) -> Self {
32+
pub(crate) fn new(cef_context: cef::Context<cef::Initialized>, window_size_sender: Sender<WindowSize>, wgpu_context: WgpuContext) -> Self {
3433
Self {
3534
cef_context,
3635
window: None,
3736
cef_schedule: Some(Instant::now()),
3837
_viewport_frame_buffer: None,
39-
ui_frame_buffer: None,
38+
_ui_frame_buffer: None,
4039
graphics_state: None,
4140
window_size_sender,
42-
event_loop_proxy,
41+
wgpu_context,
4342
}
4443
}
4544
}
4645

4746
impl ApplicationHandler<CustomEvent> for WinitApp {
4847
fn about_to_wait(&mut self, event_loop: &ActiveEventLoop) {
4948
// Set a timeout in case we miss any cef schedule requests
50-
let timeout = Instant::now() + Duration::from_millis(100);
49+
let timeout = Instant::now() + Duration::from_millis(10);
5150
let wait_until = timeout.min(self.cef_schedule.unwrap_or(timeout));
51+
self.cef_context.work();
5252
event_loop.set_control_flow(ControlFlow::WaitUntil(wait_until));
5353
}
5454

@@ -71,7 +71,7 @@ impl ApplicationHandler<CustomEvent> for WinitApp {
7171
)
7272
.unwrap(),
7373
);
74-
let graphics_state = futures::executor::block_on(GraphicsState::new(window.clone()));
74+
let graphics_state = GraphicsState::new(window.clone(), self.wgpu_context.clone());
7575

7676
self.window = Some(window);
7777
self.graphics_state = Some(graphics_state);
@@ -81,24 +81,21 @@ impl ApplicationHandler<CustomEvent> for WinitApp {
8181

8282
fn user_event(&mut self, _: &ActiveEventLoop, event: CustomEvent) {
8383
match event {
84-
CustomEvent::UiUpdate(frame_buffer) => {
84+
CustomEvent::UiUpdate(texture) => {
8585
if let Some(graphics_state) = self.graphics_state.as_mut() {
86-
graphics_state.update_texture(&frame_buffer);
86+
graphics_state.bind_texture(&texture);
87+
graphics_state.resize(texture.width(), texture.height());
8788
}
88-
self.ui_frame_buffer = Some(frame_buffer);
8989
if let Some(window) = &self.window {
9090
window.request_redraw();
9191
}
9292
}
9393
CustomEvent::ScheduleBrowserWork(instant) => {
94-
if let Some(graphics_state) = self.graphics_state.as_mut()
95-
&& let Some(frame_buffer) = &self.ui_frame_buffer
96-
&& graphics_state.ui_texture_outdated(frame_buffer)
97-
{
94+
if instant <= Instant::now() {
9895
self.cef_context.work();
99-
let _ = self.event_loop_proxy.send_event(CustomEvent::ScheduleBrowserWork(Instant::now() + Duration::from_millis(1)));
96+
} else {
97+
self.cef_schedule = Some(instant);
10098
}
101-
self.cef_schedule = Some(instant);
10299
}
103100
}
104101
}
@@ -113,9 +110,6 @@ impl ApplicationHandler<CustomEvent> for WinitApp {
113110
}
114111
WindowEvent::Resized(PhysicalSize { width, height }) => {
115112
let _ = self.window_size_sender.send(WindowSize::new(width as usize, height as usize));
116-
if let Some(ref mut graphics_state) = self.graphics_state {
117-
graphics_state.resize(width, height);
118-
}
119113
self.cef_context.notify_of_resize();
120114
}
121115

desktop/src/cef.rs

Lines changed: 43 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use crate::{CustomEvent, FrameBuffer};
1+
use crate::{CustomEvent, WgpuContext, render::FrameBufferRef};
22
use std::{
33
sync::{Arc, Mutex, mpsc::Receiver},
44
time::Instant,
@@ -15,7 +15,7 @@ use winit::event_loop::EventLoopProxy;
1515

1616
pub(crate) trait CefEventHandler: Clone {
1717
fn window_size(&self) -> WindowSize;
18-
fn draw(&self, frame_buffer: FrameBuffer);
18+
fn draw<'a>(&self, frame_buffer: FrameBufferRef<'a>);
1919
/// Scheudule the main event loop to run the cef event loop after the timeout
2020
/// [`_cef_browser_process_handler_t::on_schedule_message_pump_work`] for more documentation.
2121
fn schedule_cef_message_loop_work(&self, scheduled_time: Instant);
@@ -37,6 +37,7 @@ impl WindowSize {
3737
pub(crate) struct CefHandler {
3838
window_size_receiver: Arc<Mutex<WindowSizeReceiver>>,
3939
event_loop_proxy: EventLoopProxy<CustomEvent>,
40+
wgpu_context: WgpuContext,
4041
}
4142
struct WindowSizeReceiver {
4243
receiver: Receiver<WindowSize>,
@@ -51,10 +52,11 @@ impl WindowSizeReceiver {
5152
}
5253
}
5354
impl CefHandler {
54-
pub(crate) fn new(window_size_receiver: Receiver<WindowSize>, event_loop_proxy: EventLoopProxy<CustomEvent>) -> Self {
55+
pub(crate) fn new(window_size_receiver: Receiver<WindowSize>, event_loop_proxy: EventLoopProxy<CustomEvent>, wgpu_context: WgpuContext) -> Self {
5556
Self {
5657
window_size_receiver: Arc::new(Mutex::new(WindowSizeReceiver::new(window_size_receiver))),
5758
event_loop_proxy,
59+
wgpu_context,
5860
}
5961
}
6062
}
@@ -71,8 +73,44 @@ impl CefEventHandler for CefHandler {
7173
}
7274
*window_size
7375
}
74-
fn draw(&self, frame_buffer: FrameBuffer) {
75-
let _ = self.event_loop_proxy.send_event(CustomEvent::UiUpdate(frame_buffer));
76+
fn draw<'a>(&self, frame_buffer: FrameBufferRef<'a>) {
77+
let width = frame_buffer.width() as u32;
78+
let height = frame_buffer.height() as u32;
79+
let texture = self.wgpu_context.device.create_texture(&wgpu::TextureDescriptor {
80+
label: Some("CEF Texture"),
81+
size: wgpu::Extent3d {
82+
width,
83+
height,
84+
depth_or_array_layers: 1,
85+
},
86+
mip_level_count: 1,
87+
sample_count: 1,
88+
dimension: wgpu::TextureDimension::D2,
89+
format: wgpu::TextureFormat::Bgra8UnormSrgb,
90+
usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST,
91+
view_formats: &[],
92+
});
93+
self.wgpu_context.queue.write_texture(
94+
wgpu::TexelCopyTextureInfo {
95+
texture: &texture,
96+
mip_level: 0,
97+
origin: wgpu::Origin3d::ZERO,
98+
aspect: wgpu::TextureAspect::All,
99+
},
100+
frame_buffer.buffer(),
101+
wgpu::TexelCopyBufferLayout {
102+
offset: 0,
103+
bytes_per_row: Some(4 * width),
104+
rows_per_image: Some(height),
105+
},
106+
wgpu::Extent3d {
107+
width,
108+
height,
109+
depth_or_array_layers: 1,
110+
},
111+
);
112+
113+
let _ = self.event_loop_proxy.send_event(CustomEvent::UiUpdate(texture));
76114
}
77115

78116
fn schedule_cef_message_loop_work(&self, scheduled_time: std::time::Instant) {

desktop/src/cef/internal/app.rs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use cef::rc::{Rc, RcImpl};
22
use cef::sys::{_cef_app_t, cef_base_ref_counted_t};
3-
use cef::{BrowserProcessHandler, ImplApp, SchemeRegistrar, WrapApp};
3+
use cef::{BrowserProcessHandler, CefString, ImplApp, ImplCommandLine, SchemeRegistrar, WrapApp};
44

55
use crate::cef::CefEventHandler;
66
use crate::cef::scheme_handler::GraphiteSchemeHandlerFactory;
@@ -29,6 +29,14 @@ impl<H: CefEventHandler + Clone> ImplApp for AppImpl<H> {
2929
GraphiteSchemeHandlerFactory::register_schemes(registrar);
3030
}
3131

32+
fn on_before_command_line_processing(&self, _process_type: Option<&cef::CefString>, command_line: Option<&mut cef::CommandLine>) {
33+
if let Some(cmd) = command_line {
34+
// Disable GPU acceleration, because it is not supported for Offscreen Rendering and can cause crashes.
35+
cmd.append_switch(Some(&CefString::from("disable-gpu")));
36+
cmd.append_switch(Some(&CefString::from("disable-gpu-compositing")));
37+
}
38+
}
39+
3240
fn get_raw(&self) -> *mut _cef_app_t {
3341
self.object.cast()
3442
}

desktop/src/cef/internal/browser_process_handler.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,13 @@ impl<H: CefEventHandler + Clone> ImplBrowserProcessHandler for BrowserProcessHan
2525
cef::register_scheme_handler_factory(Some(&CefString::from(GRAPHITE_SCHEME)), None, Some(&mut SchemeHandlerFactory::new(GraphiteSchemeHandlerFactory::new())));
2626
}
2727

28-
fn get_raw(&self) -> *mut _cef_browser_process_handler_t {
29-
self.object.cast()
30-
}
31-
3228
fn on_schedule_message_pump_work(&self, delay_ms: i64) {
3329
self.event_handler.schedule_cef_message_loop_work(Instant::now() + Duration::from_millis(delay_ms as u64));
3430
}
31+
32+
fn get_raw(&self) -> *mut _cef_browser_process_handler_t {
33+
self.object.cast()
34+
}
3535
}
3636

3737
impl<H: CefEventHandler + Clone> Clone for BrowserProcessHandlerImpl<H> {

desktop/src/cef/internal/render_handler.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@ use cef::rc::{Rc, RcImpl};
22
use cef::sys::{_cef_render_handler_t, cef_base_ref_counted_t};
33
use cef::{Browser, ImplRenderHandler, PaintElementType, Rect, WrapRenderHandler};
44

5-
use crate::FrameBuffer;
65
use crate::cef::CefEventHandler;
6+
use crate::render::FrameBufferRef;
77

88
pub(crate) struct RenderHandlerImpl<H: CefEventHandler> {
99
object: *mut RcImpl<_cef_render_handler_t, Self>,
@@ -42,7 +42,7 @@ impl<H: CefEventHandler> ImplRenderHandler for RenderHandlerImpl<H> {
4242
) {
4343
let buffer_size = (width * height * 4) as usize;
4444
let buffer_slice = unsafe { std::slice::from_raw_parts(buffer, buffer_size) };
45-
let frame_buffer = FrameBuffer::new(buffer_slice.to_vec(), width as usize, height as usize).expect("Failed to create frame buffer");
45+
let frame_buffer = FrameBufferRef::new(buffer_slice, width as usize, height as usize).expect("Failed to create frame buffer");
4646

4747
self.event_handler.draw(frame_buffer)
4848
}

desktop/src/main.rs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ mod cef;
99
use cef::{Setup, WindowSize};
1010

1111
mod render;
12-
use render::FrameBuffer;
12+
use render::WgpuContext;
1313

1414
mod app;
1515
use app::WinitApp;
@@ -18,7 +18,7 @@ mod dirs;
1818

1919
#[derive(Debug)]
2020
pub(crate) enum CustomEvent {
21-
UiUpdate(FrameBuffer),
21+
UiUpdate(wgpu::Texture),
2222
ScheduleBrowserWork(Instant),
2323
}
2424

@@ -38,7 +38,8 @@ fn main() {
3838

3939
let (window_size_sender, window_size_receiver) = std::sync::mpsc::channel();
4040

41-
let cef_context = match cef_context.init(cef::CefHandler::new(window_size_receiver, event_loop.create_proxy())) {
41+
let wgpu_context = futures::executor::block_on(WgpuContext::new());
42+
let cef_context = match cef_context.init(cef::CefHandler::new(window_size_receiver, event_loop.create_proxy(), wgpu_context.clone())) {
4243
Ok(c) => c,
4344
Err(cef::InitError::InitializationFailed) => {
4445
tracing::error!("Cef initialization failed");
@@ -48,7 +49,7 @@ fn main() {
4849

4950
tracing::info!("Cef initialized successfully");
5051

51-
let mut winit_app = WinitApp::new(cef_context, window_size_sender, event_loop.create_proxy());
52+
let mut winit_app = WinitApp::new(cef_context, window_size_sender, wgpu_context);
5253

5354
event_loop.run_app(&mut winit_app).unwrap();
5455
}

0 commit comments

Comments
 (0)