linux: hook up render event, basic renderer command buffer

This commit is contained in:
Dzmitry Malyshau 2024-01-28 23:30:19 -08:00
parent 7f8c64aa6c
commit 74fde5967b
3 changed files with 129 additions and 52 deletions

View file

@ -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);
}
}

View file

@ -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))
}

View file

@ -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 request_frame(&mut self) {
if let Some(ref mut fun) = self.callbacks.request_frame {
fun();
}
}
pub fn destroy(&mut self) {
self.sprite_atlas.destroy();
}
pub fn paint(&mut self) {
//TODO
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()