Fix panic when powering off outputs

This commit is contained in:
Ottatop 2024-08-13 21:14:13 -05:00
parent d924c44611
commit 9b25372048
8 changed files with 64 additions and 77 deletions

View file

@ -1343,7 +1343,9 @@ impl output_service_server::OutputService for OutputService {
return;
};
state.backend.set_output_powered(&output, powered);
state
.backend
.set_output_powered(&output, &state.pinnacle.loop_handle, powered);
if powered {
state.schedule_render(&output);

View file

@ -7,7 +7,7 @@ use smithay::{
},
delegate_dmabuf,
output::Output,
reexports::wayland_server::protocol::wl_surface::WlSurface,
reexports::{calloop::LoopHandle, wayland_server::protocol::wl_surface::WlSurface},
wayland::dmabuf::{DmabufGlobal, DmabufHandler, DmabufState, ImportNotifier},
};
use tracing::error;
@ -98,10 +98,15 @@ impl Backend {
}
}
pub fn set_output_powered(&mut self, output: &Output, powered: bool) {
pub fn set_output_powered(
&mut self,
output: &Output,
loop_handle: &LoopHandle<'static, State>,
powered: bool,
) {
match self {
Backend::Winit(_) => (),
Backend::Udev(udev) => udev.set_output_powered(output, powered),
Backend::Udev(udev) => udev.set_output_powered(output, loop_handle, powered),
#[cfg(feature = "testing")]
Backend::Dummy(dummy) => dummy.set_output_powered(output, powered),
}
@ -122,7 +127,7 @@ impl Backend {
}
}
#[cfg(feature = "testing")]
Backend::Dummy(_) => todo!(),
Backend::Dummy(_) => (),
}
}

View file

@ -93,7 +93,7 @@ use crate::{
use self::drm::util::EdidInfo;
use super::{BackendData, RenderResult, UninitBackend};
use super::{BackendData, UninitBackend};
const SUPPORTED_FORMATS: &[Fourcc] = &[
Fourcc::Abgr2101010,
@ -463,49 +463,37 @@ impl Udev {
return;
};
// tracing::info!(state = ?surface.render_state, name = output.name());
let old_state = mem::take(&mut surface.render_state);
surface.render_state = match old_state {
RenderState::Idle => {
// let output = output.clone();
// let token = loop_handle.insert_idle(move |state| {
// state
// .backend
// .udev_mut()
// .render_surface(&mut state.pinnacle, &output);
// });
// tracing::info!(
// ?old_state,
// op = output.name(),
// powered = output.with_state(|state| state.powered),
// "scheduled render"
// );
surface.render_state = match old_state {
RenderState::Idle => RenderState::Scheduled,
RenderState::Scheduled
}
value @ (RenderState::Scheduled
| RenderState::WaitingForEstimatedVblankAndScheduled(_)) => value,
RenderState::WaitingForVblank { render_needed: _ } => RenderState::WaitingForVblank {
render_needed: true,
},
RenderState::WaitingForEstimatedVblank(token) => {
RenderState::WaitingForEstimatedVblankAndScheduled(token)
}
};
}
// pub fn render_scheduled_outputs(&mut self, pinnacle: &mut Pinnacle) {
// for output in pinnacle.outputs.keys().cloned().collect::<Vec<_>>() {
// let Some(surface) = render_surface_for_output(&output, &mut self.backends) else {
// continue;
// };
//
// if matches!(
// surface.render_state,
// RenderState::Scheduled | RenderState::WaitingForEstimatedVblankAndScheduled(_)
// ) {
// self.render_surface(pinnacle, &output);
// }
// }
// }
pub(super) fn set_output_powered(&mut self, output: &Output, powered: bool) {
pub(super) fn set_output_powered(
&mut self,
output: &Output,
loop_handle: &LoopHandle<'static, State>,
powered: bool,
) {
let UdevOutputData { device_id, crtc } =
output.user_data().get::<UdevOutputData>().unwrap();
@ -535,11 +523,12 @@ impl Udev {
}
if let Some(surface) = render_surface_for_output(output, &mut self.backends) {
// TODO: test
surface.render_state = RenderState::Idle;
// if let RenderState::Scheduled(idle) = std::mem::take(&mut surface.render_state) {
// idle.cancel();
// }
if let RenderState::WaitingForEstimatedVblankAndScheduled(token)
| RenderState::WaitingForEstimatedVblank(token) =
mem::take(&mut surface.render_state)
{
loop_handle.remove(token);
}
}
}
}
@ -1222,11 +1211,6 @@ impl Udev {
return;
};
if matches!(surface.render_state, RenderState::Idle) {
warn!(output = output.name(), "Got vblank for an idle output");
return;
}
match surface
.compositor
.frame_submitted()
@ -1284,7 +1268,6 @@ impl Udev {
RenderState::WaitingForVblank { render_needed } => render_needed,
state => {
debug!("vblank happened but render state was {state:?}",);
// TODO: unreachable
return;
}
};
@ -1317,21 +1300,21 @@ impl Udev {
/// Render to the [`RenderSurface`] associated with the given `output`.
#[tracing::instrument(level = "debug", skip(self, pinnacle), fields(output = output.name()))]
fn render_surface(&mut self, pinnacle: &mut Pinnacle, output: &Output) -> RenderResult {
fn render_surface(&mut self, pinnacle: &mut Pinnacle, output: &Output) {
let Some(surface) = render_surface_for_output(output, &mut self.backends) else {
return RenderResult::Skipped;
return;
};
if !pinnacle.outputs.contains_key(output) {
surface.render_state = RenderState::Idle;
return RenderResult::Skipped;
return;
}
// TODO: possibly lift this out and make it so that scheduling a render
// does nothing on powered off outputs
if output.with_state(|state| !state.powered) {
surface.render_state = RenderState::Idle;
return RenderResult::Skipped;
return;
}
assert_matches!(
@ -1503,7 +1486,7 @@ impl Udev {
Ok(rendered)
})();
let render_result = match result {
match result {
Ok(true) => {
let new_state = RenderState::WaitingForVblank {
render_needed: false,
@ -1527,9 +1510,9 @@ impl Udev {
pinnacle.send_frame_callbacks(output, Some(surface.frame_callback_sequence));
// Return here to not queue the estimated vblank timer on a submitted frame
return RenderResult::Submitted;
return;
}
Ok(false) => RenderResult::NoDamage,
Ok(false) => (),
Err(err) => {
warn!(output = output.name(), "Render failed for surface: {err}");
@ -1542,18 +1525,14 @@ impl Udev {
} else {
RenderState::Idle
};
RenderResult::Skipped
}
};
}
self.queue_estimated_vblank_timer(pinnacle, output, time_to_next_presentation);
// if render_after_transaction_finish {
// self.schedule_render(output);
// }
render_result
}
fn queue_estimated_vblank_timer(

View file

@ -44,7 +44,7 @@ use crate::{
state::{Pinnacle, State, WithState},
};
use super::{Backend, BackendData, RenderResult, UninitBackend};
use super::{Backend, BackendData, UninitBackend};
const LOGO_BYTES: &[u8] = include_bytes!("../../resources/pinnacle_logo_icon.rgba");
@ -244,7 +244,6 @@ impl Winit {
/// Schedule a render on the winit window.
pub fn schedule_render(&mut self) {
trace!("Scheduling winit render");
self.output_render_scheduled = true;
}
@ -255,7 +254,7 @@ impl Winit {
}
}
pub(super) fn render_winit_window(&mut self, pinnacle: &mut Pinnacle) -> RenderResult {
pub(super) fn render_winit_window(&mut self, pinnacle: &mut Pinnacle) {
let full_redraw = &mut self.full_redraw;
*full_redraw = full_redraw.saturating_sub(1);
@ -377,7 +376,7 @@ impl Winit {
})
});
let render_result = match render_res {
match render_res {
Ok(render_output_result) => {
if pinnacle.lock_state.is_unlocked() {
Winit::handle_pending_screencopy(
@ -425,14 +424,10 @@ impl Winit {
0,
wp_presentation_feedback::Kind::Vsync,
);
RenderResult::Submitted
} else {
RenderResult::NoDamage
}
}
Err(err) => {
warn!("{}", err);
RenderResult::Skipped
}
};
@ -445,8 +440,6 @@ impl Winit {
if render_after_transaction_finish {
self.schedule_render();
}
render_result
}
}

View file

@ -109,6 +109,10 @@ impl OutputFocusStack {
self.stack.retain(|op| op != &output);
self.stack.push(output);
}
pub fn remove(&mut self, output: &Output) {
self.stack.retain(|op| op != output);
}
}
/// A stack of windows, with the top one being the one in focus.

View file

@ -928,7 +928,8 @@ impl OutputManagementHandler for State {
OutputConfiguration::Disabled => {
self.pinnacle.set_output_enabled(&output, false);
// TODO: split
self.backend.set_output_powered(&output, false);
self.backend
.set_output_powered(&output, &self.pinnacle.loop_handle, false);
}
OutputConfiguration::Enabled {
mode,
@ -939,7 +940,8 @@ impl OutputManagementHandler for State {
} => {
self.pinnacle.set_output_enabled(&output, true);
// TODO: split
self.backend.set_output_powered(&output, true);
self.backend
.set_output_powered(&output, &self.pinnacle.loop_handle, true);
self.capture_snapshots_on_output(&output, []);
@ -1001,7 +1003,8 @@ impl OutputPowerManagementHandler for State {
}
fn set_mode(&mut self, output: &Output, powered: bool) {
self.backend.set_output_powered(output, powered);
self.backend
.set_output_powered(output, &self.pinnacle.loop_handle, powered);
if powered {
self.schedule_render(output);

View file

@ -349,6 +349,8 @@ impl Pinnacle {
self.space.unmap_output(output);
self.output_focus_stack.remove(output);
self.gamma_control_manager_state.output_removed(output);
self.output_power_management_state.output_removed(output);

View file

@ -243,15 +243,14 @@ impl State {
// FIXME: Don't poll this every cycle
for output in self.pinnacle.space.outputs().cloned().collect::<Vec<_>>() {
output.with_state_mut(|state| {
if state
if output.with_state_mut(|state| {
state
.layout_transaction
.as_ref()
.is_some_and(|ts| ts.ready())
{
self.schedule_render(&output);
}
});
}) {
self.schedule_render(&output);
}
}
self.pinnacle