Save `FrameState` of previous frame (#4761)

By saving the `FrameState` of the previous frame, we make it much more
versatile, and allows us to remove some other double-buffering.
This commit is contained in:
Emil Ernerfeldt 2024-07-03 10:31:02 +02:00 committed by GitHub
parent 1b06c69d01
commit dca552ea48
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 61 additions and 55 deletions

View File

@ -220,18 +220,17 @@ pub struct ViewportState {
pub input: InputState, pub input: InputState,
/// State that is collected during a frame and then cleared /// State that is collected during a frame and then cleared.
pub frame_state: FrameState, pub this_frame: FrameState,
/// The final [`FrameState`] from last frame.
///
/// Only read from.
pub prev_frame: FrameState,
/// Has this viewport been updated this frame? /// Has this viewport been updated this frame?
pub used: bool, pub used: bool,
/// Written to during the frame.
pub widgets_this_frame: WidgetRects,
/// Read
pub widgets_prev_frame: WidgetRects,
/// State related to repaint scheduling. /// State related to repaint scheduling.
repaint: ViewportRepaintInfo, repaint: ViewportRepaintInfo,
@ -451,12 +450,12 @@ impl ContextImpl {
let screen_rect = viewport.input.screen_rect; let screen_rect = viewport.input.screen_rect;
viewport.frame_state.begin_frame(screen_rect); viewport.this_frame.begin_frame(screen_rect);
{ {
let area_order = self.memory.areas().order_map(); let area_order = self.memory.areas().order_map();
let mut layers: Vec<LayerId> = viewport.widgets_prev_frame.layer_ids().collect(); let mut layers: Vec<LayerId> = viewport.prev_frame.widgets.layer_ids().collect();
layers.sort_by(|a, b| { layers.sort_by(|a, b| {
if a.order == b.order { if a.order == b.order {
@ -472,7 +471,7 @@ impl ContextImpl {
let interact_radius = self.memory.options.style.interaction.interact_radius; let interact_radius = self.memory.options.style.interaction.interact_radius;
crate::hit_test::hit_test( crate::hit_test::hit_test(
&viewport.widgets_prev_frame, &viewport.prev_frame.widgets,
&layers, &layers,
&self.memory.layer_transforms, &self.memory.layer_transforms,
pos, pos,
@ -484,7 +483,7 @@ impl ContextImpl {
viewport.interact_widgets = crate::interaction::interact( viewport.interact_widgets = crate::interaction::interact(
&viewport.interact_widgets, &viewport.interact_widgets,
&viewport.widgets_prev_frame, &viewport.prev_frame.widgets,
&viewport.hits, &viewport.hits,
&viewport.input, &viewport.input,
self.memory.interaction_mut(), self.memory.interaction_mut(),
@ -513,7 +512,7 @@ impl ContextImpl {
builder.set_transform(accesskit::Affine::scale(pixels_per_point.into())); builder.set_transform(accesskit::Affine::scale(pixels_per_point.into()));
let mut node_builders = IdMap::default(); let mut node_builders = IdMap::default();
node_builders.insert(id, builder); node_builders.insert(id, builder);
viewport.frame_state.accesskit_state = Some(AccessKitFrameState { viewport.this_frame.accesskit_state = Some(AccessKitFrameState {
node_builders, node_builders,
parent_stack: vec![id], parent_stack: vec![id],
}); });
@ -573,12 +572,7 @@ impl ContextImpl {
#[cfg(feature = "accesskit")] #[cfg(feature = "accesskit")]
fn accesskit_node_builder(&mut self, id: Id) -> &mut accesskit::NodeBuilder { fn accesskit_node_builder(&mut self, id: Id) -> &mut accesskit::NodeBuilder {
let state = self let state = self.viewport().this_frame.accesskit_state.as_mut().unwrap();
.viewport()
.frame_state
.accesskit_state
.as_mut()
.unwrap();
let builders = &mut state.node_builders; let builders = &mut state.node_builders;
if let std::collections::hash_map::Entry::Vacant(entry) = builders.entry(id) { if let std::collections::hash_map::Entry::Vacant(entry) = builders.entry(id) {
entry.insert(Default::default()); entry.insert(Default::default());
@ -879,15 +873,27 @@ impl Context {
} }
/// Read-only access to [`FrameState`]. /// Read-only access to [`FrameState`].
///
/// This is only valid between [`Context::begin_frame`] and [`Context::end_frame`].
#[inline] #[inline]
pub(crate) fn frame_state<R>(&self, reader: impl FnOnce(&FrameState) -> R) -> R { pub(crate) fn frame_state<R>(&self, reader: impl FnOnce(&FrameState) -> R) -> R {
self.write(move |ctx| reader(&ctx.viewport().frame_state)) self.write(move |ctx| reader(&ctx.viewport().this_frame))
} }
/// Read-write access to [`FrameState`]. /// Read-write access to [`FrameState`].
///
/// This is only valid between [`Context::begin_frame`] and [`Context::end_frame`].
#[inline] #[inline]
pub(crate) fn frame_state_mut<R>(&self, writer: impl FnOnce(&mut FrameState) -> R) -> R { pub(crate) fn frame_state_mut<R>(&self, writer: impl FnOnce(&mut FrameState) -> R) -> R {
self.write(move |ctx| writer(&mut ctx.viewport().frame_state)) self.write(move |ctx| writer(&mut ctx.viewport().this_frame))
}
/// Read-only access to the [`FrameState`] from the previous frame.
///
/// This is swapped at the end of each frame.
#[inline]
pub(crate) fn prev_frame_state<R>(&self, reader: impl FnOnce(&FrameState) -> R) -> R {
self.write(move |ctx| reader(&ctx.viewport().prev_frame))
} }
/// Read-only access to [`Fonts`]. /// Read-only access to [`Fonts`].
@ -1033,7 +1039,7 @@ impl Context {
// We add all widgets here, even non-interactive ones, // We add all widgets here, even non-interactive ones,
// because we need this list not only for checking for blocking widgets, // because we need this list not only for checking for blocking widgets,
// but also to know when we have reached the widget we are checking for cover. // but also to know when we have reached the widget we are checking for cover.
viewport.widgets_this_frame.insert(w.layer_id, w); viewport.this_frame.widgets.insert(w.layer_id, w);
if w.sense.focusable { if w.sense.focusable {
ctx.memory.interested_in_focus(w.id); ctx.memory.interested_in_focus(w.id);
@ -1072,9 +1078,10 @@ impl Context {
self.write(|ctx| { self.write(|ctx| {
let viewport = ctx.viewport(); let viewport = ctx.viewport();
viewport viewport
.widgets_this_frame .this_frame
.widgets
.get(id) .get(id)
.or_else(|| viewport.widgets_prev_frame.get(id)) .or_else(|| viewport.prev_frame.widgets.get(id))
.copied() .copied()
}) })
.map(|widget_rect| self.get_response(widget_rect)) .map(|widget_rect| self.get_response(widget_rect))
@ -1098,7 +1105,8 @@ impl Context {
enabled, enabled,
} = widget_rect; } = widget_rect;
let highlighted = self.frame_state(|fs| fs.highlight_this_frame.contains(&id)); // previous frame + "highlight next frame" == "highlight this frame"
let highlighted = self.prev_frame_state(|fs| fs.highlight_next_frame.contains(&id));
let mut res = Response { let mut res = Response {
ctx: self.clone(), ctx: self.clone(),
@ -1219,7 +1227,7 @@ impl Context {
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
self.write(|ctx| { self.write(|ctx| {
if ctx.memory.options.style.debug.show_interactive_widgets { if ctx.memory.options.style.debug.show_interactive_widgets {
ctx.viewport().widgets_this_frame.set_info(id, make_info()); ctx.viewport().this_frame.widgets.set_info(id, make_info());
} }
}); });
@ -1832,7 +1840,7 @@ impl Context {
let paint_widget_id = |id: Id, text: &str, color: Color32| { let paint_widget_id = |id: Id, text: &str, color: Color32| {
if let Some(widget) = if let Some(widget) =
self.write(|ctx| ctx.viewport().widgets_this_frame.get(id).copied()) self.write(|ctx| ctx.viewport().this_frame.widgets.get(id).copied())
{ {
paint_widget(&widget, text, color); paint_widget(&widget, text, color);
} }
@ -1840,7 +1848,7 @@ impl Context {
if self.style().debug.show_interactive_widgets { if self.style().debug.show_interactive_widgets {
// Show all interactive widgets: // Show all interactive widgets:
let rects = self.write(|ctx| ctx.viewport().widgets_this_frame.clone()); let rects = self.write(|ctx| ctx.viewport().this_frame.widgets.clone());
for (layer_id, rects) in rects.layers() { for (layer_id, rects) in rects.layers() {
let painter = Painter::new(self.clone(), *layer_id, Rect::EVERYTHING); let painter = Painter::new(self.clone(), *layer_id, Rect::EVERYTHING);
for rect in rects { for rect in rects {
@ -1878,7 +1886,7 @@ impl Context {
paint_widget_id(id, "contains_pointer", Color32::BLUE); paint_widget_id(id, "contains_pointer", Color32::BLUE);
} }
let widget_rects = self.write(|w| w.viewport().widgets_this_frame.clone()); let widget_rects = self.write(|w| w.viewport().this_frame.widgets.clone());
let mut contains_pointer: Vec<Id> = contains_pointer.iter().copied().collect(); let mut contains_pointer: Vec<Id> = contains_pointer.iter().copied().collect();
contains_pointer.sort_by_key(|&id| { contains_pointer.sort_by_key(|&id| {
@ -1945,7 +1953,7 @@ impl ContextImpl {
viewport.repaint.frame_nr += 1; viewport.repaint.frame_nr += 1;
self.memory.end_frame(&viewport.frame_state.used_ids); self.memory.end_frame(&viewport.this_frame.used_ids);
if let Some(fonts) = self.fonts.get(&pixels_per_point.into()) { if let Some(fonts) = self.fonts.get(&pixels_per_point.into()) {
let tex_mngr = &mut self.tex_manager.0.write(); let tex_mngr = &mut self.tex_manager.0.write();
@ -1982,7 +1990,7 @@ impl ContextImpl {
#[cfg(feature = "accesskit")] #[cfg(feature = "accesskit")]
{ {
crate::profile_scope!("accesskit"); crate::profile_scope!("accesskit");
let state = viewport.frame_state.accesskit_state.take(); let state = viewport.this_frame.accesskit_state.take();
if let Some(state) = state { if let Some(state) = state {
let root_id = crate::accesskit_root_id().accesskit_id(); let root_id = crate::accesskit_root_id().accesskit_id();
let nodes = { let nodes = {
@ -2015,21 +2023,15 @@ impl ContextImpl {
let mut repaint_needed = false; let mut repaint_needed = false;
{ if self.memory.options.repaint_on_widget_change {
if self.memory.options.repaint_on_widget_change { crate::profile_function!("compare-widget-rects");
crate::profile_function!("compare-widget-rects"); if viewport.prev_frame.widgets != viewport.this_frame.widgets {
if viewport.widgets_prev_frame != viewport.widgets_this_frame { repaint_needed = true; // Some widget has moved
repaint_needed = true; // Some widget has moved
}
} }
std::mem::swap(
&mut viewport.widgets_prev_frame,
&mut viewport.widgets_this_frame,
);
viewport.widgets_this_frame.clear();
} }
std::mem::swap(&mut viewport.prev_frame, &mut viewport.this_frame);
if repaint_needed { if repaint_needed {
self.request_repaint(ended_viewport_id, RepaintCause::new()); self.request_repaint(ended_viewport_id, RepaintCause::new());
} else if let Some(delay) = viewport.input.wants_repaint_after() { } else if let Some(delay) = viewport.input.wants_repaint_after() {
@ -2212,7 +2214,7 @@ impl Context {
/// How much space is used by panels and windows. /// How much space is used by panels and windows.
pub fn used_rect(&self) -> Rect { pub fn used_rect(&self) -> Rect {
self.write(|ctx| { self.write(|ctx| {
let mut used = ctx.viewport().frame_state.used_by_panels; let mut used = ctx.viewport().this_frame.used_by_panels;
for (_id, window) in ctx.memory.areas().visible_windows() { for (_id, window) in ctx.memory.areas().visible_windows() {
used = used.union(window.rect()); used = used.union(window.rect());
} }
@ -2879,7 +2881,7 @@ impl Context {
) -> Option<R> { ) -> Option<R> {
self.write(|ctx| { self.write(|ctx| {
ctx.viewport() ctx.viewport()
.frame_state .this_frame
.accesskit_state .accesskit_state
.is_some() .is_some()
.then(|| ctx.accesskit_node_builder(id)) .then(|| ctx.accesskit_node_builder(id))

View File

@ -27,13 +27,18 @@ pub struct AccessKitFrameState {
pub parent_stack: Vec<Id>, pub parent_stack: Vec<Id>,
} }
/// State that is collected during a frame and then cleared. /// State that is collected during a frame, then saved for the next frame,
/// Short-term (single frame) memory. /// and then cleared.
///
/// One per viewport.
#[derive(Clone)] #[derive(Clone)]
pub struct FrameState { pub struct FrameState {
/// All [`Id`]s that were used this frame. /// All [`Id`]s that were used this frame.
pub used_ids: IdMap<Rect>, pub used_ids: IdMap<Rect>,
/// All widgets produced this frame.
pub widgets: WidgetRects,
/// Starts off as the `screen_rect`, shrinks as panels are added. /// Starts off as the `screen_rect`, shrinks as panels are added.
/// The [`CentralPanel`] does not change this. /// The [`CentralPanel`] does not change this.
/// This is the area available to Window's. /// This is the area available to Window's.
@ -68,10 +73,7 @@ pub struct FrameState {
#[cfg(feature = "accesskit")] #[cfg(feature = "accesskit")]
pub accesskit_state: Option<AccessKitFrameState>, pub accesskit_state: Option<AccessKitFrameState>,
/// Highlight these widgets this next frame. Read from this. /// Highlight these widgets the next frame.
pub highlight_this_frame: IdSet,
/// Highlight these widgets the next frame. Write to this.
pub highlight_next_frame: IdSet, pub highlight_next_frame: IdSet,
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
@ -82,6 +84,7 @@ impl Default for FrameState {
fn default() -> Self { fn default() -> Self {
Self { Self {
used_ids: Default::default(), used_ids: Default::default(),
widgets: Default::default(),
available_rect: Rect::NAN, available_rect: Rect::NAN,
unused_rect: Rect::NAN, unused_rect: Rect::NAN,
used_by_panels: Rect::NAN, used_by_panels: Rect::NAN,
@ -90,7 +93,6 @@ impl Default for FrameState {
scroll_delta: Vec2::default(), scroll_delta: Vec2::default(),
#[cfg(feature = "accesskit")] #[cfg(feature = "accesskit")]
accesskit_state: None, accesskit_state: None,
highlight_this_frame: Default::default(),
highlight_next_frame: Default::default(), highlight_next_frame: Default::default(),
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
@ -104,6 +106,7 @@ impl FrameState {
crate::profile_function!(); crate::profile_function!();
let Self { let Self {
used_ids, used_ids,
widgets,
available_rect, available_rect,
unused_rect, unused_rect,
used_by_panels, used_by_panels,
@ -112,7 +115,6 @@ impl FrameState {
scroll_delta, scroll_delta,
#[cfg(feature = "accesskit")] #[cfg(feature = "accesskit")]
accesskit_state, accesskit_state,
highlight_this_frame,
highlight_next_frame, highlight_next_frame,
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
@ -120,6 +122,7 @@ impl FrameState {
} = self; } = self;
used_ids.clear(); used_ids.clear();
widgets.clear();
*available_rect = screen_rect; *available_rect = screen_rect;
*unused_rect = screen_rect; *unused_rect = screen_rect;
*used_by_panels = Rect::NOTHING; *used_by_panels = Rect::NOTHING;
@ -137,7 +140,7 @@ impl FrameState {
*accesskit_state = None; *accesskit_state = None;
} }
*highlight_this_frame = std::mem::take(highlight_next_frame); highlight_next_frame.clear();
} }
/// How much space is still available after panels has been added. /// How much space is still available after panels has been added.

View File

@ -609,7 +609,8 @@ impl Response {
let layer_id = LayerId::new(Order::Tooltip, tooltip_id); let layer_id = LayerId::new(Order::Tooltip, tooltip_id);
let tooltip_has_interactive_widget = self.ctx.viewport(|vp| { let tooltip_has_interactive_widget = self.ctx.viewport(|vp| {
vp.widgets_prev_frame vp.prev_frame
.widgets
.get_layer(layer_id) .get_layer(layer_id)
.any(|w| w.sense.interactive()) .any(|w| w.sense.interactive())
}); });