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::None => None,
|
||||||
AcquireSync::Implicit => None,
|
AcquireSync::Implicit => None,
|
||||||
AcquireSync::SyncFile { sync_file } => Some(sync_file.clone()),
|
AcquireSync::SyncFile { sync_file } => Some(sync_file.clone()),
|
||||||
|
AcquireSync::Unnecessary => None,
|
||||||
};
|
};
|
||||||
fb = dsd.fb.clone();
|
fb = dsd.fb.clone();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,11 @@ use {
|
||||||
crate::{
|
crate::{
|
||||||
async_engine::SpawnedFuture,
|
async_engine::SpawnedFuture,
|
||||||
client::{error::LookupError, objects::Objects},
|
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,
|
leaks::Tracker,
|
||||||
object::{Interface, Object, ObjectId, WL_DISPLAY_ID},
|
object::{Interface, Object, ObjectId, WL_DISPLAY_ID},
|
||||||
state::State,
|
state::State,
|
||||||
|
|
@ -149,6 +153,7 @@ impl Clients {
|
||||||
last_xwayland_serial: Cell::new(0),
|
last_xwayland_serial: Cell::new(0),
|
||||||
surfaces_by_xwayland_serial: Default::default(),
|
surfaces_by_xwayland_serial: Default::default(),
|
||||||
activation_tokens: Default::default(),
|
activation_tokens: Default::default(),
|
||||||
|
commit_timelines: Rc::new(CommitTimelines::new(&global.wait_for_sync_obj)),
|
||||||
});
|
});
|
||||||
track!(data, data);
|
track!(data, data);
|
||||||
let display = Rc::new(WlDisplay::new(&data));
|
let display = Rc::new(WlDisplay::new(&data));
|
||||||
|
|
@ -220,6 +225,7 @@ impl Drop for ClientHolder {
|
||||||
self.data.shutdown.clear();
|
self.data.shutdown.clear();
|
||||||
self.data.surfaces_by_xwayland_serial.clear();
|
self.data.surfaces_by_xwayland_serial.clear();
|
||||||
self.data.remove_activation_tokens();
|
self.data.remove_activation_tokens();
|
||||||
|
self.data.commit_timelines.clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -260,6 +266,7 @@ pub struct Client {
|
||||||
pub last_xwayland_serial: Cell<u64>,
|
pub last_xwayland_serial: Cell<u64>,
|
||||||
pub surfaces_by_xwayland_serial: CopyHashMap<u64, Rc<WlSurface>>,
|
pub surfaces_by_xwayland_serial: CopyHashMap<u64, Rc<WlSurface>>,
|
||||||
pub activation_tokens: RefCell<VecDeque<ActivationToken>>,
|
pub activation_tokens: RefCell<VecDeque<ActivationToken>>,
|
||||||
|
pub commit_timelines: Rc<CommitTimelines>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const NUM_CACHED_SERIAL_RANGES: usize = 64;
|
pub const NUM_CACHED_SERIAL_RANGES: usize = 64;
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ use {
|
||||||
xdg_surface::{xdg_toplevel::XdgToplevel, XdgSurface},
|
xdg_surface::{xdg_toplevel::XdgToplevel, XdgSurface},
|
||||||
WlSurface,
|
WlSurface,
|
||||||
},
|
},
|
||||||
|
wp_linux_drm_syncobj_timeline_v1::WpLinuxDrmSyncobjTimelineV1,
|
||||||
xdg_positioner::XdgPositioner,
|
xdg_positioner::XdgPositioner,
|
||||||
xdg_wm_base::XdgWmBase,
|
xdg_wm_base::XdgWmBase,
|
||||||
},
|
},
|
||||||
|
|
@ -29,8 +30,9 @@ use {
|
||||||
},
|
},
|
||||||
wire::{
|
wire::{
|
||||||
JayOutputId, JayScreencastId, JayWorkspaceId, WlBufferId, WlDataSourceId, WlOutputId,
|
JayOutputId, JayScreencastId, JayWorkspaceId, WlBufferId, WlDataSourceId, WlOutputId,
|
||||||
WlPointerId, WlRegionId, WlRegistryId, WlSeatId, WlSurfaceId, XdgPositionerId,
|
WlPointerId, WlRegionId, WlRegistryId, WlSeatId, WlSurfaceId,
|
||||||
XdgSurfaceId, XdgToplevelId, XdgWmBaseId, ZwpPrimarySelectionSourceV1Id,
|
WpLinuxDrmSyncobjTimelineV1Id, XdgPositionerId, XdgSurfaceId, XdgToplevelId,
|
||||||
|
XdgWmBaseId, ZwpPrimarySelectionSourceV1Id,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
std::{cell::RefCell, mem, rc::Rc},
|
std::{cell::RefCell, mem, rc::Rc},
|
||||||
|
|
@ -56,6 +58,7 @@ pub struct Objects {
|
||||||
pub xdg_wm_bases: CopyHashMap<XdgWmBaseId, Rc<XdgWmBase>>,
|
pub xdg_wm_bases: CopyHashMap<XdgWmBaseId, Rc<XdgWmBase>>,
|
||||||
pub seats: CopyHashMap<WlSeatId, Rc<WlSeat>>,
|
pub seats: CopyHashMap<WlSeatId, Rc<WlSeat>>,
|
||||||
pub screencasts: CopyHashMap<JayScreencastId, Rc<JayScreencast>>,
|
pub screencasts: CopyHashMap<JayScreencastId, Rc<JayScreencast>>,
|
||||||
|
pub timelines: CopyHashMap<WpLinuxDrmSyncobjTimelineV1Id, Rc<WpLinuxDrmSyncobjTimelineV1>>,
|
||||||
ids: RefCell<Vec<usize>>,
|
ids: RefCell<Vec<usize>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -83,6 +86,7 @@ impl Objects {
|
||||||
xdg_wm_bases: Default::default(),
|
xdg_wm_bases: Default::default(),
|
||||||
seats: Default::default(),
|
seats: Default::default(),
|
||||||
screencasts: Default::default(),
|
screencasts: Default::default(),
|
||||||
|
timelines: Default::default(),
|
||||||
ids: RefCell::new(vec![]),
|
ids: RefCell::new(vec![]),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -38,6 +38,7 @@ use {
|
||||||
oserror::OsError, queue::AsyncQueue, refcounted::RefCounted, run_toplevel::RunToplevel,
|
oserror::OsError, queue::AsyncQueue, refcounted::RefCounted, run_toplevel::RunToplevel,
|
||||||
tri::Try,
|
tri::Try,
|
||||||
},
|
},
|
||||||
|
video::drm::wait_for_sync_obj::WaitForSyncObj,
|
||||||
wheel::{Wheel, WheelError},
|
wheel::{Wheel, WheelError},
|
||||||
xkbcommon::XkbContext,
|
xkbcommon::XkbContext,
|
||||||
},
|
},
|
||||||
|
|
@ -222,6 +223,7 @@ fn start_compositor2(
|
||||||
double_click_distance: Cell::new(5),
|
double_click_distance: Cell::new(5),
|
||||||
create_default_seat: Cell::new(true),
|
create_default_seat: Cell::new(true),
|
||||||
subsurface_ids: Default::default(),
|
subsurface_ids: Default::default(),
|
||||||
|
wait_for_sync_obj: Rc::new(WaitForSyncObj::new(&ring, &engine)),
|
||||||
});
|
});
|
||||||
state.tracker.register(ClientId::from_raw(0));
|
state.tracker.register(ClientId::from_raw(0));
|
||||||
create_dummy_output(&state);
|
create_dummy_output(&state);
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ use {
|
||||||
theme::Color,
|
theme::Color,
|
||||||
tree::{Node, OutputNode},
|
tree::{Node, OutputNode},
|
||||||
utils::{clonecell::UnsafeCellCloneSafe, transform_ext::TransformExt},
|
utils::{clonecell::UnsafeCellCloneSafe, transform_ext::TransformExt},
|
||||||
video::{dmabuf::DmaBuf, gbm::GbmDevice, Modifier},
|
video::{dmabuf::DmaBuf, drm::sync_obj::SyncObjCtx, gbm::GbmDevice, Modifier},
|
||||||
},
|
},
|
||||||
ahash::AHashMap,
|
ahash::AHashMap,
|
||||||
indexmap::IndexSet,
|
indexmap::IndexSet,
|
||||||
|
|
@ -167,6 +167,7 @@ pub enum AcquireSync {
|
||||||
None,
|
None,
|
||||||
Implicit,
|
Implicit,
|
||||||
SyncFile { sync_file: SyncFile },
|
SyncFile { sync_file: SyncFile },
|
||||||
|
Unnecessary,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AcquireSync {
|
impl AcquireSync {
|
||||||
|
|
@ -191,6 +192,7 @@ impl Debug for AcquireSync {
|
||||||
AcquireSync::None => "None",
|
AcquireSync::None => "None",
|
||||||
AcquireSync::Implicit => "Implicit",
|
AcquireSync::Implicit => "Implicit",
|
||||||
AcquireSync::SyncFile { .. } => "SyncFile",
|
AcquireSync::SyncFile { .. } => "SyncFile",
|
||||||
|
AcquireSync::Unnecessary => "Unnecessary",
|
||||||
};
|
};
|
||||||
f.debug_struct(name).finish_non_exhaustive()
|
f.debug_struct(name).finish_non_exhaustive()
|
||||||
}
|
}
|
||||||
|
|
@ -517,6 +519,8 @@ pub trait GfxContext: Debug {
|
||||||
stride: i32,
|
stride: i32,
|
||||||
format: &'static Format,
|
format: &'static Format,
|
||||||
) -> Result<Rc<dyn GfxFramebuffer>, GfxError>;
|
) -> Result<Rc<dyn GfxFramebuffer>, GfxError>;
|
||||||
|
|
||||||
|
fn sync_obj_ctx(&self) -> &Rc<SyncObjCtx>;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
|
|
||||||
|
|
@ -376,7 +376,7 @@ fn render_texture(ctx: &GlRenderContext, tex: &CopyTexture) {
|
||||||
|
|
||||||
fn handle_explicit_sync(ctx: &GlRenderContext, texture: &Texture, sync: &AcquireSync) {
|
fn handle_explicit_sync(ctx: &GlRenderContext, texture: &Texture, sync: &AcquireSync) {
|
||||||
let sync_file = match sync {
|
let sync_file = match sync {
|
||||||
AcquireSync::None | AcquireSync::Implicit => return,
|
AcquireSync::None | AcquireSync::Implicit | AcquireSync::Unnecessary => return,
|
||||||
AcquireSync::SyncFile { sync_file } => sync_file,
|
AcquireSync::SyncFile { sync_file } => sync_file,
|
||||||
};
|
};
|
||||||
let sync_file = match uapi::fcntl_dupfd_cloexec(sync_file.raw(), 0) {
|
let sync_file = match uapi::fcntl_dupfd_cloexec(sync_file.raw(), 0) {
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,11 @@ use {
|
||||||
renderer::{framebuffer::Framebuffer, image::Image},
|
renderer::{framebuffer::Framebuffer, image::Image},
|
||||||
GfxGlState, RenderError, Texture,
|
GfxGlState, RenderError, Texture,
|
||||||
},
|
},
|
||||||
video::{dmabuf::DmaBuf, drm::Drm, gbm::GbmDevice},
|
video::{
|
||||||
|
dmabuf::DmaBuf,
|
||||||
|
drm::{sync_obj::SyncObjCtx, Drm},
|
||||||
|
gbm::GbmDevice,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
ahash::AHashMap,
|
ahash::AHashMap,
|
||||||
jay_config::video::GfxApi,
|
jay_config::video::GfxApi,
|
||||||
|
|
@ -53,6 +57,7 @@ pub(crate) struct TexProgs {
|
||||||
pub(in crate::gfx_apis::gl) struct GlRenderContext {
|
pub(in crate::gfx_apis::gl) struct GlRenderContext {
|
||||||
pub(crate) ctx: Rc<EglContext>,
|
pub(crate) ctx: Rc<EglContext>,
|
||||||
pub gbm: Rc<GbmDevice>,
|
pub gbm: Rc<GbmDevice>,
|
||||||
|
pub sync_ctx: Rc<SyncObjCtx>,
|
||||||
|
|
||||||
pub(crate) render_node: Rc<CString>,
|
pub(crate) render_node: Rc<CString>,
|
||||||
|
|
||||||
|
|
@ -128,6 +133,7 @@ impl GlRenderContext {
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
ctx: ctx.clone(),
|
ctx: ctx.clone(),
|
||||||
gbm: ctx.dpy.gbm.clone(),
|
gbm: ctx.dpy.gbm.clone(),
|
||||||
|
sync_ctx: Rc::new(SyncObjCtx::new(ctx.dpy.gbm.drm.fd())),
|
||||||
|
|
||||||
render_node: node.clone(),
|
render_node: node.clone(),
|
||||||
|
|
||||||
|
|
@ -271,4 +277,8 @@ impl GfxContext for GlRenderContext {
|
||||||
})?;
|
})?;
|
||||||
Ok(Rc::new(Framebuffer { ctx: self, gl: fb }))
|
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(
|
pub fn render(
|
||||||
&self,
|
&self,
|
||||||
ops: Vec<GfxApiOpt>,
|
mut ops: Vec<GfxApiOpt>,
|
||||||
clear: Option<&Color>,
|
clear: Option<&Color>,
|
||||||
) -> Result<Option<SyncFile>, RenderError> {
|
) -> Result<Option<SyncFile>, RenderError> {
|
||||||
let gles = self.ctx.ctx.dpy.gles;
|
let gles = self.ctx.ctx.dpy.gles;
|
||||||
|
|
@ -89,6 +89,7 @@ impl Framebuffer {
|
||||||
}
|
}
|
||||||
Ok(fd)
|
Ok(fd)
|
||||||
});
|
});
|
||||||
|
ops.clear();
|
||||||
*self.ctx.gfx_ops.borrow_mut() = ops;
|
*self.ctx.gfx_ops.borrow_mut() = ops;
|
||||||
res
|
res
|
||||||
}
|
}
|
||||||
|
|
@ -100,9 +101,7 @@ impl GfxFramebuffer for Framebuffer {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn take_render_ops(&self) -> Vec<GfxApiOpt> {
|
fn take_render_ops(&self) -> Vec<GfxApiOpt> {
|
||||||
let mut ops = mem::take(&mut *self.ctx.gfx_ops.borrow_mut());
|
mem::take(&mut *self.ctx.gfx_ops.borrow_mut())
|
||||||
ops.clear();
|
|
||||||
ops
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn physical_size(&self) -> (i32, i32) {
|
fn physical_size(&self) -> (i32, i32) {
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@ use {
|
||||||
utils::oserror::OsError,
|
utils::oserror::OsError,
|
||||||
video::{
|
video::{
|
||||||
dmabuf::DmaBuf,
|
dmabuf::DmaBuf,
|
||||||
drm::{Drm, DrmError},
|
drm::{sync_obj::SyncObjCtx, Drm, DrmError},
|
||||||
gbm::{GbmDevice, GbmError},
|
gbm::{GbmDevice, GbmError},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
@ -93,7 +93,7 @@ pub enum VulkanError {
|
||||||
LoadImageProperties(#[source] vk::Result),
|
LoadImageProperties(#[source] vk::Result),
|
||||||
#[error("Device does not support rending and texturing from the XRGB8888 format")]
|
#[error("Device does not support rending and texturing from the XRGB8888 format")]
|
||||||
XRGB8888,
|
XRGB8888,
|
||||||
#[error("Device does not support syncobj import")]
|
#[error("Device does not support sync obj import")]
|
||||||
SyncobjImport,
|
SyncobjImport,
|
||||||
#[error("Could not start a command buffer")]
|
#[error("Could not start a command buffer")]
|
||||||
BeginCommandBuffer(vk::Result),
|
BeginCommandBuffer(vk::Result),
|
||||||
|
|
@ -270,6 +270,10 @@ impl GfxContext for Context {
|
||||||
.create_shm_texture(format, width, height, stride, &[], true)?;
|
.create_shm_texture(format, width, height, stride, &[], true)?;
|
||||||
Ok(fb)
|
Ok(fb)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn sync_obj_ctx(&self) -> &Rc<SyncObjCtx> {
|
||||||
|
&self.0.device.sync_ctx
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for Context {
|
impl Drop for Context {
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,10 @@ use {
|
||||||
util::OnDrop,
|
util::OnDrop,
|
||||||
VulkanError,
|
VulkanError,
|
||||||
},
|
},
|
||||||
video::{drm::Drm, gbm::GbmDevice},
|
video::{
|
||||||
|
drm::{sync_obj::SyncObjCtx, Drm},
|
||||||
|
gbm::GbmDevice,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
ahash::AHashMap,
|
ahash::AHashMap,
|
||||||
arrayvec::ArrayVec,
|
arrayvec::ArrayVec,
|
||||||
|
|
@ -43,6 +46,7 @@ pub struct VulkanDevice {
|
||||||
pub(super) physical_device: PhysicalDevice,
|
pub(super) physical_device: PhysicalDevice,
|
||||||
pub(super) render_node: Rc<CString>,
|
pub(super) render_node: Rc<CString>,
|
||||||
pub(super) gbm: GbmDevice,
|
pub(super) gbm: GbmDevice,
|
||||||
|
pub(super) sync_ctx: Rc<SyncObjCtx>,
|
||||||
pub(super) instance: Rc<VulkanInstance>,
|
pub(super) instance: Rc<VulkanInstance>,
|
||||||
pub(super) device: Device,
|
pub(super) device: Device,
|
||||||
pub(super) external_memory_fd: ExternalMemoryFd,
|
pub(super) external_memory_fd: ExternalMemoryFd,
|
||||||
|
|
@ -279,6 +283,7 @@ impl VulkanInstance {
|
||||||
Ok(Rc::new(VulkanDevice {
|
Ok(Rc::new(VulkanDevice {
|
||||||
physical_device: phy_dev,
|
physical_device: phy_dev,
|
||||||
render_node,
|
render_node,
|
||||||
|
sync_ctx: Rc::new(SyncObjCtx::new(gbm.drm.fd())),
|
||||||
gbm,
|
gbm,
|
||||||
instance: self.clone(),
|
instance: self.clone(),
|
||||||
device,
|
device,
|
||||||
|
|
|
||||||
|
|
@ -574,6 +574,7 @@ impl VulkanRenderer {
|
||||||
.map_err(|e| VulkanError::Dupfd(e.into()))?;
|
.map_err(|e| VulkanError::Dupfd(e.into()))?;
|
||||||
import_sync_file(fd)?;
|
import_sync_file(fd)?;
|
||||||
}
|
}
|
||||||
|
AcquireSync::Unnecessary => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
||||||
|
|
@ -38,6 +38,8 @@ pub mod wp_content_type_v1;
|
||||||
pub mod wp_cursor_shape_device_v1;
|
pub mod wp_cursor_shape_device_v1;
|
||||||
pub mod wp_cursor_shape_manager_v1;
|
pub mod wp_cursor_shape_manager_v1;
|
||||||
pub mod wp_fractional_scale_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;
|
||||||
pub mod wp_presentation_feedback;
|
pub mod wp_presentation_feedback;
|
||||||
pub mod wp_single_pixel_buffer_manager_v1;
|
pub mod wp_single_pixel_buffer_manager_v1;
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,9 @@
|
||||||
|
pub mod commit_timeline;
|
||||||
pub mod cursor;
|
pub mod cursor;
|
||||||
pub mod ext_session_lock_surface_v1;
|
pub mod ext_session_lock_surface_v1;
|
||||||
pub mod wl_subsurface;
|
pub mod wl_subsurface;
|
||||||
pub mod wp_fractional_scale_v1;
|
pub mod wp_fractional_scale_v1;
|
||||||
|
pub mod wp_linux_drm_syncobj_surface_v1;
|
||||||
pub mod wp_tearing_control_v1;
|
pub mod wp_tearing_control_v1;
|
||||||
pub mod wp_viewport;
|
pub mod wp_viewport;
|
||||||
pub mod x_surface;
|
pub mod x_surface;
|
||||||
|
|
@ -16,7 +18,7 @@ use {
|
||||||
client::{Client, ClientError, RequestParser},
|
client::{Client, ClientError, RequestParser},
|
||||||
drm_feedback::DrmFeedback,
|
drm_feedback::DrmFeedback,
|
||||||
fixed::Fixed,
|
fixed::Fixed,
|
||||||
gfx_api::{AcquireSync, BufferResv, BufferResvUser, SampleRect, SyncFile},
|
gfx_api::{AcquireSync, BufferResv, BufferResvUser, ReleaseSync, SampleRect, SyncFile},
|
||||||
ifs::{
|
ifs::{
|
||||||
wl_buffer::WlBuffer,
|
wl_buffer::WlBuffer,
|
||||||
wl_callback::WlCallback,
|
wl_callback::WlCallback,
|
||||||
|
|
@ -25,9 +27,11 @@ use {
|
||||||
NodeSeatState, SeatId, WlSeatGlobal,
|
NodeSeatState, SeatId, WlSeatGlobal,
|
||||||
},
|
},
|
||||||
wl_surface::{
|
wl_surface::{
|
||||||
|
commit_timeline::{ClearReason, CommitTimeline, CommitTimelineError},
|
||||||
cursor::CursorSurface,
|
cursor::CursorSurface,
|
||||||
wl_subsurface::{PendingSubsurfaceData, SubsurfaceId, WlSubsurface},
|
wl_subsurface::{PendingSubsurfaceData, SubsurfaceId, WlSubsurface},
|
||||||
wp_fractional_scale_v1::WpFractionalScaleV1,
|
wp_fractional_scale_v1::WpFractionalScaleV1,
|
||||||
|
wp_linux_drm_syncobj_surface_v1::WpLinuxDrmSyncobjSurfaceV1,
|
||||||
wp_tearing_control_v1::WpTearingControlV1,
|
wp_tearing_control_v1::WpTearingControlV1,
|
||||||
wp_viewport::WpViewport,
|
wp_viewport::WpViewport,
|
||||||
x_surface::XSurface,
|
x_surface::XSurface,
|
||||||
|
|
@ -57,7 +61,10 @@ use {
|
||||||
smallmap::SmallMap,
|
smallmap::SmallMap,
|
||||||
transform_ext::TransformExt,
|
transform_ext::TransformExt,
|
||||||
},
|
},
|
||||||
video::dmabuf::DMA_BUF_SYNC_READ,
|
video::{
|
||||||
|
dmabuf::DMA_BUF_SYNC_READ,
|
||||||
|
drm::sync_obj::{SyncObj, SyncObjPoint},
|
||||||
|
},
|
||||||
wire::{
|
wire::{
|
||||||
wl_surface::*, WlOutputId, WlSurfaceId, ZwpIdleInhibitorV1Id,
|
wl_surface::*, WlOutputId, WlSurfaceId, ZwpIdleInhibitorV1Id,
|
||||||
ZwpLinuxDmabufFeedbackV1Id,
|
ZwpLinuxDmabufFeedbackV1Id,
|
||||||
|
|
@ -66,6 +73,7 @@ use {
|
||||||
xwayland::XWaylandEvent,
|
xwayland::XWaylandEvent,
|
||||||
},
|
},
|
||||||
ahash::AHashMap,
|
ahash::AHashMap,
|
||||||
|
isnt::std_1::primitive::IsntSliceExt,
|
||||||
jay_config::video::Transform,
|
jay_config::video::Transform,
|
||||||
std::{
|
std::{
|
||||||
cell::{Cell, RefCell},
|
cell::{Cell, RefCell},
|
||||||
|
|
@ -133,15 +141,46 @@ impl NodeVisitorBase for SurfaceSendPreferredTransformVisitor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct SurfaceBufferExplicitRelease {
|
||||||
|
sync_obj: Rc<SyncObj>,
|
||||||
|
point: SyncObjPoint,
|
||||||
|
}
|
||||||
|
|
||||||
pub struct SurfaceBuffer {
|
pub struct SurfaceBuffer {
|
||||||
pub buffer: Rc<WlBuffer>,
|
pub buffer: Rc<WlBuffer>,
|
||||||
sync_files: SmallMap<BufferResvUser, SyncFile, 1>,
|
sync_files: SmallMap<BufferResvUser, SyncFile, 1>,
|
||||||
pub sync: AcquireSync,
|
pub sync: AcquireSync,
|
||||||
|
pub release_sync: ReleaseSync,
|
||||||
|
release: Option<SurfaceBufferExplicitRelease>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for SurfaceBuffer {
|
impl Drop for SurfaceBuffer {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
let sync_files = self.sync_files.take();
|
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 {
|
if let Some(dmabuf) = &self.buffer.dmabuf {
|
||||||
for (_, sync_file) in &sync_files {
|
for (_, sync_file) in &sync_files {
|
||||||
if let Err(e) = dmabuf.import_sync_file(DMA_BUF_SYNC_READ, sync_file) {
|
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>,
|
pub client: Rc<Client>,
|
||||||
visible: Cell<bool>,
|
visible: Cell<bool>,
|
||||||
role: Cell<SurfaceRole>,
|
role: Cell<SurfaceRole>,
|
||||||
pending: RefCell<PendingState>,
|
pending: RefCell<Box<PendingState>>,
|
||||||
input_region: CloneCell<Option<Rc<Region>>>,
|
input_region: CloneCell<Option<Rc<Region>>>,
|
||||||
opaque_region: Cell<Option<Rc<Region>>>,
|
opaque_region: Cell<Option<Rc<Region>>>,
|
||||||
buffer_points: RefCell<BufferPoints>,
|
buffer_points: RefCell<BufferPoints>,
|
||||||
|
|
@ -209,6 +248,9 @@ pub struct WlSurface {
|
||||||
pub has_content_type_manager: Cell<bool>,
|
pub has_content_type_manager: Cell<bool>,
|
||||||
content_type: Cell<Option<ContentType>>,
|
content_type: Cell<Option<ContentType>>,
|
||||||
pub drm_feedback: CopyHashMap<ZwpLinuxDmabufFeedbackV1Id, Rc<ZwpLinuxDmabufFeedbackV1>>,
|
pub drm_feedback: CopyHashMap<ZwpLinuxDmabufFeedbackV1Id, Rc<ZwpLinuxDmabufFeedbackV1>>,
|
||||||
|
sync_obj_surface: CloneCell<Option<Rc<WpLinuxDrmSyncobjSurfaceV1>>>,
|
||||||
|
destroyed: Cell<bool>,
|
||||||
|
commit_timeline: CommitTimeline,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Debug for WlSurface {
|
impl Debug for WlSurface {
|
||||||
|
|
@ -232,7 +274,7 @@ enum CommitAction {
|
||||||
}
|
}
|
||||||
|
|
||||||
trait SurfaceExt {
|
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;
|
let _ = pending;
|
||||||
CommitAction::ContinueCommit
|
CommitAction::ContinueCommit
|
||||||
}
|
}
|
||||||
|
|
@ -329,11 +371,14 @@ struct PendingState {
|
||||||
xdg_surface: Option<Box<PendingXdgSurfaceData>>,
|
xdg_surface: Option<Box<PendingXdgSurfaceData>>,
|
||||||
layer_surface: Option<Box<PendingLayerSurfaceData>>,
|
layer_surface: Option<Box<PendingLayerSurfaceData>>,
|
||||||
subsurfaces: AHashMap<SubsurfaceId, CommittedSubsurface>,
|
subsurfaces: AHashMap<SubsurfaceId, CommittedSubsurface>,
|
||||||
|
acquire_point: Option<(Rc<SyncObj>, SyncObjPoint)>,
|
||||||
|
release_point: Option<(Rc<SyncObj>, SyncObjPoint)>,
|
||||||
|
explicit_sync: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct CommittedSubsurface {
|
struct CommittedSubsurface {
|
||||||
subsurface: Rc<WlSubsurface>,
|
subsurface: Rc<WlSubsurface>,
|
||||||
state: PendingState,
|
state: Box<PendingState>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PendingState {
|
impl PendingState {
|
||||||
|
|
@ -341,7 +386,9 @@ impl PendingState {
|
||||||
// discard state
|
// discard state
|
||||||
|
|
||||||
if next.buffer.is_some() {
|
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() {
|
if !prev.destroyed() {
|
||||||
prev.send_release();
|
prev.send_release();
|
||||||
}
|
}
|
||||||
|
|
@ -371,6 +418,8 @@ impl PendingState {
|
||||||
opt!(xwayland_serial);
|
opt!(xwayland_serial);
|
||||||
opt!(tearing);
|
opt!(tearing);
|
||||||
opt!(content_type);
|
opt!(content_type);
|
||||||
|
opt!(acquire_point);
|
||||||
|
opt!(release_point);
|
||||||
{
|
{
|
||||||
let (dx1, dy1) = self.offset;
|
let (dx1, dy1) = self.offset;
|
||||||
let (dx2, dy2) = mem::take(&mut next.offset);
|
let (dx2, dy2) = mem::take(&mut next.offset);
|
||||||
|
|
@ -478,6 +527,9 @@ impl WlSurface {
|
||||||
has_content_type_manager: Default::default(),
|
has_content_type_manager: Default::default(),
|
||||||
content_type: Default::default(),
|
content_type: Default::default(),
|
||||||
drm_feedback: 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> {
|
fn destroy(&self, parser: MsgParser<'_, '_>) -> Result<(), WlSurfaceError> {
|
||||||
let _req: Destroy = self.parse(parser)?;
|
let _req: Destroy = self.parse(parser)?;
|
||||||
|
self.commit_timeline.clear(ClearReason::Destroy);
|
||||||
self.unset_dnd_icons();
|
self.unset_dnd_icons();
|
||||||
self.unset_cursors();
|
self.unset_cursors();
|
||||||
self.ext.get().on_surface_destroy()?;
|
self.ext.get().on_surface_destroy()?;
|
||||||
|
|
@ -730,6 +783,7 @@ impl WlSurface {
|
||||||
self.client.remove_obj(self)?;
|
self.client.remove_obj(self)?;
|
||||||
self.idle_inhibitors.clear();
|
self.idle_inhibitors.clear();
|
||||||
self.constraints.take();
|
self.constraints.take();
|
||||||
|
self.destroyed.set(true);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -796,6 +850,9 @@ impl WlSurface {
|
||||||
.surface
|
.surface
|
||||||
.apply_state(&mut subsurface.state)?;
|
.apply_state(&mut subsurface.state)?;
|
||||||
}
|
}
|
||||||
|
if self.destroyed.get() {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
self.ext.get().before_apply_commit(pending)?;
|
self.ext.get().before_apply_commit(pending)?;
|
||||||
let mut scale_changed = false;
|
let mut scale_changed = false;
|
||||||
if let Some(scale) = pending.scale.take() {
|
if let Some(scale) = pending.scale.take() {
|
||||||
|
|
@ -835,10 +892,20 @@ impl WlSurface {
|
||||||
}
|
}
|
||||||
if let Some(buffer) = buffer_change {
|
if let Some(buffer) = buffer_change {
|
||||||
buffer.update_texture_or_log();
|
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 {
|
let surface_buffer = SurfaceBuffer {
|
||||||
buffer,
|
buffer,
|
||||||
sync_files: Default::default(),
|
sync_files: Default::default(),
|
||||||
sync: AcquireSync::Implicit,
|
sync,
|
||||||
|
release_sync,
|
||||||
|
release,
|
||||||
};
|
};
|
||||||
self.buffer.set(Some(Rc::new(surface_buffer)));
|
self.buffer.set(Some(Rc::new(surface_buffer)));
|
||||||
self.buf_x.fetch_add(dx);
|
self.buf_x.fetch_add(dx);
|
||||||
|
|
@ -990,12 +1057,34 @@ impl WlSurface {
|
||||||
let _req: Commit = self.parse(parser)?;
|
let _req: Commit = self.parse(parser)?;
|
||||||
let ext = self.ext.get();
|
let ext = self.ext.get();
|
||||||
let pending = &mut *self.pending.borrow_mut();
|
let pending = &mut *self.pending.borrow_mut();
|
||||||
|
self.verify_explicit_sync(pending)?;
|
||||||
if ext.commit_requested(pending) == CommitAction::ContinueCommit {
|
if ext.commit_requested(pending) == CommitAction::ContinueCommit {
|
||||||
self.apply_state(pending)?;
|
self.commit_timeline.commit(self, pending)?;
|
||||||
}
|
}
|
||||||
Ok(())
|
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> {
|
fn set_buffer_transform(&self, parser: MsgParser<'_, '_>) -> Result<(), WlSurfaceError> {
|
||||||
let req: SetBufferTransform = self.parse(parser)?;
|
let req: SetBufferTransform = self.parse(parser)?;
|
||||||
let Some(tf) = Transform::from_wl(req.transform) else {
|
let Some(tf) = Transform::from_wl(req.transform) else {
|
||||||
|
|
@ -1215,6 +1304,7 @@ impl Object for WlSurface {
|
||||||
self.tearing_control.take();
|
self.tearing_control.take();
|
||||||
self.constraints.clear();
|
self.constraints.clear();
|
||||||
self.drm_feedback.clear();
|
self.drm_feedback.clear();
|
||||||
|
self.commit_timeline.clear(ClearReason::BreakLoops);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1381,8 +1471,15 @@ pub enum WlSurfaceError {
|
||||||
ViewportOutsideBuffer,
|
ViewportOutsideBuffer,
|
||||||
#[error("attach request must not contain offset")]
|
#[error("attach request must not contain offset")]
|
||||||
OffsetInAttach,
|
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, ClientError);
|
||||||
efrom!(WlSurfaceError, XdgSurfaceError);
|
efrom!(WlSurfaceError, XdgSurfaceError);
|
||||||
efrom!(WlSurfaceError, ZwlrLayerSurfaceV1Error);
|
efrom!(WlSurfaceError, ZwlrLayerSurfaceV1Error);
|
||||||
efrom!(WlSurfaceError, MsgParserError);
|
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);
|
simple_add_obj!(WlSubsurface);
|
||||||
|
|
||||||
impl SurfaceExt for 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() {
|
if self.sync() {
|
||||||
let mut parent_pending = self.parent.pending.borrow_mut();
|
let mut parent_pending = self.parent.pending.borrow_mut();
|
||||||
match parent_pending.subsurfaces.entry(self.unique_id) {
|
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>,
|
bounds: Option<&Rect>,
|
||||||
) {
|
) {
|
||||||
if let Some(tex) = buffer.buffer.texture.get() {
|
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(
|
self.base.render_texture(
|
||||||
&tex,
|
&tex,
|
||||||
x,
|
x,
|
||||||
|
|
@ -400,7 +396,7 @@ impl Renderer<'_> {
|
||||||
bounds,
|
bounds,
|
||||||
Some(buffer.clone()),
|
Some(buffer.clone()),
|
||||||
buffer.sync.clone(),
|
buffer.sync.clone(),
|
||||||
release_sync,
|
buffer.release_sync,
|
||||||
);
|
);
|
||||||
} else if let Some(color) = &buffer.buffer.color {
|
} else if let Some(color) = &buffer.buffer.color {
|
||||||
if let Some(rect) = Rect::new_sized(x, y, tsize.0, tsize.1) {
|
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},
|
zwp_idle_inhibitor_v1::{IdleInhibitorId, IdleInhibitorIds, ZwpIdleInhibitorV1},
|
||||||
NoneSurfaceExt, WlSurface,
|
NoneSurfaceExt, WlSurface,
|
||||||
},
|
},
|
||||||
|
wp_linux_drm_syncobj_manager_v1::WpLinuxDrmSyncobjManagerV1Global,
|
||||||
zwlr_screencopy_frame_v1::ZwlrScreencopyFrameV1,
|
zwlr_screencopy_frame_v1::ZwlrScreencopyFrameV1,
|
||||||
zwp_linux_dmabuf_feedback_v1::ZwpLinuxDmabufFeedbackV1,
|
zwp_linux_dmabuf_feedback_v1::ZwpLinuxDmabufFeedbackV1,
|
||||||
zwp_linux_dmabuf_v1::ZwpLinuxDmabufV1Global,
|
zwp_linux_dmabuf_v1::ZwpLinuxDmabufV1Global,
|
||||||
|
|
@ -59,7 +60,14 @@ use {
|
||||||
linkedlist::LinkedList, numcell::NumCell, queue::AsyncQueue, refcounted::RefCounted,
|
linkedlist::LinkedList, numcell::NumCell, queue::AsyncQueue, refcounted::RefCounted,
|
||||||
run_toplevel::RunToplevel,
|
run_toplevel::RunToplevel,
|
||||||
},
|
},
|
||||||
video::{dmabuf::DmaBufIds, drm::Drm},
|
video::{
|
||||||
|
dmabuf::DmaBufIds,
|
||||||
|
drm::{
|
||||||
|
sync_obj::{SyncObj, SyncObjPoint},
|
||||||
|
wait_for_sync_obj::WaitForSyncObj,
|
||||||
|
Drm,
|
||||||
|
},
|
||||||
|
},
|
||||||
wheel::Wheel,
|
wheel::Wheel,
|
||||||
wire::{
|
wire::{
|
||||||
ExtForeignToplevelListV1Id, JayRenderCtxId, JaySeatEventsId, JayWorkspaceWatcherId,
|
ExtForeignToplevelListV1Id, JayRenderCtxId, JaySeatEventsId, JayWorkspaceWatcherId,
|
||||||
|
|
@ -163,6 +171,7 @@ pub struct State {
|
||||||
pub double_click_distance: Cell<i32>,
|
pub double_click_distance: Cell<i32>,
|
||||||
pub create_default_seat: Cell<bool>,
|
pub create_default_seat: Cell<bool>,
|
||||||
pub subsurface_ids: SubsurfaceIds,
|
pub subsurface_ids: SubsurfaceIds,
|
||||||
|
pub wait_for_sync_obj: Rc<WaitForSyncObj>,
|
||||||
}
|
}
|
||||||
|
|
||||||
// impl Drop for State {
|
// impl Drop for State {
|
||||||
|
|
@ -367,6 +376,8 @@ impl State {
|
||||||
self.render_ctx_version.fetch_add(1);
|
self.render_ctx_version.fetch_add(1);
|
||||||
self.cursors.set(None);
|
self.cursors.set(None);
|
||||||
self.drm_feedback.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: {
|
'handle_new_feedback: {
|
||||||
if let Some(ctx) = &ctx {
|
if let Some(ctx) = &ctx {
|
||||||
|
|
@ -435,11 +446,18 @@ impl State {
|
||||||
seat.render_ctx_changed();
|
seat.render_ctx_changed();
|
||||||
}
|
}
|
||||||
|
|
||||||
if ctx.is_some() && !self.render_ctx_ever_initialized.replace(true) {
|
if let Some(ctx) = &ctx {
|
||||||
self.add_global(&Rc::new(WlDrmGlobal::new(self.globals.name())));
|
if !self.render_ctx_ever_initialized.replace(true) {
|
||||||
self.add_global(&Rc::new(ZwpLinuxDmabufV1Global::new(self.globals.name())));
|
self.add_global(&Rc::new(WlDrmGlobal::new(self.globals.name())));
|
||||||
if let Some(config) = self.config.get() {
|
self.add_global(&Rc::new(ZwpLinuxDmabufV1Global::new(self.globals.name())));
|
||||||
config.graphics_initialized();
|
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);
|
self.globals.add_global(self, &seat);
|
||||||
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)]
|
#[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>> {
|
fn endpoint(&self, ep: NonNull<NodeData<T>>) -> Option<NodeRef<T>> {
|
||||||
unsafe {
|
unsafe {
|
||||||
if ep != self.root.data {
|
if ep != self.root.data {
|
||||||
|
|
@ -57,11 +74,14 @@ impl<T> LinkedList<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
pub fn is_empty(&self) -> bool {
|
pub fn is_empty(&self) -> bool {
|
||||||
self.last().is_none()
|
self.last().is_none()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn is_not_empty(&self) -> bool {
|
||||||
|
!self.is_empty()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn last(&self) -> Option<NodeRef<T>> {
|
pub fn last(&self) -> Option<NodeRef<T>> {
|
||||||
unsafe { self.endpoint(self.root.data.as_ref().prev.get()) }
|
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() }
|
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> {
|
pub fn remove(&self, k: &K) -> Option<V> {
|
||||||
unsafe { self.m.get().deref_mut().remove(k) }
|
unsafe { self.m.get().deref_mut().remove(k) }
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,6 @@
|
||||||
|
pub mod sync_obj;
|
||||||
mod sys;
|
mod sys;
|
||||||
|
pub mod wait_for_sync_obj;
|
||||||
|
|
||||||
use {
|
use {
|
||||||
crate::{
|
crate::{
|
||||||
|
|
@ -52,7 +54,7 @@ pub use sys::{
|
||||||
#[derive(Debug, Error)]
|
#[derive(Debug, Error)]
|
||||||
pub enum DrmError {
|
pub enum DrmError {
|
||||||
#[error("Could not reopen a node")]
|
#[error("Could not reopen a node")]
|
||||||
ReopenNode(#[source] crate::utils::oserror::OsError),
|
ReopenNode(#[source] OsError),
|
||||||
#[error("Could not retrieve the render node name")]
|
#[error("Could not retrieve the render node name")]
|
||||||
RenderNodeName(#[source] OsError),
|
RenderNodeName(#[source] OsError),
|
||||||
#[error("Could not retrieve the device node name")]
|
#[error("Could not retrieve the device node name")]
|
||||||
|
|
@ -113,6 +115,28 @@ pub enum DrmError {
|
||||||
Version(#[source] OsError),
|
Version(#[source] OsError),
|
||||||
#[error("Format of IN_FORMATS property is invalid")]
|
#[error("Format of IN_FORMATS property is invalid")]
|
||||||
InFormats,
|
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> {
|
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 {}
|
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