From a7e1c32fdb9f3dd1e94c9f106cacac5e8c678d7a Mon Sep 17 00:00:00 2001 From: PolyMeilex Date: Fri, 23 Feb 2024 00:23:07 +0100 Subject: [PATCH] Add presentation mode preference in RenderElement --- anvil/src/udev.rs | 13 +++--- src/backend/drm/compositor/mod.rs | 57 +++++++++++++++++++++---- src/backend/drm/mod.rs | 9 ---- src/backend/drm/surface/gbm.rs | 4 +- src/backend/renderer/element/mod.rs | 27 +++++++++++- src/backend/renderer/element/surface.rs | 10 ++++- src/backend/renderer/mod.rs | 9 ++++ 7 files changed, 102 insertions(+), 27 deletions(-) diff --git a/anvil/src/udev.rs b/anvil/src/udev.rs index bd75252bd49c..d7d6e76ab9a8 100644 --- a/anvil/src/udev.rs +++ b/anvil/src/udev.rs @@ -31,7 +31,7 @@ use smithay::{ }, drm::{ compositor::DrmCompositor, CreateDrmNodeError, DrmAccessError, DrmDevice, DrmDeviceFd, DrmError, - DrmEvent, DrmEventMetadata, DrmNode, DrmSurface, GbmBufferedSurface, NodeType, PresentationMode, + DrmEvent, DrmEventMetadata, DrmNode, DrmSurface, GbmBufferedSurface, NodeType, }, egl::{self, context::ContextPriority, EGLDevice, EGLDisplay}, input::InputEvent, @@ -42,7 +42,7 @@ use smithay::{ gles::{GlesRenderer, GlesTexture}, multigpu::{gbm::GbmGlesBackend, GpuManager, MultiRenderer}, sync::SyncPoint, - Bind, DebugFlags, ExportMem, ImportDma, ImportMemWl, Offscreen, Renderer, + Bind, DebugFlags, ExportMem, ImportDma, ImportMemWl, Offscreen, PresentationMode, Renderer, }, session::{ libseat::{self, LibSeatSession}, @@ -635,6 +635,7 @@ struct SurfaceCompositorRenderResult { states: RenderElementStates, sync: Option, damage: Option>>, + presentation_mode: PresentationMode, } impl SurfaceComposition { @@ -731,6 +732,7 @@ impl SurfaceComposition { damage: res.damage, states: res.states, sync: rendered.then_some(res.sync), + presentation_mode: PresentationMode::VSync, } }) .map_err(|err| match err { @@ -747,11 +749,13 @@ impl SurfaceComposition { if let PrimaryPlaneElement::Swapchain(element) = render_frame_result.primary_element { element.sync.wait(); } + let presentation_mode = render_frame_result.presentation_mode(); SurfaceCompositorRenderResult { rendered: !render_frame_result.is_empty, damage: None, states: render_frame_result.states, sync: None, + presentation_mode, } }) .map_err(|err| match err { @@ -1715,9 +1719,6 @@ fn render_surface<'a, 'b>( clock.now(), ); - // TODO: Plug this in, either in DRM compositor or directly here - let presentation_mode = PresentationMode::VSync; - if res.rendered { let output_presentation_feedback = take_presentation_feedback(output, space, &res.states); surface @@ -1726,7 +1727,7 @@ fn render_surface<'a, 'b>( res.sync, res.damage, Some(output_presentation_feedback), - presentation_mode, + res.presentation_mode, ) .map_err(Into::::into)?; } diff --git a/src/backend/drm/compositor/mod.rs b/src/backend/drm/compositor/mod.rs index 992b53ae3453..791b4cfa8e36 100644 --- a/src/backend/drm/compositor/mod.rs +++ b/src/backend/drm/compositor/mod.rs @@ -163,7 +163,7 @@ use crate::{ }, sync::SyncPoint, utils::{CommitCounter, DamageBag, DamageSnapshot}, - Bind, Blit, DebugFlags, Frame as RendererFrame, Renderer, Texture, + Bind, Blit, DebugFlags, Frame as RendererFrame, PresentationMode, Renderer, Texture, }, SwapBuffersError, }, @@ -172,9 +172,7 @@ use crate::{ wayland::shm, }; -use super::{ - error::AccessError, DrmDeviceFd, DrmSurface, Framebuffer, PlaneClaim, PlaneInfo, Planes, PresentationMode, -}; +use super::{error::AccessError, DrmDeviceFd, DrmSurface, Framebuffer, PlaneClaim, PlaneInfo, Planes}; pub mod dumb; mod elements; @@ -877,6 +875,9 @@ pub struct PrimarySwapchainElement { pub transform: Transform, /// The damage on the primary plane pub damage: DamageSnapshot, + /// Presentation preference for this swapchain element, created by combining mode of all elements + /// rendered on this buffer + pub presentation_mode: Option, } impl PrimarySwapchainElement { @@ -933,7 +934,22 @@ pub struct RenderFrameResult<'a, B: Buffer, F: Framebuffer, E> { supports_fencing: bool, } -impl<'a, B: Buffer, F: Framebuffer, E> RenderFrameResult<'a, B, F, E> { +fn combine_modes(a: Option, b: Option) -> Option { + match (a, b) { + // If either one wants VSync we go with VSync + (Some(PresentationMode::VSync), _) | (_, Some(PresentationMode::VSync)) => { + Some(PresentationMode::VSync) + } + // If neither wants VSync, and one wants Async we go with Async + (Some(PresentationMode::Async), _) | (_, Some(PresentationMode::Async)) => { + Some(PresentationMode::Async) + } + // Elements have no preference + (None, None) => None, + } +} + +impl<'a, B: Buffer, F: Framebuffer, E: Element> RenderFrameResult<'a, B, F, E> { /// Returns if synchronization with kms submission can't be guaranteed through the available apis. pub fn needs_sync(&self) -> bool { if let PrimaryPlaneElement::Swapchain(ref element) = self.primary_element { @@ -942,6 +958,24 @@ impl<'a, B: Buffer, F: Framebuffer, E> RenderFrameResult<'a, B, F, E> { false } } + + /// Hint for DRM backend on how the surface should be presented + pub fn presentation_mode(&self) -> PresentationMode { + let mut res = None; + + res = match &self.primary_element { + PrimaryPlaneElement::Swapchain(e) => combine_modes(res, e.presentation_mode), + PrimaryPlaneElement::Element(e) => combine_modes(res, e.presentation_mode()), + }; + + for e in self.overlay_elements.iter() { + res = combine_modes(res, e.presentation_mode()); + } + + // Let's assume that cursor element does not care about tearing + + res.unwrap_or(PresentationMode::VSync) + } } struct SwapchainElement<'a, 'b, B: Buffer> { @@ -2276,6 +2310,7 @@ where .map(|config| matches!(&config.buffer, ScanoutBuffer::Swapchain(_))) .unwrap_or(false); + let mut swapchain_presentation_mode = None; if render { trace!( "rendering {} elements on the primary {:?}", @@ -2340,6 +2375,11 @@ where .map(|e| DrmRenderElements::Other(*e)), ); + for e in elements.iter() { + swapchain_presentation_mode = + combine_modes(swapchain_presentation_mode, e.presentation_mode()); + } + let render_res = self.damage_tracker .render_output_with(renderer, dmabuf, age, &elements, clear_color); @@ -2453,6 +2493,7 @@ where transform: output_transform, damage: self.primary_plane_damage_bag.snapshot(), sync, + presentation_mode: swapchain_presentation_mode, }) } else { PrimaryPlaneElement::Element(primary_plane_scanout_element.unwrap()) @@ -2476,7 +2517,7 @@ where supports_fencing: self.supports_fencing, }; - // We only store the next frame if it acutaly contains any changes or if a commit is pending + // We only store the next frame if it actually contains any changes or if a commit is pending // Storing the (empty) frame could keep a reference to wayland buffers which // could otherwise be potentially released on `frame_submitted` if !next_frame.is_empty() { @@ -3400,7 +3441,7 @@ where let previous_fb_cache = self .element_states .get_mut(element_id) - // Note: We can mem::take the old fb_cache here here as we guarante that + // Note: We can mem::take the old fb_cache here here as we guarantee that // the element state will always overwrite the current state at the end of render_frame .map(|state| std::mem::take(&mut state.fb_cache)) .unwrap_or_default(); @@ -3561,7 +3602,7 @@ where }); if !(primary_plane_changed || overlay_plane_changed) { - // we now know that nothing changed and we can assume any previouly failed + // we now know that nothing changed and we can assume any previously failed // test will again fail let instance_state = element_state .instances diff --git a/src/backend/drm/mod.rs b/src/backend/drm/mod.rs index 3c98aa7efaca..bf20b6dc37f2 100644 --- a/src/backend/drm/mod.rs +++ b/src/backend/drm/mod.rs @@ -113,15 +113,6 @@ fn warn_legacy_fb_export() { }); } -/// Hint for DRM backend on how the surface should be presented -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum PresentationMode { - /// Vertical synchronization - VSync, - /// Tearing presentationres - Async, -} - /// Common framebuffer operations pub trait Framebuffer: AsRef { /// Retrieve the format of the framebuffer diff --git a/src/backend/drm/surface/gbm.rs b/src/backend/drm/surface/gbm.rs index 09894445f225..3c133aecc0c9 100644 --- a/src/backend/drm/surface/gbm.rs +++ b/src/backend/drm/surface/gbm.rs @@ -12,8 +12,8 @@ use crate::backend::allocator::gbm::GbmConvertError; use crate::backend::allocator::{Allocator, Format, Fourcc, Modifier, Slot, Swapchain}; use crate::backend::drm::error::AccessError; use crate::backend::drm::gbm::{framebuffer_from_bo, GbmFramebuffer}; -use crate::backend::drm::{plane_has_property, DrmError, DrmSurface, PresentationMode}; -use crate::backend::renderer::sync::SyncPoint; +use crate::backend::drm::{plane_has_property, DrmError, DrmSurface}; +use crate::backend::renderer::{sync::SyncPoint, PresentationMode}; use crate::backend::SwapBuffersError; use crate::utils::{DevPath, Physical, Point, Rectangle, Transform}; diff --git a/src/backend/renderer/element/mod.rs b/src/backend/renderer/element/mod.rs index a98bbdaaed83..05f85c744553 100644 --- a/src/backend/renderer/element/mod.rs +++ b/src/backend/renderer/element/mod.rs @@ -31,7 +31,7 @@ use crate::{ #[cfg(feature = "wayland_frontend")] use super::utils::Buffer; -use super::{utils::CommitCounter, Renderer}; +use super::{utils::CommitCounter, PresentationMode, Renderer}; pub mod memory; pub mod solid; @@ -378,6 +378,10 @@ pub trait Element { fn kind(&self) -> Kind { Kind::default() } + /// Hint for DRM backend on how the surface should be presented + fn presentation_mode(&self) -> Option { + None + } } /// A single render element @@ -462,6 +466,10 @@ where fn kind(&self) -> Kind { (*self).kind() } + + fn presentation_mode(&self) -> Option { + (*self).presentation_mode() + } } impl RenderElement for &E @@ -754,6 +762,19 @@ macro_rules! render_elements_internal { Self::_GenericCatcher(_) => unreachable!(), } } + + fn presentation_mode(&self) -> Option<$crate::backend::renderer::PresentationMode> { + match self { + $( + #[allow(unused_doc_comments)] + $( + #[$meta] + )* + Self::$body(x) => $crate::render_elements_internal!(@call presentation_mode; x) + ),*, + Self::_GenericCatcher(_) => unreachable!(), + } + } }; (@draw <$renderer:ty>; $($(#[$meta:meta])* $body:ident=$field:ty $(as <$other_renderer:ty>)?),* $(,)?) => { fn draw( @@ -1455,6 +1476,10 @@ where fn kind(&self) -> Kind { self.0.kind() } + + fn presentation_mode(&self) -> Option { + self.0.presentation_mode() + } } impl RenderElement for Wrap diff --git a/src/backend/renderer/element/surface.rs b/src/backend/renderer/element/surface.rs index e971d9b33fec..1a4712cd1330 100644 --- a/src/backend/renderer/element/surface.rs +++ b/src/backend/renderer/element/surface.rs @@ -222,7 +222,9 @@ use tracing::{instrument, warn}; use wayland_server::protocol::wl_surface; use crate::{ - backend::renderer::{utils::RendererSurfaceStateUserData, Frame, ImportAll, Renderer, Texture}, + backend::renderer::{ + utils::RendererSurfaceStateUserData, Frame, ImportAll, PresentationMode, Renderer, Texture, + }, utils::{Buffer, Physical, Point, Rectangle, Scale, Size, Transform}, wayland::compositor::{self, SurfaceData, TraversalAction}, }; @@ -494,6 +496,12 @@ impl Element for WaylandSurfaceRenderElement { fn kind(&self) -> Kind { self.kind } + + fn presentation_mode(&self) -> Option { + // On Wayland assume VSync as default preference + Some(PresentationMode::VSync) + // TODO: Return Async in case wp_tearing is attatched to the surface + } } impl RenderElement for WaylandSurfaceRenderElement diff --git a/src/backend/renderer/mod.rs b/src/backend/renderer/mod.rs index 739e6ffc924e..9f409324ceff 100644 --- a/src/backend/renderer/mod.rs +++ b/src/backend/renderer/mod.rs @@ -52,6 +52,15 @@ pub mod sync; #[cfg(feature = "renderer_test")] pub mod test; +/// Hint for DRM backend on how the surface should be presented +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum PresentationMode { + /// Vertical synchronization + VSync, + /// Tearing presentationres + Async, +} + #[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)] /// Texture filtering methods pub enum TextureFilter {