Skip to content

Commit

Permalink
Initial support for DRM async page flip
Browse files Browse the repository at this point in the history
  • Loading branch information
PolyMeilex committed Apr 19, 2024
1 parent c293ec7 commit fb65980
Show file tree
Hide file tree
Showing 12 changed files with 230 additions and 66 deletions.
5 changes: 2 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,10 @@ cursor-icon = "1.0.0"
cgmath = "0.18.0"
downcast-rs = "1.2.0"
drm-fourcc = "^2.2.0"
drm = { version = "0.11.1", optional = true }
drm = { version = "0.12.0", optional = true }
drm-ffi = { version = "0.7.1", optional = true }
errno = "0.3.5"
gbm = { version = "0.14.2", optional = true, default-features = false, features = ["drm-support"] }
gbm = { version = "0.15.0", optional = true, default-features = false, features = ["drm-support"] }
glow = { version = "0.12", optional = true }
input = { version = "0.9.0", default-features = false, features=["libinput_1_19"], optional = true }
indexmap = "2.0"
Expand Down Expand Up @@ -69,7 +69,6 @@ profiling = "1.0"
smallvec = "1.11"
pixman = { version = "0.1.0", features = ["drm-fourcc"], optional = true }


[dev-dependencies]
clap = { version = "4", features = ["derive"] }
criterion = { version = "0.5" }
Expand Down
27 changes: 20 additions & 7 deletions anvil/src/udev.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,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},
Expand Down Expand Up @@ -570,6 +570,7 @@ struct SurfaceCompositorRenderResult<'a> {
states: RenderElementStates,
sync: Option<SyncPoint>,
damage: Option<&'a Vec<Rectangle<i32, Physical>>>,
presentation_mode: PresentationMode,
}

impl SurfaceComposition {
Expand Down Expand Up @@ -620,14 +621,15 @@ impl SurfaceComposition {
sync: Option<SyncPoint>,
damage: Option<Vec<Rectangle<i32, Physical>>>,
user_data: Option<OutputPresentationFeedback>,
presentation_mode: PresentationMode,
) -> Result<(), SwapBuffersError> {
match self {
SurfaceComposition::Surface { surface, .. } => surface
.queue_buffer(sync, damage, user_data)
.queue_buffer(sync, damage, user_data, presentation_mode)
.map_err(Into::<SwapBuffersError>::into),
SurfaceComposition::Compositor(c) => c
.queue_frame(user_data, presentation_mode)
.map_err(Into::<SwapBuffersError>::into),
SurfaceComposition::Compositor(c) => {
c.queue_frame(user_data).map_err(Into::<SwapBuffersError>::into)
}
}
}

Expand Down Expand Up @@ -665,6 +667,7 @@ impl SurfaceComposition {
damage: res.damage,
states: res.states,
sync: rendered.then_some(res.sync),
presentation_mode: PresentationMode::VSync,
}
})
.map_err(|err| match err {
Expand All @@ -681,11 +684,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 {
Expand Down Expand Up @@ -1626,6 +1631,7 @@ fn render_surface<'a>(
states,
sync,
damage,
presentation_mode,
} = surface
.compositor
.render_frame::<_, _, GlesTexture>(renderer, &elements, clear_color)?;
Expand All @@ -1649,7 +1655,12 @@ fn render_surface<'a>(
let damage = damage.cloned();
surface
.compositor
.queue_frame(sync, damage, Some(output_presentation_feedback))
.queue_frame(
sync,
damage,
Some(output_presentation_feedback),
presentation_mode,
)
.map_err(Into::<SwapBuffersError>::into)?;
}

Expand All @@ -1663,7 +1674,9 @@ fn initial_render(
surface
.compositor
.render_frame::<_, CustomRenderElements<_>, GlesTexture>(renderer, &[], CLEAR_COLOR)?;
surface.compositor.queue_frame(None, None, None)?;
surface
.compositor
.queue_frame(None, None, None, PresentationMode::VSync)?;
surface.compositor.reset_buffers();

Ok(())
Expand Down
2 changes: 1 addition & 1 deletion smithay-drm-extras/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ authors = ["Bartłomiej Maryńczak <[email protected]>"]

[dependencies]
edid-rs = "0.1.0"
drm = { version = "0.11.0" }
drm = { version = "0.12.0" }

[features]
default = []
Expand Down
102 changes: 83 additions & 19 deletions src/backend/drm/compositor/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,10 @@
//! # use std::{collections::HashSet, mem::MaybeUninit};
//! #
//! use smithay::{
//! backend::drm::{compositor::DrmCompositor, DrmSurface},
//! backend::{
//! drm::{compositor::DrmCompositor, DrmSurface},
//! renderer::PresentationMode,
//! },
//! output::{Output, PhysicalProperties, Subpixel},
//! utils::Size,
//! };
Expand Down Expand Up @@ -108,7 +111,7 @@
//! .expect("failed to render frame");
//!
//! if !render_frame_result.is_empty {
//! compositor.queue_frame(()).expect("failed to queue frame");
//! compositor.queue_frame((), PresentationMode::VSync).expect("failed to queue frame");
//!
//! // ...wait for VBlank event
//!
Expand All @@ -131,7 +134,7 @@ use std::{

use ::gbm::{BufferObject, BufferObjectFlags};
use drm::{
control::{connector, crtc, framebuffer, plane, Mode, PlaneType},
control::{connector, crtc, framebuffer, plane, Mode, PageFlipFlags, PlaneType},
Device, DriverCapability,
};
use drm_fourcc::{DrmFormat, DrmFourcc, DrmModifier};
Expand Down Expand Up @@ -163,7 +166,7 @@ use crate::{
},
sync::SyncPoint,
utils::{CommitCounter, DamageBag, DamageSet, DamageSnapshot},
Bind, Blit, DebugFlags, Frame as RendererFrame, Renderer, Texture,
Bind, Blit, DebugFlags, Frame as RendererFrame, PresentationMode, Renderer, Texture,
},
SwapBuffersError,
},
Expand Down Expand Up @@ -693,12 +696,12 @@ impl<B: Framebuffer> FrameState<B> {
surface: &DrmSurface,
supports_fencing: bool,
allow_partial_update: bool,
event: bool,
flip_flags: PageFlipFlags,
) -> Result<(), crate::backend::drm::error::Error> {
debug_assert!(!self.planes.iter().any(|(_, state)| state.needs_test));
surface.commit(
self.build_planes(surface, supports_fencing, allow_partial_update),
event,
flip_flags,
)
}

Expand All @@ -708,12 +711,12 @@ impl<B: Framebuffer> FrameState<B> {
surface: &DrmSurface,
supports_fencing: bool,
allow_partial_update: bool,
event: bool,
flip_flags: PageFlipFlags,
) -> Result<(), crate::backend::drm::error::Error> {
debug_assert!(!self.planes.iter().any(|(_, state)| state.needs_test));
surface.page_flip(
self.build_planes(surface, supports_fencing, allow_partial_update),
event,
flip_flags,
)
}

Expand Down Expand Up @@ -876,6 +879,9 @@ pub struct PrimarySwapchainElement<B: Buffer, F: Framebuffer> {
pub transform: Transform,
/// The damage on the primary plane
pub damage: DamageSnapshot<i32, BufferCoords>,
/// Presentation preference for this swapchain element, created by combining mode of all elements
/// rendered on this buffer
pub presentation_mode: Option<PresentationMode>,
}

impl<B: Buffer, F: Framebuffer> PrimarySwapchainElement<B, F> {
Expand Down Expand Up @@ -932,7 +938,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<PresentationMode>, b: Option<PresentationMode>) -> Option<PresentationMode> {
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 {
Expand All @@ -941,6 +962,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> {
Expand Down Expand Up @@ -1419,6 +1458,7 @@ where
struct QueuedFrame<A: Allocator, F: ExportFramebuffer<<A as Allocator>::Buffer>, U> {
prepared_frame: PreparedFrame<A, F>,
user_data: U,
presentation_mode: PresentationMode,
}

impl<A, F, U> std::fmt::Debug for QueuedFrame<A, F, U>
Expand Down Expand Up @@ -2277,6 +2317,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 {:?}",
Expand Down Expand Up @@ -2339,6 +2380,11 @@ where
)
.collect::<Vec<_>>();

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);
Expand Down Expand Up @@ -2452,6 +2498,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())
Expand All @@ -2475,7 +2522,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() {
Expand All @@ -2502,7 +2549,11 @@ where
///
/// `user_data` can be used to attach some data to a specific buffer and later retrieved with [`DrmCompositor::frame_submitted`]
#[profiling::function]
pub fn queue_frame(&mut self, user_data: U) -> FrameResult<(), A, F> {
pub fn queue_frame(
&mut self,
user_data: U,
presentation_mode: PresentationMode,
) -> FrameResult<(), A, F> {
if !self.surface.is_active() {
return Err(FrameErrorType::<A, F>::DrmError(DrmError::DeviceInactive));
}
Expand All @@ -2528,6 +2579,7 @@ where
self.queued_frame = Some(QueuedFrame {
prepared_frame,
user_data,
presentation_mode,
});
if self.pending_frame.is_none() {
self.submit()?;
Expand Down Expand Up @@ -2555,17 +2607,29 @@ where
let QueuedFrame {
mut prepared_frame,
user_data,
presentation_mode,
} = self.queued_frame.take().unwrap();

let mut flip_flags = PageFlipFlags::EVENT;
if presentation_mode == PresentationMode::Async {
flip_flags |= PageFlipFlags::ASYNC;
}

let allow_partial_update = prepared_frame.kind == PreparedFrameKind::Partial;
let flip = if self.surface.commit_pending() {
prepared_frame
.frame
.commit(&self.surface, self.supports_fencing, allow_partial_update, true)
prepared_frame.frame.commit(
&self.surface,
self.supports_fencing,
allow_partial_update,
flip_flags,
)
} else {
prepared_frame
.frame
.page_flip(&self.surface, self.supports_fencing, allow_partial_update, true)
prepared_frame.frame.page_flip(
&self.surface,
self.supports_fencing,
allow_partial_update,
flip_flags,
)
};

match flip {
Expand Down Expand Up @@ -3378,7 +3442,7 @@ where
let previous_fb_cache = self
.previous_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();
Expand Down Expand Up @@ -3539,7 +3603,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
Expand Down
Loading

0 comments on commit fb65980

Please sign in to comment.