1
0
mirror of https://github.com/emilk/egui.git synced 2026-09-01 06:10:06 -04:00

Clean up request-repaint logic: always request at least two repaints

This commit is contained in:
Emil Ernerfeldt
2023-11-14 19:17:32 +01:00
parent bfaef3a497
commit 09a728ba34

View File

@@ -1,34 +1,40 @@
#![warn(missing_docs)] // Let's keep `Context` well-documented. #![warn(missing_docs)] // Let's keep `Context` well-documented.
use std::sync::Arc; use std::{borrow::Cow, cell::RefCell, sync::Arc, time::Duration};
use std::{borrow::Cow, cell::RefCell};
use crate::load::Bytes;
use crate::load::SizedTexture;
use crate::{
animation_manager::AnimationManager, data::output::PlatformOutput, frame_state::FrameState,
input_state::*, layers::GraphicLayers, load::Loaders, memory::Options, os::OperatingSystem,
output::FullOutput, util::IdTypeMap, TextureHandle, ViewportCommand, *,
};
use ahash::HashMap; use ahash::HashMap;
use epaint::{mutex::*, stats::*, text::Fonts, TessellationOptions, *}; use epaint::{mutex::*, stats::*, text::Fonts, TessellationOptions, *};
use crate::{
animation_manager::AnimationManager,
data::output::PlatformOutput,
frame_state::FrameState,
input_state::*,
layers::GraphicLayers,
load::{Bytes, Loaders, SizedTexture},
memory::Options,
os::OperatingSystem,
output::FullOutput,
util::IdTypeMap,
TextureHandle, ViewportCommand, *,
};
/// Information given to the backend about when it is time to repaint the ui. /// Information given to the backend about when it is time to repaint the ui.
/// ///
/// This is given in the callback set by [`Context::set_request_repaint_callback`]. /// This is given in the callback set by [`Context::set_request_repaint_callback`].
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy, Debug)]
pub struct RequestRepaintInfo { pub struct RequestRepaintInfo {
/// This is used to specify what viewport that should repaint.
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: std::time::Duration, pub after: Duration,
/// The current frame number. /// The current frame number.
/// ///
/// This can be compared to [`Context::frame_nr`] to see if we've already /// This can be compared to [`Context::frame_nr`] to see if we've already
/// triggered the painting of the next frame. /// triggered the painting of the next frame.
pub current_frame_nr: u64, pub current_frame_nr: u64,
/// This is used to specify what viewport that should repaint.
pub viewport_id: ViewportId,
} }
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
@@ -56,33 +62,43 @@ impl Default for WrappedTextureManager {
/// Logic related to repainting the ui. /// Logic related to repainting the ui.
#[derive(Default)] #[derive(Default)]
struct Repaint { struct Repaint {
/// The current frame number. request_repaint_callback: Option<Box<dyn Fn(RequestRepaintInfo) + Send + Sync>>,
/// viewports: ViewportIdMap<ViewportRepaintInfo>,
/// Incremented at the end of each frame. }
viewports_frame_nr: ViewportIdMap<u64>,
#[derive(Default)]
struct ViewportRepaintInfo {
frame_nr: u64,
/// While positive, keep requesting repaints. Decrement at the start of each frame. /// While positive, keep requesting repaints. Decrement at the start of each frame.
repaint_request: ViewportIdMap<u8>, outstanding_repaints: u8,
request_repaint_callback: Option<Box<dyn Fn(RequestRepaintInfo) + Send + Sync>>,
requested_repaint_last_frame: ViewportIdMap<bool>, requested_repaint_last_frame: bool,
} }
impl Repaint { impl Repaint {
fn request_repaint(&mut self, viewport_id: ViewportId) { fn request_repaint(&mut self, viewport_id: ViewportId) {
self.request_repaint_after(std::time::Duration::ZERO, viewport_id); self.request_repaint_after(Duration::ZERO, viewport_id);
} }
fn request_repaint_after(&mut self, after: std::time::Duration, viewport_id: ViewportId) { fn request_repaint_after(&mut self, after: Duration, viewport_id: ViewportId) {
let requests = self.repaint_request.entry(viewport_id).or_default(); let mut viewport = self.viewports.entry(viewport_id).or_default();
*requests = 1.max(*requests);
// 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.
viewport.outstanding_repaints = 1;
let current_frame_nr = viewport.frame_nr;
self.call_callback(RequestRepaintInfo {
viewport_id,
after,
current_frame_nr,
});
}
fn call_callback(&mut self, info: RequestRepaintInfo) {
if let Some(callback) = &self.request_repaint_callback { if let Some(callback) = &self.request_repaint_callback {
let info = RequestRepaintInfo {
after,
current_frame_nr: *self.viewports_frame_nr.entry(viewport_id).or_default(),
viewport_id,
};
(callback)(info); (callback)(info);
} else { } else {
eprintln!( eprintln!(
@@ -92,47 +108,40 @@ impl Repaint {
} }
} }
fn request_repaint_settle(&mut self, viewport_id: ViewportId) {
self.repaint_request.insert(viewport_id, 2);
self.request_repaint(viewport_id);
}
fn start_frame(&mut self, viewport_id: ViewportId) { fn start_frame(&mut self, viewport_id: ViewportId) {
let request = self.repaint_request.entry(viewport_id).or_default(); let mut viewport = self.viewports.entry(viewport_id).or_default();
self.requested_repaint_last_frame
.insert(viewport_id, *request > 0); if 0 < viewport.outstanding_repaints {
if *request > 0 { viewport.outstanding_repaints -= 1;
*request -= 1;
if *request > 0 { let current_frame_nr = viewport.frame_nr;
self.request_repaint(viewport_id);
} self.call_callback(RequestRepaintInfo {
viewport_id,
after: Duration::ZERO,
current_frame_nr,
});
} }
} }
// returns what is needed to be repainted // returns what is needed to be repainted
fn end_frame(&mut self, viewport_id: ViewportId, viewports: &ViewportIdSet) { fn end_frame(&mut self, viewport_id: ViewportId, all_viewports: &ViewportIdSet) {
*self.viewports_frame_nr.entry(viewport_id).or_default() += 1; let mut viewport = self.viewports.entry(viewport_id).or_default();
viewport.frame_nr += 1;
self.requested_repaint_last_frame self.viewports.retain(|id, _| all_viewports.contains(id));
.retain(|id, _| viewports.contains(id));
self.viewports_frame_nr
.retain(|id, _| viewports.contains(id));
self.repaint_request.retain(|id, _| viewports.contains(id));
} }
fn requested_repaint_last_frame(&self, viewport_id: &ViewportId) -> bool { fn requested_repaint_last_frame(&self, viewport_id: &ViewportId) -> bool {
self.requested_repaint_last_frame self.viewports
.get(viewport_id) .get(viewport_id)
.copied() .map_or(false, |v| v.requested_repaint_last_frame)
.unwrap_or_default()
} }
fn requested_repaint(&self, viewport_id: &ViewportId) -> bool { fn requested_repaint(&self, viewport_id: &ViewportId) -> bool {
self.repaint_request self.viewports
.get(viewport_id) .get(viewport_id)
.copied() .map_or(false, |v| 0 < v.outstanding_repaints)
.unwrap_or_default()
> 0
} }
} }
@@ -1108,13 +1117,7 @@ impl Context {
/// ///
/// Between calls to [`Self::run`], this is the frame number of the coming frame. /// Between calls to [`Self::run`], this is the frame number of the coming frame.
pub fn frame_nr_for(&self, id: ViewportId) -> u64 { pub fn frame_nr_for(&self, id: ViewportId) -> u64 {
self.read(|ctx| { self.read(|ctx| ctx.repaint.viewports.get(&id).map_or(0, |v| v.frame_nr))
ctx.repaint
.viewports_frame_nr
.get(&id)
.copied()
.unwrap_or_default()
})
} }
/// Call this if there is need to repaint the UI, i.e. if you are showing an animation. /// Call this if there is need to repaint the UI, i.e. if you are showing an animation.
@@ -1173,7 +1176,7 @@ impl Context {
/// during app idle time where we are not receiving any new input events. /// during app idle time where we are not receiving any new input events.
/// ///
/// This repaints the current viewport /// This repaints the current viewport
pub fn request_repaint_after(&self, duration: std::time::Duration) { pub fn request_repaint_after(&self, duration: Duration) {
self.request_repaint_after_for(duration, self.viewport_id()); self.request_repaint_after_for(duration, self.viewport_id());
} }
@@ -1205,26 +1208,30 @@ impl Context {
/// during app idle time where we are not receiving any new input events. /// during app idle time where we are not receiving any new input events.
/// ///
/// This repaints the specified viewport /// This repaints the specified viewport
pub fn request_repaint_after_for(&self, duration: std::time::Duration, id: ViewportId) { pub fn request_repaint_after_for(&self, duration: Duration, id: ViewportId) {
self.write(|ctx| ctx.repaint.request_repaint_after(duration, id)); self.write(|ctx| ctx.repaint.request_repaint_after(duration, id));
} }
/// With this you can know if the application stal before /// Was a repaint requested last frame for the current viewport?
#[must_use]
pub fn requested_repaint_last_frame(&self) -> bool { pub fn requested_repaint_last_frame(&self) -> bool {
self.requested_repaint_last_frame_for(&self.viewport_id()) self.requested_repaint_last_frame_for(&self.viewport_id())
} }
/// With this you can know if the viewport stal before /// Was a repaint requested last frame for the given viewport?
#[must_use]
pub fn requested_repaint_last_frame_for(&self, viewport_id: &ViewportId) -> bool { pub fn requested_repaint_last_frame_for(&self, viewport_id: &ViewportId) -> bool {
self.read(|ctx| ctx.repaint.requested_repaint_last_frame(viewport_id)) self.read(|ctx| ctx.repaint.requested_repaint_last_frame(viewport_id))
} }
/// With this you will know if the application will redraw /// Has a repaint been requested for the current viewport?
#[must_use]
pub fn requested_repaint(&self) -> bool { pub fn requested_repaint(&self) -> bool {
self.requested_repaint_for(&self.viewport_id()) self.requested_repaint_for(&self.viewport_id())
} }
/// With this you will know if the viewport will redraw /// Has a repaint been requested for the given viewport?
#[must_use]
pub fn requested_repaint_for(&self, viewport_id: &ViewportId) -> bool { pub fn requested_repaint_for(&self, viewport_id: &ViewportId) -> bool {
self.read(|ctx| ctx.repaint.requested_repaint(viewport_id)) self.read(|ctx| ctx.repaint.requested_repaint(viewport_id))
} }
@@ -1318,9 +1325,9 @@ impl Context {
if pixels_per_point != self.pixels_per_point() { if pixels_per_point != self.pixels_per_point() {
self.write(|ctx| { self.write(|ctx| {
for viewport in ctx.viewports.values() { for viewport in ctx.viewports.values() {
ctx.repaint.request_repaint_settle(viewport.ids.this); ctx.repaint.request_repaint(viewport.ids.this);
} }
ctx.repaint.request_repaint_settle(ViewportId::ROOT); ctx.repaint.request_repaint(ViewportId::ROOT);
ctx.memory.override_pixels_per_point = Some(pixels_per_point); ctx.memory.override_pixels_per_point = Some(pixels_per_point);
}); });
} }