diff --git a/anvil/src/udev.rs b/anvil/src/udev.rs index d7896b429f36..d1e4241a56af 100644 --- a/anvil/src/udev.rs +++ b/anvil/src/udev.rs @@ -88,6 +88,7 @@ use smithay::{ DrmLease, DrmLeaseBuilder, DrmLeaseHandler, DrmLeaseRequest, DrmLeaseState, LeaseRejected, }, drm_syncobj::{supports_syncobj_eventfd, DrmSyncobjHandler, DrmSyncobjState}, + presentation::Refresh, }, }; use smithay_drm_extras::{ @@ -1292,8 +1293,10 @@ impl AnvilState { clock, output .current_mode() - .map(|mode| Duration::from_secs_f64(1_000f64 / mode.refresh as f64)) - .unwrap_or_default(), + .map(|mode| { + Refresh::fixed(Duration::from_secs_f64(1_000f64 / mode.refresh as f64)) + }) + .unwrap_or(Refresh::Unknown), seq as u64, flags, ); diff --git a/anvil/src/winit.rs b/anvil/src/winit.rs index 7ef8c2be8974..f2ca111966ed 100644 --- a/anvil/src/winit.rs +++ b/anvil/src/winit.rs @@ -42,6 +42,7 @@ use smithay::{ dmabuf::{ DmabufFeedback, DmabufFeedbackBuilder, DmabufGlobal, DmabufHandler, DmabufState, ImportNotifier, }, + presentation::Refresh, }, }; use tracing::{error, info, warn}; @@ -399,8 +400,10 @@ pub fn run_winit() { frame_target, output .current_mode() - .map(|mode| Duration::from_secs_f64(1_000f64 / mode.refresh as f64)) - .unwrap_or_default(), + .map(|mode| { + Refresh::fixed(Duration::from_secs_f64(1_000f64 / mode.refresh as f64)) + }) + .unwrap_or(Refresh::Unknown), 0, wp_presentation_feedback::Kind::Vsync, ) diff --git a/anvil/src/x11.rs b/anvil/src/x11.rs index 8f30a3899003..50e3ce1c92aa 100644 --- a/anvil/src/x11.rs +++ b/anvil/src/x11.rs @@ -47,6 +47,7 @@ use smithay::{ dmabuf::{ DmabufFeedback, DmabufFeedbackBuilder, DmabufGlobal, DmabufHandler, DmabufState, ImportNotifier, }, + presentation::Refresh, }, }; use tracing::{error, info, trace, warn}; @@ -418,8 +419,10 @@ pub fn run_x11() { frame_target, output .current_mode() - .map(|mode| Duration::from_secs_f64(1_000f64 / mode.refresh as f64)) - .unwrap_or_default(), + .map(|mode| { + Refresh::fixed(Duration::from_secs_f64(1_000f64 / mode.refresh as f64)) + }) + .unwrap_or(Refresh::Unknown), 0, wp_presentation_feedback::Kind::Vsync, ) diff --git a/src/desktop/wayland/utils.rs b/src/desktop/wayland/utils.rs index 273be64b89a8..fedc47fb4959 100644 --- a/src/desktop/wayland/utils.rs +++ b/src/desktop/wayland/utils.rs @@ -13,7 +13,7 @@ use crate::{ wayland::{ compositor::{with_surface_tree_downward, SurfaceAttributes, SurfaceData, TraversalAction}, dmabuf::{DmabufFeedback, SurfaceDmabufFeedbackState}, - presentation::{PresentationFeedbackCachedState, PresentationFeedbackCallback}, + presentation::{PresentationFeedbackCachedState, PresentationFeedbackCallback, Refresh}, }, }; use std::{cell::RefCell, sync::Mutex, time::Duration}; @@ -339,13 +339,11 @@ impl SurfacePresentationFeedback { output: &Output, clk_id: u32, time: impl Into, - refresh: impl Into, + refresh: Refresh, seq: u64, flags: wp_presentation_feedback::Kind, ) { let time = time.into(); - let refresh = refresh.into(); - for callback in self.callbacks.drain(..) { if callback.clk_id() == clk_id { callback.presented(output, time, refresh, seq, flags | self.flags) @@ -401,7 +399,7 @@ impl OutputPresentationFeedback { pub fn presented( &mut self, time: T, - refresh: impl Into, + refresh: Refresh, seq: u64, flags: wp_presentation_feedback::Kind, ) where @@ -409,7 +407,6 @@ impl OutputPresentationFeedback { Kind: crate::utils::NonNegativeClockSource, { let time = time.into(); - let refresh = refresh.into(); let clk_id = Kind::ID as u32; if let Some(output) = self.output.upgrade() { for mut callback in self.callbacks.drain(..) { diff --git a/src/wayland/presentation/mod.rs b/src/wayland/presentation/mod.rs index 942493ef0d33..e16afb810e03 100644 --- a/src/wayland/presentation/mod.rs +++ b/src/wayland/presentation/mod.rs @@ -38,7 +38,7 @@ //! # use smithay::{output::{Output, PhysicalProperties, Subpixel}, wayland::compositor::with_states}; //! # use wayland_server::{backend::ObjectId, protocol::wl_surface::WlSurface, Resource}; //! # use std::time::Duration; -//! use smithay::wayland::presentation::PresentationFeedbackCachedState; +//! use smithay::wayland::presentation::{PresentationFeedbackCachedState, Refresh}; //! use wayland_protocols::wp::presentation_time::server::wp_presentation_feedback; //! //! # struct State; @@ -63,7 +63,7 @@ //! // ... send frame callbacks and present frame //! //! # let time = Duration::ZERO; -//! # let refresh = Duration::from_secs_f64(1_000f64 / 60_000f64); +//! # let refresh = Refresh::fixed(Duration::from_secs_f64(1_000f64 / 60_000f64)); //! # let seq = 0; //! for feedback in presentation_feedbacks { //! feedback.presented(&output, time, refresh, seq, wp_presentation_feedback::Kind::Vsync); @@ -81,6 +81,8 @@ use crate::output::Output; use super::compositor::{with_states, Cacheable}; +const EVT_PRESENTED_VARIABLE_SINCE: u32 = 2; + /// State of the wp_presentation global #[derive(Debug)] pub struct PresentationState { @@ -100,7 +102,7 @@ impl PresentationState { + 'static, { PresentationState { - global: display.create_global::(1, clk_id), + global: display.create_global::(2, clk_id), } } @@ -154,7 +156,7 @@ where .cached_state .get::() .pending() - .add_callback(surface.clone(), *data, callback); + .add_callback(&surface, *data, callback); }); } wp_presentation::Request::Destroy => { @@ -196,6 +198,31 @@ pub struct PresentationFeedbackCallback { callback: wp_presentation_feedback::WpPresentationFeedback, } +/// Refresh of the output on which the surface was presented +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub enum Refresh { + /// The output does not have a known refresh rate + Unknown, + /// The output has a variable refresh rate + /// + /// The passed refresh rate has to correspond to the minimum (fastest) rate + Variable(Duration), + /// The output has a fixed rate + Fixed(Duration), +} + +impl Refresh { + /// Construct a [`Refresh::Variable`] from a [`Duration`] + pub fn variable(min: impl Into) -> Self { + Self::Variable(min.into()) + } + + /// Construct a [`Refresh::Fixed`] from a [`Duration`] + pub fn fixed(rate: impl Into) -> Self { + Self::Fixed(rate.into()) + } +} + impl PresentationFeedbackCallback { /// Get the id of the clock that was used at bind pub fn clk_id(&self) -> u32 { @@ -207,7 +234,7 @@ impl PresentationFeedbackCallback { self, output: &Output, time: impl Into, - refresh: impl Into, + refresh: Refresh, seq: u64, flags: wp_presentation_feedback::Kind, ) { @@ -233,11 +260,22 @@ impl PresentationFeedbackCallback { self.callback.sync_output(&output); } + // Since version 2 wp_presentation supports variable refresh rates, + // in which case the minimum (fastest rate) refresh is sent. + // Clients binding an earlier version shall receive a zero refresh in this case. + let refresh = match refresh { + Refresh::Fixed(duration) => duration, + Refresh::Variable(duration) if self.callback.version() >= EVT_PRESENTED_VARIABLE_SINCE => { + duration + } + _ => Duration::ZERO, + }; + let time = time.into(); let tv_sec_hi = (time.as_secs() >> 32) as u32; let tv_sec_lo = (time.as_secs() & 0xFFFFFFFF) as u32; let tv_nsec = time.subsec_nanos(); - let refresh = refresh.into().as_nanos() as u32; + let refresh = refresh.as_nanos() as u32; let seq_hi = (seq >> 32) as u32; let seq_lo = (seq & 0xFFFFFFFF) as u32; @@ -262,7 +300,7 @@ pub struct PresentationFeedbackCachedState { impl PresentationFeedbackCachedState { fn add_callback( &mut self, - surface: wl_surface::WlSurface, + surface: &wl_surface::WlSurface, clk_id: u32, callback: wp_presentation_feedback::WpPresentationFeedback, ) {