linux/x11: prioritize input in the event loop (#8253)

With this change, interaction with Zed is actually real-time and usable
🚀 🎉

The gist of it is - trying to process all of the input events before
rendering anything.

Release Notes:
- N/A

**Note**: this can be further improved in a follow-up.
Currently, once the input and runnables are processed, we'd try to draw
+ render a frame.
Presentation starts with acquiring a new frame. We currently have FIFO
presentation method, so acquiring a frame is blocking on that swapchain
image to become available. As the result, presentation takes around 16
ms, most of which is just busy wait.
Ideally, we'd be able to process more input in this time frame, instead.

**Note2**: it's a bit laggy in Debug for me, but that's just because of
the extra-long `draw` times, which is unrelated to rendering (or
platform support, for the matter). I'm curious how come on MacOS the
`draw()` times in Debug are more modest.
This commit is contained in:
Dzmitry Malyshau 2024-02-23 16:22:54 -08:00 committed by GitHub
parent cab8b5a9a3
commit 885ae2d863
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 26 additions and 18 deletions

View File

@ -507,16 +507,16 @@ impl BladeRenderer {
}
pub fn draw(&mut self, scene: &Scene) {
self.command_encoder.start();
self.atlas.before_frame(&mut self.command_encoder);
self.rasterize_paths(scene.paths());
let frame = {
profiling::scope!("acquire frame");
self.gpu.acquire_frame()
};
self.command_encoder.start();
self.command_encoder.init_texture(frame.texture());
self.atlas.before_frame(&mut self.command_encoder);
self.rasterize_paths(scene.paths());
let globals = GlobalParams {
viewport_size: [
self.viewport_size.width as f32,

View File

@ -4,7 +4,7 @@ use parking_lot::Mutex;
use xcb::{x, Xid as _};
use xkbcommon::xkb;
use collections::HashMap;
use collections::{HashMap, HashSet};
use crate::platform::linux::client::Client;
use crate::platform::{
@ -67,18 +67,33 @@ impl X11Client {
impl Client for X11Client {
fn run(&self, on_finish_launching: Box<dyn FnOnce()>) {
on_finish_launching();
//Note: here and below, don't keep the lock() open when calling
// into window functions as they may invoke callbacks that need
// to immediately access the platform (self).
let mut windows_to_refresh = HashSet::<x::Window>::default();
while !self.platform_inner.state.lock().quit_requested {
let event = {
// We prioritize work in the following order:
// 1. input events from X11
// 2. runnables for the main thread
// 3. drawing/presentation
let event = if let Some(event) = self.xcb_connection.poll_for_event().unwrap() {
event
} else if let Ok(runnable) = self.platform_inner.main_receiver.try_recv() {
runnable.run();
continue;
} else if let Some(x_window) = windows_to_refresh.iter().next().cloned() {
windows_to_refresh.remove(&x_window);
let window = self.get_window(x_window);
window.refresh();
window.request_refresh();
continue;
} else {
profiling::scope!("Wait for event");
self.xcb_connection.wait_for_event().unwrap()
};
match event {
xcb::Event::X(x::Event::ClientMessage(ev)) => {
if let x::ClientMessageData::Data32([atom, ..]) = ev.data() {
if atom == self.atoms.wm_del_window.resource_id() {
windows_to_refresh.remove(&ev.window());
// window "x" button clicked by user, we gracefully exit
let window = self.state.lock().windows.remove(&ev.window()).unwrap();
window.destroy();
@ -89,7 +104,7 @@ impl Client for X11Client {
}
}
xcb::Event::X(x::Event::Expose(ev)) => {
self.get_window(ev.window()).refresh();
windows_to_refresh.insert(ev.window());
}
xcb::Event::X(x::Event::ConfigureNotify(ev)) => {
let bounds = Bounds {
@ -105,9 +120,7 @@ impl Client for X11Client {
self.get_window(ev.window()).configure(bounds)
}
xcb::Event::Present(xcb::present::Event::CompleteNotify(ev)) => {
let window = self.get_window(ev.window());
window.refresh();
window.request_refresh();
windows_to_refresh.insert(ev.window());
}
xcb::Event::Present(xcb::present::Event::IdleNotify(_ev)) => {}
xcb::Event::X(x::Event::FocusIn(ev)) => {
@ -212,11 +225,6 @@ impl Client for X11Client {
}
_ => {}
}
profiling::scope!("Runnables");
if let Ok(runnable) = self.platform_inner.main_receiver.try_recv() {
runnable.run();
}
}
if let Some(ref mut fun) = self.platform_inner.callbacks.lock().quit {