wayland: implement linux-drm-syncobj-v1
This commit is contained in:
parent
816315170f
commit
aaf73d6fdc
29 changed files with 1507 additions and 35 deletions
|
|
@ -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);
|
||||
Loading…
Add table
Add a link
Reference in a new issue