diff --git a/crates/gpui/src/platform/linux/blade_renderer.rs b/crates/gpui/src/platform/linux/blade_renderer.rs index 1f87b30efe96a75b5e3885da0316805618635306..e37129d622600cc284120616ebb2950394ae8cd2 100644 --- a/crates/gpui/src/platform/linux/blade_renderer.rs +++ b/crates/gpui/src/platform/linux/blade_renderer.rs @@ -1,11 +1,58 @@ +use crate::Scene; + use std::sync::Arc; +const SURFACE_FRAME_COUNT: u32 = 3; +const MAX_FRAME_TIME_MS: u32 = 1000; + pub struct BladeRenderer { gpu: Arc, + command_encoder: blade::CommandEncoder, + last_sync_point: Option, } impl BladeRenderer { - pub fn new(gpu: Arc) -> Self { - Self { gpu } + pub fn new(gpu: Arc, size: blade::Extent) -> Self { + let _surface_format = gpu.resize(blade::SurfaceConfig { + size, + usage: blade::TextureUsage::TARGET, + frame_count: SURFACE_FRAME_COUNT, + }); + let command_encoder = gpu.create_command_encoder(blade::CommandEncoderDesc { + name: "main", + buffer_count: 2, + }); + Self { + gpu, + command_encoder, + last_sync_point: None, + } + } + + pub fn destroy(&mut self) { + self.gpu.destroy_command_encoder(&mut self.command_encoder); + } + + pub fn resize(&mut self, size: blade::Extent) { + self.gpu.resize(blade::SurfaceConfig { + size, + usage: blade::TextureUsage::TARGET, + frame_count: SURFACE_FRAME_COUNT, + }); + } + + pub fn draw(&mut self, scene: &Scene) { + let frame = self.gpu.acquire_frame(); + self.command_encoder.start(); + + self.command_encoder.present(frame); + + let sync_point = self.gpu.submit(&mut self.command_encoder); + if let Some(ref last_sp) = self.last_sync_point { + if !self.gpu.wait_for(last_sp, MAX_FRAME_TIME_MS) { + panic!("GPU hung"); + } + } + self.last_sync_point = Some(sync_point); } } diff --git a/crates/gpui/src/platform/linux/platform.rs b/crates/gpui/src/platform/linux/platform.rs index 91eacc9d4247851ce04b6e919daefc8b78718e56..b708f28e4d97987ff7ed423ecd3133b7f272dbff 100644 --- a/crates/gpui/src/platform/linux/platform.rs +++ b/crates/gpui/src/platform/linux/platform.rs @@ -85,44 +85,46 @@ impl Platform for LinuxPlatform { fn run(&self, on_finish_launching: Box) { on_finish_launching(); - let mut need_repaint = HashSet::::default(); - while !self.0.lock().windows.is_empty() { let event = self.0.lock().xcb_connection.wait_for_event().unwrap(); + let mut repaint_x_window = None; match event { xcb::Event::X(x::Event::ClientMessage(ev)) => { if let x::ClientMessageData::Data32([atom, ..]) = ev.data() { - let mut lock = self.0.lock(); - if atom == lock.atoms.wm_del_window.resource_id() { + let mut this = self.0.lock(); + if atom == this.atoms.wm_del_window.resource_id() { // window "x" button clicked by user, we gracefully exit { - let mut window = lock.windows[&ev.window()].lock(); + let mut window = this.windows[&ev.window()].lock(); window.destroy(); } - lock.windows.remove(&ev.window()); + this.xcb_connection.send_request(&x::UnmapWindow { + window: ev.window(), + }); + this.xcb_connection.send_request(&x::DestroyWindow { + window: ev.window(), + }); + this.windows.remove(&ev.window()); break; } } } - _ => {} /* - Event::Expose(event) => { - if event.count == 0 { - need_repaint.insert(event.window); - } - } - Event::ConfigureNotify(event) => { - let lock = self.0.lock(); - let mut window = lock.windows[&event.window].lock(); - window.resize(event.width, event.height); - } - _ => {}*/ + xcb::Event::X(x::Event::Expose(ev)) => { + repaint_x_window = Some(ev.window()); + } + xcb::Event::X(x::Event::ResizeRequest(ev)) => { + let this = self.0.lock(); + let mut window = this.windows[&ev.window()].lock(); + window.resize(ev.width(), ev.height()); + } + _ => {} } - for x_window in need_repaint.drain() { - let lock = self.0.lock(); - let mut window = lock.windows[&x_window].lock(); - window.paint(); - lock.xcb_connection.flush(); + if let Some(x_window) = repaint_x_window { + let this = self.0.lock(); + let mut window = this.windows[&x_window].lock(); + window.request_frame(); + this.xcb_connection.flush(); } } } @@ -140,22 +142,22 @@ impl Platform for LinuxPlatform { fn unhide_other_apps(&self) {} fn displays(&self) -> Vec> { - let lock = self.0.lock(); - let setup = lock.xcb_connection.get_setup(); + let this = self.0.lock(); + let setup = this.xcb_connection.get_setup(); setup .roots() .enumerate() .map(|(root_id, _)| { - Rc::new(LinuxDisplay::new(&lock.xcb_connection, root_id as i32)) + Rc::new(LinuxDisplay::new(&this.xcb_connection, root_id as i32)) as Rc }) .collect() } fn display(&self, id: DisplayId) -> Option> { - let lock = self.0.lock(); + let this = self.0.lock(); Some(Rc::new(LinuxDisplay::new( - &lock.xcb_connection, + &this.xcb_connection, id.0 as i32, ))) } @@ -169,18 +171,18 @@ impl Platform for LinuxPlatform { handle: AnyWindowHandle, options: WindowOptions, ) -> Box { - let mut lock = self.0.lock(); - let x_window = lock.xcb_connection.generate_id(); + let mut this = self.0.lock(); + let x_window = this.xcb_connection.generate_id(); let window_ptr = LinuxWindowState::new_ptr( options, handle, - &lock.xcb_connection, - lock.x_root_index, + &this.xcb_connection, + this.x_root_index, x_window, - &lock.atoms, + &this.atoms, ); - lock.windows.insert(x_window, window_ptr.clone()); + this.windows.insert(x_window, window_ptr.clone()); Box::new(LinuxWindow(window_ptr)) } diff --git a/crates/gpui/src/platform/linux/window.rs b/crates/gpui/src/platform/linux/window.rs index 9f6851ca933354151d7094549149f5d2e67c7445..ce91b3523f481e8df4eabc2cc9cc502a2696db8d 100644 --- a/crates/gpui/src/platform/linux/window.rs +++ b/crates/gpui/src/platform/linux/window.rs @@ -11,6 +11,12 @@ use std::{ }; use xcb::{x, Xid as _}; +#[derive(Default)] +struct Callbacks { + request_frame: Option>, + resize: Option, f32)>>, +} + pub(crate) struct LinuxWindowState { display: Rc, x_window: x::Window, @@ -18,6 +24,7 @@ pub(crate) struct LinuxWindowState { content_size: Size, sprite_atlas: Arc, renderer: BladeRenderer, + callbacks: Callbacks, } pub(crate) type LinuxWindowStatePtr = Arc>; @@ -65,7 +72,9 @@ impl LinuxWindowState { let xcb_values = [ x::Cw::BackPixel(screen.white_pixel()), - x::Cw::EventMask(x::EventMask::EXPOSURE | x::EventMask::KEY_PRESS), + x::Cw::EventMask( + x::EventMask::EXPOSURE | x::EventMask::RESIZE_REDIRECT | x::EventMask::KEY_PRESS, + ), ]; let (bound_x, bound_y, bound_width, bound_height) = match options.bounds { @@ -137,6 +146,11 @@ impl LinuxWindowState { } .unwrap(), ); + let gpu_extent = blade::Extent { + width: bound_width as u32, + height: bound_height as u32, + depth: 1, + }; Arc::new(Mutex::new(Self { display: Rc::new(LinuxDisplay::new(xcb_connection, x_screen_index)), @@ -147,7 +161,8 @@ impl LinuxWindowState { height: Pixels(bound_height as f32), }, sprite_atlas: Arc::new(BladeAtlas::new(&gpu)), - renderer: BladeRenderer::new(gpu), + renderer: BladeRenderer::new(gpu, gpu_extent), + callbacks: Callbacks::default(), })) } @@ -156,14 +171,25 @@ impl LinuxWindowState { width: Pixels(width as f32), height: Pixels(height as f32), }; + self.renderer.resize(blade::Extent { + width: width as u32, + height: height as u32, + depth: 1, + }); + if let Some(ref mut fun) = self.callbacks.resize { + fun(self.content_size, 1.0); + } } - pub fn destroy(&mut self) { - self.sprite_atlas.destroy(); + pub fn request_frame(&mut self) { + if let Some(ref mut fun) = self.callbacks.request_frame { + fun(); + } } - pub fn paint(&mut self) { - //TODO + pub fn destroy(&mut self) { + self.sprite_atlas.destroy(); + self.renderer.destroy(); } } @@ -243,27 +269,27 @@ impl PlatformWindow for LinuxWindow { unimplemented!() } - fn on_request_frame(&self, _callback: Box) {} + fn on_request_frame(&self, callback: Box) { + self.0.lock().callbacks.request_frame = Some(callback); + } fn on_input(&self, callback: Box bool>) {} fn on_active_status_change(&self, callback: Box) {} - fn on_resize(&self, callback: Box, f32)>) {} + fn on_resize(&self, callback: Box, f32)>) { + self.0.lock().callbacks.resize = Some(callback); + } fn on_fullscreen(&self, _callback: Box) {} fn on_moved(&self, callback: Box) {} - fn on_should_close(&self, callback: Box bool>) {} + fn on_should_close(&self, _callback: Box bool>) {} - fn on_close(&self, _callback: Box) { - unimplemented!() - } + fn on_close(&self, _callback: Box) {} - fn on_appearance_changed(&self, _callback: Box) { - unimplemented!() - } + fn on_appearance_changed(&self, _callback: Box) {} fn is_topmost_for_position(&self, _position: crate::Point) -> bool { unimplemented!() @@ -271,7 +297,9 @@ impl PlatformWindow for LinuxWindow { fn invalidate(&self) {} - fn draw(&self, _scene: &crate::Scene) {} + fn draw(&self, scene: &crate::Scene) { + self.0.lock().renderer.draw(scene); + } fn sprite_atlas(&self) -> sync::Arc { self.0.lock().sprite_atlas.clone()