diff --git a/local/recipes/wayland/redbear-compositor/source/src/main.rs b/local/recipes/wayland/redbear-compositor/source/src/main.rs index af924905a2..9babcb4ab8 100644 --- a/local/recipes/wayland/redbear-compositor/source/src/main.rs +++ b/local/recipes/wayland/redbear-compositor/source/src/main.rs @@ -25,7 +25,7 @@ use std::net::Shutdown; use std::os::fd::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; use std::os::unix::net::{UnixListener, UnixStream}; use std::sync::{ - atomic::{AtomicU32, Ordering}, + atomic::{AtomicU32, AtomicU64, Ordering}, Mutex, }; @@ -1306,10 +1306,20 @@ pub struct Compositor { fb_stride: u32, fb_data: Mutex, drm: Mutex>, - clients: Mutex>, + clients: Mutex>, pointer_state: Mutex, keyboard_state: Mutex, interactive_grab: Mutex, + refresh_nsec: u64, + frame_seq: AtomicU64, + pending_feedbacks: Mutex>, +} + +struct PendingFeedback { + client_id: u32, + feedback_id: u32, + surface_id: u32, + queue_time_nsec: u64, } impl Compositor { @@ -1433,6 +1443,9 @@ impl Compositor { pointer_state: Mutex::new(PointerState::default()), keyboard_state: Mutex::new(KeyboardState::default()), interactive_grab: Mutex::new(InteractiveGrabState::default()), + refresh_nsec: 16_693_334, + frame_seq: AtomicU64::new(0), + pending_feedbacks: Mutex::new(Vec::new()), }) } @@ -1627,31 +1640,32 @@ impl Compositor { self.write_event(client_id, stream, &msg, "wl_registry.global_remove") } - fn handle_client(&self, client_id: u32, mut stream: UnixStream) { - let mut buf = [0u8; 4096]; - loop { - match recv_with_rights(&mut stream, &mut buf) { - Ok((0, _)) => { - eprintln!("redbear-compositor: client {} disconnected", client_id); - self.clients.lock().unwrap().remove(&client_id); - break; - } - Ok((n, mut fds)) => { - if let Err(e) = self.dispatch(client_id, &buf[..n], &mut fds, &mut stream) { - eprintln!("redbear-compositor: dispatch error: {}", e); - } - while let Some(fd) = fds.pop_front() { - let _ = unsafe { libc::close(fd) }; + fn handle_client(&self, client_id: u32, mut stream: UnixStream) { + let mut buf = [0u8; 4096]; + loop { + match recv_with_rights(&mut stream, &mut buf) { + Ok((0, _)) => { + eprintln!("redbear-compositor: client {} disconnected", client_id); + self.clients.lock().unwrap().remove(&client_id); + break; + } + Ok((n, mut fds)) => { + if let Err(e) = self.dispatch(client_id, &buf[..n], &mut fds, &mut stream) { + eprintln!("redbear-compositor: dispatch error: {}", e); + } + while let Some(fd) = fds.pop_front() { + let _ = unsafe { libc::close(fd) }; + } + let _ = self.drain_pending_feedbacks(client_id, &mut stream); + } + Err(e) => { + eprintln!("redbear-compositor: read error: {}", e); + break; + } } } - Err(e) => { - eprintln!("redbear-compositor: read error: {}", e); - break; - } + self.clients.lock().unwrap().remove(&client_id); } - } - self.clients.lock().unwrap().remove(&client_id); - } fn dispatch( &self, @@ -3038,8 +3052,13 @@ impl Compositor { ); } drop(clients); - self.send_presentation_feedback_discarded(client_id, stream, new_id)?; - self.send_presentation_feedback_presented(client_id, stream, new_id)?; + let queue_time_nsec = clock_monotonic_nsec(); + self.pending_feedbacks.lock().unwrap().push(PendingFeedback { + client_id, + feedback_id: new_id, + surface_id, + queue_time_nsec, + }); } } _ => { @@ -3532,6 +3551,7 @@ OBJECT_TYPE_WP_PRESENTATION_FEEDBACK => match opcode { drm.flip(); } } + self.frame_seq.fetch_add(1, Ordering::Relaxed); } fn send_buffer_release( @@ -3858,19 +3878,100 @@ OBJECT_TYPE_WP_PRESENTATION_FEEDBACK => match opcode { client_id: u32, stream: &mut UnixStream, feedback_id: u32, + presented_nsec: u64, + refresh_nsec: u64, + high_crtc: u32, + low_crtc: u32, ) -> Result<(), String> { + let (sec, nsec) = nsec_to_clock_pair(presented_nsec); let mut msg = Vec::with_capacity(40); push_header(&mut msg, feedback_id, WP_PRESENTATION_FEEDBACK_PRESENTED, 32); - push_u32(&mut msg, 0); - push_u32(&mut msg, 0); - push_u32(&mut msg, 0); - push_u32(&mut msg, 0); - push_u32(&mut msg, 0); - push_u32(&mut msg, 0); - push_u32(&mut msg, 0); + push_u32(&mut msg, sec); + push_u32(&mut msg, nsec); + push_u32(&mut msg, (refresh_nsec >> 16) as u32); + push_u32(&mut msg, (refresh_nsec & 0xFFFF) as u32); + push_u32(&mut msg, high_crtc); + push_u32(&mut msg, low_crtc); + push_u32(&mut msg, 1); push_u32(&mut msg, 0); self.write_event(client_id, stream, &msg, "wp_presentation_feedback.presented") } + + fn drain_pending_feedbacks( + &self, + client_id: u32, + stream: &mut UnixStream, + ) -> Result<(), String> { + let drained: Vec = { + let mut queue = self.pending_feedbacks.lock().unwrap(); + let mut matching = Vec::new(); + for fb in queue.iter() { + if fb.client_id == client_id { + matching.push(PendingFeedback { + client_id: fb.client_id, + feedback_id: fb.feedback_id, + surface_id: fb.surface_id, + queue_time_nsec: fb.queue_time_nsec, + }); + } + } + queue.retain(|fb| fb.client_id != client_id); + matching + }; + let now = clock_monotonic_nsec(); + let frame_seq = self.frame_seq.load(Ordering::Relaxed); + let frame_hi = (frame_seq >> 32) as u32; + let frame_lo = frame_seq as u32; + for fb in &drained { + if now >= fb.queue_time_nsec { + self.send_presentation_feedback_presented( + client_id, + stream, + fb.feedback_id, + now, + self.refresh_nsec, + frame_hi, + frame_lo, + )?; + } else { + self.pending_feedbacks.lock().unwrap().push(PendingFeedback { + client_id: fb.client_id, + feedback_id: fb.feedback_id, + surface_id: fb.surface_id, + queue_time_nsec: fb.queue_time_nsec, + }); + } + } + Ok(()) + } +} + +fn clock_monotonic_nsec() -> u64 { + let mut ts = libc_timespec { tv_sec: 0, tv_nsec: 0 }; + unsafe { + libc_clock_gettime(LIBC_CLOCK_MONOTONIC, &mut ts); + } + ts.tv_sec as u64 * 1_000_000_000 + ts.tv_nsec as u64 +} + +fn nsec_to_clock_pair(nsec: u64) -> (u32, u32) { + let sec = (nsec / 1_000_000_000) as u64; + let nsec_remainder = (nsec % 1_000_000_000) as u32; + let hi = (sec >> 32) as u32; + let lo = (sec & 0xFFFFFFFF) as u32; + (hi, lo) +} + +const LIBC_CLOCK_MONOTONIC: i32 = 1; + +#[repr(C)] +struct libc_timespec { + tv_sec: i64, + tv_nsec: i64, +} + +extern "C" { + fn libc_clock_gettime(clk_id: i32, tp: *mut libc_timespec) -> i32; } fn main() {