@@ -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<blade::Context>,
+ command_encoder: blade::CommandEncoder,
+ last_sync_point: Option<blade::SyncPoint>,
}
impl BladeRenderer {
- pub fn new(gpu: Arc<blade::Context>) -> Self {
- Self { gpu }
+ pub fn new(gpu: Arc<blade::Context>, 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);
}
}
@@ -85,44 +85,46 @@ impl Platform for LinuxPlatform {
fn run(&self, on_finish_launching: Box<dyn FnOnce()>) {
on_finish_launching();
- let mut need_repaint = HashSet::<x::Window>::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<Rc<dyn PlatformDisplay>> {
- 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<dyn PlatformDisplay>
})
.collect()
}
fn display(&self, id: DisplayId) -> Option<Rc<dyn PlatformDisplay>> {
- 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<dyn PlatformWindow> {
- 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))
}
@@ -11,6 +11,12 @@ use std::{
};
use xcb::{x, Xid as _};
+#[derive(Default)]
+struct Callbacks {
+ request_frame: Option<Box<dyn FnMut()>>,
+ resize: Option<Box<dyn FnMut(Size<Pixels>, f32)>>,
+}
+
pub(crate) struct LinuxWindowState {
display: Rc<dyn PlatformDisplay>,
x_window: x::Window,
@@ -18,6 +24,7 @@ pub(crate) struct LinuxWindowState {
content_size: Size<Pixels>,
sprite_atlas: Arc<BladeAtlas>,
renderer: BladeRenderer,
+ callbacks: Callbacks,
}
pub(crate) type LinuxWindowStatePtr = Arc<Mutex<LinuxWindowState>>;
@@ -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<dyn FnMut()>) {}
+ fn on_request_frame(&self, callback: Box<dyn FnMut()>) {
+ self.0.lock().callbacks.request_frame = Some(callback);
+ }
fn on_input(&self, callback: Box<dyn FnMut(crate::PlatformInput) -> bool>) {}
fn on_active_status_change(&self, callback: Box<dyn FnMut(bool)>) {}
- fn on_resize(&self, callback: Box<dyn FnMut(Size<Pixels>, f32)>) {}
+ fn on_resize(&self, callback: Box<dyn FnMut(Size<Pixels>, f32)>) {
+ self.0.lock().callbacks.resize = Some(callback);
+ }
fn on_fullscreen(&self, _callback: Box<dyn FnMut(bool)>) {}
fn on_moved(&self, callback: Box<dyn FnMut()>) {}
- fn on_should_close(&self, callback: Box<dyn FnMut() -> bool>) {}
+ fn on_should_close(&self, _callback: Box<dyn FnMut() -> bool>) {}
- fn on_close(&self, _callback: Box<dyn FnOnce()>) {
- unimplemented!()
- }
+ fn on_close(&self, _callback: Box<dyn FnOnce()>) {}
- fn on_appearance_changed(&self, _callback: Box<dyn FnMut()>) {
- unimplemented!()
- }
+ fn on_appearance_changed(&self, _callback: Box<dyn FnMut()>) {}
fn is_topmost_for_position(&self, _position: crate::Point<Pixels>) -> 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<dyn crate::PlatformAtlas> {
self.0.lock().sprite_atlas.clone()