Fix winit rendering

This commit is contained in:
Ottatop 2023-10-15 03:13:21 -05:00
parent 7fbacbd52f
commit 7572523cfc
3 changed files with 22 additions and 124 deletions

View file

@ -680,7 +680,7 @@ impl State {
let then = udev.last_vblank_time; let then = udev.last_vblank_time;
let now = Instant::now(); let now = Instant::now();
let diff = now.duration_since(then); let diff = now.duration_since(then);
tracing::debug!(time = diff.as_secs_f64(), "Time since last vblank"); // tracing::debug!(time = diff.as_secs_f64(), "Time since last vblank");
udev.last_vblank_time = now; udev.last_vblank_time = now;
} }
data.state.on_vblank(node, crtc, metadata); data.state.on_vblank(node, crtc, metadata);
@ -1160,19 +1160,10 @@ impl State {
}); });
} }
} }
// if schedule_render {
// // Anvil had some stuff here about delaying a render to reduce latency,
// // but it introduces visible hitching when scrolling, so I'm removing it here.
// //
// // If latency is a problem then future me can deal with it :)
// self.loop_handle.insert_idle(move |data| {
// data.state.render_surface(&output);
// });
// }
} }
/// Render to the [`RenderSurface`] associated with the given `output`. /// Render to the [`RenderSurface`] associated with the given `output`.
#[tracing::instrument(level = "debug", skip(self), fields(output = output.name()))]
fn render_surface(&mut self, output: &Output) { fn render_surface(&mut self, output: &Output) {
let udev = self.backend.udev_mut(); let udev = self.backend.udev_mut();
@ -1267,63 +1258,6 @@ impl State {
Ok(true) => surface.render_state = RenderState::WaitingForVblank { dirty: false }, Ok(true) => surface.render_state = RenderState::WaitingForVblank { dirty: false },
Ok(false) | Err(_) => surface.render_state = RenderState::Idle, Ok(false) | Err(_) => surface.render_state = RenderState::Idle,
} }
// let reschedule = match &result {
// Ok(has_rendered) => !has_rendered,
// Err(err) => {
// tracing::warn!("Error during rendering: {:?}", err);
// match err {
// SwapBuffersError::AlreadySwapped => false,
// SwapBuffersError::TemporaryFailure(err) => !matches!(
// err.downcast_ref::<DrmError>(),
// Some(&DrmError::DeviceInactive)
// | Some(&DrmError::Access {
// source: drm::SystemError::PermissionDenied,
// ..
// })
// ),
// SwapBuffersError::ContextLost(err) => panic!("Rendering loop lost: {}", err),
// }
// }
// };
//
// if reschedule {
// tracing::debug!("rescheduling due to no dmg or error");
// let Some(data) = output.user_data().get::<UdevOutputData>() else {
// unreachable!()
// };
//
// // Literally no idea if this refresh time calculation is doing anything, but we're
// // gonna keep it here because I already added the stuff for it
// let refresh_time = if let Some(mode) = data.mode {
// self::utils::refresh_time(mode)
// } else {
// let output_refresh = match output.current_mode() {
// Some(mode) => mode.refresh,
// None => {
// return;
// }
// };
// Duration::from_millis((1_000_000f32 / output_refresh as f32) as u64)
// };
//
// // If reschedule is true we either hit a temporary failure or more likely rendering
// // did not cause any damage on the output. In this case we just re-schedule a repaint
// // after approx. one frame to re-test for damage.
// tracing::trace!(
// "reschedule repaint timer with delay {:?} on {}",
// refresh_time,
// output.name(),
// );
// let timer = Timer::from_duration(refresh_time);
// let output = output.clone();
// self.loop_handle
// .insert_source(timer, move |_, _, data| {
// data.state.render_surface(&output);
// TimeoutAction::Drop
// })
// .expect("failed to schedule frame timer");
// }
} }
} }

View file

@ -24,7 +24,10 @@ use smithay::{
timer::{TimeoutAction, Timer}, timer::{TimeoutAction, Timer},
EventLoop, EventLoop,
}, },
wayland_protocols::wp::presentation_time::server::wp_presentation_feedback, wayland_protocols::{
wp::presentation_time::server::wp_presentation_feedback,
xdg::shell::server::xdg_toplevel,
},
wayland_server::{protocol::wl_surface::WlSurface, Display}, wayland_server::{protocol::wl_surface::WlSurface, Display},
}, },
utils::{IsAlive, Transform}, utils::{IsAlive, Transform},
@ -44,7 +47,6 @@ pub struct Winit {
pub damage_tracker: OutputDamageTracker, pub damage_tracker: OutputDamageTracker,
pub dmabuf_state: (DmabufState, DmabufGlobal, Option<DmabufFeedback>), pub dmabuf_state: (DmabufState, DmabufGlobal, Option<DmabufFeedback>),
pub full_redraw: u8, pub full_redraw: u8,
render_state: RenderState,
} }
impl BackendData for Winit { impl BackendData for Winit {
@ -60,11 +62,6 @@ impl BackendData for Winit {
} }
impl Backend { impl Backend {
fn winit(&self) -> &Winit {
let Backend::Winit(winit) = self else { unreachable!() };
winit
}
fn winit_mut(&mut self) -> &mut Winit { fn winit_mut(&mut self) -> &mut Winit {
let Backend::Winit(winit) = self else { unreachable!() }; let Backend::Winit(winit) = self else { unreachable!() };
winit winit
@ -169,7 +166,6 @@ pub fn run_winit() -> anyhow::Result<()> {
damage_tracker: OutputDamageTracker::from_output(&output), damage_tracker: OutputDamageTracker::from_output(&output),
dmabuf_state, dmabuf_state,
full_redraw: 0, full_redraw: 0,
render_state: RenderState::Idle,
}), }),
display, display,
event_loop.get_signal(), event_loop.get_signal(),
@ -227,7 +223,7 @@ pub fn run_winit() -> anyhow::Result<()> {
state.process_input_event(input_evt); state.process_input_event(input_evt);
} }
WinitEvent::Refresh => { WinitEvent::Refresh => {
state.schedule_render(&output); state.render_window(&output);
} }
}); });
@ -238,33 +234,14 @@ pub fn run_winit() -> anyhow::Result<()> {
} }
}; };
state.render_window(&output);
TimeoutAction::ToDuration(Duration::from_micros(((1.0 / 144.0) * 1000000.0) as u64)) TimeoutAction::ToDuration(Duration::from_micros(((1.0 / 144.0) * 1000000.0) as u64))
}); });
if let Err(err) = insert_ret { if let Err(err) = insert_ret {
anyhow::bail!("Failed to insert winit events into event loop: {err}"); anyhow::bail!("Failed to insert winit events into event loop: {err}");
} }
let frame_time = Duration::from_micros(((1.0 / 144.0) * 1000000.0) as u64);
let refresh_timer = Timer::from_duration(frame_time);
state
.loop_handle
.insert_source(refresh_timer, move |instant, _, data| {
let winit = data.state.backend.winit();
winit.backend.window().request_redraw();
let frame_time = winit
.backend
.window()
.current_monitor()
.and_then(|monitor| monitor.refresh_rate_millihertz())
.map(|rate| Duration::from_secs_f64(1000.0 / rate as f64))
.unwrap_or(frame_time);
TimeoutAction::ToInstant(instant + frame_time)
})
.expect("failed to insert render timer into event loop");
event_loop.run( event_loop.run(
Some(Duration::from_micros(((1.0 / 144.0) * 1000000.0) as u64)), Some(Duration::from_micros(((1.0 / 144.0) * 1000000.0) as u64)),
&mut CalloopData { &mut CalloopData {
@ -283,31 +260,10 @@ pub fn run_winit() -> anyhow::Result<()> {
Ok(()) Ok(())
} }
enum RenderState {
Idle,
Scheduled,
}
impl Winit {
pub fn schedule_render(&mut self, loop_handle: &LoopHandle<CalloopData>, output: &Output) {
match &self.render_state {
RenderState::Idle => {
let output = output.clone();
loop_handle.insert_idle(move |data| data.state.render_window(&output));
self.render_state = RenderState::Scheduled;
}
RenderState::Scheduled => (),
}
}
}
impl State { impl State {
fn render_window(&mut self, output: &Output) { fn render_window(&mut self, output: &Output) {
let winit = self.backend.winit_mut(); let winit = self.backend.winit_mut();
assert!(matches!(winit.render_state, RenderState::Scheduled));
let pending_wins = self let pending_wins = self
.windows .windows
.iter() .iter()
@ -322,6 +278,16 @@ impl State {
}; };
pending_size || win.with_state(|state| !state.loc_request_state.is_idle()) pending_size || win.with_state(|state| !state.loc_request_state.is_idle())
}) })
.filter(|win| {
if let WindowElement::Wayland(win) = win {
!win.toplevel()
.current_state()
.states
.contains(xdg_toplevel::State::Resizing)
} else {
true
}
})
.map(|win| { .map(|win| {
( (
win.class().unwrap_or("None".to_string()), win.class().unwrap_or("None".to_string()),
@ -347,7 +313,6 @@ impl State {
// TODO: still draw the cursor here // TODO: still draw the cursor here
winit.render_state = RenderState::Idle;
return; return;
} }
let full_redraw = &mut winit.full_redraw; let full_redraw = &mut winit.full_redraw;
@ -448,6 +413,5 @@ impl State {
tracing::warn!("{}", err); tracing::warn!("{}", err);
} }
} }
winit.render_state = RenderState::Idle;
} }
} }

View file

@ -407,9 +407,9 @@ pub fn take_presentation_feedback(
impl State { impl State {
pub fn schedule_render(&mut self, output: &Output) { pub fn schedule_render(&mut self, output: &Output) {
match &mut self.backend { // I'm relegating winit to render every frame because it's not my priority right now
Backend::Winit(winit) => winit.schedule_render(&self.loop_handle, output), if let Backend::Udev(udev) = &mut self.backend {
Backend::Udev(udev) => udev.schedule_render(&self.loop_handle, output), udev.schedule_render(&self.loop_handle, output);
} }
} }
} }