mirror of
https://github.com/emilk/egui.git
synced 2026-09-01 14:20:04 -04:00
big refractor
egui: * removed FullOutput::repaint_after * now for redraw only request_repaint_callback is used! * now on every Context::request_repaint() will repaint only once * exposed Context::requested_repaint and Context::requested_repaint_last_frame eframe: * now event result is returned as EventResult insted of Vec<EventResult> * fix to many redraw requests ----: * fix egui not waking when a repaint was from other thread * now every thing feels more responsive!
This commit is contained in:
@@ -54,42 +54,18 @@ impl Default for WrappedTextureManager {
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
/// Logic related to repainting the ui.
|
||||
#[derive(Default)]
|
||||
struct Repaint {
|
||||
/// The current frame number.
|
||||
///
|
||||
/// Incremented at the end of each frame.
|
||||
viewports_frame_nr: HashMap<ViewportId, u64>,
|
||||
|
||||
/// The duration 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.
|
||||
pub repaint_after: HashMap<ViewportId, std::time::Duration>,
|
||||
|
||||
/// While positive, keep requesting repaints. Decrement at the end of each frame.
|
||||
repaint_requests: HashMap<ViewportId, u32>,
|
||||
repaint_request: HashMap<ViewportId, bool>,
|
||||
request_repaint_callback: Option<Box<dyn Fn(RequestRepaintInfo) + Send + Sync>>,
|
||||
|
||||
requested_repaint_last_frame: bool,
|
||||
}
|
||||
|
||||
impl Default for Repaint {
|
||||
fn default() -> Self {
|
||||
let mut repaint_after = HashMap::default();
|
||||
|
||||
// Start with painting an extra frame to compensate for some widgets
|
||||
// that take two frames before they "settle":
|
||||
repaint_after.insert(ViewportId::MAIN, std::time::Duration::from_millis(100));
|
||||
let mut repaint_requests = HashMap::default();
|
||||
repaint_requests.insert(ViewportId::MAIN, 1);
|
||||
Self {
|
||||
viewports_frame_nr: HashMap::default(),
|
||||
repaint_after,
|
||||
repaint_requests,
|
||||
request_repaint_callback: None,
|
||||
requested_repaint_last_frame: false,
|
||||
}
|
||||
}
|
||||
requested_repaint_last_frame: HashMap<ViewportId, bool>,
|
||||
}
|
||||
|
||||
impl Repaint {
|
||||
@@ -99,76 +75,53 @@ impl Repaint {
|
||||
|
||||
fn request_repaint_after(&mut self, after: std::time::Duration, viewport_id: ViewportId) {
|
||||
if after == std::time::Duration::ZERO {
|
||||
// Do a few extra frames to let things settle.
|
||||
// This is a bit of a hack, and we don't support it for `repaint_after` callbacks yet.
|
||||
self.repaint_requests.insert(viewport_id, 2);
|
||||
// This will only work if the current viewport is drawing
|
||||
self.repaint_request.insert(viewport_id, true);
|
||||
}
|
||||
|
||||
// We only re-call the callback if we get a lower duration,
|
||||
// otherwise it's already been covered by the previous callback.
|
||||
if after
|
||||
< self
|
||||
.repaint_after
|
||||
.get(&viewport_id)
|
||||
.copied()
|
||||
.unwrap_or(std::time::Duration::MAX)
|
||||
{
|
||||
self.repaint_after.insert(viewport_id, after);
|
||||
|
||||
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);
|
||||
}
|
||||
// This will work always
|
||||
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);
|
||||
} else {
|
||||
log::warn!("request_repaint_callback is not implemented by egui integration!\nIf is your integration you need to call `Context::set_request_repaint_callback`");
|
||||
}
|
||||
}
|
||||
|
||||
fn start_frame(&mut self, viewport_id: ViewportId) {
|
||||
// We are repainting; no need to reschedule a repaint unless the user asks for it again.
|
||||
self.repaint_after.remove(&viewport_id);
|
||||
let request = self.repaint_request.entry(viewport_id).or_default();
|
||||
self.requested_repaint_last_frame
|
||||
.insert(viewport_id, *request);
|
||||
*request = false;
|
||||
}
|
||||
|
||||
// returns how long to wait until repaint
|
||||
fn end_frame(
|
||||
&mut self,
|
||||
viewport_id: ViewportId,
|
||||
viewports: &[ViewportId],
|
||||
) -> HashMap<ViewportId, std::time::Duration> {
|
||||
// if repaint_requests is greater than zero. just set the duration to zero for immediate
|
||||
// repaint. if there's no repaint requests, then we can use the actual repaint_after instead.
|
||||
let repaint_after = if self
|
||||
.repaint_requests
|
||||
.get(&viewport_id)
|
||||
.copied()
|
||||
.unwrap_or(0)
|
||||
> 0
|
||||
{
|
||||
if let Some(requests) = self.repaint_requests.get_mut(&viewport_id) {
|
||||
*requests -= 1;
|
||||
}
|
||||
|
||||
std::time::Duration::ZERO
|
||||
} else {
|
||||
self.repaint_after
|
||||
.get(&viewport_id)
|
||||
.copied()
|
||||
.unwrap_or(std::time::Duration::MAX)
|
||||
};
|
||||
self.repaint_after.insert(viewport_id, repaint_after);
|
||||
|
||||
self.requested_repaint_last_frame = repaint_after.is_zero();
|
||||
// returns what is needed to be repainted
|
||||
fn end_frame(&mut self, viewport_id: ViewportId, viewports: &[ViewportId]) {
|
||||
*self.viewports_frame_nr.entry(viewport_id).or_default() += 1;
|
||||
|
||||
self.repaint_after.retain(|id, _| viewports.contains(id));
|
||||
self.requested_repaint_last_frame
|
||||
.retain(|id, _| viewports.contains(id));
|
||||
self.viewports_frame_nr
|
||||
.retain(|id, _| viewports.contains(id));
|
||||
self.repaint_requests
|
||||
.retain(|id, repaints| viewports.contains(id) && *repaints != 0);
|
||||
self.repaint_request.retain(|id, _| viewports.contains(id));
|
||||
}
|
||||
|
||||
self.repaint_after.clone()
|
||||
fn requested_repaint_last_frame(&self, viewport_id: &ViewportId) -> bool {
|
||||
self.requested_repaint_last_frame
|
||||
.get(viewport_id)
|
||||
.copied()
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
fn requested_repaint(&self, viewport_id: &ViewportId) -> bool {
|
||||
self.repaint_request
|
||||
.get(viewport_id)
|
||||
.copied()
|
||||
.unwrap_or_default()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -283,11 +236,10 @@ impl ContextImpl {
|
||||
pair.this,
|
||||
);
|
||||
|
||||
let input = self
|
||||
.input
|
||||
.remove(&pair)
|
||||
.unwrap_or_default()
|
||||
.begin_frame(new_raw_input, self.repaint.requested_repaint_last_frame);
|
||||
let input = self.input.remove(&pair).unwrap_or_default().begin_frame(
|
||||
new_raw_input,
|
||||
self.repaint.requested_repaint_last_frame(&pair),
|
||||
);
|
||||
self.input.insert(pair.this, input);
|
||||
|
||||
self.frame_state
|
||||
@@ -1279,6 +1231,26 @@ impl Context {
|
||||
self.write(|ctx| ctx.repaint.request_repaint_after(duration, id));
|
||||
}
|
||||
|
||||
/// With this you can know if the application stal before
|
||||
pub fn requested_repaint_last_frame(&self) -> bool {
|
||||
self.requested_repaint_last_frame_for(&self.viewport_id())
|
||||
}
|
||||
|
||||
/// With this you can know if the viewport stal before
|
||||
pub fn requested_repaint_last_frame_for(&self, viewport_id: &ViewportId) -> bool {
|
||||
self.read(|ctx| ctx.repaint.requested_repaint_last_frame(viewport_id))
|
||||
}
|
||||
|
||||
/// With this you will know if the application will redraw
|
||||
pub fn requested_repaint(&self) -> bool {
|
||||
self.requested_repaint_for(&self.viewport_id())
|
||||
}
|
||||
|
||||
/// With this you will know if the viewport will redraw
|
||||
pub fn requested_repaint_for(&self, viewport_id: &ViewportId) -> bool {
|
||||
self.read(|ctx| ctx.repaint.requested_repaint(viewport_id))
|
||||
}
|
||||
|
||||
/// For integrations: this callback will be called when an egui user calls [`Self::request_repaint`].
|
||||
///
|
||||
/// This lets you wake up a sleeping UI thread.
|
||||
@@ -1665,12 +1637,10 @@ impl Context {
|
||||
});
|
||||
}
|
||||
|
||||
let repaint_after =
|
||||
self.write(|ctx| ctx.repaint.end_frame(viewport_id, &avalibile_viewports));
|
||||
self.write(|ctx| ctx.repaint.end_frame(viewport_id, &avalibile_viewports));
|
||||
|
||||
FullOutput {
|
||||
platform_output,
|
||||
repaint_after,
|
||||
textures_delta,
|
||||
shapes,
|
||||
viewports,
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
//! All the data egui returns to the backend at the end of each frame.
|
||||
|
||||
use ahash::HashMap;
|
||||
|
||||
use crate::ViewportId;
|
||||
use crate::{ViewportCommand, ViewportOutput, WidgetType};
|
||||
|
||||
@@ -13,16 +11,6 @@ pub struct FullOutput {
|
||||
/// Non-rendering related output.
|
||||
pub platform_output: PlatformOutput,
|
||||
|
||||
/// If `Duration::is_zero()`, egui is requesting immediate repaint (i.e. on the next frame).
|
||||
///
|
||||
/// This happens for instance when there is an animation, or if a user has called `Context::request_repaint()`.
|
||||
///
|
||||
/// If `Duration` is greater than zero, egui wants to be repainted at or before the specified
|
||||
/// duration elapses. when in reactive mode, egui spends forever waiting for input and only then,
|
||||
/// will it repaint itself. this can be used to make sure that backend will only wait for a
|
||||
/// specified amount of time, and repaint egui without any new input.
|
||||
pub repaint_after: HashMap<ViewportId, std::time::Duration>,
|
||||
|
||||
/// Texture changes since last frame (including the font texture).
|
||||
///
|
||||
/// The backend needs to apply [`crate::TexturesDelta::set`] _before_ painting,
|
||||
@@ -44,7 +32,6 @@ impl FullOutput {
|
||||
pub fn append(&mut self, newer: Self) {
|
||||
let Self {
|
||||
platform_output,
|
||||
repaint_after,
|
||||
textures_delta,
|
||||
shapes,
|
||||
mut viewports,
|
||||
@@ -52,7 +39,6 @@ impl FullOutput {
|
||||
} = newer;
|
||||
|
||||
self.platform_output.append(platform_output);
|
||||
self.repaint_after = repaint_after; // if the last frame doesn't need a repaint, then we don't need to repaint
|
||||
self.textures_delta.append(textures_delta);
|
||||
self.shapes = shapes; // Only paint the latest
|
||||
self.viewports.append(&mut viewports);
|
||||
|
||||
Reference in New Issue
Block a user