1
0
mirror of https://github.com/emilk/egui.git synced 2026-08-31 22:00:03 -04:00

Output the repaint delay for integrations that don't install a callback

This commit is contained in:
Emil Ernerfeldt
2023-11-15 20:41:53 +01:00
parent 09b639e586
commit e222247ee1
3 changed files with 73 additions and 36 deletions

View File

@@ -1169,6 +1169,7 @@ mod glow_integration {
builder, builder,
viewport_ui_cb, viewport_ui_cb,
commands, commands,
repaint_delay: _, // ignored - we listend to the repaint callback instead
}, },
) in viewport_output ) in viewport_output
{ {
@@ -1386,7 +1387,7 @@ mod glow_integration {
.egui_ctx .egui_ctx
.set_request_repaint_callback(move |info| { .set_request_repaint_callback(move |info| {
log::trace!("request_repaint_callback: {info:?}"); log::trace!("request_repaint_callback: {info:?}");
let when = Instant::now() + info.after; let when = Instant::now() + info.delay;
let frame_nr = info.current_frame_nr; let frame_nr = info.current_frame_nr;
event_loop_proxy event_loop_proxy
.lock() .lock()
@@ -1989,7 +1990,7 @@ mod wgpu_integration {
.egui_ctx .egui_ctx
.set_request_repaint_callback(move |info| { .set_request_repaint_callback(move |info| {
log::trace!("request_repaint_callback: {info:?}"); log::trace!("request_repaint_callback: {info:?}");
let when = Instant::now() + info.after; let when = Instant::now() + info.delay;
let frame_nr = info.current_frame_nr; let frame_nr = info.current_frame_nr;
event_loop_proxy event_loop_proxy
@@ -2676,6 +2677,7 @@ mod wgpu_integration {
builder, builder,
viewport_ui_cb, viewport_ui_cb,
commands, commands,
repaint_delay: _, // ignored - we listend to the repaint callback instead
}, },
) in viewport_output ) in viewport_output
{ {

View File

@@ -28,7 +28,7 @@ pub struct RequestRepaintInfo {
pub viewport_id: ViewportId, pub viewport_id: ViewportId,
/// Repaint after this duration. If zero, repaint as soon as possible. /// Repaint after this duration. If zero, repaint as soon as possible.
pub after: Duration, pub delay: Duration,
/// The current frame number. /// The current frame number.
/// ///
@@ -71,30 +71,26 @@ impl ContextImpl {
self.request_repaint_after(Duration::ZERO, viewport_id); self.request_repaint_after(Duration::ZERO, viewport_id);
} }
fn request_repaint_after(&mut self, after: Duration, viewport_id: ViewportId) { fn request_repaint_after(&mut self, delay: Duration, viewport_id: ViewportId) {
let mut viewport = self.viewports.entry(viewport_id).or_default(); let mut viewport = self.viewports.entry(viewport_id).or_default();
// Each request results in two repaints, just to give some things time to settle. // Each request results in two repaints, just to give some things time to settle.
// This solves some corner-cases of missing repaints on frame-delayed responses. // This solves some corner-cases of missing repaints on frame-delayed responses.
viewport.repaint.outstanding = 1; viewport.repaint.outstanding = 1;
let current_frame_nr = viewport.repaint.frame_nr;
self.call_repaint_callback(RequestRepaintInfo {
viewport_id,
after,
current_frame_nr,
});
}
fn call_repaint_callback(&mut self, info: RequestRepaintInfo) {
if let Some(callback) = &self.request_repaint_callback { if let Some(callback) = &self.request_repaint_callback {
(callback)(info); // We save some CPU time by only calling the callback if we need to.
} else { // If the new delay is greater or equal to the previous lowest,
eprintln!( // it means we have already called the callback, and don't need to do it again.
"request_repaint_callback is not implemented by egui integration! if delay < viewport.repaint.repaint_delay {
If is your integration you need to call `Context::set_request_repaint_callback`" viewport.repaint.repaint_delay = delay;
);
(callback)(RequestRepaintInfo {
viewport_id,
delay,
current_frame_nr: viewport.repaint.frame_nr,
});
}
} }
} }
@@ -106,10 +102,10 @@ impl ContextImpl {
} }
#[must_use] #[must_use]
fn requested_repaint(&self, viewport_id: &ViewportId) -> bool { fn has_requested_repaint(&self, viewport_id: &ViewportId) -> bool {
self.viewports self.viewports.get(viewport_id).map_or(false, |v| {
.get(viewport_id) 0 < v.repaint.outstanding || v.repaint.repaint_delay < Duration::MAX
.map_or(false, |v| 0 < v.repaint.outstanding) })
} }
} }
@@ -152,11 +148,18 @@ struct ViewportState {
} }
/// Per-viewport state related to repaint scheduling. /// Per-viewport state related to repaint scheduling.
#[derive(Default)]
struct ViewportRepaintInfo { struct ViewportRepaintInfo {
/// Monotonically increasing counter. /// Monotonically increasing counter.
frame_nr: u64, frame_nr: u64,
/// The duration which the backend will poll for new events
/// before forcing another egui update, even if there's no new events.
///
/// Also used to suppress multiple calls to the repaint callback during the same frame.
///
/// This is also returned in [`crate::ViewportOutput`].
repaint_delay: Duration,
/// While positive, keep requesting repaints. Decrement at the start of each frame. /// While positive, keep requesting repaints. Decrement at the start of each frame.
outstanding: u8, outstanding: u8,
@@ -164,6 +167,22 @@ struct ViewportRepaintInfo {
requested_last_frame: bool, requested_last_frame: bool,
} }
impl Default for ViewportRepaintInfo {
fn default() -> Self {
Self {
frame_nr: 0,
// We haven't scheduled a repaint yet.
repaint_delay: Duration::MAX,
// Let's run a couple of frames at the start, because why not.
outstanding: 1,
requested_last_frame: false,
}
}
}
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
#[derive(Default)] #[derive(Default)]
@@ -206,14 +225,19 @@ impl ContextImpl {
self.viewport_stack.push(ids); self.viewport_stack.push(ids);
let viewport = self.viewports.entry(viewport_id).or_default(); let viewport = self.viewports.entry(viewport_id).or_default();
if 0 < viewport.repaint.outstanding { if viewport.repaint.outstanding == 0 {
// We are repainting now, so we can wait a while for the next repaint.
viewport.repaint.repaint_delay = Duration::MAX;
} else {
viewport.repaint.repaint_delay = Duration::ZERO;
viewport.repaint.outstanding -= 1; viewport.repaint.outstanding -= 1;
let current_frame_nr = viewport.repaint.frame_nr; if let Some(callback) = &self.request_repaint_callback {
self.call_repaint_callback(RequestRepaintInfo { (callback)(RequestRepaintInfo {
viewport_id, viewport_id,
after: Duration::ZERO, delay: Duration::ZERO,
current_frame_nr, current_frame_nr: viewport.repaint.frame_nr,
}); });
}
} }
if let Some(new_pixels_per_point) = self.memory.override_pixels_per_point { if let Some(new_pixels_per_point) = self.memory.override_pixels_per_point {
@@ -1222,14 +1246,14 @@ impl Context {
/// Has a repaint been requested for the current viewport? /// Has a repaint been requested for the current viewport?
#[must_use] #[must_use]
pub fn requested_repaint(&self) -> bool { pub fn has_requested_repaint(&self) -> bool {
self.requested_repaint_for(&self.viewport_id()) self.has_requested_repaint_for(&self.viewport_id())
} }
/// Has a repaint been requested for the given viewport? /// Has a repaint been requested for the given viewport?
#[must_use] #[must_use]
pub fn requested_repaint_for(&self, viewport_id: &ViewportId) -> bool { pub fn has_requested_repaint_for(&self, viewport_id: &ViewportId) -> bool {
self.read(|ctx| ctx.requested_repaint(viewport_id)) self.read(|ctx| ctx.has_requested_repaint(viewport_id))
} }
/// For integrations: this callback will be called when an egui user calls [`Self::request_repaint`] or [`Self::request_repaint_after`]. /// For integrations: this callback will be called when an egui user calls [`Self::request_repaint`] or [`Self::request_repaint_after`].
@@ -1590,6 +1614,7 @@ impl ContextImpl {
builder: viewport.builder.clone(), builder: viewport.builder.clone(),
viewport_ui_cb: viewport.viewport_ui_cb.clone(), viewport_ui_cb: viewport.viewport_ui_cb.clone(),
commands, commands,
repaint_delay: viewport.repaint.repaint_delay,
}, },
) )
}) })

View File

@@ -677,6 +677,14 @@ pub struct ViewportOutput {
/// Commands to change the viewport, e.g. window title and size. /// Commands to change the viewport, e.g. window title and size.
pub commands: Vec<ViewportCommand>, pub commands: Vec<ViewportCommand>,
/// Schedulare a repaint of this viewport after this delay.
///
/// It is preferably to instead install a [`Context::set_request_repaint_callback`],
/// but if you haven't, you can use this instead.
///
/// If the duration is zero, schedule a repaint immediately.
pub repaint_delay: std::time::Duration,
} }
impl ViewportOutput { impl ViewportOutput {
@@ -691,12 +699,14 @@ impl ViewportOutput {
builder, builder,
viewport_ui_cb, viewport_ui_cb,
mut commands, mut commands,
repaint_delay,
} = newer; } = newer;
self.ids = ids; self.ids = ids;
self.builder.patch(&builder); self.builder.patch(&builder);
self.viewport_ui_cb = viewport_ui_cb; self.viewport_ui_cb = viewport_ui_cb;
self.commands.append(&mut commands); self.commands.append(&mut commands);
self.repaint_delay = self.repaint_delay.min(repaint_delay);
} }
} }