gpui: Reduce window.refresh
to improve cache hit of the cached views (#25009)
Release Notes: - Improved performance when using the scroll wheel and some other mouse interactions. Based on some cache details about GPUI `AnyView::cached` that I found in the discussion of https://github.com/zed-industries/zed/discussions/24260#discussioncomment-12135749, and combined with the optimization points found in actual applications. This change may have some scenarios that I have not considered, so I just make a draft to put forward my ideas first for discussion. From my analysis, `AnyView::cached` will always invalid by Div's mouse events, because of it called `window.refresh`. I understand that (mouse move event) this is because the interface changes related to hover and mouse_move will be affected style, so `window.refresh` is required. Since Div does not have the `entity_id` of View, it is impossible to know which View should be refreshed, so the entire window can only be refreshed. With this change, we can reduce a lot of `render` method calls on ScrollWheel or Mouse Event.
This commit is contained in:
parent
89ae4ca9a3
commit
985ac4e5f2
4 changed files with 38 additions and 22 deletions
|
@ -1708,13 +1708,14 @@ impl Interactivity {
|
||||||
});
|
});
|
||||||
|
|
||||||
let was_hovered = hitbox.is_hovered(window);
|
let was_hovered = hitbox.is_hovered(window);
|
||||||
|
let current_view = window.current_view();
|
||||||
window.on_mouse_event({
|
window.on_mouse_event({
|
||||||
let hitbox = hitbox.clone();
|
let hitbox = hitbox.clone();
|
||||||
move |_: &MouseMoveEvent, phase, window, _| {
|
move |_: &MouseMoveEvent, phase, window, cx| {
|
||||||
if phase == DispatchPhase::Capture {
|
if phase == DispatchPhase::Capture {
|
||||||
let hovered = hitbox.is_hovered(window);
|
let hovered = hitbox.is_hovered(window);
|
||||||
if hovered != was_hovered {
|
if hovered != was_hovered {
|
||||||
window.refresh();
|
cx.notify(current_view)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1828,10 +1829,11 @@ impl Interactivity {
|
||||||
{
|
{
|
||||||
let hitbox = hitbox.clone();
|
let hitbox = hitbox.clone();
|
||||||
let was_hovered = hitbox.is_hovered(window);
|
let was_hovered = hitbox.is_hovered(window);
|
||||||
window.on_mouse_event(move |_: &MouseMoveEvent, phase, window, _cx| {
|
let current_view = window.current_view();
|
||||||
|
window.on_mouse_event(move |_: &MouseMoveEvent, phase, window, cx| {
|
||||||
let hovered = hitbox.is_hovered(window);
|
let hovered = hitbox.is_hovered(window);
|
||||||
if phase == DispatchPhase::Capture && hovered != was_hovered {
|
if phase == DispatchPhase::Capture && hovered != was_hovered {
|
||||||
window.refresh();
|
cx.notify(current_view);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -2117,10 +2119,11 @@ impl Interactivity {
|
||||||
|
|
||||||
if let Some(group_hitbox) = group_hitbox {
|
if let Some(group_hitbox) = group_hitbox {
|
||||||
let was_hovered = group_hitbox.is_hovered(window);
|
let was_hovered = group_hitbox.is_hovered(window);
|
||||||
window.on_mouse_event(move |_: &MouseMoveEvent, phase, window, _cx| {
|
let current_view = window.current_view();
|
||||||
|
window.on_mouse_event(move |_: &MouseMoveEvent, phase, window, cx| {
|
||||||
let hovered = group_hitbox.is_hovered(window);
|
let hovered = group_hitbox.is_hovered(window);
|
||||||
if phase == DispatchPhase::Capture && hovered != was_hovered {
|
if phase == DispatchPhase::Capture && hovered != was_hovered {
|
||||||
window.refresh();
|
cx.notify(current_view);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -2139,6 +2142,7 @@ impl Interactivity {
|
||||||
let restrict_scroll_to_axis = style.restrict_scroll_to_axis;
|
let restrict_scroll_to_axis = style.restrict_scroll_to_axis;
|
||||||
let line_height = window.line_height();
|
let line_height = window.line_height();
|
||||||
let hitbox = hitbox.clone();
|
let hitbox = hitbox.clone();
|
||||||
|
let current_view = window.current_view();
|
||||||
window.on_mouse_event(move |event: &ScrollWheelEvent, phase, window, cx| {
|
window.on_mouse_event(move |event: &ScrollWheelEvent, phase, window, cx| {
|
||||||
if phase == DispatchPhase::Bubble && hitbox.is_hovered(window) {
|
if phase == DispatchPhase::Bubble && hitbox.is_hovered(window) {
|
||||||
let mut scroll_offset = scroll_offset.borrow_mut();
|
let mut scroll_offset = scroll_offset.borrow_mut();
|
||||||
|
@ -2172,7 +2176,7 @@ impl Interactivity {
|
||||||
scroll_offset.x += delta_x;
|
scroll_offset.x += delta_x;
|
||||||
cx.stop_propagation();
|
cx.stop_propagation();
|
||||||
if *scroll_offset != old_scroll_offset {
|
if *scroll_offset != old_scroll_offset {
|
||||||
window.refresh();
|
cx.notify(current_view);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -9,8 +9,8 @@
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
point, px, size, AnyElement, App, AvailableSpace, Bounds, ContentMask, DispatchPhase, Edges,
|
point, px, size, AnyElement, App, AvailableSpace, Bounds, ContentMask, DispatchPhase, Edges,
|
||||||
Element, FocusHandle, GlobalElementId, Hitbox, IntoElement, Pixels, Point, ScrollWheelEvent,
|
Element, EntityId, FocusHandle, GlobalElementId, Hitbox, IntoElement, Pixels, Point,
|
||||||
Size, Style, StyleRefinement, Styled, Window,
|
ScrollWheelEvent, Size, Style, StyleRefinement, Styled, Window,
|
||||||
};
|
};
|
||||||
use collections::VecDeque;
|
use collections::VecDeque;
|
||||||
use refineable::Refineable as _;
|
use refineable::Refineable as _;
|
||||||
|
@ -371,6 +371,7 @@ impl StateInner {
|
||||||
scroll_top: &ListOffset,
|
scroll_top: &ListOffset,
|
||||||
height: Pixels,
|
height: Pixels,
|
||||||
delta: Point<Pixels>,
|
delta: Point<Pixels>,
|
||||||
|
current_view: EntityId,
|
||||||
window: &mut Window,
|
window: &mut Window,
|
||||||
cx: &mut App,
|
cx: &mut App,
|
||||||
) {
|
) {
|
||||||
|
@ -413,7 +414,7 @@ impl StateInner {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
window.refresh();
|
cx.notify(current_view);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn logical_scroll_top(&self) -> ListOffset {
|
fn logical_scroll_top(&self) -> ListOffset {
|
||||||
|
@ -847,6 +848,7 @@ impl Element for List {
|
||||||
window: &mut Window,
|
window: &mut Window,
|
||||||
cx: &mut App,
|
cx: &mut App,
|
||||||
) {
|
) {
|
||||||
|
let current_view = window.current_view();
|
||||||
window.with_content_mask(Some(ContentMask { bounds }), |window| {
|
window.with_content_mask(Some(ContentMask { bounds }), |window| {
|
||||||
for item in &mut prepaint.layout.item_layouts {
|
for item in &mut prepaint.layout.item_layouts {
|
||||||
item.element.paint(window, cx);
|
item.element.paint(window, cx);
|
||||||
|
@ -863,6 +865,7 @@ impl Element for List {
|
||||||
&scroll_top,
|
&scroll_top,
|
||||||
height,
|
height,
|
||||||
event.delta.pixel_delta(px(20.)),
|
event.delta.pixel_delta(px(20.)),
|
||||||
|
current_view,
|
||||||
window,
|
window,
|
||||||
cx,
|
cx,
|
||||||
)
|
)
|
||||||
|
@ -967,7 +970,10 @@ mod test {
|
||||||
|
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
fn test_reset_after_paint_before_scroll(cx: &mut TestAppContext) {
|
fn test_reset_after_paint_before_scroll(cx: &mut TestAppContext) {
|
||||||
use crate::{div, list, point, px, size, Element, ListState, Styled};
|
use crate::{
|
||||||
|
div, list, point, px, size, AppContext, Context, Element, IntoElement, ListState,
|
||||||
|
Render, Styled, Window,
|
||||||
|
};
|
||||||
|
|
||||||
let cx = cx.add_empty_window();
|
let cx = cx.add_empty_window();
|
||||||
|
|
||||||
|
@ -981,9 +987,16 @@ mod test {
|
||||||
offset_in_item: px(0.0),
|
offset_in_item: px(0.0),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
struct TestView(ListState);
|
||||||
|
impl Render for TestView {
|
||||||
|
fn render(&mut self, _: &mut Window, _: &mut Context<Self>) -> impl IntoElement {
|
||||||
|
list(self.0.clone()).w_full().h_full()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Paint
|
// Paint
|
||||||
cx.draw(point(px(0.), px(0.)), size(px(100.), px(20.)), |_, _| {
|
cx.draw(point(px(0.), px(0.)), size(px(100.), px(20.)), |_, cx| {
|
||||||
list(state.clone()).w_full().h_full()
|
cx.new(|_| TestView(state.clone()))
|
||||||
});
|
});
|
||||||
|
|
||||||
// Reset
|
// Reset
|
||||||
|
|
|
@ -691,6 +691,7 @@ impl Element for InteractiveText {
|
||||||
window: &mut Window,
|
window: &mut Window,
|
||||||
cx: &mut App,
|
cx: &mut App,
|
||||||
) {
|
) {
|
||||||
|
let current_view = window.current_view();
|
||||||
let text_layout = self.text.layout().clone();
|
let text_layout = self.text.layout().clone();
|
||||||
window.with_element_state::<InteractiveTextState, _>(
|
window.with_element_state::<InteractiveTextState, _>(
|
||||||
global_id.unwrap(),
|
global_id.unwrap(),
|
||||||
|
@ -764,7 +765,7 @@ impl Element for InteractiveText {
|
||||||
if let Some(hover_listener) = hover_listener.as_ref() {
|
if let Some(hover_listener) = hover_listener.as_ref() {
|
||||||
hover_listener(updated, event.clone(), window, cx);
|
hover_listener(updated, event.clone(), window, cx);
|
||||||
}
|
}
|
||||||
window.refresh();
|
cx.notify(current_view);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -265,6 +265,8 @@ mod uniform_list {
|
||||||
window: &mut Window,
|
window: &mut Window,
|
||||||
_cx: &mut App,
|
_cx: &mut App,
|
||||||
) {
|
) {
|
||||||
|
let current_view = window.current_view();
|
||||||
|
|
||||||
match prepaint {
|
match prepaint {
|
||||||
IndentGuidesElementPrepaintState::Static => {
|
IndentGuidesElementPrepaintState::Static => {
|
||||||
for indent_guide in self.indent_guides.as_ref() {
|
for indent_guide in self.indent_guides.as_ref() {
|
||||||
|
@ -326,7 +328,7 @@ mod uniform_list {
|
||||||
window.on_mouse_event({
|
window.on_mouse_event({
|
||||||
let prev_hovered_hitbox_id = hovered_hitbox_id;
|
let prev_hovered_hitbox_id = hovered_hitbox_id;
|
||||||
let hitboxes = hitboxes.clone();
|
let hitboxes = hitboxes.clone();
|
||||||
move |_: &MouseMoveEvent, phase, window, _cx| {
|
move |_: &MouseMoveEvent, phase, window, cx| {
|
||||||
let mut hovered_hitbox_id = None;
|
let mut hovered_hitbox_id = None;
|
||||||
for hitbox in hitboxes.as_ref() {
|
for hitbox in hitboxes.as_ref() {
|
||||||
if hitbox.is_hovered(window) {
|
if hitbox.is_hovered(window) {
|
||||||
|
@ -339,15 +341,11 @@ mod uniform_list {
|
||||||
match (prev_hovered_hitbox_id, hovered_hitbox_id) {
|
match (prev_hovered_hitbox_id, hovered_hitbox_id) {
|
||||||
(Some(prev_id), Some(id)) => {
|
(Some(prev_id), Some(id)) => {
|
||||||
if prev_id != id {
|
if prev_id != id {
|
||||||
window.refresh();
|
cx.notify(current_view)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(None, Some(_)) => {
|
(None, Some(_)) => cx.notify(current_view),
|
||||||
window.refresh();
|
(Some(_), None) => cx.notify(current_view),
|
||||||
}
|
|
||||||
(Some(_), None) => {
|
|
||||||
window.refresh();
|
|
||||||
}
|
|
||||||
(None, None) => {}
|
(None, None) => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue