From 00086221cd9bc13b1a14b50f83aa17d22049a292 Mon Sep 17 00:00:00 2001 From: Schmarni Date: Sat, 9 Aug 2025 00:00:58 +0200 Subject: [PATCH] feat(wayland): WIP implement wp_presentation_time Signed-off-by: Schmarni --- src/main.rs | 1 + src/wayland/core/surface.rs | 63 ++++++++++++++++++++++++++++++++++++- src/wayland/mod.rs | 39 +++++++++++++++++++++-- src/wayland/registry.rs | 27 +++++++++++----- 4 files changed, 119 insertions(+), 11 deletions(-) diff --git a/src/main.rs b/src/main.rs index a3eb8e1..8217da9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -337,6 +337,7 @@ fn bevy_loop( if args.overlay_priority.is_some() { exts.enable_extx_overlay(); } + exts.khr_convert_timespec_time = true; exts }, ..default() diff --git a/src/wayland/core/surface.rs b/src/wayland/core/surface.rs index 173579c..095910d 100644 --- a/src/wayland/core/surface.rs +++ b/src/wayland/core/surface.rs @@ -6,6 +6,7 @@ use crate::{ wayland::{ Message, MessageSink, core::buffer::BufferUsage, + presentation::{MonotonicTimestamp, PresentationFeedback}, util::{ClientExt, DoubleBuffer}, xdg::{popup::Popup, toplevel::Toplevel}, }, @@ -22,7 +23,10 @@ use std::sync::{Arc, OnceLock}; use waynest::{ server::{ Client, Dispatcher, Result, - protocol::core::wayland::{wl_output::Transform, wl_surface::*}, + protocol::{ + core::wayland::{wl_output::Transform, wl_surface::*}, + stable::presentation_time::wp_presentation_feedback::{Kind, WpPresentationFeedback}, + }, }, wire::ObjectId, }; @@ -71,10 +75,12 @@ pub struct Surface { pub id: ObjectId, state: Mutex>, pub message_sink: MessageSink, + // TODO: This should probably be a OnceLock? wayland doesn't support changing the surface role pub role: Mutex>, on_commit_handlers: Mutex>, material: OnceLock>, pending_material_applications: Registry, + presentation_feedback: Mutex>>, } impl std::fmt::Debug for Surface { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { @@ -100,6 +106,7 @@ impl Surface { on_commit_handlers: Mutex::new(Vec::new()), material: OnceLock::new(), pending_material_applications: Registry::new(), + presentation_feedback: Mutex::default(), } } @@ -212,6 +219,60 @@ impl Surface { // Ok(()) // } + + #[tracing::instrument(level = "debug", skip_all)] + pub fn add_presentation_feedback(&self, feedback: Arc) { + self.presentation_feedback.lock().push(feedback); + } + + pub fn submit_presentation_feedback( + self: &Arc, + display_timestamp: MonotonicTimestamp, + refresh_cycle: u64, + ) { + self.message_sink + .send(Message::SendPresentationFeedback { + surface: self.clone(), + display_timestamp, + refresh_cycle, + }) + .unwrap(); + } + + #[tracing::instrument(level = "debug", skip_all)] + pub async fn send_presentation_feedback( + &self, + client: &mut Client, + display_timestamp: MonotonicTimestamp, + refresh_cycle: u64, + ) -> Result<()> { + let feedbacks = self + .presentation_feedback + .lock() + .drain(..) + .collect::>(); + for feedback in feedbacks { + feedback + .sync_output(client, feedback.0, client.display().output.get().unwrap().0) + .await?; + let cycle_lo = refresh_cycle as u32; + let cycle_hi = (refresh_cycle >> 32) as u32; + feedback + .presented( + client, + feedback.0, + display_timestamp.secs_hi(), + display_timestamp.secs_lo(), + display_timestamp.subsec_nanos(), + 0, + cycle_hi, + cycle_lo, + Kind::empty(), + ) + .await?; + } + Ok(()) + } } impl WlSurface for Surface { /// https://wayland.app/protocols/wayland#wl_surface:request:attach diff --git a/src/wayland/mod.rs b/src/wayland/mod.rs index 3a8cb1f..1dccc74 100644 --- a/src/wayland/mod.rs +++ b/src/wayland/mod.rs @@ -2,6 +2,7 @@ mod core; mod display; mod dmabuf; mod mesa_drm; +mod presentation; mod registry; mod util; mod vulkano_data; @@ -10,6 +11,8 @@ mod xdg; use crate::core::registry::OwnedRegistry; use crate::nodes::drawable::model::ModelNodeSystemSet; use crate::wayland::core::seat::SeatMessage; +use crate::wayland::core::surface::Surface; +use crate::wayland::presentation::MonotonicTimestamp; use crate::{ BevyMaterial, core::{ @@ -18,13 +21,15 @@ use crate::{ }, }; use bevy::app::{App, Plugin, Update}; +use bevy::diagnostic::FrameCount; use bevy::ecs::schedule::IntoScheduleConfigs; -use bevy::ecs::system::{Res, ResMut}; +use bevy::ecs::system::{Local, Res, ResMut}; use bevy::prelude::{Deref, DerefMut}; use bevy::render::renderer::RenderDevice; use bevy::render::{Render, RenderApp}; use bevy::{asset::Assets, ecs::resource::Resource, image::Image}; use bevy_dmabuf::import::ImportedDmatexs; +use bevy_mod_openxr::render::end_frame; use bevy_mod_xr::session::XrRenderSet; use cluFlock::{FlockLock, ToFlock}; use core::buffer::BufferUsage; @@ -124,6 +129,11 @@ pub enum Message { active: bool, }, Seat(SeatMessage), + SendPresentationFeedback { + surface: Arc, + display_timestamp: MonotonicTimestamp, + refresh_cycle: u64, + }, } pub type MessageSink = mpsc::UnboundedSender; @@ -234,6 +244,15 @@ impl WaylandClient { seat.handle_message(client, seat_message).await?; } } + Message::SendPresentationFeedback { + surface, + display_timestamp, + refresh_cycle, + } => { + surface + .send_presentation_feedback(client, display_timestamp, refresh_cycle) + .await?; + } } Ok(false) } @@ -310,7 +329,13 @@ impl Plugin for WaylandPlugin { app.sub_app_mut(RenderApp) .add_systems(Render, setup_vulkano_context) .add_systems(Render, before_render.in_set(XrRenderSet::PreRender)) - .add_systems(Render, after_render.in_set(XrRenderSet::PostRender)); + .add_systems(Render, after_render.in_set(XrRenderSet::PostRender)) + .add_systems( + Render, + submit_frame_timings + .in_set(XrRenderSet::PostRender) + .after(end_frame), + ); } } @@ -354,3 +379,13 @@ fn update_graphics( surface.update_graphics(&dmatexes, &mut materials, &mut images); } } + +#[instrument(level = "debug", name = "Wayland frame", skip_all)] +fn submit_frame_timings(mut frame_count: Local) { + *frame_count += 1; + for surface in WL_SURFACE_REGISTRY.get_valid_contents() { + let display_timestamp = + rustix::time::clock_gettime(rustix::time::ClockId::Monotonic).into(); + surface.submit_presentation_feedback(display_timestamp, *frame_count); + } +} diff --git a/src/wayland/registry.rs b/src/wayland/registry.rs index 4add289..45200e4 100644 --- a/src/wayland/registry.rs +++ b/src/wayland/registry.rs @@ -5,20 +5,15 @@ use crate::wayland::{ output::{Output, WlOutput}, seat::{Seat, WlSeat}, shm::{Shm, WlShm}, - }, - dmabuf::Dmabuf, - mesa_drm::MesaDrm, - util::ClientExt, - xdg::wm_base::{WmBase, XdgWmBase}, + }, dmabuf::Dmabuf, mesa_drm::MesaDrm, presentation::Presentation, util::ClientExt, xdg::wm_base::{WmBase, XdgWmBase} }; use waynest::{ server::{ - Client, Dispatcher, Error, Result, protocol::{ core::wayland::{wl_data_device_manager::WlDataDeviceManager, wl_registry::*}, external::drm::wl_drm::WlDrm, - stable::linux_dmabuf_v1::zwp_linux_dmabuf_v1::ZwpLinuxDmabufV1, - }, + stable::{linux_dmabuf_v1::zwp_linux_dmabuf_v1::ZwpLinuxDmabufV1, presentation_time::wp_presentation::WpPresentation}, + }, Client, Dispatcher, Error, Result }, wire::{NewId, ObjectId}, }; @@ -33,6 +28,7 @@ impl RegistryGlobals { pub const OUTPUT: u32 = 5; pub const DMABUF: u32 = 6; pub const WL_DRM: u32 = 7; + pub const PRESENTATION: u32 = 8; } #[derive(Debug, Dispatcher, Default)] @@ -112,6 +108,15 @@ impl Registry { ) .await?; + self.global( + client, + sender_id, + RegistryGlobals::PRESENTATION, + Presentation::INTERFACE.to_string(), + Presentation::VERSION, + ) + .await?; + Ok(()) } } @@ -170,6 +175,12 @@ impl WlRegistry for Registry { let drm = MesaDrm::new(client, new_id.object_id, new_id.version).await?; client.insert(new_id.object_id, drm); } + RegistryGlobals::PRESENTATION => { + tracing::info!("Binding wp_presentation"); + + let presentation = Presentation::new(new_id.version); + client.insert(new_id.object_id, presentation); + } id => { tracing::error!(id, "Wayland: failed to bind to registry global"); return Err(Error::MissingObject(unsafe { ObjectId::from_raw(name) }));