feat(wayland): WIP implement wp_presentation_time

Signed-off-by: Schmarni <marnistromer@gmail.com>
This commit is contained in:
Schmarni
2025-08-09 00:00:58 +02:00
parent b31f6bc983
commit 00086221cd
4 changed files with 119 additions and 11 deletions

View File

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

View File

@@ -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<DoubleBuffer<SurfaceState>>,
pub message_sink: MessageSink,
// TODO: This should probably be a OnceLock? wayland doesn't support changing the surface role
pub role: Mutex<Option<SurfaceRole>>,
on_commit_handlers: Mutex<Vec<OnCommitCallback>>,
material: OnceLock<Handle<BevyMaterial>>,
pending_material_applications: Registry<ModelPart>,
presentation_feedback: Mutex<Vec<Arc<PresentationFeedback>>>,
}
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<PresentationFeedback>) {
self.presentation_feedback.lock().push(feedback);
}
pub fn submit_presentation_feedback(
self: &Arc<Self>,
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::<Vec<_>>();
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

View File

@@ -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<Surface>,
display_timestamp: MonotonicTimestamp,
refresh_cycle: u64,
},
}
pub type MessageSink = mpsc::UnboundedSender<Message>;
@@ -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<u64>) {
*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);
}
}

View File

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