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 Feb 17, 2024
1 parent 91e61f1 commit 1a8c49e
Show file tree
Hide file tree
Showing 9 changed files with 147 additions and 56 deletions.
2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@ profiling = "1.0"
smallvec = "1.11"
pixman = { version = "0.1.0", features = ["drm-fourcc"], optional = true }

[patch.crates-io]
drm = { git="https://github.com/PolyMeilex/drm-rs.git", branch="update-drm-sys-to-get-new-caps" }

[dev-dependencies]
clap = { version = "4", features = ["derive"] }
Expand Down
25 changes: 18 additions & 7 deletions anvil/src/udev.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ use smithay::{
},
drm::{
compositor::DrmCompositor, CreateDrmNodeError, DrmAccessError, DrmDevice, DrmDeviceFd, DrmError,
DrmEvent, DrmEventMetadata, DrmNode, DrmSurface, GbmBufferedSurface, NodeType,
DrmEvent, DrmEventMetadata, DrmNode, DrmSurface, GbmBufferedSurface, NodeType, PresentationMode,
},
egl::{self, context::ContextPriority, EGLDevice, EGLDisplay},
input::InputEvent,
Expand Down Expand Up @@ -685,14 +685,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 @@ -1714,11 +1715,19 @@ 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
.compositor
.queue_frame(res.sync, res.damage, Some(output_presentation_feedback))
.queue_frame(
res.sync,
res.damage,
Some(output_presentation_feedback),
presentation_mode,
)
.map_err(Into::<SwapBuffersError>::into)?;
}

Expand All @@ -1732,7 +1741,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
50 changes: 35 additions & 15 deletions src/backend/drm/compositor/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@
//! # use std::{collections::HashSet, mem::MaybeUninit};
//! #
//! use smithay::{
//! backend::drm::{compositor::DrmCompositor, DrmSurface},
//! backend::drm::{compositor::DrmCompositor, DrmSurface, PresentationMode},
//! output::{Output, PhysicalProperties, Subpixel},
//! utils::Size,
//! };
Expand Down Expand Up @@ -108,7 +108,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 +131,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 @@ -172,7 +172,9 @@ use crate::{
wayland::shm,
};

use super::{error::AccessError, DrmDeviceFd, DrmSurface, Framebuffer, PlaneClaim, PlaneInfo, Planes};
use super::{
error::AccessError, DrmDeviceFd, DrmSurface, Framebuffer, PlaneClaim, PlaneInfo, Planes, PresentationMode,
};

pub mod dumb;
mod elements;
Expand Down Expand Up @@ -692,12 +694,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 @@ -707,12 +709,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 @@ -1426,6 +1428,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 @@ -2500,7 +2503,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 @@ -2526,6 +2533,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 @@ -2553,17 +2561,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
13 changes: 13 additions & 0 deletions src/backend/drm/device/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ pub struct DrmDevice {
pub(super) dev_id: dev_t,
pub(crate) internal: Arc<DrmDeviceInternal>,
has_universal_planes: bool,
has_tearing_page_flips: bool,
cursor_size: Size<u32, Buffer>,
resources: ResourceHandles,
plane_claim_storage: PlaneClaimStorage,
Expand Down Expand Up @@ -146,6 +147,14 @@ impl DrmDeviceInternal {
}
}

fn has_tearing_page_flips(&self) -> bool {
let async_cap = match self {
DrmDeviceInternal::Atomic(_) => DriverCapability::AtomicASyncPageFlip,
DrmDeviceInternal::Legacy(_) => DriverCapability::ASyncPageFlip,
};
self.device_fd().get_driver_capability(async_cap).unwrap_or(0) == 1
}

fn span(&self) -> &tracing::Span {
match self {
DrmDeviceInternal::Atomic(internal) => &internal.span,
Expand Down Expand Up @@ -218,12 +227,14 @@ impl DrmDevice {
})?;

let internal = Arc::new(DrmDevice::create_internal(fd, active, disable_connectors)?);
let has_tearing_page_flips = internal.has_tearing_page_flips();

Ok((
DrmDevice {
dev_id,
internal: internal.clone(),
has_universal_planes,
has_tearing_page_flips,
cursor_size,
resources,
plane_claim_storage: Default::default(),
Expand Down Expand Up @@ -359,6 +370,7 @@ impl DrmDevice {
mapping,
mode,
connectors,
self.has_tearing_page_flips,
)?)
} else {
DrmSurfaceInternal::Legacy(LegacyDrmSurface::new(
Expand All @@ -367,6 +379,7 @@ impl DrmDevice {
crtc,
mode,
connectors,
self.has_tearing_page_flips,
)?)
};
let internal = Arc::new(internal);
Expand Down
10 changes: 10 additions & 0 deletions src/backend/drm/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,16 @@ 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,
/// Thearing presentationres
ASync,
// NOTE: Auto for detecting when thearing is desired would probably go here
}

/// Common framebuffer operations
pub trait Framebuffer: AsRef<framebuffer::Handle> {
/// Retrieve the format of the framebuffer
Expand Down
33 changes: 26 additions & 7 deletions src/backend/drm/surface/atomic.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
use drm::control::atomic::AtomicModeReq;
use drm::control::Device as ControlDevice;
use drm::control::{
connector, crtc, dumbbuffer::DumbBuffer, framebuffer, plane, property, AtomicCommitFlags, Mode, PlaneType,
};
use drm::control::{Device as ControlDevice, PageFlipFlags};

use std::collections::HashSet;
use std::os::unix::io::AsRawFd;
Expand Down Expand Up @@ -162,6 +162,7 @@ pub struct AtomicDrmSurface {
state: RwLock<State>,
pending: RwLock<State>,
pub(super) span: tracing::Span,
supports_async_page_flips: bool,
}

impl AtomicDrmSurface {
Expand All @@ -174,6 +175,7 @@ impl AtomicDrmSurface {
mut prop_mapping: Mapping,
mode: Mode,
connectors: &[connector::Handle],
supports_async_page_flips: bool,
) -> Result<Self, Error> {
let span = info_span!("drm_atomic", crtc = ?crtc);
let _guard = span.enter();
Expand All @@ -198,6 +200,7 @@ impl AtomicDrmSurface {
};

drop(_guard);

let surface = AtomicDrmSurface {
fd,
active,
Expand All @@ -208,6 +211,7 @@ impl AtomicDrmSurface {
state: RwLock::new(state),
pending: RwLock::new(pending),
span,
supports_async_page_flips,
};

Ok(surface)
Expand Down Expand Up @@ -563,13 +567,26 @@ impl AtomicDrmSurface {
})
}

fn flip_flags(&self, value: PageFlipFlags) -> AtomicCommitFlags {
let mut v = AtomicCommitFlags::empty();
if value.contains(PageFlipFlags::EVENT) {
v |= AtomicCommitFlags::PAGE_FLIP_EVENT;
}
if self.supports_async_page_flips && value.contains(PageFlipFlags::ASYNC) {
v |= AtomicCommitFlags::PAGE_FLIP_ASYNC;
}
v
}

#[instrument(level = "trace", parent = &self.span, skip(self, planes))]
#[profiling::function]
pub fn commit<'a>(
&self,
planes: impl IntoIterator<Item = PlaneState<'a>>,
event: bool,
flip_flags: super::PageFlipFlags,
) -> Result<(), Error> {
let flip_flags = self.flip_flags(flip_flags);

if !self.active.load(Ordering::SeqCst) {
return Err(Error::DeviceInactive);
}
Expand Down Expand Up @@ -636,9 +653,9 @@ impl AtomicDrmSurface {
let result = self
.fd
.atomic_commit(
if event {
if flip_flags.contains(AtomicCommitFlags::PAGE_FLIP_EVENT) {
// on the atomic api we can modeset and trigger a page_flip event on the same call!
AtomicCommitFlags::PAGE_FLIP_EVENT | AtomicCommitFlags::ALLOW_MODESET
flip_flags | AtomicCommitFlags::ALLOW_MODESET
// we also *should* not need to wait for completion, like with `set_crtc`,
// because we have tested this exact commit already, so we do not expect any errors later down the line.
//
Expand Down Expand Up @@ -679,8 +696,10 @@ impl AtomicDrmSurface {
pub fn page_flip<'a>(
&self,
planes: impl IntoIterator<Item = PlaneState<'a>>,
event: bool,
flip_flags: PageFlipFlags,
) -> Result<(), Error> {
let flip_flags = self.flip_flags(flip_flags);

if !self.active.load(Ordering::SeqCst) {
return Err(Error::DeviceInactive);
}
Expand All @@ -698,8 +717,8 @@ impl AtomicDrmSurface {
let res = self
.fd
.atomic_commit(
if event {
AtomicCommitFlags::PAGE_FLIP_EVENT | AtomicCommitFlags::NONBLOCK
if flip_flags.contains(AtomicCommitFlags::PAGE_FLIP_EVENT) {
flip_flags | AtomicCommitFlags::NONBLOCK
} else {
AtomicCommitFlags::NONBLOCK
},
Expand Down
Loading

0 comments on commit 1a8c49e

Please sign in to comment.