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,
|
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))
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
|
||||||
|
|
@ -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())
|
||||||
});
|
});
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue