wayland: implement linux-drm-syncobj-v1
This commit is contained in:
parent
816315170f
commit
aaf73d6fdc
29 changed files with 1507 additions and 35 deletions
|
|
@ -667,6 +667,7 @@ impl MetalConnector {
|
|||
AcquireSync::None => None,
|
||||
AcquireSync::Implicit => None,
|
||||
AcquireSync::SyncFile { sync_file } => Some(sync_file.clone()),
|
||||
AcquireSync::Unnecessary => None,
|
||||
};
|
||||
fb = dsd.fb.clone();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,11 @@ use {
|
|||
crate::{
|
||||
async_engine::SpawnedFuture,
|
||||
client::{error::LookupError, objects::Objects},
|
||||
ifs::{wl_display::WlDisplay, wl_registry::WlRegistry, wl_surface::WlSurface},
|
||||
ifs::{
|
||||
wl_display::WlDisplay,
|
||||
wl_registry::WlRegistry,
|
||||
wl_surface::{commit_timeline::CommitTimelines, WlSurface},
|
||||
},
|
||||
leaks::Tracker,
|
||||
object::{Interface, Object, ObjectId, WL_DISPLAY_ID},
|
||||
state::State,
|
||||
|
|
@ -149,6 +153,7 @@ impl Clients {
|
|||
last_xwayland_serial: Cell::new(0),
|
||||
surfaces_by_xwayland_serial: Default::default(),
|
||||
activation_tokens: Default::default(),
|
||||
commit_timelines: Rc::new(CommitTimelines::new(&global.wait_for_sync_obj)),
|
||||
});
|
||||
track!(data, data);
|
||||
let display = Rc::new(WlDisplay::new(&data));
|
||||
|
|
@ -220,6 +225,7 @@ impl Drop for ClientHolder {
|
|||
self.data.shutdown.clear();
|
||||
self.data.surfaces_by_xwayland_serial.clear();
|
||||
self.data.remove_activation_tokens();
|
||||
self.data.commit_timelines.clear();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -260,6 +266,7 @@ pub struct Client {
|
|||
pub last_xwayland_serial: Cell<u64>,
|
||||
pub surfaces_by_xwayland_serial: CopyHashMap<u64, Rc<WlSurface>>,
|
||||
pub activation_tokens: RefCell<VecDeque<ActivationToken>>,
|
||||
pub commit_timelines: Rc<CommitTimelines>,
|
||||
}
|
||||
|
||||
pub const NUM_CACHED_SERIAL_RANGES: usize = 64;
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ use {
|
|||
xdg_surface::{xdg_toplevel::XdgToplevel, XdgSurface},
|
||||
WlSurface,
|
||||
},
|
||||
wp_linux_drm_syncobj_timeline_v1::WpLinuxDrmSyncobjTimelineV1,
|
||||
xdg_positioner::XdgPositioner,
|
||||
xdg_wm_base::XdgWmBase,
|
||||
},
|
||||
|
|
@ -29,8 +30,9 @@ use {
|
|||
},
|
||||
wire::{
|
||||
JayOutputId, JayScreencastId, JayWorkspaceId, WlBufferId, WlDataSourceId, WlOutputId,
|
||||
WlPointerId, WlRegionId, WlRegistryId, WlSeatId, WlSurfaceId, XdgPositionerId,
|
||||
XdgSurfaceId, XdgToplevelId, XdgWmBaseId, ZwpPrimarySelectionSourceV1Id,
|
||||
WlPointerId, WlRegionId, WlRegistryId, WlSeatId, WlSurfaceId,
|
||||
WpLinuxDrmSyncobjTimelineV1Id, XdgPositionerId, XdgSurfaceId, XdgToplevelId,
|
||||
XdgWmBaseId, ZwpPrimarySelectionSourceV1Id,
|
||||
},
|
||||
},
|
||||
std::{cell::RefCell, mem, rc::Rc},
|
||||
|
|
@ -56,6 +58,7 @@ pub struct Objects {
|
|||
pub xdg_wm_bases: CopyHashMap<XdgWmBaseId, Rc<XdgWmBase>>,
|
||||
pub seats: CopyHashMap<WlSeatId, Rc<WlSeat>>,
|
||||
pub screencasts: CopyHashMap<JayScreencastId, Rc<JayScreencast>>,
|
||||
pub timelines: CopyHashMap<WpLinuxDrmSyncobjTimelineV1Id, Rc<WpLinuxDrmSyncobjTimelineV1>>,
|
||||
ids: RefCell<Vec<usize>>,
|
||||
}
|
||||
|
||||
|
|
@ -83,6 +86,7 @@ impl Objects {
|
|||
xdg_wm_bases: Default::default(),
|
||||
seats: Default::default(),
|
||||
screencasts: Default::default(),
|
||||
timelines: Default::default(),
|
||||
ids: RefCell::new(vec![]),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@ use {
|
|||
oserror::OsError, queue::AsyncQueue, refcounted::RefCounted, run_toplevel::RunToplevel,
|
||||
tri::Try,
|
||||
},
|
||||
video::drm::wait_for_sync_obj::WaitForSyncObj,
|
||||
wheel::{Wheel, WheelError},
|
||||
xkbcommon::XkbContext,
|
||||
},
|
||||
|
|
@ -222,6 +223,7 @@ fn start_compositor2(
|
|||
double_click_distance: Cell::new(5),
|
||||
create_default_seat: Cell::new(true),
|
||||
subsurface_ids: Default::default(),
|
||||
wait_for_sync_obj: Rc::new(WaitForSyncObj::new(&ring, &engine)),
|
||||
});
|
||||
state.tracker.register(ClientId::from_raw(0));
|
||||
create_dummy_output(&state);
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ use {
|
|||
theme::Color,
|
||||
tree::{Node, OutputNode},
|
||||
utils::{clonecell::UnsafeCellCloneSafe, transform_ext::TransformExt},
|
||||
video::{dmabuf::DmaBuf, gbm::GbmDevice, Modifier},
|
||||
video::{dmabuf::DmaBuf, drm::sync_obj::SyncObjCtx, gbm::GbmDevice, Modifier},
|
||||
},
|
||||
ahash::AHashMap,
|
||||
indexmap::IndexSet,
|
||||
|
|
@ -167,6 +167,7 @@ pub enum AcquireSync {
|
|||
None,
|
||||
Implicit,
|
||||
SyncFile { sync_file: SyncFile },
|
||||
Unnecessary,
|
||||
}
|
||||
|
||||
impl AcquireSync {
|
||||
|
|
@ -191,6 +192,7 @@ impl Debug for AcquireSync {
|
|||
AcquireSync::None => "None",
|
||||
AcquireSync::Implicit => "Implicit",
|
||||
AcquireSync::SyncFile { .. } => "SyncFile",
|
||||
AcquireSync::Unnecessary => "Unnecessary",
|
||||
};
|
||||
f.debug_struct(name).finish_non_exhaustive()
|
||||
}
|
||||
|
|
@ -517,6 +519,8 @@ pub trait GfxContext: Debug {
|
|||
stride: i32,
|
||||
format: &'static Format,
|
||||
) -> Result<Rc<dyn GfxFramebuffer>, GfxError>;
|
||||
|
||||
fn sync_obj_ctx(&self) -> &Rc<SyncObjCtx>;
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
|
|
|||
|
|
@ -376,7 +376,7 @@ fn render_texture(ctx: &GlRenderContext, tex: &CopyTexture) {
|
|||
|
||||
fn handle_explicit_sync(ctx: &GlRenderContext, texture: &Texture, sync: &AcquireSync) {
|
||||
let sync_file = match sync {
|
||||
AcquireSync::None | AcquireSync::Implicit => return,
|
||||
AcquireSync::None | AcquireSync::Implicit | AcquireSync::Unnecessary => return,
|
||||
AcquireSync::SyncFile { sync_file } => sync_file,
|
||||
};
|
||||
let sync_file = match uapi::fcntl_dupfd_cloexec(sync_file.raw(), 0) {
|
||||
|
|
|
|||
|
|
@ -14,7 +14,11 @@ use {
|
|||
renderer::{framebuffer::Framebuffer, image::Image},
|
||||
GfxGlState, RenderError, Texture,
|
||||
},
|
||||
video::{dmabuf::DmaBuf, drm::Drm, gbm::GbmDevice},
|
||||
video::{
|
||||
dmabuf::DmaBuf,
|
||||
drm::{sync_obj::SyncObjCtx, Drm},
|
||||
gbm::GbmDevice,
|
||||
},
|
||||
},
|
||||
ahash::AHashMap,
|
||||
jay_config::video::GfxApi,
|
||||
|
|
@ -53,6 +57,7 @@ pub(crate) struct TexProgs {
|
|||
pub(in crate::gfx_apis::gl) struct GlRenderContext {
|
||||
pub(crate) ctx: Rc<EglContext>,
|
||||
pub gbm: Rc<GbmDevice>,
|
||||
pub sync_ctx: Rc<SyncObjCtx>,
|
||||
|
||||
pub(crate) render_node: Rc<CString>,
|
||||
|
||||
|
|
@ -128,6 +133,7 @@ impl GlRenderContext {
|
|||
Ok(Self {
|
||||
ctx: ctx.clone(),
|
||||
gbm: ctx.dpy.gbm.clone(),
|
||||
sync_ctx: Rc::new(SyncObjCtx::new(ctx.dpy.gbm.drm.fd())),
|
||||
|
||||
render_node: node.clone(),
|
||||
|
||||
|
|
@ -271,4 +277,8 @@ impl GfxContext for GlRenderContext {
|
|||
})?;
|
||||
Ok(Rc::new(Framebuffer { ctx: self, gl: fb }))
|
||||
}
|
||||
|
||||
fn sync_obj_ctx(&self) -> &Rc<SyncObjCtx> {
|
||||
&self.sync_ctx
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -67,7 +67,7 @@ impl Framebuffer {
|
|||
|
||||
pub fn render(
|
||||
&self,
|
||||
ops: Vec<GfxApiOpt>,
|
||||
mut ops: Vec<GfxApiOpt>,
|
||||
clear: Option<&Color>,
|
||||
) -> Result<Option<SyncFile>, RenderError> {
|
||||
let gles = self.ctx.ctx.dpy.gles;
|
||||
|
|
@ -89,6 +89,7 @@ impl Framebuffer {
|
|||
}
|
||||
Ok(fd)
|
||||
});
|
||||
ops.clear();
|
||||
*self.ctx.gfx_ops.borrow_mut() = ops;
|
||||
res
|
||||
}
|
||||
|
|
@ -100,9 +101,7 @@ impl GfxFramebuffer for Framebuffer {
|
|||
}
|
||||
|
||||
fn take_render_ops(&self) -> Vec<GfxApiOpt> {
|
||||
let mut ops = mem::take(&mut *self.ctx.gfx_ops.borrow_mut());
|
||||
ops.clear();
|
||||
ops
|
||||
mem::take(&mut *self.ctx.gfx_ops.borrow_mut())
|
||||
}
|
||||
|
||||
fn physical_size(&self) -> (i32, i32) {
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ use {
|
|||
utils::oserror::OsError,
|
||||
video::{
|
||||
dmabuf::DmaBuf,
|
||||
drm::{Drm, DrmError},
|
||||
drm::{sync_obj::SyncObjCtx, Drm, DrmError},
|
||||
gbm::{GbmDevice, GbmError},
|
||||
},
|
||||
},
|
||||
|
|
@ -93,7 +93,7 @@ pub enum VulkanError {
|
|||
LoadImageProperties(#[source] vk::Result),
|
||||
#[error("Device does not support rending and texturing from the XRGB8888 format")]
|
||||
XRGB8888,
|
||||
#[error("Device does not support syncobj import")]
|
||||
#[error("Device does not support sync obj import")]
|
||||
SyncobjImport,
|
||||
#[error("Could not start a command buffer")]
|
||||
BeginCommandBuffer(vk::Result),
|
||||
|
|
@ -270,6 +270,10 @@ impl GfxContext for Context {
|
|||
.create_shm_texture(format, width, height, stride, &[], true)?;
|
||||
Ok(fb)
|
||||
}
|
||||
|
||||
fn sync_obj_ctx(&self) -> &Rc<SyncObjCtx> {
|
||||
&self.0.device.sync_ctx
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Context {
|
||||
|
|
|
|||
|
|
@ -10,7 +10,10 @@ use {
|
|||
util::OnDrop,
|
||||
VulkanError,
|
||||
},
|
||||
video::{drm::Drm, gbm::GbmDevice},
|
||||
video::{
|
||||
drm::{sync_obj::SyncObjCtx, Drm},
|
||||
gbm::GbmDevice,
|
||||
},
|
||||
},
|
||||
ahash::AHashMap,
|
||||
arrayvec::ArrayVec,
|
||||
|
|
@ -43,6 +46,7 @@ pub struct VulkanDevice {
|
|||
pub(super) physical_device: PhysicalDevice,
|
||||
pub(super) render_node: Rc<CString>,
|
||||
pub(super) gbm: GbmDevice,
|
||||
pub(super) sync_ctx: Rc<SyncObjCtx>,
|
||||
pub(super) instance: Rc<VulkanInstance>,
|
||||
pub(super) device: Device,
|
||||
pub(super) external_memory_fd: ExternalMemoryFd,
|
||||
|
|
@ -279,6 +283,7 @@ impl VulkanInstance {
|
|||
Ok(Rc::new(VulkanDevice {
|
||||
physical_device: phy_dev,
|
||||
render_node,
|
||||
sync_ctx: Rc::new(SyncObjCtx::new(gbm.drm.fd())),
|
||||
gbm,
|
||||
instance: self.clone(),
|
||||
device,
|
||||
|
|
|
|||
|
|
@ -574,6 +574,7 @@ impl VulkanRenderer {
|
|||
.map_err(|e| VulkanError::Dupfd(e.into()))?;
|
||||
import_sync_file(fd)?;
|
||||
}
|
||||
AcquireSync::Unnecessary => {}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
|
|
|
|||
|
|
@ -38,6 +38,8 @@ pub mod wp_content_type_v1;
|
|||
pub mod wp_cursor_shape_device_v1;
|
||||
pub mod wp_cursor_shape_manager_v1;
|
||||
pub mod wp_fractional_scale_manager_v1;
|
||||
pub mod wp_linux_drm_syncobj_manager_v1;
|
||||
pub mod wp_linux_drm_syncobj_timeline_v1;
|
||||
pub mod wp_presentation;
|
||||
pub mod wp_presentation_feedback;
|
||||
pub mod wp_single_pixel_buffer_manager_v1;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
pub mod commit_timeline;
|
||||
pub mod cursor;
|
||||
pub mod ext_session_lock_surface_v1;
|
||||
pub mod wl_subsurface;
|
||||
pub mod wp_fractional_scale_v1;
|
||||
pub mod wp_linux_drm_syncobj_surface_v1;
|
||||
pub mod wp_tearing_control_v1;
|
||||
pub mod wp_viewport;
|
||||
pub mod x_surface;
|
||||
|
|
@ -16,7 +18,7 @@ use {
|
|||
client::{Client, ClientError, RequestParser},
|
||||
drm_feedback::DrmFeedback,
|
||||
fixed::Fixed,
|
||||
gfx_api::{AcquireSync, BufferResv, BufferResvUser, SampleRect, SyncFile},
|
||||
gfx_api::{AcquireSync, BufferResv, BufferResvUser, ReleaseSync, SampleRect, SyncFile},
|
||||
ifs::{
|
||||
wl_buffer::WlBuffer,
|
||||
wl_callback::WlCallback,
|
||||
|
|
@ -25,9 +27,11 @@ use {
|
|||
NodeSeatState, SeatId, WlSeatGlobal,
|
||||
},
|
||||
wl_surface::{
|
||||
commit_timeline::{ClearReason, CommitTimeline, CommitTimelineError},
|
||||
cursor::CursorSurface,
|
||||
wl_subsurface::{PendingSubsurfaceData, SubsurfaceId, WlSubsurface},
|
||||
wp_fractional_scale_v1::WpFractionalScaleV1,
|
||||
wp_linux_drm_syncobj_surface_v1::WpLinuxDrmSyncobjSurfaceV1,
|
||||
wp_tearing_control_v1::WpTearingControlV1,
|
||||
wp_viewport::WpViewport,
|
||||
x_surface::XSurface,
|
||||
|
|
@ -57,7 +61,10 @@ use {
|
|||
smallmap::SmallMap,
|
||||
transform_ext::TransformExt,
|
||||
},
|
||||
video::dmabuf::DMA_BUF_SYNC_READ,
|
||||
video::{
|
||||
dmabuf::DMA_BUF_SYNC_READ,
|
||||
drm::sync_obj::{SyncObj, SyncObjPoint},
|
||||
},
|
||||
wire::{
|
||||
wl_surface::*, WlOutputId, WlSurfaceId, ZwpIdleInhibitorV1Id,
|
||||
ZwpLinuxDmabufFeedbackV1Id,
|
||||
|
|
@ -66,6 +73,7 @@ use {
|
|||
xwayland::XWaylandEvent,
|
||||
},
|
||||
ahash::AHashMap,
|
||||
isnt::std_1::primitive::IsntSliceExt,
|
||||
jay_config::video::Transform,
|
||||
std::{
|
||||
cell::{Cell, RefCell},
|
||||
|
|
@ -133,15 +141,46 @@ impl NodeVisitorBase for SurfaceSendPreferredTransformVisitor {
|
|||
}
|
||||
}
|
||||
|
||||
struct SurfaceBufferExplicitRelease {
|
||||
sync_obj: Rc<SyncObj>,
|
||||
point: SyncObjPoint,
|
||||
}
|
||||
|
||||
pub struct SurfaceBuffer {
|
||||
pub buffer: Rc<WlBuffer>,
|
||||
sync_files: SmallMap<BufferResvUser, SyncFile, 1>,
|
||||
pub sync: AcquireSync,
|
||||
pub release_sync: ReleaseSync,
|
||||
release: Option<SurfaceBufferExplicitRelease>,
|
||||
}
|
||||
|
||||
impl Drop for SurfaceBuffer {
|
||||
fn drop(&mut self) {
|
||||
let sync_files = self.sync_files.take();
|
||||
if let Some(release) = &self.release {
|
||||
let Some(ctx) = self.buffer.client.state.render_ctx.get() else {
|
||||
log::error!("Cannot signal release point because there is no render context");
|
||||
return;
|
||||
};
|
||||
let ctx = ctx.sync_obj_ctx();
|
||||
if sync_files.is_not_empty() {
|
||||
let res = ctx.import_sync_files(
|
||||
&release.sync_obj,
|
||||
release.point,
|
||||
sync_files.iter().map(|f| &f.1),
|
||||
);
|
||||
match res {
|
||||
Ok(_) => return,
|
||||
Err(e) => {
|
||||
log::error!("Could not import sync files into sync obj: {}", ErrorFmt(e));
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Err(e) = ctx.signal(&release.sync_obj, release.point) {
|
||||
log::error!("Could not signal release point: {}", ErrorFmt(e));
|
||||
}
|
||||
return;
|
||||
}
|
||||
if let Some(dmabuf) = &self.buffer.dmabuf {
|
||||
for (_, sync_file) in &sync_files {
|
||||
if let Err(e) = dmabuf.import_sync_file(DMA_BUF_SYNC_READ, sync_file) {
|
||||
|
|
@ -173,7 +212,7 @@ pub struct WlSurface {
|
|||
pub client: Rc<Client>,
|
||||
visible: Cell<bool>,
|
||||
role: Cell<SurfaceRole>,
|
||||
pending: RefCell<PendingState>,
|
||||
pending: RefCell<Box<PendingState>>,
|
||||
input_region: CloneCell<Option<Rc<Region>>>,
|
||||
opaque_region: Cell<Option<Rc<Region>>>,
|
||||
buffer_points: RefCell<BufferPoints>,
|
||||
|
|
@ -209,6 +248,9 @@ pub struct WlSurface {
|
|||
pub has_content_type_manager: Cell<bool>,
|
||||
content_type: Cell<Option<ContentType>>,
|
||||
pub drm_feedback: CopyHashMap<ZwpLinuxDmabufFeedbackV1Id, Rc<ZwpLinuxDmabufFeedbackV1>>,
|
||||
sync_obj_surface: CloneCell<Option<Rc<WpLinuxDrmSyncobjSurfaceV1>>>,
|
||||
destroyed: Cell<bool>,
|
||||
commit_timeline: CommitTimeline,
|
||||
}
|
||||
|
||||
impl Debug for WlSurface {
|
||||
|
|
@ -232,7 +274,7 @@ enum CommitAction {
|
|||
}
|
||||
|
||||
trait SurfaceExt {
|
||||
fn commit_requested(self: Rc<Self>, pending: &mut PendingState) -> CommitAction {
|
||||
fn commit_requested(self: Rc<Self>, pending: &mut Box<PendingState>) -> CommitAction {
|
||||
let _ = pending;
|
||||
CommitAction::ContinueCommit
|
||||
}
|
||||
|
|
@ -329,11 +371,14 @@ struct PendingState {
|
|||
xdg_surface: Option<Box<PendingXdgSurfaceData>>,
|
||||
layer_surface: Option<Box<PendingLayerSurfaceData>>,
|
||||
subsurfaces: AHashMap<SubsurfaceId, CommittedSubsurface>,
|
||||
acquire_point: Option<(Rc<SyncObj>, SyncObjPoint)>,
|
||||
release_point: Option<(Rc<SyncObj>, SyncObjPoint)>,
|
||||
explicit_sync: bool,
|
||||
}
|
||||
|
||||
struct CommittedSubsurface {
|
||||
subsurface: Rc<WlSubsurface>,
|
||||
state: PendingState,
|
||||
state: Box<PendingState>,
|
||||
}
|
||||
|
||||
impl PendingState {
|
||||
|
|
@ -341,7 +386,9 @@ impl PendingState {
|
|||
// discard state
|
||||
|
||||
if next.buffer.is_some() {
|
||||
if let Some(Some(prev)) = self.buffer.take() {
|
||||
if let Some((sync_obj, point)) = self.release_point.take() {
|
||||
client.state.signal_point(&sync_obj, point);
|
||||
} else if let Some(Some(prev)) = self.buffer.take() {
|
||||
if !prev.destroyed() {
|
||||
prev.send_release();
|
||||
}
|
||||
|
|
@ -371,6 +418,8 @@ impl PendingState {
|
|||
opt!(xwayland_serial);
|
||||
opt!(tearing);
|
||||
opt!(content_type);
|
||||
opt!(acquire_point);
|
||||
opt!(release_point);
|
||||
{
|
||||
let (dx1, dy1) = self.offset;
|
||||
let (dx2, dy2) = mem::take(&mut next.offset);
|
||||
|
|
@ -478,6 +527,9 @@ impl WlSurface {
|
|||
has_content_type_manager: Default::default(),
|
||||
content_type: Default::default(),
|
||||
drm_feedback: Default::default(),
|
||||
sync_obj_surface: Default::default(),
|
||||
destroyed: Cell::new(false),
|
||||
commit_timeline: client.commit_timelines.create_timeline(),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -706,6 +758,7 @@ impl WlSurface {
|
|||
|
||||
fn destroy(&self, parser: MsgParser<'_, '_>) -> Result<(), WlSurfaceError> {
|
||||
let _req: Destroy = self.parse(parser)?;
|
||||
self.commit_timeline.clear(ClearReason::Destroy);
|
||||
self.unset_dnd_icons();
|
||||
self.unset_cursors();
|
||||
self.ext.get().on_surface_destroy()?;
|
||||
|
|
@ -730,6 +783,7 @@ impl WlSurface {
|
|||
self.client.remove_obj(self)?;
|
||||
self.idle_inhibitors.clear();
|
||||
self.constraints.take();
|
||||
self.destroyed.set(true);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
@ -796,6 +850,9 @@ impl WlSurface {
|
|||
.surface
|
||||
.apply_state(&mut subsurface.state)?;
|
||||
}
|
||||
if self.destroyed.get() {
|
||||
return Ok(());
|
||||
}
|
||||
self.ext.get().before_apply_commit(pending)?;
|
||||
let mut scale_changed = false;
|
||||
if let Some(scale) = pending.scale.take() {
|
||||
|
|
@ -835,10 +892,20 @@ impl WlSurface {
|
|||
}
|
||||
if let Some(buffer) = buffer_change {
|
||||
buffer.update_texture_or_log();
|
||||
let (sync, release_sync) = match pending.explicit_sync {
|
||||
false => (AcquireSync::Implicit, ReleaseSync::Implicit),
|
||||
true => (AcquireSync::Unnecessary, ReleaseSync::Explicit),
|
||||
};
|
||||
let release = pending
|
||||
.release_point
|
||||
.take()
|
||||
.map(|(sync_obj, point)| SurfaceBufferExplicitRelease { sync_obj, point });
|
||||
let surface_buffer = SurfaceBuffer {
|
||||
buffer,
|
||||
sync_files: Default::default(),
|
||||
sync: AcquireSync::Implicit,
|
||||
sync,
|
||||
release_sync,
|
||||
release,
|
||||
};
|
||||
self.buffer.set(Some(Rc::new(surface_buffer)));
|
||||
self.buf_x.fetch_add(dx);
|
||||
|
|
@ -990,12 +1057,34 @@ impl WlSurface {
|
|||
let _req: Commit = self.parse(parser)?;
|
||||
let ext = self.ext.get();
|
||||
let pending = &mut *self.pending.borrow_mut();
|
||||
self.verify_explicit_sync(pending)?;
|
||||
if ext.commit_requested(pending) == CommitAction::ContinueCommit {
|
||||
self.apply_state(pending)?;
|
||||
self.commit_timeline.commit(self, pending)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn verify_explicit_sync(&self, pending: &mut PendingState) -> Result<(), WlSurfaceError> {
|
||||
pending.explicit_sync = self.sync_obj_surface.is_some();
|
||||
if !pending.explicit_sync {
|
||||
return Ok(());
|
||||
}
|
||||
let have_new_buffer = match &pending.buffer {
|
||||
None => false,
|
||||
Some(b) => b.is_some(),
|
||||
};
|
||||
match (
|
||||
pending.release_point.is_some(),
|
||||
pending.acquire_point.is_some(),
|
||||
have_new_buffer,
|
||||
) {
|
||||
(true, true, true) => Ok(()),
|
||||
(false, false, false) => Ok(()),
|
||||
(_, _, true) => Err(WlSurfaceError::MissingSyncPoints),
|
||||
(_, _, false) => Err(WlSurfaceError::UnexpectedSyncPoints),
|
||||
}
|
||||
}
|
||||
|
||||
fn set_buffer_transform(&self, parser: MsgParser<'_, '_>) -> Result<(), WlSurfaceError> {
|
||||
let req: SetBufferTransform = self.parse(parser)?;
|
||||
let Some(tf) = Transform::from_wl(req.transform) else {
|
||||
|
|
@ -1215,6 +1304,7 @@ impl Object for WlSurface {
|
|||
self.tearing_control.take();
|
||||
self.constraints.clear();
|
||||
self.drm_feedback.clear();
|
||||
self.commit_timeline.clear(ClearReason::BreakLoops);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1381,8 +1471,15 @@ pub enum WlSurfaceError {
|
|||
ViewportOutsideBuffer,
|
||||
#[error("attach request must not contain offset")]
|
||||
OffsetInAttach,
|
||||
#[error(transparent)]
|
||||
CommitTimelineError(Box<CommitTimelineError>),
|
||||
#[error("Explicit sync buffer is attached but acquire or release points are not set")]
|
||||
MissingSyncPoints,
|
||||
#[error("No buffer is attached but acquire or release point is set")]
|
||||
UnexpectedSyncPoints,
|
||||
}
|
||||
efrom!(WlSurfaceError, ClientError);
|
||||
efrom!(WlSurfaceError, XdgSurfaceError);
|
||||
efrom!(WlSurfaceError, ZwlrLayerSurfaceV1Error);
|
||||
efrom!(WlSurfaceError, MsgParserError);
|
||||
efrom!(WlSurfaceError, CommitTimelineError);
|
||||
|
|
|
|||
295
src/ifs/wl_surface/commit_timeline.rs
Normal file
295
src/ifs/wl_surface/commit_timeline.rs
Normal file
|
|
@ -0,0 +1,295 @@
|
|||
use {
|
||||
crate::{
|
||||
ifs::wl_surface::{PendingState, WlSurface, WlSurfaceError},
|
||||
utils::{
|
||||
clonecell::CloneCell,
|
||||
copyhashmap::CopyHashMap,
|
||||
linkedlist::{LinkedList, LinkedNode, NodeRef},
|
||||
numcell::NumCell,
|
||||
},
|
||||
video::drm::{
|
||||
sync_obj::{SyncObj, SyncObjPoint},
|
||||
wait_for_sync_obj::{SyncObjWaiter, WaitForSyncObj, WaitForSyncObjHandle},
|
||||
DrmError,
|
||||
},
|
||||
},
|
||||
isnt::std_1::primitive::IsntSliceExt,
|
||||
smallvec::SmallVec,
|
||||
std::{
|
||||
cell::{Cell, RefCell},
|
||||
mem,
|
||||
ops::{Deref, DerefMut},
|
||||
rc::Rc,
|
||||
},
|
||||
thiserror::Error,
|
||||
};
|
||||
|
||||
const MAX_TIMELINE_DEPTH: usize = 256;
|
||||
|
||||
linear_ids!(CommitTimelineIds, CommitTimelineId, u64);
|
||||
|
||||
pub struct CommitTimelines {
|
||||
next_id: CommitTimelineIds,
|
||||
wfs: Rc<WaitForSyncObj>,
|
||||
depth: NumCell<usize>,
|
||||
gc: CopyHashMap<CommitTimelineId, LinkedList<Entry>>,
|
||||
}
|
||||
|
||||
pub struct CommitTimeline {
|
||||
shared: Rc<CommitTimelines>,
|
||||
own_timeline: Rc<Inner>,
|
||||
effective_timeline: CloneCell<Rc<Inner>>,
|
||||
effective_timeline_id: Cell<CommitTimelineId>,
|
||||
}
|
||||
|
||||
struct Inner {
|
||||
id: CommitTimelineId,
|
||||
entries: LinkedList<Entry>,
|
||||
}
|
||||
|
||||
fn add_entry(
|
||||
list: &LinkedList<Entry>,
|
||||
shared: &Rc<CommitTimelines>,
|
||||
kind: EntryKind,
|
||||
) -> NodeRef<Entry> {
|
||||
shared.depth.fetch_add(1);
|
||||
let link = list.add_last(Entry {
|
||||
link: Cell::new(None),
|
||||
shared: shared.clone(),
|
||||
kind,
|
||||
});
|
||||
let noderef = link.to_ref();
|
||||
noderef.link.set(Some(link));
|
||||
noderef
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum CommitTimelineError {
|
||||
#[error(transparent)]
|
||||
ImmediateCommit(WlSurfaceError),
|
||||
#[error("Could not apply a delayed commit")]
|
||||
DelayedCommit(#[source] WlSurfaceError),
|
||||
#[error("Could not register a wait")]
|
||||
RegisterWait(#[source] DrmError),
|
||||
#[error("Syncobj wait failed")]
|
||||
Wait(#[source] DrmError),
|
||||
#[error("The client has too many pending commits")]
|
||||
Depth,
|
||||
}
|
||||
|
||||
impl CommitTimelines {
|
||||
pub fn new(wfs: &Rc<WaitForSyncObj>) -> Self {
|
||||
Self {
|
||||
next_id: Default::default(),
|
||||
depth: NumCell::new(0),
|
||||
wfs: wfs.clone(),
|
||||
gc: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_timeline(self: &Rc<Self>) -> CommitTimeline {
|
||||
let id = self.next_id.next();
|
||||
let timeline = Rc::new(Inner {
|
||||
id,
|
||||
entries: Default::default(),
|
||||
});
|
||||
CommitTimeline {
|
||||
shared: self.clone(),
|
||||
own_timeline: timeline.clone(),
|
||||
effective_timeline: CloneCell::new(timeline),
|
||||
effective_timeline_id: Cell::new(id),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn clear(&self) {
|
||||
for (_, list) in self.gc.lock().drain() {
|
||||
break_loops(&list);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub enum ClearReason {
|
||||
BreakLoops,
|
||||
Destroy,
|
||||
}
|
||||
|
||||
fn break_loops(list: &LinkedList<Entry>) {
|
||||
for entry in list.iter() {
|
||||
entry.link.take();
|
||||
}
|
||||
}
|
||||
|
||||
impl CommitTimeline {
|
||||
pub fn clear(&self, reason: ClearReason) {
|
||||
match reason {
|
||||
ClearReason::BreakLoops => break_loops(&self.own_timeline.entries),
|
||||
ClearReason::Destroy => {
|
||||
if self.own_timeline.entries.is_not_empty() {
|
||||
let list = LinkedList::new();
|
||||
list.append_all(&self.own_timeline.entries);
|
||||
add_entry(&list, &self.shared, EntryKind::Gc(self.own_timeline.id));
|
||||
self.shared.gc.set(self.own_timeline.id, list);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn commit(
|
||||
&self,
|
||||
surface: &Rc<WlSurface>,
|
||||
pending: &mut Box<PendingState>,
|
||||
) -> Result<(), CommitTimelineError> {
|
||||
let mut points = SmallVec::new();
|
||||
consume_acquire_points(pending, &mut points);
|
||||
if points.is_empty() && self.own_timeline.entries.is_empty() {
|
||||
return surface
|
||||
.apply_state(pending)
|
||||
.map_err(CommitTimelineError::ImmediateCommit);
|
||||
}
|
||||
if self.shared.depth.get() >= MAX_TIMELINE_DEPTH {
|
||||
return Err(CommitTimelineError::Depth);
|
||||
}
|
||||
set_effective_timeline(self, pending, &self.own_timeline);
|
||||
let noderef = add_entry(
|
||||
&self.own_timeline.entries,
|
||||
&self.shared,
|
||||
EntryKind::Commit(Commit {
|
||||
surface: surface.clone(),
|
||||
pending: RefCell::new(mem::take(pending)),
|
||||
sync_obj: NumCell::new(points.len()),
|
||||
wait_handles: Cell::new(Default::default()),
|
||||
}),
|
||||
);
|
||||
if points.is_not_empty() {
|
||||
let mut wait_handles = SmallVec::new();
|
||||
let noderef = Rc::new(noderef);
|
||||
for (sync_obj, point) in points {
|
||||
let handle = self
|
||||
.shared
|
||||
.wfs
|
||||
.wait(&sync_obj, point, true, noderef.clone())
|
||||
.map_err(CommitTimelineError::RegisterWait)?;
|
||||
wait_handles.push(handle);
|
||||
}
|
||||
let EntryKind::Commit(commit) = &noderef.kind else {
|
||||
unreachable!();
|
||||
};
|
||||
commit.wait_handles.set(wait_handles);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl SyncObjWaiter for NodeRef<Entry> {
|
||||
fn done(self: Rc<Self>, result: Result<(), DrmError>) {
|
||||
let EntryKind::Commit(commit) = &self.kind else {
|
||||
unreachable!();
|
||||
};
|
||||
if let Err(e) = result {
|
||||
commit.surface.client.error(CommitTimelineError::Wait(e));
|
||||
return;
|
||||
}
|
||||
commit.sync_obj.fetch_sub(1);
|
||||
if let Err(e) = flush_from(self.deref().clone()) {
|
||||
commit
|
||||
.surface
|
||||
.client
|
||||
.error(CommitTimelineError::DelayedCommit(e));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct Entry {
|
||||
link: Cell<Option<LinkedNode<Entry>>>,
|
||||
shared: Rc<CommitTimelines>,
|
||||
kind: EntryKind,
|
||||
}
|
||||
|
||||
enum EntryKind {
|
||||
Commit(Commit),
|
||||
Wait(Cell<bool>),
|
||||
Signal(NodeRef<Entry>),
|
||||
Gc(CommitTimelineId),
|
||||
}
|
||||
|
||||
struct Commit {
|
||||
surface: Rc<WlSurface>,
|
||||
pending: RefCell<Box<PendingState>>,
|
||||
sync_obj: NumCell<usize>,
|
||||
wait_handles: Cell<SmallVec<[WaitForSyncObjHandle; 1]>>,
|
||||
}
|
||||
|
||||
fn flush_from(mut point: NodeRef<Entry>) -> Result<(), WlSurfaceError> {
|
||||
let mut gc_list = None;
|
||||
while point.maybe_apply(&mut gc_list)? {
|
||||
point.shared.depth.fetch_sub(1);
|
||||
let _link = point.link.take();
|
||||
match point.next() {
|
||||
None => break,
|
||||
Some(n) => point = n,
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
impl NodeRef<Entry> {
|
||||
fn maybe_apply(&self, gc_list: &mut Option<LinkedList<Entry>>) -> Result<bool, WlSurfaceError> {
|
||||
if self.prev().is_some() {
|
||||
return Ok(false);
|
||||
}
|
||||
match &self.kind {
|
||||
EntryKind::Commit(c) => {
|
||||
if c.sync_obj.get() > 0 {
|
||||
return Ok(false);
|
||||
}
|
||||
c.surface.apply_state(c.pending.borrow_mut().deref_mut())?;
|
||||
Ok(true)
|
||||
}
|
||||
EntryKind::Wait(signaled) => Ok(signaled.get()),
|
||||
EntryKind::Signal(s) => match &s.kind {
|
||||
EntryKind::Wait(signaled) => {
|
||||
signaled.set(true);
|
||||
flush_from(s.clone())?;
|
||||
Ok(true)
|
||||
}
|
||||
_ => unreachable!(),
|
||||
},
|
||||
EntryKind::Gc(id) => {
|
||||
*gc_list = self.shared.gc.remove(id);
|
||||
Ok(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type Point = (Rc<SyncObj>, SyncObjPoint);
|
||||
|
||||
fn consume_acquire_points(pending: &mut PendingState, points: &mut SmallVec<[Point; 1]>) {
|
||||
if let Some(point) = pending.acquire_point.take() {
|
||||
points.push(point);
|
||||
}
|
||||
for ss in pending.subsurfaces.values_mut() {
|
||||
consume_acquire_points(&mut ss.state, points);
|
||||
}
|
||||
}
|
||||
|
||||
fn set_effective_timeline(
|
||||
timeline: &CommitTimeline,
|
||||
pending: &PendingState,
|
||||
effective: &Rc<Inner>,
|
||||
) {
|
||||
if timeline.effective_timeline_id.replace(effective.id) != effective.id {
|
||||
let prev = timeline.effective_timeline.set(effective.clone());
|
||||
if prev.entries.is_not_empty() {
|
||||
let noderef = add_entry(
|
||||
&effective.entries,
|
||||
&timeline.shared,
|
||||
EntryKind::Wait(Cell::new(false)),
|
||||
);
|
||||
add_entry(&prev.entries, &timeline.shared, EntryKind::Signal(noderef));
|
||||
}
|
||||
}
|
||||
for ss in pending.subsurfaces.values() {
|
||||
set_effective_timeline(&ss.subsurface.surface.commit_timeline, &ss.state, effective);
|
||||
}
|
||||
}
|
||||
|
|
@ -313,7 +313,7 @@ impl Object for WlSubsurface {
|
|||
simple_add_obj!(WlSubsurface);
|
||||
|
||||
impl SurfaceExt for WlSubsurface {
|
||||
fn commit_requested(self: Rc<Self>, pending: &mut PendingState) -> CommitAction {
|
||||
fn commit_requested(self: Rc<Self>, pending: &mut Box<PendingState>) -> CommitAction {
|
||||
if self.sync() {
|
||||
let mut parent_pending = self.parent.pending.borrow_mut();
|
||||
match parent_pending.subsurfaces.entry(self.unique_id) {
|
||||
|
|
|
|||
103
src/ifs/wl_surface/wp_linux_drm_syncobj_surface_v1.rs
Normal file
103
src/ifs/wl_surface/wp_linux_drm_syncobj_surface_v1.rs
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
use {
|
||||
crate::{
|
||||
client::{Client, ClientError},
|
||||
ifs::wl_surface::WlSurface,
|
||||
leaks::Tracker,
|
||||
object::Object,
|
||||
utils::buffd::{MsgParser, MsgParserError},
|
||||
video::drm::sync_obj::SyncObjPoint,
|
||||
wire::{wp_linux_drm_syncobj_surface_v1::*, WpLinuxDrmSyncobjSurfaceV1Id},
|
||||
},
|
||||
std::rc::Rc,
|
||||
thiserror::Error,
|
||||
};
|
||||
|
||||
pub struct WpLinuxDrmSyncobjSurfaceV1 {
|
||||
id: WpLinuxDrmSyncobjSurfaceV1Id,
|
||||
client: Rc<Client>,
|
||||
surface: Rc<WlSurface>,
|
||||
pub tracker: Tracker<Self>,
|
||||
}
|
||||
|
||||
impl WpLinuxDrmSyncobjSurfaceV1 {
|
||||
pub fn new(
|
||||
id: WpLinuxDrmSyncobjSurfaceV1Id,
|
||||
client: &Rc<Client>,
|
||||
surface: &Rc<WlSurface>,
|
||||
) -> Self {
|
||||
Self {
|
||||
id,
|
||||
client: client.clone(),
|
||||
tracker: Default::default(),
|
||||
surface: surface.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn install(self: &Rc<Self>) -> Result<(), WpLinuxDrmSyncobjSurfaceV1Error> {
|
||||
if self.surface.sync_obj_surface.is_some() {
|
||||
return Err(WpLinuxDrmSyncobjSurfaceV1Error::Exists);
|
||||
}
|
||||
self.surface.sync_obj_surface.set(Some(self.clone()));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn destroy(&self, parser: MsgParser<'_, '_>) -> Result<(), WpLinuxDrmSyncobjSurfaceV1Error> {
|
||||
let _req: Destroy = self.client.parse(self, parser)?;
|
||||
self.surface.sync_obj_surface.take();
|
||||
let pending = &mut *self.surface.pending.borrow_mut();
|
||||
pending.release_point.take();
|
||||
pending.acquire_point.take();
|
||||
self.client.remove_obj(self)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn set_acquire_point(
|
||||
&self,
|
||||
parser: MsgParser<'_, '_>,
|
||||
) -> Result<(), WpLinuxDrmSyncobjSurfaceV1Error> {
|
||||
let req: SetAcquirePoint = self.client.parse(self, parser)?;
|
||||
let point = point(req.point_hi, req.point_lo);
|
||||
let timeline = self.client.lookup(req.timeline)?;
|
||||
self.surface.pending.borrow_mut().acquire_point = Some((timeline.sync_obj.clone(), point));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn set_release_point(
|
||||
&self,
|
||||
parser: MsgParser<'_, '_>,
|
||||
) -> Result<(), WpLinuxDrmSyncobjSurfaceV1Error> {
|
||||
let req: SetReleasePoint = self.client.parse(self, parser)?;
|
||||
let point = point(req.point_hi, req.point_lo);
|
||||
let timeline = self.client.lookup(req.timeline)?;
|
||||
self.surface.pending.borrow_mut().release_point = Some((timeline.sync_obj.clone(), point));
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn point(hi: u32, lo: u32) -> SyncObjPoint {
|
||||
SyncObjPoint((hi as u64) << 32 | (lo as u64))
|
||||
}
|
||||
|
||||
object_base! {
|
||||
self = WpLinuxDrmSyncobjSurfaceV1;
|
||||
|
||||
DESTROY => destroy,
|
||||
SET_ACQUIRE_POINT => set_acquire_point,
|
||||
SET_RELEASE_POINT => set_release_point,
|
||||
}
|
||||
|
||||
impl Object for WpLinuxDrmSyncobjSurfaceV1 {}
|
||||
|
||||
simple_add_obj!(WpLinuxDrmSyncobjSurfaceV1);
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum WpLinuxDrmSyncobjSurfaceV1Error {
|
||||
#[error("Parsing failed")]
|
||||
MsgParserError(#[source] Box<MsgParserError>),
|
||||
#[error(transparent)]
|
||||
ClientError(Box<ClientError>),
|
||||
#[error("The surface already has a syncobj extension attached")]
|
||||
Exists,
|
||||
}
|
||||
efrom!(WpLinuxDrmSyncobjSurfaceV1Error, MsgParserError);
|
||||
efrom!(WpLinuxDrmSyncobjSurfaceV1Error, ClientError);
|
||||
130
src/ifs/wp_linux_drm_syncobj_manager_v1.rs
Normal file
130
src/ifs/wp_linux_drm_syncobj_manager_v1.rs
Normal file
|
|
@ -0,0 +1,130 @@
|
|||
use {
|
||||
crate::{
|
||||
client::{Client, ClientError},
|
||||
globals::{Global, GlobalName},
|
||||
ifs::{
|
||||
wl_surface::wp_linux_drm_syncobj_surface_v1::{
|
||||
WpLinuxDrmSyncobjSurfaceV1, WpLinuxDrmSyncobjSurfaceV1Error,
|
||||
},
|
||||
wp_linux_drm_syncobj_timeline_v1::WpLinuxDrmSyncobjTimelineV1,
|
||||
},
|
||||
leaks::Tracker,
|
||||
object::Object,
|
||||
utils::buffd::{MsgParser, MsgParserError},
|
||||
video::drm::sync_obj::SyncObj,
|
||||
wire::{wp_linux_drm_syncobj_manager_v1::*, WpLinuxDrmSyncobjManagerV1Id},
|
||||
},
|
||||
std::rc::Rc,
|
||||
thiserror::Error,
|
||||
};
|
||||
|
||||
pub struct WpLinuxDrmSyncobjManagerV1Global {
|
||||
pub name: GlobalName,
|
||||
}
|
||||
|
||||
pub struct WpLinuxDrmSyncobjManagerV1 {
|
||||
pub id: WpLinuxDrmSyncobjManagerV1Id,
|
||||
pub client: Rc<Client>,
|
||||
pub tracker: Tracker<Self>,
|
||||
}
|
||||
|
||||
impl WpLinuxDrmSyncobjManagerV1Global {
|
||||
pub fn new(name: GlobalName) -> Self {
|
||||
Self { name }
|
||||
}
|
||||
|
||||
fn bind_(
|
||||
self: Rc<Self>,
|
||||
id: WpLinuxDrmSyncobjManagerV1Id,
|
||||
client: &Rc<Client>,
|
||||
_version: u32,
|
||||
) -> Result<(), WpLinuxDrmSyncobjManagerV1Error> {
|
||||
let obj = Rc::new(WpLinuxDrmSyncobjManagerV1 {
|
||||
id,
|
||||
client: client.clone(),
|
||||
tracker: Default::default(),
|
||||
});
|
||||
track!(client, obj);
|
||||
client.add_client_obj(&obj)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
global_base!(
|
||||
WpLinuxDrmSyncobjManagerV1Global,
|
||||
WpLinuxDrmSyncobjManagerV1,
|
||||
WpLinuxDrmSyncobjManagerV1Error
|
||||
);
|
||||
|
||||
impl Global for WpLinuxDrmSyncobjManagerV1Global {
|
||||
fn singleton(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn version(&self) -> u32 {
|
||||
1
|
||||
}
|
||||
}
|
||||
|
||||
simple_add_global!(WpLinuxDrmSyncobjManagerV1Global);
|
||||
|
||||
impl WpLinuxDrmSyncobjManagerV1 {
|
||||
fn destroy(&self, msg: MsgParser<'_, '_>) -> Result<(), WpLinuxDrmSyncobjManagerV1Error> {
|
||||
let _req: Destroy = self.client.parse(self, msg)?;
|
||||
self.client.remove_obj(self)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn get_surface(&self, msg: MsgParser<'_, '_>) -> Result<(), WpLinuxDrmSyncobjManagerV1Error> {
|
||||
let req: GetSurface = self.client.parse(self, msg)?;
|
||||
let surface = self.client.lookup(req.surface)?;
|
||||
let sync = Rc::new(WpLinuxDrmSyncobjSurfaceV1::new(
|
||||
req.id,
|
||||
&self.client,
|
||||
&surface,
|
||||
));
|
||||
track!(self.client, sync);
|
||||
sync.install()?;
|
||||
self.client.add_client_obj(&sync)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn import_timeline(
|
||||
&self,
|
||||
msg: MsgParser<'_, '_>,
|
||||
) -> Result<(), WpLinuxDrmSyncobjManagerV1Error> {
|
||||
let req: ImportTimeline = self.client.parse(self, msg)?;
|
||||
let sync_obj = Rc::new(SyncObj::new(&req.fd));
|
||||
let sync = Rc::new(WpLinuxDrmSyncobjTimelineV1::new(
|
||||
req.id,
|
||||
&self.client,
|
||||
&sync_obj,
|
||||
));
|
||||
self.client.add_client_obj(&sync)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
object_base! {
|
||||
self = WpLinuxDrmSyncobjManagerV1;
|
||||
|
||||
DESTROY => destroy,
|
||||
GET_SURFACE => get_surface,
|
||||
IMPORT_TIMELINE => import_timeline,
|
||||
}
|
||||
|
||||
impl Object for WpLinuxDrmSyncobjManagerV1 {}
|
||||
|
||||
simple_add_obj!(WpLinuxDrmSyncobjManagerV1);
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum WpLinuxDrmSyncobjManagerV1Error {
|
||||
#[error("Parsing failed")]
|
||||
MsgParserError(#[source] Box<MsgParserError>),
|
||||
#[error(transparent)]
|
||||
ClientError(Box<ClientError>),
|
||||
#[error(transparent)]
|
||||
WpLinuxDrmSyncobjSurfaceV1Error(#[from] WpLinuxDrmSyncobjSurfaceV1Error),
|
||||
}
|
||||
efrom!(WpLinuxDrmSyncobjManagerV1Error, MsgParserError);
|
||||
efrom!(WpLinuxDrmSyncobjManagerV1Error, ClientError);
|
||||
64
src/ifs/wp_linux_drm_syncobj_timeline_v1.rs
Normal file
64
src/ifs/wp_linux_drm_syncobj_timeline_v1.rs
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
use {
|
||||
crate::{
|
||||
client::{Client, ClientError},
|
||||
leaks::Tracker,
|
||||
object::Object,
|
||||
utils::buffd::{MsgParser, MsgParserError},
|
||||
video::drm::sync_obj::SyncObj,
|
||||
wire::{wp_linux_drm_syncobj_timeline_v1::*, WpLinuxDrmSyncobjTimelineV1Id},
|
||||
},
|
||||
std::rc::Rc,
|
||||
thiserror::Error,
|
||||
};
|
||||
|
||||
pub struct WpLinuxDrmSyncobjTimelineV1 {
|
||||
id: WpLinuxDrmSyncobjTimelineV1Id,
|
||||
client: Rc<Client>,
|
||||
pub sync_obj: Rc<SyncObj>,
|
||||
pub tracker: Tracker<Self>,
|
||||
}
|
||||
|
||||
impl WpLinuxDrmSyncobjTimelineV1 {
|
||||
pub fn new(
|
||||
id: WpLinuxDrmSyncobjTimelineV1Id,
|
||||
client: &Rc<Client>,
|
||||
sync_obj: &Rc<SyncObj>,
|
||||
) -> Self {
|
||||
Self {
|
||||
id,
|
||||
client: client.clone(),
|
||||
tracker: Default::default(),
|
||||
sync_obj: sync_obj.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
fn destroy(&self, parser: MsgParser<'_, '_>) -> Result<(), WpLinuxDrmSyncobjTimelineV1Error> {
|
||||
let _destroy: Destroy = self.client.parse(self, parser)?;
|
||||
self.client.remove_obj(self)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
object_base! {
|
||||
self = WpLinuxDrmSyncobjTimelineV1;
|
||||
|
||||
DESTROY => destroy,
|
||||
}
|
||||
|
||||
impl Object for WpLinuxDrmSyncobjTimelineV1 {}
|
||||
|
||||
dedicated_add_obj!(
|
||||
WpLinuxDrmSyncobjTimelineV1,
|
||||
WpLinuxDrmSyncobjTimelineV1Id,
|
||||
timelines
|
||||
);
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum WpLinuxDrmSyncobjTimelineV1Error {
|
||||
#[error("Parsing failed")]
|
||||
MsgParserError(#[source] Box<MsgParserError>),
|
||||
#[error(transparent)]
|
||||
ClientError(Box<ClientError>),
|
||||
}
|
||||
efrom!(WpLinuxDrmSyncobjTimelineV1Error, MsgParserError);
|
||||
efrom!(WpLinuxDrmSyncobjTimelineV1Error, ClientError);
|
||||
|
|
@ -386,10 +386,6 @@ impl Renderer<'_> {
|
|||
bounds: Option<&Rect>,
|
||||
) {
|
||||
if let Some(tex) = buffer.buffer.texture.get() {
|
||||
let release_sync = match buffer.sync {
|
||||
AcquireSync::Implicit => ReleaseSync::Implicit,
|
||||
AcquireSync::None | AcquireSync::SyncFile { .. } => ReleaseSync::Explicit,
|
||||
};
|
||||
self.base.render_texture(
|
||||
&tex,
|
||||
x,
|
||||
|
|
@ -400,7 +396,7 @@ impl Renderer<'_> {
|
|||
bounds,
|
||||
Some(buffer.clone()),
|
||||
buffer.sync.clone(),
|
||||
release_sync,
|
||||
buffer.release_sync,
|
||||
);
|
||||
} else if let Some(color) = &buffer.buffer.color {
|
||||
if let Some(rect) = Rect::new_sized(x, y, tsize.0, tsize.1) {
|
||||
|
|
|
|||
40
src/state.rs
40
src/state.rs
|
|
@ -37,6 +37,7 @@ use {
|
|||
zwp_idle_inhibitor_v1::{IdleInhibitorId, IdleInhibitorIds, ZwpIdleInhibitorV1},
|
||||
NoneSurfaceExt, WlSurface,
|
||||
},
|
||||
wp_linux_drm_syncobj_manager_v1::WpLinuxDrmSyncobjManagerV1Global,
|
||||
zwlr_screencopy_frame_v1::ZwlrScreencopyFrameV1,
|
||||
zwp_linux_dmabuf_feedback_v1::ZwpLinuxDmabufFeedbackV1,
|
||||
zwp_linux_dmabuf_v1::ZwpLinuxDmabufV1Global,
|
||||
|
|
@ -59,7 +60,14 @@ use {
|
|||
linkedlist::LinkedList, numcell::NumCell, queue::AsyncQueue, refcounted::RefCounted,
|
||||
run_toplevel::RunToplevel,
|
||||
},
|
||||
video::{dmabuf::DmaBufIds, drm::Drm},
|
||||
video::{
|
||||
dmabuf::DmaBufIds,
|
||||
drm::{
|
||||
sync_obj::{SyncObj, SyncObjPoint},
|
||||
wait_for_sync_obj::WaitForSyncObj,
|
||||
Drm,
|
||||
},
|
||||
},
|
||||
wheel::Wheel,
|
||||
wire::{
|
||||
ExtForeignToplevelListV1Id, JayRenderCtxId, JaySeatEventsId, JayWorkspaceWatcherId,
|
||||
|
|
@ -163,6 +171,7 @@ pub struct State {
|
|||
pub double_click_distance: Cell<i32>,
|
||||
pub create_default_seat: Cell<bool>,
|
||||
pub subsurface_ids: SubsurfaceIds,
|
||||
pub wait_for_sync_obj: Rc<WaitForSyncObj>,
|
||||
}
|
||||
|
||||
// impl Drop for State {
|
||||
|
|
@ -367,6 +376,8 @@ impl State {
|
|||
self.render_ctx_version.fetch_add(1);
|
||||
self.cursors.set(None);
|
||||
self.drm_feedback.set(None);
|
||||
self.wait_for_sync_obj
|
||||
.set_ctx(ctx.as_ref().map(|c| c.sync_obj_ctx().clone()));
|
||||
|
||||
'handle_new_feedback: {
|
||||
if let Some(ctx) = &ctx {
|
||||
|
|
@ -435,11 +446,18 @@ impl State {
|
|||
seat.render_ctx_changed();
|
||||
}
|
||||
|
||||
if ctx.is_some() && !self.render_ctx_ever_initialized.replace(true) {
|
||||
self.add_global(&Rc::new(WlDrmGlobal::new(self.globals.name())));
|
||||
self.add_global(&Rc::new(ZwpLinuxDmabufV1Global::new(self.globals.name())));
|
||||
if let Some(config) = self.config.get() {
|
||||
config.graphics_initialized();
|
||||
if let Some(ctx) = &ctx {
|
||||
if !self.render_ctx_ever_initialized.replace(true) {
|
||||
self.add_global(&Rc::new(WlDrmGlobal::new(self.globals.name())));
|
||||
self.add_global(&Rc::new(ZwpLinuxDmabufV1Global::new(self.globals.name())));
|
||||
if ctx.sync_obj_ctx().supports_async_wait() {
|
||||
self.add_global(&Rc::new(WpLinuxDrmSyncobjManagerV1Global::new(
|
||||
self.globals.name(),
|
||||
)));
|
||||
}
|
||||
if let Some(config) = self.config.get() {
|
||||
config.graphics_initialized();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -931,6 +949,16 @@ impl State {
|
|||
self.globals.add_global(self, &seat);
|
||||
seat
|
||||
}
|
||||
|
||||
pub fn signal_point(&self, sync_obj: &SyncObj, point: SyncObjPoint) {
|
||||
let Some(ctx) = self.render_ctx.get() else {
|
||||
log::error!("Cannot signal sync obj point because there is no render context");
|
||||
return;
|
||||
};
|
||||
if let Err(e) = ctx.sync_obj_ctx().signal(sync_obj, point) {
|
||||
log::error!("Could not signal sync obj: {}", ErrorFmt(e));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
|
|
|
|||
|
|
@ -46,6 +46,23 @@ impl<T> LinkedList<T> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn append_all(&self, other: &LinkedList<T>) {
|
||||
if other.is_empty() {
|
||||
return;
|
||||
}
|
||||
unsafe {
|
||||
let o_root = other.root.data.as_ref();
|
||||
let o_first = o_root.next.get();
|
||||
let o_last = o_root.prev.get();
|
||||
let s_first = self.root.data;
|
||||
let s_last = s_first.as_ref().prev.get();
|
||||
o_first.as_ref().prev.set(s_last);
|
||||
s_last.as_ref().next.set(o_first);
|
||||
o_last.as_ref().next.set(s_first);
|
||||
s_first.as_ref().prev.set(o_last);
|
||||
}
|
||||
}
|
||||
|
||||
fn endpoint(&self, ep: NonNull<NodeData<T>>) -> Option<NodeRef<T>> {
|
||||
unsafe {
|
||||
if ep != self.root.data {
|
||||
|
|
@ -57,11 +74,14 @@ impl<T> LinkedList<T> {
|
|||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.last().is_none()
|
||||
}
|
||||
|
||||
pub fn is_not_empty(&self) -> bool {
|
||||
!self.is_empty()
|
||||
}
|
||||
|
||||
pub fn last(&self) -> Option<NodeRef<T>> {
|
||||
unsafe { self.endpoint(self.root.data.as_ref().prev.get()) }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -58,6 +58,10 @@ impl<K: Eq, V, const N: usize> SmallMap<K, V, N> {
|
|||
unsafe { self.m.get().deref().is_empty() }
|
||||
}
|
||||
|
||||
pub fn is_not_empty(&self) -> bool {
|
||||
!self.is_empty()
|
||||
}
|
||||
|
||||
pub fn remove(&self, k: &K) -> Option<V> {
|
||||
unsafe { self.m.get().deref_mut().remove(k) }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
pub mod sync_obj;
|
||||
mod sys;
|
||||
pub mod wait_for_sync_obj;
|
||||
|
||||
use {
|
||||
crate::{
|
||||
|
|
@ -52,7 +54,7 @@ pub use sys::{
|
|||
#[derive(Debug, Error)]
|
||||
pub enum DrmError {
|
||||
#[error("Could not reopen a node")]
|
||||
ReopenNode(#[source] crate::utils::oserror::OsError),
|
||||
ReopenNode(#[source] OsError),
|
||||
#[error("Could not retrieve the render node name")]
|
||||
RenderNodeName(#[source] OsError),
|
||||
#[error("Could not retrieve the device node name")]
|
||||
|
|
@ -113,6 +115,28 @@ pub enum DrmError {
|
|||
Version(#[source] OsError),
|
||||
#[error("Format of IN_FORMATS property is invalid")]
|
||||
InFormats,
|
||||
#[error("Could not import a sync obj")]
|
||||
ImportSyncObj(#[source] OsError),
|
||||
#[error("Could not create a sync obj")]
|
||||
CreateSyncObj(#[source] OsError),
|
||||
#[error("Could not export a sync obj")]
|
||||
ExportSyncObj(#[source] OsError),
|
||||
#[error("Could not register an eventfd with a sync obj")]
|
||||
RegisterEventfd(#[source] OsError),
|
||||
#[error("Could not create an eventfd")]
|
||||
EventFd(#[source] OsError),
|
||||
#[error("Could not read from an eventfd")]
|
||||
ReadEventFd(#[source] IoUringError),
|
||||
#[error("No sync obj context available")]
|
||||
NoSyncObjContextAvailable,
|
||||
#[error("Could not signal the sync obj")]
|
||||
SignalSyncObj(#[source] OsError),
|
||||
#[error("Could not transfer a sync obj point")]
|
||||
TransferPoint(#[source] OsError),
|
||||
#[error("Could not merge two sync files")]
|
||||
Merge(#[source] OsError),
|
||||
#[error("Could not import a sync file into a sync obj")]
|
||||
ImportSyncFile(#[source] OsError),
|
||||
}
|
||||
|
||||
fn render_node_name(fd: c::c_int) -> Result<Ustring, DrmError> {
|
||||
|
|
|
|||
238
src/video/drm/sync_obj.rs
Normal file
238
src/video/drm/sync_obj.rs
Normal file
|
|
@ -0,0 +1,238 @@
|
|||
use {
|
||||
crate::{
|
||||
gfx_api::SyncFile,
|
||||
utils::{
|
||||
clonecell::{CloneCell, UnsafeCellCloneSafe},
|
||||
copyhashmap::CopyHashMap,
|
||||
errorfmt::ErrorFmt,
|
||||
linkedlist::{LinkedList, LinkedNode},
|
||||
oserror::OsError,
|
||||
},
|
||||
video::drm::{
|
||||
sys::{
|
||||
sync_ioc_merge, sync_obj_create, sync_obj_destroy, sync_obj_eventfd,
|
||||
sync_obj_fd_to_handle, sync_obj_handle_to_fd, sync_obj_signal, sync_obj_transfer,
|
||||
DRM_SYNCOBJ_FD_TO_HANDLE_FLAGS_IMPORT_SYNC_FILE,
|
||||
DRM_SYNCOBJ_WAIT_FLAGS_WAIT_AVAILABLE,
|
||||
},
|
||||
DrmError,
|
||||
},
|
||||
},
|
||||
std::{
|
||||
rc::Rc,
|
||||
sync::atomic::{AtomicU64, Ordering::Relaxed},
|
||||
},
|
||||
uapi::{c, OwnedFd},
|
||||
};
|
||||
|
||||
static SYNCOBJ_ID: AtomicU64 = AtomicU64::new(0);
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
|
||||
pub struct SyncObjId(u64);
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
|
||||
struct SyncObjHandle(u32);
|
||||
|
||||
unsafe impl UnsafeCellCloneSafe for SyncObjHandle {}
|
||||
|
||||
pub struct SyncObj {
|
||||
id: SyncObjId,
|
||||
fd: Rc<OwnedFd>,
|
||||
importers: LinkedList<Rc<Handles>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Ord, PartialOrd)]
|
||||
pub struct SyncObjPoint(pub u64);
|
||||
|
||||
impl SyncObj {
|
||||
pub fn new(fd: &Rc<OwnedFd>) -> Self {
|
||||
Self {
|
||||
id: SyncObjId(SYNCOBJ_ID.fetch_add(1, Relaxed)),
|
||||
fd: fd.clone(),
|
||||
importers: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for SyncObj {
|
||||
fn drop(&mut self) {
|
||||
let mut links = vec![];
|
||||
for importer in self.importers.iter() {
|
||||
if let Some(handle) = importer.handles.remove(&self.id) {
|
||||
destroy(&importer.drm, handle);
|
||||
}
|
||||
if let Some(link) = importer.links.remove(&self.id) {
|
||||
links.push(link);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct Handles {
|
||||
drm: Rc<OwnedFd>,
|
||||
handles: CopyHashMap<SyncObjId, SyncObjHandle>,
|
||||
links: CopyHashMap<SyncObjId, LinkedNode<Rc<Handles>>>,
|
||||
}
|
||||
|
||||
pub struct SyncObjCtx {
|
||||
inner: Rc<Handles>,
|
||||
dummy: CloneCell<Option<Rc<SyncObj>>>,
|
||||
}
|
||||
|
||||
impl SyncObjCtx {
|
||||
pub fn new(drm: &Rc<OwnedFd>) -> Self {
|
||||
Self {
|
||||
inner: Rc::new(Handles {
|
||||
drm: drm.clone(),
|
||||
handles: Default::default(),
|
||||
links: Default::default(),
|
||||
}),
|
||||
dummy: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
fn get_handle(&self, sync_obj: &SyncObj) -> Result<SyncObjHandle, DrmError> {
|
||||
if let Some(handle) = self.inner.handles.get(&sync_obj.id) {
|
||||
return Ok(handle);
|
||||
}
|
||||
let handle = sync_obj_fd_to_handle(self.inner.drm.raw(), sync_obj.fd.raw(), 0, 0)
|
||||
.map_err(DrmError::ImportSyncObj)?;
|
||||
let handle = SyncObjHandle(handle);
|
||||
let link = sync_obj.importers.add_last(self.inner.clone());
|
||||
self.inner.handles.set(sync_obj.id, handle);
|
||||
self.inner.links.set(sync_obj.id, link);
|
||||
Ok(handle)
|
||||
}
|
||||
|
||||
pub fn create_sync_obj(&self) -> Result<SyncObj, DrmError> {
|
||||
let handle = sync_obj_create(self.inner.drm.raw(), 0).map_err(DrmError::CreateSyncObj)?;
|
||||
let handle = SyncObjHandle(handle);
|
||||
let fd = sync_obj_handle_to_fd(self.inner.drm.raw(), handle.0, 0);
|
||||
if fd.is_err() {
|
||||
destroy(&self.inner.drm, handle);
|
||||
}
|
||||
let fd = fd.map_err(DrmError::ExportSyncObj).map(Rc::new)?;
|
||||
let sync_obj = SyncObj::new(&fd);
|
||||
let link = sync_obj.importers.add_last(self.inner.clone());
|
||||
self.inner.handles.set(sync_obj.id, handle);
|
||||
self.inner.links.set(sync_obj.id, link);
|
||||
Ok(sync_obj)
|
||||
}
|
||||
|
||||
pub fn wait_for_point(
|
||||
&self,
|
||||
eventfd: &OwnedFd,
|
||||
sync_obj: &SyncObj,
|
||||
point: SyncObjPoint,
|
||||
signaled: bool,
|
||||
) -> Result<(), DrmError> {
|
||||
let handle = self.get_handle(sync_obj)?;
|
||||
let flags = match signaled {
|
||||
true => 0,
|
||||
false => DRM_SYNCOBJ_WAIT_FLAGS_WAIT_AVAILABLE,
|
||||
};
|
||||
sync_obj_eventfd(
|
||||
self.inner.drm.raw(),
|
||||
eventfd.raw(),
|
||||
handle.0,
|
||||
point.0,
|
||||
flags,
|
||||
)
|
||||
.map_err(DrmError::RegisterEventfd)
|
||||
}
|
||||
|
||||
pub fn supports_async_wait(&self) -> bool {
|
||||
self.supports_async_wait_().is_ok()
|
||||
}
|
||||
|
||||
fn supports_async_wait_(&self) -> Result<(), DrmError> {
|
||||
let sync_obj = self.create_sync_obj()?;
|
||||
let eventfd = uapi::eventfd(0, c::EFD_CLOEXEC)
|
||||
.map_err(OsError::from)
|
||||
.map_err(DrmError::EventFd)?;
|
||||
self.wait_for_point(&eventfd, &sync_obj, SyncObjPoint(1), true)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn signal(&self, sync_obj: &SyncObj, point: SyncObjPoint) -> Result<(), DrmError> {
|
||||
let handle = self.get_handle(sync_obj)?;
|
||||
sync_obj_signal(self.inner.drm.raw(), handle.0, point.0).map_err(DrmError::SignalSyncObj)
|
||||
}
|
||||
|
||||
pub fn import_sync_files<'a, I>(
|
||||
&self,
|
||||
sync_obj: &SyncObj,
|
||||
point: SyncObjPoint,
|
||||
sync_files: I,
|
||||
) -> Result<(), DrmError>
|
||||
where
|
||||
I: IntoIterator<Item = &'a SyncFile>,
|
||||
{
|
||||
let mut sync_files = sync_files.into_iter();
|
||||
let Some(first) = sync_files.next() else {
|
||||
return self.signal(sync_obj, point);
|
||||
};
|
||||
let mut stash;
|
||||
let mut fd = &*first.0;
|
||||
for next in sync_files {
|
||||
stash = sync_ioc_merge(fd.raw(), next.raw()).map_err(DrmError::Merge)?;
|
||||
fd = &stash;
|
||||
}
|
||||
let dummy = self.get_dummy()?;
|
||||
sync_obj_fd_to_handle(
|
||||
self.inner.drm.raw(),
|
||||
fd.raw(),
|
||||
DRM_SYNCOBJ_FD_TO_HANDLE_FLAGS_IMPORT_SYNC_FILE,
|
||||
self.get_handle(&dummy)?.0,
|
||||
)
|
||||
.map_err(DrmError::ImportSyncFile)?;
|
||||
self.transfer(&dummy, SyncObjPoint(0), sync_obj, point)
|
||||
}
|
||||
|
||||
fn transfer(
|
||||
&self,
|
||||
src_sync_obj: &SyncObj,
|
||||
src_point: SyncObjPoint,
|
||||
dst_sync_obj: &SyncObj,
|
||||
dst_point: SyncObjPoint,
|
||||
) -> Result<(), DrmError> {
|
||||
let src_handle = self.get_handle(src_sync_obj)?;
|
||||
let dst_handle = self.get_handle(dst_sync_obj)?;
|
||||
sync_obj_transfer(
|
||||
self.inner.drm.raw(),
|
||||
src_handle.0,
|
||||
src_point.0,
|
||||
dst_handle.0,
|
||||
dst_point.0,
|
||||
0,
|
||||
)
|
||||
.map_err(DrmError::TransferPoint)
|
||||
}
|
||||
|
||||
fn get_dummy(&self) -> Result<Rc<SyncObj>, DrmError> {
|
||||
match self.dummy.get() {
|
||||
Some(d) => Ok(d),
|
||||
None => {
|
||||
let d = Rc::new(self.create_sync_obj()?);
|
||||
self.dummy.set(Some(d.clone()));
|
||||
Ok(d)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for SyncObjCtx {
|
||||
fn drop(&mut self) {
|
||||
self.inner.links.clear();
|
||||
let mut map = self.inner.handles.lock();
|
||||
for (_, handle) in map.drain() {
|
||||
destroy(&self.inner.drm, handle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn destroy(drm: &OwnedFd, handle: SyncObjHandle) {
|
||||
if let Err(e) = sync_obj_destroy(drm.raw(), handle.0) {
|
||||
log::error!("Could not destroy sync obj: {}", ErrorFmt(e));
|
||||
}
|
||||
}
|
||||
|
|
@ -1156,3 +1156,201 @@ pub struct drm_format_modifier {
|
|||
}
|
||||
|
||||
unsafe impl Pod for drm_format_modifier {}
|
||||
|
||||
// pub const DRM_SYNCOBJ_CREATE_SIGNALED: u32 = 1 << 0;
|
||||
|
||||
#[repr(C)]
|
||||
struct drm_syncobj_create {
|
||||
handle: u32,
|
||||
flags: u32,
|
||||
}
|
||||
|
||||
const DRM_IOCTL_SYNCOBJ_CREATE: u64 = drm_iowr::<drm_syncobj_create>(0xBF);
|
||||
|
||||
pub fn sync_obj_create(drm: c::c_int, flags: u32) -> Result<u32, OsError> {
|
||||
let mut res = drm_syncobj_create { handle: 0, flags };
|
||||
unsafe {
|
||||
ioctl(drm, DRM_IOCTL_SYNCOBJ_CREATE, &mut res)?;
|
||||
}
|
||||
Ok(res.handle)
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
struct drm_syncobj_destroy {
|
||||
handle: u32,
|
||||
pad: u32,
|
||||
}
|
||||
|
||||
const DRM_IOCTL_SYNCOBJ_DESTROY: u64 = drm_iowr::<drm_syncobj_destroy>(0xC0);
|
||||
|
||||
pub fn sync_obj_destroy(drm: c::c_int, handle: u32) -> Result<(), OsError> {
|
||||
let mut res = drm_syncobj_destroy { handle, pad: 0 };
|
||||
unsafe {
|
||||
ioctl(drm, DRM_IOCTL_SYNCOBJ_DESTROY, &mut res)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub const DRM_SYNCOBJ_FD_TO_HANDLE_FLAGS_IMPORT_SYNC_FILE: u32 = 1 << 0;
|
||||
// pub const DRM_SYNCOBJ_HANDLE_TO_FD_FLAGS_EXPORT_SYNC_FILE: u32 = 1 << 0;
|
||||
|
||||
#[repr(C)]
|
||||
struct drm_syncobj_handle {
|
||||
handle: u32,
|
||||
flags: u32,
|
||||
fd: i32,
|
||||
pad: u32,
|
||||
}
|
||||
|
||||
const DRM_IOCTL_SYNCOBJ_HANDLE_TO_FD: u64 = drm_iowr::<drm_syncobj_handle>(0xC1);
|
||||
const DRM_IOCTL_SYNCOBJ_FD_TO_HANDLE: u64 = drm_iowr::<drm_syncobj_handle>(0xC2);
|
||||
|
||||
pub fn sync_obj_handle_to_fd(drm: c::c_int, handle: u32, flags: u32) -> Result<OwnedFd, OsError> {
|
||||
let mut res = drm_syncobj_handle {
|
||||
handle,
|
||||
flags,
|
||||
fd: 0,
|
||||
pad: 0,
|
||||
};
|
||||
unsafe {
|
||||
ioctl(drm, DRM_IOCTL_SYNCOBJ_HANDLE_TO_FD, &mut res)?;
|
||||
}
|
||||
Ok(OwnedFd::new(res.fd))
|
||||
}
|
||||
|
||||
pub fn sync_obj_fd_to_handle(
|
||||
drm: c::c_int,
|
||||
fd: c::c_int,
|
||||
flags: u32,
|
||||
handle: u32,
|
||||
) -> Result<u32, OsError> {
|
||||
let mut res = drm_syncobj_handle {
|
||||
handle,
|
||||
flags,
|
||||
fd,
|
||||
pad: 0,
|
||||
};
|
||||
unsafe {
|
||||
ioctl(drm, DRM_IOCTL_SYNCOBJ_FD_TO_HANDLE, &mut res)?;
|
||||
}
|
||||
Ok(res.handle)
|
||||
}
|
||||
|
||||
// pub const DRM_SYNCOBJ_WAIT_FLAGS_WAIT_ALL: u32 = 1 << 0;
|
||||
// pub const DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT: u32 = 1 << 1;
|
||||
pub const DRM_SYNCOBJ_WAIT_FLAGS_WAIT_AVAILABLE: u32 = 1 << 2;
|
||||
|
||||
#[repr(C)]
|
||||
struct drm_syncobj_eventfd {
|
||||
handle: u32,
|
||||
flags: u32,
|
||||
point: u64,
|
||||
fd: i32,
|
||||
pad: u32,
|
||||
}
|
||||
|
||||
const DRM_IOCTL_SYNCOBJ_EVENTFD: u64 = drm_iowr::<drm_syncobj_eventfd>(0xCF);
|
||||
|
||||
pub fn sync_obj_eventfd(
|
||||
drm: c::c_int,
|
||||
eventfd: c::c_int,
|
||||
handle: u32,
|
||||
point: u64,
|
||||
flags: u32,
|
||||
) -> Result<(), OsError> {
|
||||
let mut res = drm_syncobj_eventfd {
|
||||
handle,
|
||||
flags,
|
||||
point,
|
||||
fd: eventfd,
|
||||
pad: 0,
|
||||
};
|
||||
unsafe {
|
||||
ioctl(drm, DRM_IOCTL_SYNCOBJ_EVENTFD, &mut res)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
struct drm_syncobj_timeline_array {
|
||||
handles: u64,
|
||||
points: u64,
|
||||
count_handles: u32,
|
||||
flags: u32,
|
||||
}
|
||||
|
||||
const DRM_IOCTL_SYNCOBJ_TIMELINE_SIGNAL: u64 = drm_iowr::<drm_syncobj_timeline_array>(0xCD);
|
||||
|
||||
pub fn sync_obj_signal(drm: c::c_int, handle: u32, point: u64) -> Result<(), OsError> {
|
||||
let mut res = drm_syncobj_timeline_array {
|
||||
handles: &handle as *const u32 as u64,
|
||||
points: &point as *const u64 as u64,
|
||||
count_handles: 1,
|
||||
flags: 0,
|
||||
};
|
||||
unsafe {
|
||||
ioctl(drm, DRM_IOCTL_SYNCOBJ_TIMELINE_SIGNAL, &mut res)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
struct drm_syncobj_transfer {
|
||||
src_handle: u32,
|
||||
dst_handle: u32,
|
||||
src_point: u64,
|
||||
dst_point: u64,
|
||||
flags: u32,
|
||||
pad: u32,
|
||||
}
|
||||
|
||||
const DRM_IOCTL_SYNCOBJ_TRANSFER: u64 = drm_iowr::<drm_syncobj_transfer>(0xCC);
|
||||
|
||||
pub fn sync_obj_transfer(
|
||||
drm: c::c_int,
|
||||
src_handle: u32,
|
||||
src_point: u64,
|
||||
dst_handle: u32,
|
||||
dst_point: u64,
|
||||
flags: u32,
|
||||
) -> Result<(), OsError> {
|
||||
let mut res = drm_syncobj_transfer {
|
||||
src_handle,
|
||||
dst_handle,
|
||||
src_point,
|
||||
dst_point,
|
||||
flags,
|
||||
pad: 0,
|
||||
};
|
||||
unsafe {
|
||||
ioctl(drm, DRM_IOCTL_SYNCOBJ_TRANSFER, &mut res)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
struct sync_merge_data {
|
||||
name: [u8; 32],
|
||||
fd2: i32,
|
||||
fence: i32,
|
||||
flags: u32,
|
||||
pad: u32,
|
||||
}
|
||||
|
||||
const SYNC_IOC_MAGIC: u64 = b'>' as _;
|
||||
|
||||
const SYNC_IOC_MERGE: u64 = uapi::_IOWR::<sync_merge_data>(SYNC_IOC_MAGIC, 3);
|
||||
|
||||
pub fn sync_ioc_merge(left: c::c_int, right: c::c_int) -> Result<OwnedFd, OsError> {
|
||||
let mut res = sync_merge_data {
|
||||
name: [0; 32],
|
||||
fd2: right,
|
||||
fence: 0,
|
||||
flags: 0,
|
||||
pad: 0,
|
||||
};
|
||||
unsafe {
|
||||
ioctl(left, SYNC_IOC_MERGE, &mut res)?;
|
||||
}
|
||||
Ok(OwnedFd::new(res.fence))
|
||||
}
|
||||
|
|
|
|||
199
src/video/drm/wait_for_sync_obj.rs
Normal file
199
src/video/drm/wait_for_sync_obj.rs
Normal file
|
|
@ -0,0 +1,199 @@
|
|||
use {
|
||||
crate::{
|
||||
async_engine::{AsyncEngine, SpawnedFuture},
|
||||
io_uring::IoUring,
|
||||
utils::{
|
||||
asyncevent::AsyncEvent, buf::Buf, clonecell::CloneCell, copyhashmap::CopyHashMap,
|
||||
numcell::NumCell, oserror::OsError, stack::Stack,
|
||||
},
|
||||
video::drm::{
|
||||
sync_obj::{SyncObj, SyncObjCtx, SyncObjPoint},
|
||||
DrmError,
|
||||
},
|
||||
},
|
||||
std::{cell::Cell, rc::Rc},
|
||||
uapi::{c, OwnedFd},
|
||||
};
|
||||
|
||||
pub struct WaitForSyncObj {
|
||||
inner: Rc<Inner>,
|
||||
eng: Rc<AsyncEngine>,
|
||||
}
|
||||
|
||||
pub trait SyncObjWaiter {
|
||||
fn done(self: Rc<Self>, result: Result<(), DrmError>);
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
|
||||
struct JobId(u64);
|
||||
|
||||
pub struct WaitForSyncObjHandle {
|
||||
inner: Rc<Inner>,
|
||||
id: JobId,
|
||||
}
|
||||
|
||||
struct Inner {
|
||||
ctx: CloneCell<Option<Rc<SyncObjCtx>>>,
|
||||
next_id: NumCell<u64>,
|
||||
ring: Rc<IoUring>,
|
||||
busy: CopyHashMap<JobId, BusyWaiter>,
|
||||
idle: Stack<Waiter>,
|
||||
}
|
||||
|
||||
struct BusyWaiter {
|
||||
waiter: Waiter,
|
||||
job: Job,
|
||||
sow: Rc<dyn SyncObjWaiter>,
|
||||
}
|
||||
|
||||
struct Waiter {
|
||||
_task: SpawnedFuture<()>,
|
||||
inner: Rc<WaiterInner>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
struct Job {
|
||||
id: JobId,
|
||||
sync_obj: Rc<SyncObj>,
|
||||
point: SyncObjPoint,
|
||||
signaled: bool,
|
||||
}
|
||||
|
||||
struct WaiterInner {
|
||||
inner: Rc<Inner>,
|
||||
eventfd: Rc<OwnedFd>,
|
||||
next: Cell<Option<Job>>,
|
||||
trigger: AsyncEvent,
|
||||
}
|
||||
|
||||
impl Drop for WaitForSyncObjHandle {
|
||||
fn drop(&mut self) {
|
||||
let _ = self.inner.busy.remove(&self.id);
|
||||
}
|
||||
}
|
||||
|
||||
impl WaitForSyncObj {
|
||||
pub fn new(ring: &Rc<IoUring>, eng: &Rc<AsyncEngine>) -> Self {
|
||||
Self {
|
||||
inner: Rc::new(Inner {
|
||||
ctx: Default::default(),
|
||||
next_id: Default::default(),
|
||||
ring: ring.clone(),
|
||||
busy: Default::default(),
|
||||
idle: Default::default(),
|
||||
}),
|
||||
eng: eng.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_ctx(&self, ctx: Option<Rc<SyncObjCtx>>) {
|
||||
self.inner.ctx.set(ctx);
|
||||
let busy_waiters: Vec<_> = self.inner.busy.lock().drain().map(|(_, w)| w).collect();
|
||||
for waiter in busy_waiters {
|
||||
let res = self.submit_job(
|
||||
waiter.job.id,
|
||||
&waiter.job.sync_obj,
|
||||
waiter.job.point,
|
||||
waiter.job.signaled,
|
||||
waiter.sow.clone(),
|
||||
);
|
||||
if res.is_err() {
|
||||
waiter.sow.done(res);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn wait(
|
||||
&self,
|
||||
sync_obj: &Rc<SyncObj>,
|
||||
point: SyncObjPoint,
|
||||
signaled: bool,
|
||||
sow: Rc<dyn SyncObjWaiter>,
|
||||
) -> Result<WaitForSyncObjHandle, DrmError> {
|
||||
let job_id = JobId(self.inner.next_id.fetch_add(1));
|
||||
self.submit_job(job_id, sync_obj, point, signaled, sow)?;
|
||||
Ok(WaitForSyncObjHandle {
|
||||
inner: self.inner.clone(),
|
||||
id: job_id,
|
||||
})
|
||||
}
|
||||
|
||||
fn submit_job(
|
||||
&self,
|
||||
job_id: JobId,
|
||||
sync_obj: &Rc<SyncObj>,
|
||||
point: SyncObjPoint,
|
||||
signaled: bool,
|
||||
sow: Rc<dyn SyncObjWaiter>,
|
||||
) -> Result<(), DrmError> {
|
||||
let waiter = match self.inner.idle.pop() {
|
||||
Some(w) => w,
|
||||
None => {
|
||||
let eventfd = uapi::eventfd(0, c::EFD_CLOEXEC)
|
||||
.map_err(OsError::from)
|
||||
.map_err(DrmError::EventFd)?;
|
||||
let waiter = Rc::new(WaiterInner {
|
||||
inner: self.inner.clone(),
|
||||
eventfd: Rc::new(eventfd),
|
||||
next: Cell::new(None),
|
||||
trigger: Default::default(),
|
||||
});
|
||||
Waiter {
|
||||
_task: self.eng.spawn(waiter.clone().run()),
|
||||
inner: waiter,
|
||||
}
|
||||
}
|
||||
};
|
||||
let job = Job {
|
||||
id: job_id,
|
||||
sync_obj: sync_obj.clone(),
|
||||
point,
|
||||
signaled,
|
||||
};
|
||||
let waiter = BusyWaiter {
|
||||
waiter,
|
||||
job: job.clone(),
|
||||
sow: sow.clone(),
|
||||
};
|
||||
waiter.waiter.inner.next.set(Some(job));
|
||||
waiter.waiter.inner.trigger.trigger();
|
||||
self.inner.busy.set(job_id, waiter);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for WaitForSyncObj {
|
||||
fn drop(&mut self) {
|
||||
self.inner.busy.clear();
|
||||
self.inner.idle.take();
|
||||
}
|
||||
}
|
||||
|
||||
impl WaiterInner {
|
||||
async fn run(self: Rc<Self>) {
|
||||
let mut buf = Buf::new(8);
|
||||
loop {
|
||||
self.trigger.triggered().await;
|
||||
let job = self.next.take().unwrap();
|
||||
let res = self.wait(&mut buf, &job).await;
|
||||
if let Some(waiter) = self.inner.busy.remove(&job.id) {
|
||||
waiter.sow.done(res);
|
||||
self.inner.idle.push(waiter.waiter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn wait(&self, buf: &mut Buf, job: &Job) -> Result<(), DrmError> {
|
||||
let ctx = match self.inner.ctx.get() {
|
||||
None => return Err(DrmError::NoSyncObjContextAvailable),
|
||||
Some(c) => c,
|
||||
};
|
||||
ctx.wait_for_point(&self.eventfd, &job.sync_obj, job.point, job.signaled)?;
|
||||
self.inner
|
||||
.ring
|
||||
.read(&self.eventfd, buf.clone())
|
||||
.await
|
||||
.map(drop)
|
||||
.map_err(DrmError::ReadEventFd)
|
||||
}
|
||||
}
|
||||
15
wire/wp_linux_drm_syncobj_manager_v1.txt
Normal file
15
wire/wp_linux_drm_syncobj_manager_v1.txt
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
# requests
|
||||
|
||||
msg destroy = 0 {
|
||||
|
||||
}
|
||||
|
||||
msg get_surface = 1 {
|
||||
id: id(wp_linux_drm_syncobj_surface_v1),
|
||||
surface: id(wl_surface),
|
||||
}
|
||||
|
||||
msg import_timeline = 2 {
|
||||
id: id(wp_linux_drm_syncobj_timeline_v1),
|
||||
fd: fd,
|
||||
}
|
||||
17
wire/wp_linux_drm_syncobj_surface_v1.txt
Normal file
17
wire/wp_linux_drm_syncobj_surface_v1.txt
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
# requests
|
||||
|
||||
msg destroy = 0 {
|
||||
|
||||
}
|
||||
|
||||
msg set_acquire_point = 1 {
|
||||
timeline: id(wp_linux_drm_syncobj_timeline_v1),
|
||||
point_hi: u32,
|
||||
point_lo: u32,
|
||||
}
|
||||
|
||||
msg set_release_point = 2 {
|
||||
timeline: id(wp_linux_drm_syncobj_timeline_v1),
|
||||
point_hi: u32,
|
||||
point_lo: u32,
|
||||
}
|
||||
5
wire/wp_linux_drm_syncobj_timeline_v1.txt
Normal file
5
wire/wp_linux_drm_syncobj_timeline_v1.txt
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
# requests
|
||||
|
||||
msg destroy = 0 {
|
||||
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue