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:
parent
1b06c69d01
commit
dca552ea48
|
|
@ -220,18 +220,17 @@ pub struct ViewportState {
|
|||
|
||||
pub input: InputState,
|
||||
|
||||
/// State that is collected during a frame and then cleared
|
||||
pub frame_state: FrameState,
|
||||
/// State that is collected during a frame and then cleared.
|
||||
pub this_frame: FrameState,
|
||||
|
||||
/// The final [`FrameState`] from last frame.
|
||||
///
|
||||
/// Only read from.
|
||||
pub prev_frame: FrameState,
|
||||
|
||||
/// Has this viewport been updated this frame?
|
||||
pub used: bool,
|
||||
|
||||
/// Written to during the frame.
|
||||
pub widgets_this_frame: WidgetRects,
|
||||
|
||||
/// Read
|
||||
pub widgets_prev_frame: WidgetRects,
|
||||
|
||||
/// State related to repaint scheduling.
|
||||
repaint: ViewportRepaintInfo,
|
||||
|
||||
|
|
@ -451,12 +450,12 @@ impl ContextImpl {
|
|||
|
||||
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 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| {
|
||||
if a.order == b.order {
|
||||
|
|
@ -472,7 +471,7 @@ impl ContextImpl {
|
|||
let interact_radius = self.memory.options.style.interaction.interact_radius;
|
||||
|
||||
crate::hit_test::hit_test(
|
||||
&viewport.widgets_prev_frame,
|
||||
&viewport.prev_frame.widgets,
|
||||
&layers,
|
||||
&self.memory.layer_transforms,
|
||||
pos,
|
||||
|
|
@ -484,7 +483,7 @@ impl ContextImpl {
|
|||
|
||||
viewport.interact_widgets = crate::interaction::interact(
|
||||
&viewport.interact_widgets,
|
||||
&viewport.widgets_prev_frame,
|
||||
&viewport.prev_frame.widgets,
|
||||
&viewport.hits,
|
||||
&viewport.input,
|
||||
self.memory.interaction_mut(),
|
||||
|
|
@ -513,7 +512,7 @@ impl ContextImpl {
|
|||
builder.set_transform(accesskit::Affine::scale(pixels_per_point.into()));
|
||||
let mut node_builders = IdMap::default();
|
||||
node_builders.insert(id, builder);
|
||||
viewport.frame_state.accesskit_state = Some(AccessKitFrameState {
|
||||
viewport.this_frame.accesskit_state = Some(AccessKitFrameState {
|
||||
node_builders,
|
||||
parent_stack: vec![id],
|
||||
});
|
||||
|
|
@ -573,12 +572,7 @@ impl ContextImpl {
|
|||
|
||||
#[cfg(feature = "accesskit")]
|
||||
fn accesskit_node_builder(&mut self, id: Id) -> &mut accesskit::NodeBuilder {
|
||||
let state = self
|
||||
.viewport()
|
||||
.frame_state
|
||||
.accesskit_state
|
||||
.as_mut()
|
||||
.unwrap();
|
||||
let state = self.viewport().this_frame.accesskit_state.as_mut().unwrap();
|
||||
let builders = &mut state.node_builders;
|
||||
if let std::collections::hash_map::Entry::Vacant(entry) = builders.entry(id) {
|
||||
entry.insert(Default::default());
|
||||
|
|
@ -879,15 +873,27 @@ impl Context {
|
|||
}
|
||||
|
||||
/// Read-only access to [`FrameState`].
|
||||
///
|
||||
/// This is only valid between [`Context::begin_frame`] and [`Context::end_frame`].
|
||||
#[inline]
|
||||
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`].
|
||||
///
|
||||
/// This is only valid between [`Context::begin_frame`] and [`Context::end_frame`].
|
||||
#[inline]
|
||||
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`].
|
||||
|
|
@ -1033,7 +1039,7 @@ impl Context {
|
|||
// We add all widgets here, even non-interactive ones,
|
||||
// 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.
|
||||
viewport.widgets_this_frame.insert(w.layer_id, w);
|
||||
viewport.this_frame.widgets.insert(w.layer_id, w);
|
||||
|
||||
if w.sense.focusable {
|
||||
ctx.memory.interested_in_focus(w.id);
|
||||
|
|
@ -1072,9 +1078,10 @@ impl Context {
|
|||
self.write(|ctx| {
|
||||
let viewport = ctx.viewport();
|
||||
viewport
|
||||
.widgets_this_frame
|
||||
.this_frame
|
||||
.widgets
|
||||
.get(id)
|
||||
.or_else(|| viewport.widgets_prev_frame.get(id))
|
||||
.or_else(|| viewport.prev_frame.widgets.get(id))
|
||||
.copied()
|
||||
})
|
||||
.map(|widget_rect| self.get_response(widget_rect))
|
||||
|
|
@ -1098,7 +1105,8 @@ impl Context {
|
|||
enabled,
|
||||
} = 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 {
|
||||
ctx: self.clone(),
|
||||
|
|
@ -1219,7 +1227,7 @@ impl Context {
|
|||
#[cfg(debug_assertions)]
|
||||
self.write(|ctx| {
|
||||
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| {
|
||||
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);
|
||||
}
|
||||
|
|
@ -1840,7 +1848,7 @@ impl Context {
|
|||
|
||||
if self.style().debug.show_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() {
|
||||
let painter = Painter::new(self.clone(), *layer_id, Rect::EVERYTHING);
|
||||
for rect in rects {
|
||||
|
|
@ -1878,7 +1886,7 @@ impl Context {
|
|||
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();
|
||||
contains_pointer.sort_by_key(|&id| {
|
||||
|
|
@ -1945,7 +1953,7 @@ impl ContextImpl {
|
|||
|
||||
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()) {
|
||||
let tex_mngr = &mut self.tex_manager.0.write();
|
||||
|
|
@ -1982,7 +1990,7 @@ impl ContextImpl {
|
|||
#[cfg(feature = "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 {
|
||||
let root_id = crate::accesskit_root_id().accesskit_id();
|
||||
let nodes = {
|
||||
|
|
@ -2015,21 +2023,15 @@ impl ContextImpl {
|
|||
|
||||
let mut repaint_needed = false;
|
||||
|
||||
{
|
||||
if self.memory.options.repaint_on_widget_change {
|
||||
crate::profile_function!("compare-widget-rects");
|
||||
if viewport.widgets_prev_frame != viewport.widgets_this_frame {
|
||||
repaint_needed = true; // Some widget has moved
|
||||
}
|
||||
if self.memory.options.repaint_on_widget_change {
|
||||
crate::profile_function!("compare-widget-rects");
|
||||
if viewport.prev_frame.widgets != viewport.this_frame.widgets {
|
||||
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 {
|
||||
self.request_repaint(ended_viewport_id, RepaintCause::new());
|
||||
} 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.
|
||||
pub fn used_rect(&self) -> Rect {
|
||||
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() {
|
||||
used = used.union(window.rect());
|
||||
}
|
||||
|
|
@ -2879,7 +2881,7 @@ impl Context {
|
|||
) -> Option<R> {
|
||||
self.write(|ctx| {
|
||||
ctx.viewport()
|
||||
.frame_state
|
||||
.this_frame
|
||||
.accesskit_state
|
||||
.is_some()
|
||||
.then(|| ctx.accesskit_node_builder(id))
|
||||
|
|
|
|||
|
|
@ -27,13 +27,18 @@ pub struct AccessKitFrameState {
|
|||
pub parent_stack: Vec<Id>,
|
||||
}
|
||||
|
||||
/// State that is collected during a frame and then cleared.
|
||||
/// Short-term (single frame) memory.
|
||||
/// State that is collected during a frame, then saved for the next frame,
|
||||
/// and then cleared.
|
||||
///
|
||||
/// One per viewport.
|
||||
#[derive(Clone)]
|
||||
pub struct FrameState {
|
||||
/// All [`Id`]s that were used this frame.
|
||||
pub used_ids: IdMap<Rect>,
|
||||
|
||||
/// All widgets produced this frame.
|
||||
pub widgets: WidgetRects,
|
||||
|
||||
/// Starts off as the `screen_rect`, shrinks as panels are added.
|
||||
/// The [`CentralPanel`] does not change this.
|
||||
/// This is the area available to Window's.
|
||||
|
|
@ -68,10 +73,7 @@ pub struct FrameState {
|
|||
#[cfg(feature = "accesskit")]
|
||||
pub accesskit_state: Option<AccessKitFrameState>,
|
||||
|
||||
/// Highlight these widgets this next frame. Read from this.
|
||||
pub highlight_this_frame: IdSet,
|
||||
|
||||
/// Highlight these widgets the next frame. Write to this.
|
||||
/// Highlight these widgets the next frame.
|
||||
pub highlight_next_frame: IdSet,
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
|
|
@ -82,6 +84,7 @@ impl Default for FrameState {
|
|||
fn default() -> Self {
|
||||
Self {
|
||||
used_ids: Default::default(),
|
||||
widgets: Default::default(),
|
||||
available_rect: Rect::NAN,
|
||||
unused_rect: Rect::NAN,
|
||||
used_by_panels: Rect::NAN,
|
||||
|
|
@ -90,7 +93,6 @@ impl Default for FrameState {
|
|||
scroll_delta: Vec2::default(),
|
||||
#[cfg(feature = "accesskit")]
|
||||
accesskit_state: None,
|
||||
highlight_this_frame: Default::default(),
|
||||
highlight_next_frame: Default::default(),
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
|
|
@ -104,6 +106,7 @@ impl FrameState {
|
|||
crate::profile_function!();
|
||||
let Self {
|
||||
used_ids,
|
||||
widgets,
|
||||
available_rect,
|
||||
unused_rect,
|
||||
used_by_panels,
|
||||
|
|
@ -112,7 +115,6 @@ impl FrameState {
|
|||
scroll_delta,
|
||||
#[cfg(feature = "accesskit")]
|
||||
accesskit_state,
|
||||
highlight_this_frame,
|
||||
highlight_next_frame,
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
|
|
@ -120,6 +122,7 @@ impl FrameState {
|
|||
} = self;
|
||||
|
||||
used_ids.clear();
|
||||
widgets.clear();
|
||||
*available_rect = screen_rect;
|
||||
*unused_rect = screen_rect;
|
||||
*used_by_panels = Rect::NOTHING;
|
||||
|
|
@ -137,7 +140,7 @@ impl FrameState {
|
|||
*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.
|
||||
|
|
|
|||
|
|
@ -609,7 +609,8 @@ impl Response {
|
|||
let layer_id = LayerId::new(Order::Tooltip, tooltip_id);
|
||||
|
||||
let tooltip_has_interactive_widget = self.ctx.viewport(|vp| {
|
||||
vp.widgets_prev_frame
|
||||
vp.prev_frame
|
||||
.widgets
|
||||
.get_layer(layer_id)
|
||||
.any(|w| w.sense.interactive())
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in New Issue