wayland: implement commit-timing-v1
This commit is contained in:
parent
d45aaffdb3
commit
fac5445f2e
19 changed files with 434 additions and 15 deletions
|
|
@ -156,6 +156,7 @@ Jay supports the following wayland protocols:
|
||||||
| wl_shm | 2 | |
|
| wl_shm | 2 | |
|
||||||
| wl_subcompositor | 1 | |
|
| wl_subcompositor | 1 | |
|
||||||
| wp_alpha_modifier_v1 | 1 | |
|
| wp_alpha_modifier_v1 | 1 | |
|
||||||
|
| wp_commit_timing_manager_v1 | 1 | |
|
||||||
| wp_content_type_manager_v1 | 1 | |
|
| wp_content_type_manager_v1 | 1 | |
|
||||||
| wp_cursor_shape_manager_v1 | 1 | |
|
| wp_cursor_shape_manager_v1 | 1 | |
|
||||||
| wp_drm_lease_device_v1 | 1 | |
|
| wp_drm_lease_device_v1 | 1 | |
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@
|
||||||
- Implement screencast session restoration.
|
- Implement screencast session restoration.
|
||||||
- Fix screen sharing in zoom.
|
- Fix screen sharing in zoom.
|
||||||
- Implement wp-fifo-v1.
|
- Implement wp-fifo-v1.
|
||||||
|
- Implement wp-commit-timing-v1.
|
||||||
|
|
||||||
# 1.6.0 (2024-09-25)
|
# 1.6.0 (2024-09-25)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -65,6 +65,15 @@ pub struct Mode {
|
||||||
pub refresh_rate_millihz: u32,
|
pub refresh_rate_millihz: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Mode {
|
||||||
|
pub fn refresh_nsec(&self) -> u64 {
|
||||||
|
match self.refresh_rate_millihz {
|
||||||
|
0 => u64::MAX,
|
||||||
|
n => 1_000_000_000_000 / (n as u64),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct MonitorInfo {
|
pub struct MonitorInfo {
|
||||||
pub modes: Vec<Mode>,
|
pub modes: Vec<Mode>,
|
||||||
|
|
|
||||||
|
|
@ -101,6 +101,9 @@ impl MetalConnector {
|
||||||
if !self.can_present.get() {
|
if !self.can_present.get() {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
let Some(node) = self.state.root.outputs.get(&self.connector_id) else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
let mut expected_sequence = self.sequence.get() + 1;
|
let mut expected_sequence = self.sequence.get() + 1;
|
||||||
let mut start = Time::now_unchecked();
|
let mut start = Time::now_unchecked();
|
||||||
let use_frame_scheduling = !self.try_async_flip();
|
let use_frame_scheduling = !self.try_async_flip();
|
||||||
|
|
@ -118,7 +121,15 @@ impl MetalConnector {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
frame!(frame_name);
|
frame!(frame_name);
|
||||||
if let Err(e) = self.present_once().await {
|
{
|
||||||
|
let now = start.nsec();
|
||||||
|
let flip = match self.try_async_flip() {
|
||||||
|
true => now,
|
||||||
|
false => self.next_vblank_nsec.get(),
|
||||||
|
};
|
||||||
|
node.before_latch(flip).await;
|
||||||
|
}
|
||||||
|
if let Err(e) = self.present_once(&node).await {
|
||||||
log::error!("Could not present: {}", ErrorFmt(e));
|
log::error!("Could not present: {}", ErrorFmt(e));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
@ -138,7 +149,7 @@ impl MetalConnector {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn present_once(&self) -> Result<(), MetalError> {
|
async fn present_once(&self, node: &Rc<OutputNode>) -> Result<(), MetalError> {
|
||||||
let version = self.version.get();
|
let version = self.version.get();
|
||||||
if !self.can_present.get() {
|
if !self.can_present.get() {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
|
|
@ -146,9 +157,6 @@ impl MetalConnector {
|
||||||
if !self.backend.check_render_context(&self.dev) {
|
if !self.backend.check_render_context(&self.dev) {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
let Some(node) = self.state.root.outputs.get(&self.connector_id) else {
|
|
||||||
return Ok(());
|
|
||||||
};
|
|
||||||
let crtc = match self.crtc.get() {
|
let crtc = match self.crtc.get() {
|
||||||
Some(crtc) => crtc,
|
Some(crtc) => crtc,
|
||||||
_ => return Ok(()),
|
_ => return Ok(()),
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ use {
|
||||||
gfx_api::{AcquireSync, GfxContext, GfxError, GfxFramebuffer, GfxTexture, ReleaseSync},
|
gfx_api::{AcquireSync, GfxContext, GfxError, GfxFramebuffer, GfxTexture, ReleaseSync},
|
||||||
ifs::wl_output::OutputId,
|
ifs::wl_output::OutputId,
|
||||||
state::State,
|
state::State,
|
||||||
|
time::Time,
|
||||||
utils::{
|
utils::{
|
||||||
clonecell::CloneCell, copyhashmap::CopyHashMap, errorfmt::ErrorFmt, numcell::NumCell,
|
clonecell::CloneCell, copyhashmap::CopyHashMap, errorfmt::ErrorFmt, numcell::NumCell,
|
||||||
queue::AsyncQueue, syncqueue::SyncQueue,
|
queue::AsyncQueue, syncqueue::SyncQueue,
|
||||||
|
|
@ -745,6 +746,8 @@ impl XBackend {
|
||||||
image.last_serial.set(serial);
|
image.last_serial.set(serial);
|
||||||
|
|
||||||
if let Some(node) = self.state.root.outputs.get(&output.id) {
|
if let Some(node) = self.state.root.outputs.get(&output.id) {
|
||||||
|
let now = Time::now_unchecked().nsec();
|
||||||
|
node.before_latch(now).await;
|
||||||
let res = self.state.present_output(
|
let res = self.state.present_output(
|
||||||
&node,
|
&node,
|
||||||
&image.fb.get(),
|
&image.fb.get(),
|
||||||
|
|
|
||||||
|
|
@ -548,7 +548,7 @@ fn create_dummy_output(state: &Rc<State>) {
|
||||||
&backend::Mode {
|
&backend::Mode {
|
||||||
width: 0,
|
width: 0,
|
||||||
height: 0,
|
height: 0,
|
||||||
refresh_rate_millihz: 0,
|
refresh_rate_millihz: 40_000,
|
||||||
},
|
},
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
|
|
@ -582,8 +582,10 @@ fn create_dummy_output(state: &Rc<State>) {
|
||||||
vblank_event: Default::default(),
|
vblank_event: Default::default(),
|
||||||
latch_event: Default::default(),
|
latch_event: Default::default(),
|
||||||
presentation_event: Default::default(),
|
presentation_event: Default::default(),
|
||||||
|
render_margin_ns: Default::default(),
|
||||||
flip_margin_ns: Default::default(),
|
flip_margin_ns: Default::default(),
|
||||||
ext_copy_sessions: Default::default(),
|
ext_copy_sessions: Default::default(),
|
||||||
|
before_latch_event: Default::default(),
|
||||||
});
|
});
|
||||||
let dummy_workspace = Rc::new(WorkspaceNode {
|
let dummy_workspace = Rc::new(WorkspaceNode {
|
||||||
id: state.node_ids.next(),
|
id: state.node_ids.next(),
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,7 @@ use {
|
||||||
wl_subcompositor::WlSubcompositorGlobal,
|
wl_subcompositor::WlSubcompositorGlobal,
|
||||||
wl_surface::xwayland_shell_v1::XwaylandShellV1Global,
|
wl_surface::xwayland_shell_v1::XwaylandShellV1Global,
|
||||||
wp_alpha_modifier_v1::WpAlphaModifierV1Global,
|
wp_alpha_modifier_v1::WpAlphaModifierV1Global,
|
||||||
|
wp_commit_timing_manager_v1::WpCommitTimingManagerV1Global,
|
||||||
wp_content_type_manager_v1::WpContentTypeManagerV1Global,
|
wp_content_type_manager_v1::WpContentTypeManagerV1Global,
|
||||||
wp_cursor_shape_manager_v1::WpCursorShapeManagerV1Global,
|
wp_cursor_shape_manager_v1::WpCursorShapeManagerV1Global,
|
||||||
wp_fifo_manager_v1::WpFifoManagerV1Global,
|
wp_fifo_manager_v1::WpFifoManagerV1Global,
|
||||||
|
|
@ -205,6 +206,7 @@ impl Globals {
|
||||||
add_singleton!(ExtForeignToplevelImageCaptureSourceManagerV1Global);
|
add_singleton!(ExtForeignToplevelImageCaptureSourceManagerV1Global);
|
||||||
add_singleton!(ExtImageCopyCaptureManagerV1Global);
|
add_singleton!(ExtImageCopyCaptureManagerV1Global);
|
||||||
add_singleton!(WpFifoManagerV1Global);
|
add_singleton!(WpFifoManagerV1Global);
|
||||||
|
add_singleton!(WpCommitTimingManagerV1Global);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_backend_singletons(&self, backend: &Rc<dyn Backend>) {
|
pub fn add_backend_singletons(&self, backend: &Rc<dyn Backend>) {
|
||||||
|
|
|
||||||
|
|
@ -45,6 +45,7 @@ pub mod wl_shm_pool;
|
||||||
pub mod wl_subcompositor;
|
pub mod wl_subcompositor;
|
||||||
pub mod wl_surface;
|
pub mod wl_surface;
|
||||||
pub mod wp_alpha_modifier_v1;
|
pub mod wp_alpha_modifier_v1;
|
||||||
|
pub mod wp_commit_timing_manager_v1;
|
||||||
pub mod wp_content_type_manager_v1;
|
pub mod wp_content_type_manager_v1;
|
||||||
pub mod wp_content_type_v1;
|
pub mod wp_content_type_v1;
|
||||||
pub mod wp_cursor_shape_device_v1;
|
pub mod wp_cursor_shape_device_v1;
|
||||||
|
|
|
||||||
|
|
@ -60,6 +60,7 @@ pub struct WlOutputGlobal {
|
||||||
pub pos: Cell<Rect>,
|
pub pos: Cell<Rect>,
|
||||||
pub output_id: Rc<OutputId>,
|
pub output_id: Rc<OutputId>,
|
||||||
pub mode: Cell<backend::Mode>,
|
pub mode: Cell<backend::Mode>,
|
||||||
|
pub refresh_nsec: Cell<u64>,
|
||||||
pub modes: Vec<backend::Mode>,
|
pub modes: Vec<backend::Mode>,
|
||||||
pub formats: CloneCell<Rc<Vec<&'static Format>>>,
|
pub formats: CloneCell<Rc<Vec<&'static Format>>>,
|
||||||
pub format: Cell<&'static Format>,
|
pub format: Cell<&'static Format>,
|
||||||
|
|
@ -157,6 +158,7 @@ impl WlOutputGlobal {
|
||||||
pos: Cell::new(Rect::new_sized(x, y, width, height).unwrap()),
|
pos: Cell::new(Rect::new_sized(x, y, width, height).unwrap()),
|
||||||
output_id: output_id.clone(),
|
output_id: output_id.clone(),
|
||||||
mode: Cell::new(*mode),
|
mode: Cell::new(*mode),
|
||||||
|
refresh_nsec: Cell::new(mode.refresh_nsec()),
|
||||||
modes,
|
modes,
|
||||||
formats: CloneCell::new(Rc::new(vec![])),
|
formats: CloneCell::new(Rc::new(vec![])),
|
||||||
format: Cell::new(XRGB8888),
|
format: Cell::new(XRGB8888),
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ pub mod dnd_icon;
|
||||||
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_alpha_modifier_surface_v1;
|
pub mod wp_alpha_modifier_surface_v1;
|
||||||
|
pub mod wp_commit_timer_v1;
|
||||||
pub mod wp_fifo_v1;
|
pub mod wp_fifo_v1;
|
||||||
pub mod wp_fractional_scale_v1;
|
pub mod wp_fractional_scale_v1;
|
||||||
pub mod wp_linux_drm_syncobj_surface_v1;
|
pub mod wp_linux_drm_syncobj_surface_v1;
|
||||||
|
|
@ -47,6 +48,7 @@ use {
|
||||||
dnd_icon::DndIcon,
|
dnd_icon::DndIcon,
|
||||||
wl_subsurface::{PendingSubsurfaceData, SubsurfaceId, WlSubsurface},
|
wl_subsurface::{PendingSubsurfaceData, SubsurfaceId, WlSubsurface},
|
||||||
wp_alpha_modifier_surface_v1::WpAlphaModifierSurfaceV1,
|
wp_alpha_modifier_surface_v1::WpAlphaModifierSurfaceV1,
|
||||||
|
wp_commit_timer_v1::WpCommitTimerV1,
|
||||||
wp_fifo_v1::WpFifoV1,
|
wp_fifo_v1::WpFifoV1,
|
||||||
wp_fractional_scale_v1::WpFractionalScaleV1,
|
wp_fractional_scale_v1::WpFractionalScaleV1,
|
||||||
wp_linux_drm_syncobj_surface_v1::WpLinuxDrmSyncobjSurfaceV1,
|
wp_linux_drm_syncobj_surface_v1::WpLinuxDrmSyncobjSurfaceV1,
|
||||||
|
|
@ -60,14 +62,15 @@ use {
|
||||||
wp_presentation_feedback::WpPresentationFeedback,
|
wp_presentation_feedback::WpPresentationFeedback,
|
||||||
zwp_linux_dmabuf_feedback_v1::ZwpLinuxDmabufFeedbackV1,
|
zwp_linux_dmabuf_feedback_v1::ZwpLinuxDmabufFeedbackV1,
|
||||||
},
|
},
|
||||||
|
io_uring::IoUringError,
|
||||||
leaks::Tracker,
|
leaks::Tracker,
|
||||||
object::{Object, Version},
|
object::{Object, Version},
|
||||||
rect::{DamageQueue, Rect, Region},
|
rect::{DamageQueue, Rect, Region},
|
||||||
renderer::Renderer,
|
renderer::Renderer,
|
||||||
tree::{
|
tree::{
|
||||||
ContainerNode, FindTreeResult, FoundNode, LatchListener, Node, NodeId, NodeVisitor,
|
BeforeLatchListener, BeforeLatchResult, ContainerNode, FindTreeResult, FoundNode,
|
||||||
NodeVisitorBase, OutputNode, PlaceholderNode, PresentationListener, ToplevelNode,
|
LatchListener, Node, NodeId, NodeVisitor, NodeVisitorBase, OutputNode, PlaceholderNode,
|
||||||
VblankListener,
|
PresentationListener, ToplevelNode, VblankListener,
|
||||||
},
|
},
|
||||||
utils::{
|
utils::{
|
||||||
cell_ext::CellExt, clonecell::CloneCell, copyhashmap::CopyHashMap,
|
cell_ext::CellExt, clonecell::CloneCell, copyhashmap::CopyHashMap,
|
||||||
|
|
@ -321,6 +324,8 @@ pub struct WlSurface {
|
||||||
latched_commit_version: Cell<u64>,
|
latched_commit_version: Cell<u64>,
|
||||||
fifo: CloneCell<Option<Rc<WpFifoV1>>>,
|
fifo: CloneCell<Option<Rc<WpFifoV1>>>,
|
||||||
clear_fifo_on_vblank: Cell<bool>,
|
clear_fifo_on_vblank: Cell<bool>,
|
||||||
|
commit_timer: CloneCell<Option<Rc<WpCommitTimerV1>>>,
|
||||||
|
before_latch_listener: EventListener<dyn BeforeLatchListener>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Debug for WlSurface {
|
impl Debug for WlSurface {
|
||||||
|
|
@ -444,6 +449,7 @@ struct PendingState {
|
||||||
explicit_sync: bool,
|
explicit_sync: bool,
|
||||||
fifo_barrier_set: bool,
|
fifo_barrier_set: bool,
|
||||||
fifo_barrier_wait: bool,
|
fifo_barrier_wait: bool,
|
||||||
|
commit_time: Option<u64>,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct AttachedSubsurfaceState {
|
struct AttachedSubsurfaceState {
|
||||||
|
|
@ -494,6 +500,7 @@ impl PendingState {
|
||||||
opt!(tearing);
|
opt!(tearing);
|
||||||
opt!(content_type);
|
opt!(content_type);
|
||||||
opt!(alpha_multiplier);
|
opt!(alpha_multiplier);
|
||||||
|
opt!(commit_time);
|
||||||
{
|
{
|
||||||
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);
|
||||||
|
|
@ -648,6 +655,8 @@ impl WlSurface {
|
||||||
latched_commit_version: Default::default(),
|
latched_commit_version: Default::default(),
|
||||||
fifo: Default::default(),
|
fifo: Default::default(),
|
||||||
clear_fifo_on_vblank: Default::default(),
|
clear_fifo_on_vblank: Default::default(),
|
||||||
|
commit_timer: Default::default(),
|
||||||
|
before_latch_listener: EventListener::new(slf.clone()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1657,6 +1666,7 @@ impl Object for WlSurface {
|
||||||
self.alpha_modifier.take();
|
self.alpha_modifier.take();
|
||||||
self.text_input_connections.clear();
|
self.text_input_connections.clear();
|
||||||
self.fifo.take();
|
self.fifo.take();
|
||||||
|
self.commit_timer.take();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2001,6 +2011,8 @@ pub enum WlSurfaceError {
|
||||||
CreateAsyncShmTexture(#[source] GfxError),
|
CreateAsyncShmTexture(#[source] GfxError),
|
||||||
#[error("Could not prepare upload to a shm texture")]
|
#[error("Could not prepare upload to a shm texture")]
|
||||||
PrepareAsyncUpload(#[source] GfxError),
|
PrepareAsyncUpload(#[source] GfxError),
|
||||||
|
#[error("Could not register a commit timeout")]
|
||||||
|
RegisterCommitTimeout(#[source] IoUringError),
|
||||||
}
|
}
|
||||||
efrom!(WlSurfaceError, ClientError);
|
efrom!(WlSurfaceError, ClientError);
|
||||||
efrom!(WlSurfaceError, XdgSurfaceError);
|
efrom!(WlSurfaceError, XdgSurfaceError);
|
||||||
|
|
@ -2134,6 +2146,12 @@ impl VblankListener for WlSurface {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl BeforeLatchListener for WlSurface {
|
||||||
|
fn before_latch(self: Rc<Self>, present: u64) -> BeforeLatchResult {
|
||||||
|
self.commit_timeline.before_latch(&self, present)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl LatchListener for WlSurface {
|
impl LatchListener for WlSurface {
|
||||||
fn after_latch(self: Rc<Self>, _on: &OutputNode, tearing: bool) {
|
fn after_latch(self: Rc<Self>, _on: &OutputNode, tearing: bool) {
|
||||||
if self.visible.get() {
|
if self.visible.get() {
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,10 @@ use {
|
||||||
wl_buffer::WlBufferStorage,
|
wl_buffer::WlBufferStorage,
|
||||||
wl_surface::{PendingState, WlSurface, WlSurfaceError},
|
wl_surface::{PendingState, WlSurface, WlSurfaceError},
|
||||||
},
|
},
|
||||||
io_uring::{IoUring, IoUringError, PendingPoll, PollCallback},
|
io_uring::{
|
||||||
|
IoUring, IoUringError, PendingPoll, PendingTimeout, PollCallback, TimeoutCallback,
|
||||||
|
},
|
||||||
|
tree::BeforeLatchResult,
|
||||||
utils::{
|
utils::{
|
||||||
clonecell::CloneCell,
|
clonecell::CloneCell,
|
||||||
copyhashmap::CopyHashMap,
|
copyhashmap::CopyHashMap,
|
||||||
|
|
@ -28,7 +31,7 @@ use {
|
||||||
std::{
|
std::{
|
||||||
cell::{Cell, RefCell},
|
cell::{Cell, RefCell},
|
||||||
mem,
|
mem,
|
||||||
ops::DerefMut,
|
ops::{Deref, DerefMut},
|
||||||
rc::{Rc, Weak},
|
rc::{Rc, Weak},
|
||||||
slice,
|
slice,
|
||||||
},
|
},
|
||||||
|
|
@ -50,6 +53,11 @@ pub struct CommitTimelines {
|
||||||
_flush_requests_future: SpawnedFuture<()>,
|
_flush_requests_future: SpawnedFuture<()>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct CommitTimeWaiter {
|
||||||
|
node: NodeRef<Entry>,
|
||||||
|
present: u64,
|
||||||
|
}
|
||||||
|
|
||||||
pub struct CommitTimeline {
|
pub struct CommitTimeline {
|
||||||
shared: Rc<CommitTimelines>,
|
shared: Rc<CommitTimelines>,
|
||||||
own_timeline: Rc<Inner>,
|
own_timeline: Rc<Inner>,
|
||||||
|
|
@ -57,6 +65,7 @@ pub struct CommitTimeline {
|
||||||
effective_timeline_id: Cell<CommitTimelineId>,
|
effective_timeline_id: Cell<CommitTimelineId>,
|
||||||
fifo_barrier_set: Cell<bool>,
|
fifo_barrier_set: Cell<bool>,
|
||||||
fifo_waiter: Cell<Option<NodeRef<Entry>>>,
|
fifo_waiter: Cell<Option<NodeRef<Entry>>>,
|
||||||
|
commit_time_waiter: RefCell<Option<CommitTimeWaiter>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Inner {
|
struct Inner {
|
||||||
|
|
@ -98,6 +107,8 @@ pub enum CommitTimelineError {
|
||||||
RegisterImplicitPoll(#[source] IoUringError),
|
RegisterImplicitPoll(#[source] IoUringError),
|
||||||
#[error("Could not wait for a dmabuf to become idle")]
|
#[error("Could not wait for a dmabuf to become idle")]
|
||||||
PollDmabuf(#[source] OsError),
|
PollDmabuf(#[source] OsError),
|
||||||
|
#[error("Could not wait for the commit timeout")]
|
||||||
|
CommitTimeout(#[source] OsError),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CommitTimelines {
|
impl CommitTimelines {
|
||||||
|
|
@ -136,6 +147,7 @@ impl CommitTimelines {
|
||||||
effective_timeline_id: Cell::new(id),
|
effective_timeline_id: Cell::new(id),
|
||||||
fifo_barrier_set: Cell::new(false),
|
fifo_barrier_set: Cell::new(false),
|
||||||
fifo_waiter: Default::default(),
|
fifo_waiter: Default::default(),
|
||||||
|
commit_time_waiter: Default::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -168,6 +180,7 @@ impl CommitTimeline {
|
||||||
match reason {
|
match reason {
|
||||||
ClearReason::BreakLoops => {
|
ClearReason::BreakLoops => {
|
||||||
self.fifo_waiter.take();
|
self.fifo_waiter.take();
|
||||||
|
self.commit_time_waiter.take();
|
||||||
break_loops(&self.own_timeline.entries)
|
break_loops(&self.own_timeline.entries)
|
||||||
}
|
}
|
||||||
ClearReason::Destroy => {
|
ClearReason::Destroy => {
|
||||||
|
|
@ -191,13 +204,18 @@ impl CommitTimeline {
|
||||||
acquire_points: Default::default(),
|
acquire_points: Default::default(),
|
||||||
shm_uploads: 0,
|
shm_uploads: 0,
|
||||||
implicit_dmabufs: Default::default(),
|
implicit_dmabufs: Default::default(),
|
||||||
|
commit_time: Default::default(),
|
||||||
};
|
};
|
||||||
collector.collect(pending);
|
collector.collect(pending);
|
||||||
let points = collector.acquire_points;
|
let points = collector.acquire_points;
|
||||||
let pending_uploads = collector.shm_uploads;
|
let pending_uploads = collector.shm_uploads;
|
||||||
let implicit_dmabufs = collector.implicit_dmabufs;
|
let implicit_dmabufs = collector.implicit_dmabufs;
|
||||||
let has_dependencies =
|
let commit_time = collector.commit_time;
|
||||||
points.is_not_empty() || pending_uploads > 0 || implicit_dmabufs.is_not_empty();
|
let has_commit_time = commit_time > 0;
|
||||||
|
let has_dependencies = points.is_not_empty()
|
||||||
|
|| pending_uploads > 0
|
||||||
|
|| implicit_dmabufs.is_not_empty()
|
||||||
|
|| has_commit_time;
|
||||||
let must_be_queued = has_dependencies
|
let must_be_queued = has_dependencies
|
||||||
|| self.own_timeline.entries.is_not_empty()
|
|| self.own_timeline.entries.is_not_empty()
|
||||||
|| (pending.fifo_barrier_wait && self.fifo_barrier_set.get());
|
|| (pending.fifo_barrier_wait && self.fifo_barrier_set.get());
|
||||||
|
|
@ -227,6 +245,7 @@ impl CommitTimeline {
|
||||||
num_pending_polls: NumCell::new(implicit_dmabufs.len()),
|
num_pending_polls: NumCell::new(implicit_dmabufs.len()),
|
||||||
pending_polls: Cell::new(Default::default()),
|
pending_polls: Cell::new(Default::default()),
|
||||||
fifo_state: Cell::new(commit_fifo_state),
|
fifo_state: Cell::new(commit_fifo_state),
|
||||||
|
commit_times: RefCell::new(CommitTimesState::Ready),
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
let mut needs_flush = commit_fifo_state == CommitFifoState::Queued;
|
let mut needs_flush = commit_fifo_state == CommitFifoState::Queued;
|
||||||
|
|
@ -263,6 +282,13 @@ impl CommitTimeline {
|
||||||
}
|
}
|
||||||
commit.pending_polls.set(pending_polls);
|
commit.pending_polls.set(pending_polls);
|
||||||
}
|
}
|
||||||
|
if has_commit_time {
|
||||||
|
*commit.commit_times.borrow_mut() = CommitTimesState::Queued {
|
||||||
|
rc: noderef.clone(),
|
||||||
|
time: commit_time,
|
||||||
|
};
|
||||||
|
needs_flush = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if needs_flush && noderef.prev().is_none() {
|
if needs_flush && noderef.prev().is_none() {
|
||||||
flush_from(noderef.clone()).map_err(CommitTimelineError::DelayedCommit)?;
|
flush_from(noderef.clone()).map_err(CommitTimelineError::DelayedCommit)?;
|
||||||
|
|
@ -284,6 +310,30 @@ impl CommitTimeline {
|
||||||
pub fn has_fifo_barrier(&self) -> bool {
|
pub fn has_fifo_barrier(&self) -> bool {
|
||||||
self.fifo_barrier_set.get()
|
self.fifo_barrier_set.get()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn before_latch(&self, surface: &WlSurface, present: u64) -> BeforeLatchResult {
|
||||||
|
let waiter = &mut *self.commit_time_waiter.borrow_mut();
|
||||||
|
if let Some(w) = waiter {
|
||||||
|
if w.present <= present {
|
||||||
|
let EntryKind::Commit(c) = &w.node.kind else {
|
||||||
|
unreachable!();
|
||||||
|
};
|
||||||
|
*c.commit_times.borrow_mut() = CommitTimesState::Ready;
|
||||||
|
self.shared
|
||||||
|
.flush_requests
|
||||||
|
.flush_waiters
|
||||||
|
.push(w.node.clone());
|
||||||
|
*waiter = None;
|
||||||
|
surface.before_latch_listener.detach();
|
||||||
|
BeforeLatchResult::Yield
|
||||||
|
} else {
|
||||||
|
BeforeLatchResult::None
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
surface.before_latch_listener.detach();
|
||||||
|
BeforeLatchResult::None
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SyncObjWaiter for NodeRef<Entry> {
|
impl SyncObjWaiter for NodeRef<Entry> {
|
||||||
|
|
@ -343,6 +393,25 @@ impl PollCallback for NodeRef<Entry> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl TimeoutCallback for NodeRef<Entry> {
|
||||||
|
fn completed(self: Rc<Self>, res: Result<(), OsError>, _data: u64) {
|
||||||
|
let EntryKind::Commit(commit) = &self.kind else {
|
||||||
|
unreachable!();
|
||||||
|
};
|
||||||
|
commit.surface.commit_timeline.commit_time_waiter.take();
|
||||||
|
commit.surface.before_latch_listener.detach();
|
||||||
|
if let Err(e) = res {
|
||||||
|
commit
|
||||||
|
.surface
|
||||||
|
.client
|
||||||
|
.error(CommitTimelineError::CommitTimeout(e));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
*commit.commit_times.borrow_mut() = CommitTimesState::Ready;
|
||||||
|
flush_commit(&self, commit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
struct Entry {
|
struct Entry {
|
||||||
link: Cell<Option<LinkedNode<Entry>>>,
|
link: Cell<Option<LinkedNode<Entry>>>,
|
||||||
shared: Rc<CommitTimelines>,
|
shared: Rc<CommitTimelines>,
|
||||||
|
|
@ -362,6 +431,12 @@ enum ShmUploadState {
|
||||||
Scheduled(#[expect(dead_code)] SmallVec<[PendingShmTransfer; 1]>),
|
Scheduled(#[expect(dead_code)] SmallVec<[PendingShmTransfer; 1]>),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum CommitTimesState {
|
||||||
|
Ready,
|
||||||
|
Queued { rc: Rc<NodeRef<Entry>>, time: u64 },
|
||||||
|
Registered { _pending: PendingTimeout },
|
||||||
|
}
|
||||||
|
|
||||||
struct Commit {
|
struct Commit {
|
||||||
surface: Rc<WlSurface>,
|
surface: Rc<WlSurface>,
|
||||||
pending: RefCell<Box<PendingState>>,
|
pending: RefCell<Box<PendingState>>,
|
||||||
|
|
@ -372,6 +447,7 @@ struct Commit {
|
||||||
num_pending_polls: NumCell<usize>,
|
num_pending_polls: NumCell<usize>,
|
||||||
pending_polls: Cell<SmallVec<[PendingPoll; 1]>>,
|
pending_polls: Cell<SmallVec<[PendingPoll; 1]>>,
|
||||||
fifo_state: Cell<CommitFifoState>,
|
fifo_state: Cell<CommitFifoState>,
|
||||||
|
commit_times: RefCell<CommitTimesState>,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn flush_from(mut point: NodeRef<Entry>) -> Result<(), WlSurfaceError> {
|
fn flush_from(mut point: NodeRef<Entry>) -> Result<(), WlSurfaceError> {
|
||||||
|
|
@ -421,6 +497,19 @@ impl NodeRef<Entry> {
|
||||||
CommitFifoState::Mailbox => {}
|
CommitFifoState::Mailbox => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
let commit_times = &mut *c.commit_times.borrow_mut();
|
||||||
|
match commit_times {
|
||||||
|
CommitTimesState::Ready => {}
|
||||||
|
CommitTimesState::Queued { rc, time } => {
|
||||||
|
*commit_times = register_commit_time(tl, rc, c, *time)?;
|
||||||
|
if let CommitTimesState::Registered { .. } = commit_times {
|
||||||
|
has_unmet_dependencies = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
CommitTimesState::Registered { .. } => {
|
||||||
|
has_unmet_dependencies = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
if has_unmet_dependencies {
|
if has_unmet_dependencies {
|
||||||
return Ok(false);
|
return Ok(false);
|
||||||
}
|
}
|
||||||
|
|
@ -455,6 +544,36 @@ fn check_shm_uploads(c: &Commit) -> Result<(), WlSurfaceError> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn register_commit_time(
|
||||||
|
tl: &CommitTimeline,
|
||||||
|
rc: &Rc<NodeRef<Entry>>,
|
||||||
|
c: &Commit,
|
||||||
|
time: u64,
|
||||||
|
) -> Result<CommitTimesState, WlSurfaceError> {
|
||||||
|
let output = c.surface.output.get();
|
||||||
|
let render_margin = output.render_margin_ns.get();
|
||||||
|
let flip_margin = output.flip_margin_ns.get().unwrap_or_default();
|
||||||
|
let refresh = output.global.refresh_nsec.get();
|
||||||
|
let present_margin = render_margin.saturating_add(flip_margin).min(refresh);
|
||||||
|
let timeout = time.saturating_sub(present_margin);
|
||||||
|
if timeout <= c.surface.client.state.now_nsec() {
|
||||||
|
return Ok(CommitTimesState::Ready);
|
||||||
|
}
|
||||||
|
let pending = tl
|
||||||
|
.shared
|
||||||
|
.ring
|
||||||
|
.timeout_external(timeout, rc.clone(), 0)
|
||||||
|
.map_err(WlSurfaceError::RegisterCommitTimeout)?;
|
||||||
|
*tl.commit_time_waiter.borrow_mut() = Some(CommitTimeWaiter {
|
||||||
|
node: rc.deref().clone(),
|
||||||
|
present: time,
|
||||||
|
});
|
||||||
|
c.surface
|
||||||
|
.before_latch_listener
|
||||||
|
.attach(&output.before_latch_event);
|
||||||
|
Ok(CommitTimesState::Registered { _pending: pending })
|
||||||
|
}
|
||||||
|
|
||||||
fn schedule_async_uploads(
|
fn schedule_async_uploads(
|
||||||
node_ref: &Rc<NodeRef<Entry>>,
|
node_ref: &Rc<NodeRef<Entry>>,
|
||||||
surface: &WlSurface,
|
surface: &WlSurface,
|
||||||
|
|
@ -554,6 +673,7 @@ struct CommitDataCollector {
|
||||||
acquire_points: SmallVec<[Point; 1]>,
|
acquire_points: SmallVec<[Point; 1]>,
|
||||||
shm_uploads: usize,
|
shm_uploads: usize,
|
||||||
implicit_dmabufs: SmallVec<[Rc<OwnedFd>; 1]>,
|
implicit_dmabufs: SmallVec<[Rc<OwnedFd>; 1]>,
|
||||||
|
commit_time: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CommitDataCollector {
|
impl CommitDataCollector {
|
||||||
|
|
@ -573,6 +693,9 @@ impl CommitDataCollector {
|
||||||
if let Some(point) = pending.acquire_point.take() {
|
if let Some(point) = pending.acquire_point.take() {
|
||||||
self.acquire_points.push(point);
|
self.acquire_points.push(point);
|
||||||
}
|
}
|
||||||
|
if let Some(commit_time) = pending.commit_time.take() {
|
||||||
|
self.commit_time = self.commit_time.max(commit_time);
|
||||||
|
}
|
||||||
for ss in pending.subsurfaces.values_mut() {
|
for ss in pending.subsurfaces.values_mut() {
|
||||||
if let Some(state) = &mut ss.pending.state {
|
if let Some(state) = &mut ss.pending.state {
|
||||||
self.collect(state);
|
self.collect(state);
|
||||||
|
|
|
||||||
94
src/ifs/wl_surface/wp_commit_timer_v1.rs
Normal file
94
src/ifs/wl_surface/wp_commit_timer_v1.rs
Normal file
|
|
@ -0,0 +1,94 @@
|
||||||
|
use {
|
||||||
|
crate::{
|
||||||
|
client::{Client, ClientError},
|
||||||
|
ifs::wl_surface::WlSurface,
|
||||||
|
leaks::Tracker,
|
||||||
|
object::{Object, Version},
|
||||||
|
wire::{
|
||||||
|
wp_commit_timer_v1::{Destroy, SetTimestamp, WpCommitTimerV1RequestHandler},
|
||||||
|
WpCommitTimerV1Id,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
std::rc::Rc,
|
||||||
|
thiserror::Error,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct WpCommitTimerV1 {
|
||||||
|
pub id: WpCommitTimerV1Id,
|
||||||
|
pub client: Rc<Client>,
|
||||||
|
pub surface: Rc<WlSurface>,
|
||||||
|
pub tracker: Tracker<Self>,
|
||||||
|
pub version: Version,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WpCommitTimerV1 {
|
||||||
|
pub fn new(id: WpCommitTimerV1Id, 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<(), WpCommitTimerV1Error> {
|
||||||
|
if self.surface.commit_timer.is_some() {
|
||||||
|
return Err(WpCommitTimerV1Error::Exists);
|
||||||
|
}
|
||||||
|
self.surface.commit_timer.set(Some(self.clone()));
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WpCommitTimerV1RequestHandler for WpCommitTimerV1 {
|
||||||
|
type Error = WpCommitTimerV1Error;
|
||||||
|
|
||||||
|
fn set_timestamp(&self, req: SetTimestamp, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
||||||
|
if req.tv_nsec >= 1_000_000_000 {
|
||||||
|
return Err(WpCommitTimerV1Error::InvalidNsec);
|
||||||
|
}
|
||||||
|
let nsec = (((req.tv_sec_hi as u64) << 32) | (req.tv_sec_lo as u64))
|
||||||
|
.checked_mul(1_000_000_000)
|
||||||
|
.and_then(|n| n.checked_add(req.tv_nsec as u64));
|
||||||
|
let Some(nsec) = nsec else {
|
||||||
|
return Err(WpCommitTimerV1Error::Overflow);
|
||||||
|
};
|
||||||
|
let pending = &mut *self.surface.pending.borrow_mut();
|
||||||
|
if pending.commit_time.is_some() {
|
||||||
|
return Err(WpCommitTimerV1Error::TimestampExists);
|
||||||
|
}
|
||||||
|
pending.commit_time = Some(nsec);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn destroy(&self, _req: Destroy, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
||||||
|
self.surface.commit_timer.take();
|
||||||
|
self.client.remove_obj(self)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
object_base! {
|
||||||
|
self = WpCommitTimerV1;
|
||||||
|
version = self.version;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Object for WpCommitTimerV1 {}
|
||||||
|
|
||||||
|
simple_add_obj!(WpCommitTimerV1);
|
||||||
|
|
||||||
|
#[derive(Debug, Error)]
|
||||||
|
pub enum WpCommitTimerV1Error {
|
||||||
|
#[error(transparent)]
|
||||||
|
ClientError(Box<ClientError>),
|
||||||
|
#[error("The surface already has a commit timer extension attached")]
|
||||||
|
Exists,
|
||||||
|
#[error("The tv_nsec is larger than 999_999_999")]
|
||||||
|
InvalidNsec,
|
||||||
|
#[error("The timestamp overflowed")]
|
||||||
|
Overflow,
|
||||||
|
#[error("The commit already has a timestamp")]
|
||||||
|
TimestampExists,
|
||||||
|
}
|
||||||
|
efrom!(WpCommitTimerV1Error, ClientError);
|
||||||
105
src/ifs/wp_commit_timing_manager_v1.rs
Normal file
105
src/ifs/wp_commit_timing_manager_v1.rs
Normal file
|
|
@ -0,0 +1,105 @@
|
||||||
|
use {
|
||||||
|
crate::{
|
||||||
|
client::{Client, ClientError},
|
||||||
|
globals::{Global, GlobalName},
|
||||||
|
ifs::wl_surface::wp_commit_timer_v1::{WpCommitTimerV1, WpCommitTimerV1Error},
|
||||||
|
leaks::Tracker,
|
||||||
|
object::{Object, Version},
|
||||||
|
wire::{
|
||||||
|
wp_commit_timing_manager_v1::{
|
||||||
|
Destroy, GetTimer, WpCommitTimingManagerV1RequestHandler,
|
||||||
|
},
|
||||||
|
WpCommitTimingManagerV1Id,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
std::rc::Rc,
|
||||||
|
thiserror::Error,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct WpCommitTimingManagerV1Global {
|
||||||
|
pub name: GlobalName,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct WpCommitTimingManagerV1 {
|
||||||
|
pub id: WpCommitTimingManagerV1Id,
|
||||||
|
pub client: Rc<Client>,
|
||||||
|
pub tracker: Tracker<Self>,
|
||||||
|
pub version: Version,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WpCommitTimingManagerV1Global {
|
||||||
|
pub fn new(name: GlobalName) -> Self {
|
||||||
|
Self { name }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn bind_(
|
||||||
|
self: Rc<Self>,
|
||||||
|
id: WpCommitTimingManagerV1Id,
|
||||||
|
client: &Rc<Client>,
|
||||||
|
version: Version,
|
||||||
|
) -> Result<(), WpCommitTimingManagerV1Error> {
|
||||||
|
let obj = Rc::new(WpCommitTimingManagerV1 {
|
||||||
|
id,
|
||||||
|
client: client.clone(),
|
||||||
|
tracker: Default::default(),
|
||||||
|
version,
|
||||||
|
});
|
||||||
|
track!(client, obj);
|
||||||
|
client.add_client_obj(&obj)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
global_base!(
|
||||||
|
WpCommitTimingManagerV1Global,
|
||||||
|
WpCommitTimingManagerV1,
|
||||||
|
WpCommitTimingManagerV1Error
|
||||||
|
);
|
||||||
|
|
||||||
|
impl Global for WpCommitTimingManagerV1Global {
|
||||||
|
fn singleton(&self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
fn version(&self) -> u32 {
|
||||||
|
1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
simple_add_global!(WpCommitTimingManagerV1Global);
|
||||||
|
|
||||||
|
impl WpCommitTimingManagerV1RequestHandler for WpCommitTimingManagerV1 {
|
||||||
|
type Error = WpCommitTimingManagerV1Error;
|
||||||
|
|
||||||
|
fn destroy(&self, _req: Destroy, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
||||||
|
self.client.remove_obj(self)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_timer(&self, req: GetTimer, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
||||||
|
let surface = self.client.lookup(req.surface)?;
|
||||||
|
let obj = Rc::new(WpCommitTimerV1::new(req.id, self.version, &surface));
|
||||||
|
track!(self.client, obj);
|
||||||
|
obj.install()?;
|
||||||
|
self.client.add_client_obj(&obj)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
object_base! {
|
||||||
|
self = WpCommitTimingManagerV1;
|
||||||
|
version = self.version;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Object for WpCommitTimingManagerV1 {}
|
||||||
|
|
||||||
|
simple_add_obj!(WpCommitTimingManagerV1);
|
||||||
|
|
||||||
|
#[derive(Debug, Error)]
|
||||||
|
pub enum WpCommitTimingManagerV1Error {
|
||||||
|
#[error(transparent)]
|
||||||
|
ClientError(Box<ClientError>),
|
||||||
|
#[error(transparent)]
|
||||||
|
WpCommitTimerV1Error(#[from] WpCommitTimerV1Error),
|
||||||
|
}
|
||||||
|
efrom!(WpCommitTimingManagerV1Error, ClientError);
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
pub use ops::{
|
pub use ops::{
|
||||||
poll_external::{PendingPoll, PollCallback},
|
poll_external::{PendingPoll, PollCallback},
|
||||||
|
timeout_external::{PendingTimeout, TimeoutCallback},
|
||||||
TaskResultExt,
|
TaskResultExt,
|
||||||
};
|
};
|
||||||
use {
|
use {
|
||||||
|
|
|
||||||
|
|
@ -45,7 +45,6 @@ pub struct TimeoutExternalTask {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IoUring {
|
impl IoUring {
|
||||||
#[expect(dead_code)]
|
|
||||||
pub fn timeout_external(
|
pub fn timeout_external(
|
||||||
&self,
|
&self,
|
||||||
timeout_nsec: u64,
|
timeout_nsec: u64,
|
||||||
|
|
|
||||||
|
|
@ -184,8 +184,10 @@ impl ConnectorHandler {
|
||||||
latch_event: Default::default(),
|
latch_event: Default::default(),
|
||||||
vblank_event: Default::default(),
|
vblank_event: Default::default(),
|
||||||
presentation_event: Default::default(),
|
presentation_event: Default::default(),
|
||||||
|
render_margin_ns: Default::default(),
|
||||||
flip_margin_ns: Default::default(),
|
flip_margin_ns: Default::default(),
|
||||||
ext_copy_sessions: Default::default(),
|
ext_copy_sessions: Default::default(),
|
||||||
|
before_latch_event: Default::default(),
|
||||||
});
|
});
|
||||||
on.update_visible();
|
on.update_visible();
|
||||||
on.update_rects();
|
on.update_rects();
|
||||||
|
|
|
||||||
|
|
@ -53,7 +53,7 @@ use {
|
||||||
std::{
|
std::{
|
||||||
cell::{Cell, RefCell},
|
cell::{Cell, RefCell},
|
||||||
fmt::{Debug, Formatter},
|
fmt::{Debug, Formatter},
|
||||||
ops::Deref,
|
ops::{BitOrAssign, Deref},
|
||||||
rc::Rc,
|
rc::Rc,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
@ -89,9 +89,29 @@ pub struct OutputNode {
|
||||||
pub latch_event: EventSource<dyn LatchListener>,
|
pub latch_event: EventSource<dyn LatchListener>,
|
||||||
pub vblank_event: EventSource<dyn VblankListener>,
|
pub vblank_event: EventSource<dyn VblankListener>,
|
||||||
pub presentation_event: EventSource<dyn PresentationListener>,
|
pub presentation_event: EventSource<dyn PresentationListener>,
|
||||||
|
pub render_margin_ns: Cell<u64>,
|
||||||
pub flip_margin_ns: Cell<Option<u64>>,
|
pub flip_margin_ns: Cell<Option<u64>>,
|
||||||
pub ext_copy_sessions:
|
pub ext_copy_sessions:
|
||||||
CopyHashMap<(ClientId, ExtImageCopyCaptureSessionV1Id), Rc<ExtImageCopyCaptureSessionV1>>,
|
CopyHashMap<(ClientId, ExtImageCopyCaptureSessionV1Id), Rc<ExtImageCopyCaptureSessionV1>>,
|
||||||
|
pub before_latch_event: EventSource<dyn BeforeLatchListener>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||||
|
pub enum BeforeLatchResult {
|
||||||
|
None,
|
||||||
|
Yield,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BitOrAssign for BeforeLatchResult {
|
||||||
|
fn bitor_assign(&mut self, rhs: Self) {
|
||||||
|
if rhs == BeforeLatchResult::Yield {
|
||||||
|
*self = rhs;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait BeforeLatchListener {
|
||||||
|
fn before_latch(self: Rc<Self>, present: u64) -> BeforeLatchResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait LatchListener {
|
pub trait LatchListener {
|
||||||
|
|
@ -135,6 +155,16 @@ pub async fn output_render_data(state: Rc<State>) {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl OutputNode {
|
impl OutputNode {
|
||||||
|
pub async fn before_latch(&self, present: u64) {
|
||||||
|
let mut res = BeforeLatchResult::None;
|
||||||
|
for listener in self.before_latch_event.iter() {
|
||||||
|
res |= listener.before_latch(present);
|
||||||
|
}
|
||||||
|
if res == BeforeLatchResult::Yield {
|
||||||
|
self.state.eng.yield_now().await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn latched(&self, tearing: bool) {
|
pub fn latched(&self, tearing: bool) {
|
||||||
self.schedule.latched();
|
self.schedule.latched();
|
||||||
for listener in self.latch_event.iter() {
|
for listener in self.latch_event.iter() {
|
||||||
|
|
@ -703,6 +733,7 @@ impl OutputNode {
|
||||||
}
|
}
|
||||||
let (old_width, old_height) = self.global.pixel_size();
|
let (old_width, old_height) = self.global.pixel_size();
|
||||||
self.global.mode.set(mode);
|
self.global.mode.set(mode);
|
||||||
|
self.global.refresh_nsec.set(mode.refresh_nsec());
|
||||||
self.global.persistent.transform.set(transform);
|
self.global.persistent.transform.set(transform);
|
||||||
let (new_width, new_height) = self.global.pixel_size();
|
let (new_width, new_height) = self.global.pixel_size();
|
||||||
self.change_extents_(&self.calculate_extents());
|
self.change_extents_(&self.calculate_extents());
|
||||||
|
|
|
||||||
9
wire/wp_commit_timer_v1.txt
Normal file
9
wire/wp_commit_timer_v1.txt
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
request set_timestamp {
|
||||||
|
tv_sec_hi: u32,
|
||||||
|
tv_sec_lo: u32,
|
||||||
|
tv_nsec: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
request destroy {
|
||||||
|
|
||||||
|
}
|
||||||
8
wire/wp_commit_timing_manager_v1.txt
Normal file
8
wire/wp_commit_timing_manager_v1.txt
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
request destroy {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
request get_timer {
|
||||||
|
id: id(wp_commit_timer_v1),
|
||||||
|
surface: id(wl_surface),
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue