Schedule redraws for pending wallpaper upload retries
This commit is contained in:
1 parent
4da59f3906
commit
08a586612f
2 files changed
+95
-1
No files matched your search
@@ -135,9 +135,17 @@ fn main() -> Result<(), Box<dyn Error>> {
|
|||||||
}
|
}
|
||||||
// All input/Wayland callbacks have returned, releasing seat locks.
|
// All input/Wayland callbacks have returned, releasing seat locks.
|
||||||
// Coalesce cursor changes and redraw here, never inside cursor_image.
|
// Coalesce cursor changes and redraw here, never inside cursor_image.
|
||||||
if std::mem::take(&mut state.cursor_redraw_pending) || wallpaper_changed {
|
let wallpaper_retry = state.wallpaper.take_retry_wakeup();
|
||||||
|
if std::mem::take(&mut state.cursor_redraw_pending) || wallpaper_changed || wallpaper_retry
|
||||||
|
{
|
||||||
backends::direct::render_all(state);
|
backends::direct::render_all(state);
|
||||||
}
|
}
|
||||||
|
if let Err(error) = state
|
||||||
|
.wallpaper
|
||||||
|
.arm_retry_timer(&reload_handle, std::time::Instant::now())
|
||||||
|
{
|
||||||
|
warn!(%error, "could not schedule wallpaper upload retry");
|
||||||
|
}
|
||||||
// Registry/sync/configure replies must not depend on a submitted
|
// Registry/sync/configure replies must not depend on a submitted
|
||||||
// frame: at startup clients need these before they can draw anything.
|
// frame: at startup clients need these before they can draw anything.
|
||||||
// This also keeps idle DRM sessions responsive when there is no damage.
|
// This also keeps idle DRM sessions responsive when there is no damage.
|
||||||
|
|||||||
@@ -16,8 +16,10 @@ use smithay::{
|
|||||||
utils::{Buffer, Physical, Rectangle, Size},
|
utils::{Buffer, Physical, Rectangle, Size},
|
||||||
};
|
};
|
||||||
use std::{
|
use std::{
|
||||||
|
cell::Cell,
|
||||||
collections::HashMap,
|
collections::HashMap,
|
||||||
path::PathBuf,
|
path::PathBuf,
|
||||||
|
rc::Rc,
|
||||||
sync::mpsc,
|
sync::mpsc,
|
||||||
time::{Duration, Instant},
|
time::{Duration, Instant},
|
||||||
};
|
};
|
||||||
@@ -68,6 +70,8 @@ pub(crate) struct WallpaperState {
|
|||||||
textures: HashMap<ErasedContextId, WallpaperTexture>,
|
textures: HashMap<ErasedContextId, WallpaperTexture>,
|
||||||
upload_retries: HashMap<ErasedContextId, Instant>,
|
upload_retries: HashMap<ErasedContextId, Instant>,
|
||||||
wakeup: Option<smithay::reexports::calloop::LoopSignal>,
|
wakeup: Option<smithay::reexports::calloop::LoopSignal>,
|
||||||
|
retry_timer_pending: Rc<Cell<bool>>,
|
||||||
|
retry_wakeup: Rc<Cell<bool>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WallpaperState {
|
impl WallpaperState {
|
||||||
@@ -114,6 +118,8 @@ impl WallpaperState {
|
|||||||
textures: HashMap::new(),
|
textures: HashMap::new(),
|
||||||
upload_retries: HashMap::new(),
|
upload_retries: HashMap::new(),
|
||||||
wakeup,
|
wakeup,
|
||||||
|
retry_timer_pending: Rc::default(),
|
||||||
|
retry_wakeup: Rc::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -163,6 +169,45 @@ impl WallpaperState {
|
|||||||
.is_none_or(|retry| now >= *retry)
|
.is_none_or(|retry| now >= *retry)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn take_retry_wakeup(&self) -> bool {
|
||||||
|
self.retry_wakeup.replace(false) && !self.upload_retries.is_empty()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn next_retry_deadline(&self, now: Instant) -> Option<Instant> {
|
||||||
|
self.upload_retries.values().copied().min().map(|deadline| {
|
||||||
|
// A suspended/hidden output may not have attempted an upload when
|
||||||
|
// the timer fired. Avoid immediately rescheduling an expired timer.
|
||||||
|
if deadline <= now {
|
||||||
|
now + UPLOAD_RETRY_DELAY
|
||||||
|
} else {
|
||||||
|
deadline
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn arm_retry_timer<Data: 'static>(
|
||||||
|
&self,
|
||||||
|
handle: &smithay::reexports::calloop::LoopHandle<'static, Data>,
|
||||||
|
now: Instant,
|
||||||
|
) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
use smithay::reexports::calloop::timer::{TimeoutAction, Timer};
|
||||||
|
if self.retry_timer_pending.get() {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
let Some(deadline) = self.next_retry_deadline(now) else {
|
||||||
|
return Ok(());
|
||||||
|
};
|
||||||
|
let pending = self.retry_timer_pending.clone();
|
||||||
|
let wakeup = self.retry_wakeup.clone();
|
||||||
|
handle.insert_source(Timer::from_deadline(deadline), move |_, _, _| {
|
||||||
|
pending.set(false);
|
||||||
|
wakeup.set(true);
|
||||||
|
TimeoutAction::Drop
|
||||||
|
})?;
|
||||||
|
self.retry_timer_pending.set(true);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
pub fn poll(&mut self) -> bool {
|
pub fn poll(&mut self) -> bool {
|
||||||
let Some(receiver) = &self.receiver else {
|
let Some(receiver) = &self.receiver else {
|
||||||
return false;
|
return false;
|
||||||
@@ -310,6 +355,47 @@ fn image_geometry(
|
|||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn failed_upload_retry_wakes_idle_loop_and_does_not_spin_when_output_is_inactive() {
|
||||||
|
use smithay::backend::renderer::ContextId;
|
||||||
|
use smithay::reexports::calloop::EventLoop;
|
||||||
|
let mut event_loop = EventLoop::<()>::try_new().unwrap();
|
||||||
|
let mut state = WallpaperState::new(WallpaperConfig {
|
||||||
|
path: None,
|
||||||
|
mode: WallpaperMode::Fill,
|
||||||
|
});
|
||||||
|
let context = ContextId::<GlesTexture>::new().erased();
|
||||||
|
let now = Instant::now();
|
||||||
|
state
|
||||||
|
.upload_retries
|
||||||
|
.insert(context.clone(), now + Duration::from_millis(10));
|
||||||
|
state.arm_retry_timer(&event_loop.handle(), now).unwrap();
|
||||||
|
state.arm_retry_timer(&event_loop.handle(), now).unwrap();
|
||||||
|
assert!(state.retry_timer_pending.get());
|
||||||
|
assert!(!state.take_retry_wakeup());
|
||||||
|
event_loop
|
||||||
|
.dispatch(Some(Duration::from_secs(1)), &mut ())
|
||||||
|
.unwrap();
|
||||||
|
assert!(!state.retry_timer_pending.get());
|
||||||
|
assert!(
|
||||||
|
state.take_retry_wakeup(),
|
||||||
|
"retry must request redraw without input or client activity"
|
||||||
|
);
|
||||||
|
assert!(!state.take_retry_wakeup());
|
||||||
|
let after = Instant::now();
|
||||||
|
assert_eq!(
|
||||||
|
state.next_retry_deadline(after),
|
||||||
|
Some(after + UPLOAD_RETRY_DELAY)
|
||||||
|
);
|
||||||
|
state.forget_context(&context);
|
||||||
|
assert_eq!(state.next_retry_deadline(after), None);
|
||||||
|
state.retry_wakeup.set(true);
|
||||||
|
assert!(
|
||||||
|
!state.take_retry_wakeup(),
|
||||||
|
"cleared failures must not trigger stale redraws"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn decoder_wakes_an_idle_event_loop() {
|
fn decoder_wakes_an_idle_event_loop() {
|
||||||
use smithay::reexports::calloop::{EventLoop, timer::Timer};
|
use smithay::reexports::calloop::{EventLoop, timer::Timer};
|
||||||
|
|||||||
Reference in new issue
Block a user