1
0
Fork 0
forked from wry/wry

wayland: implement fifo-v1

This commit is contained in:
Julian Orth 2024-09-13 16:01:49 +02:00
parent d8e0375c48
commit fe7175fab2
19 changed files with 416 additions and 23 deletions

View file

@ -269,7 +269,7 @@ impl ExtImageCopyCaptureSessionV1RequestHandler for ExtImageCopyCaptureSessionV1
}
impl LatchListener for ExtImageCopyCaptureSessionV1 {
fn after_latch(self: Rc<Self>, on: &OutputNode) {
fn after_latch(self: Rc<Self>, on: &OutputNode, _tearing: bool) {
let ImageCaptureSource::Toplevel(tl) = &self.source else {
return;
};

View file

@ -89,7 +89,7 @@ enum Target {
}
impl LatchListener for JayScreencast {
fn after_latch(self: Rc<Self>, _on: &OutputNode) {
fn after_latch(self: Rc<Self>, _on: &OutputNode, _tearing: bool) {
self.schedule_toplevel_screencast();
}
}

View file

@ -4,6 +4,7 @@ pub mod dnd_icon;
pub mod ext_session_lock_surface_v1;
pub mod wl_subsurface;
pub mod wp_alpha_modifier_surface_v1;
pub mod wp_fifo_v1;
pub mod wp_fractional_scale_v1;
pub mod wp_linux_drm_syncobj_surface_v1;
pub mod wp_tearing_control_v1;
@ -46,6 +47,7 @@ use {
dnd_icon::DndIcon,
wl_subsurface::{PendingSubsurfaceData, SubsurfaceId, WlSubsurface},
wp_alpha_modifier_surface_v1::WpAlphaModifierSurfaceV1,
wp_fifo_v1::WpFifoV1,
wp_fractional_scale_v1::WpFractionalScaleV1,
wp_linux_drm_syncobj_surface_v1::WpLinuxDrmSyncobjSurfaceV1,
wp_tearing_control_v1::WpTearingControlV1,
@ -317,6 +319,8 @@ pub struct WlSurface {
presentation_listener: EventListener<dyn PresentationListener>,
commit_version: NumCell<u64>,
latched_commit_version: Cell<u64>,
fifo: CloneCell<Option<Rc<WpFifoV1>>>,
clear_fifo_on_vblank: Cell<bool>,
}
impl Debug for WlSurface {
@ -438,6 +442,8 @@ struct PendingState {
release_point: Option<(Rc<SyncObj>, SyncObjPoint)>,
alpha_multiplier: Option<Option<f32>>,
explicit_sync: bool,
fifo_barrier_set: bool,
fifo_barrier_wait: bool,
}
struct AttachedSubsurfaceState {
@ -515,6 +521,8 @@ impl PendingState {
&mut self.presentation_feedback,
&mut next.presentation_feedback,
);
self.fifo_barrier_set |= mem::take(&mut next.fifo_barrier_set);
self.fifo_barrier_wait |= mem::take(&mut next.fifo_barrier_wait);
macro_rules! merge_ext {
($name:ident) => {
if let Some(e) = &mut self.$name {
@ -638,6 +646,8 @@ impl WlSurface {
presentation_listener: EventListener::new(slf.clone()),
commit_version: Default::default(),
latched_commit_version: Default::default(),
fifo: Default::default(),
clear_fifo_on_vblank: Default::default(),
}
}
@ -1314,14 +1324,22 @@ impl WlSurface {
}
}
self.ext.get().after_apply_commit();
let fifo_barrier_set = mem::take(&mut pending.fifo_barrier_set);
if fifo_barrier_set {
self.commit_timeline.set_fifo_barrier();
}
if self.visible.get() {
let output = self.output.get();
if has_frame_requests {
self.vblank_listener.attach(&output.vblank_event);
}
if has_presentation_feedback {
if has_presentation_feedback || fifo_barrier_set {
self.latch_listener.attach(&output.latch_event);
}
if fifo_barrier_set {
// If we have a fifo barrier, must trigger latching.
output.global.connector.damage();
}
if damage_full {
let mut damage = buffer_abs_pos
.with_size(max_surface_size.0, max_surface_size.1)
@ -1341,10 +1359,16 @@ impl WlSurface {
let rect = output.global.pos.get();
self.client.state.damage(rect);
}
} else {
if fifo_barrier_set {
self.latch_listener
.attach(&self.client.state.const_40hz_latch);
}
}
pending.buffer_damage.clear();
pending.surface_damage.clear();
pending.damage_full = false;
pending.fifo_barrier_wait = false;
if tearing_changed {
if let Some(tl) = self.toplevel.get() {
if tl.tl_data().is_fullscreen.get() {
@ -1631,6 +1655,8 @@ impl Object for WlSurface {
self.drm_feedback.clear();
self.commit_timeline.clear(ClearReason::BreakLoops);
self.alpha_modifier.take();
self.text_input_connections.clear();
self.fifo.take();
}
}
@ -2101,12 +2127,15 @@ impl VblankListener for WlSurface {
let _ = fr.client.remove_obj(&*fr);
}
}
if self.clear_fifo_on_vblank.take() {
self.commit_timeline.clear_fifo_barrier();
}
self.vblank_listener.detach();
}
}
impl LatchListener for WlSurface {
fn after_latch(self: Rc<Self>, _on: &OutputNode) {
fn after_latch(self: Rc<Self>, _on: &OutputNode, tearing: bool) {
if self.visible.get() {
if self.latched_commit_version.get() < self.commit_version.get() {
let latched = &mut *self.latched_presentation_feedback.borrow_mut();
@ -2122,6 +2151,14 @@ impl LatchListener for WlSurface {
self.latched_commit_version.set(self.commit_version.get());
}
}
if tearing && self.visible.get() {
if self.commit_timeline.has_fifo_barrier() {
self.vblank_listener.attach(&self.output.get().vblank_event);
self.clear_fifo_on_vblank.set(true);
}
} else {
self.commit_timeline.clear_fifo_barrier();
}
self.latch_listener.detach();
}
}

View file

@ -1,5 +1,7 @@
use {
crate::{
async_engine::{AsyncEngine, SpawnedFuture},
client::Client,
gfx_api::{AsyncShmGfxTextureCallback, GfxError, PendingShmTransfer, STAGING_UPLOAD},
ifs::{
wl_buffer::WlBufferStorage,
@ -13,6 +15,7 @@ use {
linkedlist::{LinkedList, LinkedNode, NodeRef},
numcell::NumCell,
oserror::OsError,
queue::AsyncQueue,
},
video::drm::{
sync_obj::{SyncObj, SyncObjPoint},
@ -26,7 +29,7 @@ use {
cell::{Cell, RefCell},
mem,
ops::DerefMut,
rc::Rc,
rc::{Rc, Weak},
slice,
},
thiserror::Error,
@ -43,6 +46,8 @@ pub struct CommitTimelines {
ring: Rc<IoUring>,
depth: NumCell<usize>,
gc: CopyHashMap<CommitTimelineId, LinkedList<Entry>>,
flush_requests: Rc<FlushRequests>,
_flush_requests_future: SpawnedFuture<()>,
}
pub struct CommitTimeline {
@ -50,6 +55,8 @@ pub struct CommitTimeline {
own_timeline: Rc<Inner>,
effective_timeline: CloneCell<Rc<Inner>>,
effective_timeline_id: Cell<CommitTimelineId>,
fifo_barrier_set: Cell<bool>,
fifo_waiter: Cell<Option<NodeRef<Entry>>>,
}
struct Inner {
@ -94,8 +101,20 @@ pub enum CommitTimelineError {
}
impl CommitTimelines {
pub fn new(wfs: &Rc<WaitForSyncObj>, ring: &Rc<IoUring>) -> Self {
pub fn new(
wfs: &Rc<WaitForSyncObj>,
ring: &Rc<IoUring>,
eng: &Rc<AsyncEngine>,
client: &Weak<Client>,
) -> Self {
let flush_requests = Rc::new(FlushRequests::default());
let flush_request_future = eng.spawn(
"wl_surface flush requests",
process_flush_requests(client.clone(), flush_requests.clone()),
);
Self {
flush_requests,
_flush_requests_future: flush_request_future,
next_id: Default::default(),
depth: NumCell::new(0),
wfs: wfs.clone(),
@ -115,10 +134,13 @@ impl CommitTimelines {
own_timeline: timeline.clone(),
effective_timeline: CloneCell::new(timeline),
effective_timeline_id: Cell::new(id),
fifo_barrier_set: Cell::new(false),
fifo_waiter: Default::default(),
}
}
pub fn clear(&self) {
self.flush_requests.flush_waiters.clear();
for list in self.gc.lock().drain_values() {
break_loops(&list);
}
@ -144,8 +166,12 @@ fn break_loops(list: &LinkedList<Entry>) {
impl CommitTimeline {
pub fn clear(&self, reason: ClearReason) {
match reason {
ClearReason::BreakLoops => break_loops(&self.own_timeline.entries),
ClearReason::BreakLoops => {
self.fifo_waiter.take();
break_loops(&self.own_timeline.entries)
}
ClearReason::Destroy => {
self.clear_fifo_barrier();
if self.own_timeline.entries.is_not_empty() {
let list = LinkedList::new();
list.append_all(&self.own_timeline.entries);
@ -172,7 +198,10 @@ impl CommitTimeline {
);
let has_dependencies =
points.is_not_empty() || pending_uploads > 0 || implicit_dmabufs.is_not_empty();
if !has_dependencies && self.own_timeline.entries.is_empty() {
let must_be_queued = has_dependencies
|| self.own_timeline.entries.is_not_empty()
|| (pending.fifo_barrier_wait && self.fifo_barrier_set.get());
if !must_be_queued {
return surface
.apply_state(pending)
.map_err(CommitTimelineError::ImmediateCommit);
@ -181,6 +210,10 @@ impl CommitTimeline {
return Err(CommitTimelineError::Depth);
}
set_effective_timeline(self, pending, &self.own_timeline);
let commit_fifo_state = match pending.fifo_barrier_wait {
true => CommitFifoState::Queued,
false => CommitFifoState::Mailbox,
};
let noderef = add_entry(
&self.own_timeline.entries,
&self.shared,
@ -193,9 +226,10 @@ impl CommitTimeline {
shm_upload: RefCell::new(ShmUploadState::None),
num_pending_polls: NumCell::new(implicit_dmabufs.len()),
pending_polls: Cell::new(Default::default()),
fifo_state: Cell::new(commit_fifo_state),
}),
);
let mut needs_flush = false;
let mut needs_flush = commit_fifo_state == CommitFifoState::Queued;
if has_dependencies {
let noderef = Rc::new(noderef.clone());
let EntryKind::Commit(commit) = &noderef.kind else {
@ -235,6 +269,21 @@ impl CommitTimeline {
}
Ok(())
}
pub fn set_fifo_barrier(&self) {
self.fifo_barrier_set.set(true);
}
pub fn clear_fifo_barrier(&self) {
self.fifo_barrier_set.set(false);
if let Some(waiter) = self.fifo_waiter.take() {
self.shared.flush_requests.flush_waiters.push(waiter);
}
}
pub fn has_fifo_barrier(&self) -> bool {
self.fifo_barrier_set.get()
}
}
impl SyncObjWaiter for NodeRef<Entry> {
@ -322,6 +371,7 @@ struct Commit {
shm_upload: RefCell<ShmUploadState>,
num_pending_polls: NumCell<usize>,
pending_polls: Cell<SmallVec<[PendingPoll; 1]>>,
fifo_state: Cell<CommitFifoState>,
}
fn flush_from(mut point: NodeRef<Entry>) -> Result<(), WlSurfaceError> {
@ -357,6 +407,20 @@ impl NodeRef<Entry> {
if c.num_pending_polls.get() > 0 {
has_unmet_dependencies = true;
}
let tl = &c.surface.commit_timeline;
if tl.fifo_barrier_set.get() {
match c.fifo_state.get() {
CommitFifoState::Queued => {
tl.fifo_waiter.set(Some(self.clone()));
c.fifo_state.set(CommitFifoState::Registered);
has_unmet_dependencies = true;
}
CommitFifoState::Registered => {
has_unmet_dependencies = true;
}
CommitFifoState::Mailbox => {}
}
}
if has_unmet_dependencies {
return Ok(false);
}
@ -536,3 +600,29 @@ fn set_effective_timeline(
}
}
}
#[derive(Default)]
struct FlushRequests {
flush_waiters: AsyncQueue<NodeRef<Entry>>,
}
async fn process_flush_requests(client: Weak<Client>, requests: Rc<FlushRequests>) {
loop {
requests.flush_waiters.non_empty().await;
while let Some(entry) = requests.flush_waiters.try_pop() {
if let Err(e) = flush_from(entry) {
if let Some(client) = client.upgrade() {
client.error(e);
}
return;
}
}
}
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
enum CommitFifoState {
Queued,
Registered,
Mailbox,
}

View file

@ -0,0 +1,77 @@
use {
crate::{
client::{Client, ClientError},
ifs::wl_surface::WlSurface,
leaks::Tracker,
object::{Object, Version},
wire::{wp_fifo_v1::*, WpFifoV1Id},
},
std::rc::Rc,
thiserror::Error,
};
pub struct WpFifoV1 {
pub id: WpFifoV1Id,
pub client: Rc<Client>,
pub surface: Rc<WlSurface>,
pub tracker: Tracker<Self>,
pub version: Version,
}
impl WpFifoV1 {
pub fn new(id: WpFifoV1Id, version: Version, surface: &Rc<WlSurface>) -> Self {
Self {
id,
client: surface.client.clone(),
surface: surface.clone(),
tracker: Default::default(),
version,
}
}
pub fn install(self: &Rc<Self>) -> Result<(), WpFifoV1Error> {
if self.surface.fifo.is_some() {
return Err(WpFifoV1Error::Exists);
}
self.surface.fifo.set(Some(self.clone()));
Ok(())
}
}
impl WpFifoV1RequestHandler for WpFifoV1 {
type Error = WpFifoV1Error;
fn set_barrier(&self, _req: SetBarrier, _slf: &Rc<Self>) -> Result<(), Self::Error> {
self.surface.pending.borrow_mut().fifo_barrier_set = true;
Ok(())
}
fn wait_barrier(&self, _req: WaitBarrier, _slf: &Rc<Self>) -> Result<(), Self::Error> {
self.surface.pending.borrow_mut().fifo_barrier_wait = true;
Ok(())
}
fn destroy(&self, _req: Destroy, _slf: &Rc<Self>) -> Result<(), Self::Error> {
self.surface.fifo.take();
self.client.remove_obj(self)?;
Ok(())
}
}
object_base! {
self = WpFifoV1;
version = self.version;
}
impl Object for WpFifoV1 {}
simple_add_obj!(WpFifoV1);
#[derive(Debug, Error)]
pub enum WpFifoV1Error {
#[error(transparent)]
ClientError(Box<ClientError>),
#[error("The surface already has a fifo extension attached")]
Exists,
}
efrom!(WpFifoV1Error, ClientError);

View file

@ -0,0 +1,96 @@
use {
crate::{
client::{Client, ClientError},
globals::{Global, GlobalName},
ifs::wl_surface::wp_fifo_v1::{WpFifoV1, WpFifoV1Error},
leaks::Tracker,
object::{Object, Version},
wire::{wp_fifo_manager_v1::*, WpFifoManagerV1Id},
},
std::rc::Rc,
thiserror::Error,
};
pub struct WpFifoManagerV1Global {
pub name: GlobalName,
}
pub struct WpFifoManagerV1 {
pub id: WpFifoManagerV1Id,
pub client: Rc<Client>,
pub tracker: Tracker<Self>,
pub version: Version,
}
impl WpFifoManagerV1Global {
pub fn new(name: GlobalName) -> Self {
Self { name }
}
fn bind_(
self: Rc<Self>,
id: WpFifoManagerV1Id,
client: &Rc<Client>,
version: Version,
) -> Result<(), WpFifoManagerV1Error> {
let obj = Rc::new(WpFifoManagerV1 {
id,
client: client.clone(),
tracker: Default::default(),
version,
});
track!(client, obj);
client.add_client_obj(&obj)?;
Ok(())
}
}
global_base!(WpFifoManagerV1Global, WpFifoManagerV1, WpFifoManagerV1Error);
impl Global for WpFifoManagerV1Global {
fn singleton(&self) -> bool {
true
}
fn version(&self) -> u32 {
1
}
}
simple_add_global!(WpFifoManagerV1Global);
impl WpFifoManagerV1RequestHandler for WpFifoManagerV1 {
type Error = WpFifoManagerV1Error;
fn destroy(&self, _req: Destroy, _slf: &Rc<Self>) -> Result<(), Self::Error> {
self.client.remove_obj(self)?;
Ok(())
}
fn get_fifo(&self, req: GetFifo, _slf: &Rc<Self>) -> Result<(), Self::Error> {
let surface = self.client.lookup(req.surface)?;
let fs = Rc::new(WpFifoV1::new(req.id, self.version, &surface));
track!(self.client, fs);
fs.install()?;
self.client.add_client_obj(&fs)?;
Ok(())
}
}
object_base! {
self = WpFifoManagerV1;
version = self.version;
}
impl Object for WpFifoManagerV1 {}
simple_add_obj!(WpFifoManagerV1);
#[derive(Debug, Error)]
pub enum WpFifoManagerV1Error {
#[error(transparent)]
ClientError(Box<ClientError>),
#[error(transparent)]
WpFifoV1Error(#[from] WpFifoV1Error),
}
efrom!(WpFifoManagerV1Error, ClientError);