mirror of
https://github.com/emilk/egui.git
synced 2026-08-31 22:00:03 -04:00
Clean up request-repaint logic: always request at least two repaints
This commit is contained in:
@@ -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);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user