Split paint/layout to not layout a second time when doing actual paint

Co-Authored-By: Max Brunsfeld <max@zed.dev>
This commit is contained in:
Julia
2024-02-19 16:24:00 -05:00
parent 409bc387c0
commit e4ec510925
8 changed files with 140 additions and 86 deletions

View File

@@ -1279,7 +1279,7 @@ impl Render for AssistantPanel {
let conversation_count = self.saved_conversations.len();
canvas(move |bounds, cx| {
uniform_list(
view,
view.clone(),
"saved_conversations",
conversation_count,
|this, range, cx| {
@@ -1288,7 +1288,7 @@ impl Render for AssistantPanel {
.collect()
},
)
.track_scroll(scroll_handle)
.track_scroll(scroll_handle.clone())
.into_any_element()
.draw(
bounds.origin,

View File

@@ -414,13 +414,13 @@ impl Render for ExtensionsPage {
let item_count = self.extensions_entries.len();
move |bounds, cx| {
uniform_list::<_, Div, _>(
view,
view.clone(),
"entries",
item_count,
Self::render_extensions,
)
.size_full()
.track_scroll(scroll_handle)
.track_scroll(scroll_handle.clone())
.into_any_element()
.draw(
bounds.origin,

View File

@@ -103,13 +103,13 @@ pub trait IntoElement: Sized {
{
let element = self.into_element();
let element_id = element.element_id();
let element = DrawableElement {
let mut element = DrawableElement {
element: Some(element),
phase: ElementDrawPhase::Start,
};
let frame_state =
DrawableElement::draw(element, origin, available_space.map(Into::into), cx);
DrawableElement::draw(&mut element, origin, available_space.map(Into::into), cx);
if let Some(mut frame_state) = frame_state {
f(&mut frame_state, cx)
@@ -301,8 +301,8 @@ impl<E: Element> DrawableElement<E> {
layout_id
}
fn paint(mut self, cx: &mut ElementContext) -> Option<E::State> {
match self.phase {
fn paint(&mut self, cx: &mut ElementContext) -> Option<&mut E::State> {
match &mut self.phase {
ElementDrawPhase::LayoutRequested {
layout_id,
frame_state,
@@ -312,13 +312,13 @@ impl<E: Element> DrawableElement<E> {
frame_state,
..
} => {
let bounds = cx.layout_bounds(layout_id);
let bounds = cx.layout_bounds(*layout_id);
if let Some(mut frame_state) = frame_state {
if let Some(frame_state) = frame_state {
self.element
.take()
.as_mut()
.unwrap()
.paint(bounds, &mut frame_state, cx);
.paint(bounds, frame_state, cx);
Some(frame_state)
} else {
let element_id = self
@@ -330,7 +330,7 @@ impl<E: Element> DrawableElement<E> {
cx.with_element_state(element_id, |element_state, cx| {
let mut element_state = element_state.unwrap();
self.element
.take()
.as_mut()
.unwrap()
.paint(bounds, &mut element_state, cx);
((), element_state)
@@ -384,11 +384,11 @@ impl<E: Element> DrawableElement<E> {
}
fn draw(
mut self,
&mut self,
origin: Point<Pixels>,
available_space: Size<AvailableSpace>,
cx: &mut ElementContext,
) -> Option<E::State> {
) -> Option<&mut E::State> {
self.measure(available_space, cx);
cx.with_absolute_element_offset(origin, |cx| self.paint(cx))
}
@@ -408,7 +408,7 @@ where
}
fn paint(&mut self, cx: &mut ElementContext) {
DrawableElement::paint(self.take().unwrap(), cx);
DrawableElement::paint(self.as_mut().unwrap(), cx);
}
fn measure(
@@ -425,7 +425,7 @@ where
available_space: Size<AvailableSpace>,
cx: &mut ElementContext,
) {
DrawableElement::draw(self.take().unwrap(), origin, available_space, cx);
DrawableElement::draw(self.as_mut().unwrap(), origin, available_space, cx);
}
}

View File

@@ -4,7 +4,7 @@ use crate::{Bounds, Element, ElementContext, IntoElement, Pixels, Style, StyleRe
/// Construct a canvas element with the given paint callback.
/// Useful for adding short term custom drawing to a view.
pub fn canvas(callback: impl 'static + FnOnce(&Bounds<Pixels>, &mut ElementContext)) -> Canvas {
pub fn canvas(callback: impl 'static + FnMut(&Bounds<Pixels>, &mut ElementContext)) -> Canvas {
Canvas {
paint_callback: Some(Box::new(callback)),
style: StyleRefinement::default(),
@@ -14,7 +14,7 @@ pub fn canvas(callback: impl 'static + FnOnce(&Bounds<Pixels>, &mut ElementConte
/// A canvas element, meant for accessing the low level paint API without defining a whole
/// custom element
pub struct Canvas {
paint_callback: Option<Box<dyn FnOnce(&Bounds<Pixels>, &mut ElementContext)>>,
paint_callback: Option<Box<dyn FnMut(&Bounds<Pixels>, &mut ElementContext)>>,
style: StyleRefinement,
}
@@ -46,7 +46,7 @@ impl Element for Canvas {
fn paint(&mut self, bounds: Bounds<Pixels>, style: &mut Style, cx: &mut ElementContext) {
style.paint(bounds, cx, |cx| {
(self.paint_callback.take().unwrap())(&bounds, cx)
(self.paint_callback.as_mut().unwrap())(&bounds, cx)
});
}
}

View File

@@ -105,7 +105,7 @@ impl<V: Render> Element for View<V> {
}
fn paint(&mut self, _: Bounds<Pixels>, element: &mut Self::State, cx: &mut ElementContext) {
cx.paint_view(self.entity_id(), |cx| element.take().unwrap().paint(cx));
cx.paint_view(self.entity_id(), |cx| element.as_mut().unwrap().paint(cx));
}
}
@@ -247,22 +247,67 @@ impl AnyView {
self.model.entity_id()
}
pub(crate) fn draw(
pub(crate) fn layout(
&self,
origin: Point<Pixels>,
available_space: Size<AvailableSpace>,
cx: &mut ElementContext,
) {
cx.paint_view(self.entity_id(), |cx| {
) -> AnyElement {
cx.in_layout(true);
let element = cx.paint_view(self.entity_id(), |cx| {
cx.with_absolute_element_offset(origin, |cx| {
let (layout_id, mut rendered_element) = (self.request_layout)(self, cx);
cx.compute_layout(layout_id, available_space);
rendered_element.paint(cx)
rendered_element
})
});
cx.in_layout(false);
element
}
pub(crate) fn paint(
&self,
origin: Point<Pixels>,
rendered_element: &mut AnyElement,
cx: &mut ElementContext,
) {
cx.paint_view(self.entity_id(), |cx| {
cx.with_absolute_element_offset(origin, |cx| {
rendered_element.paint(cx);
});
})
});
}
}
// pub(crate) fn draw(
// &self,
// origin: Point<Pixels>,
// available_space: Size<AvailableSpace>,
// cx: &mut ElementContext,
// ) {
// cx.paint_view(self.entity_id(), |cx| {
// cx.with_absolute_element_offset(origin, |cx| {
// cx.in_layout(true);
// let (layout_id, mut rendered_element) = (self.request_layout)(self, cx);
// assert_eq!(
// *cx.window.next_frame.next_stacking_order_ids.last().unwrap(),
// 0
// );
// // let prev_last_stacking_order_id = cx.window.next_frame.next_stacking_order_ids.last();
// cx.compute_layout(layout_id, available_space);
// cx.in_layout(false);
// // assert_eq!(cx.window.next_frame.z_index_stack.len(), 0);
// *cx.window
// .next_frame
// .next_stacking_order_ids
// .last_mut()
// .unwrap() = 0;
// rendered_element.paint(cx)
// });
// })
// }
// }
impl<V: Render> From<View<V>> for AnyView {
fn from(value: View<V>) -> Self {
AnyView {
@@ -307,7 +352,7 @@ impl Element for AnyView {
fn paint(&mut self, bounds: Bounds<Pixels>, state: &mut Self::State, cx: &mut ElementContext) {
cx.paint_view(self.entity_id(), |cx| {
if !self.cache {
state.element.take().unwrap().paint(cx);
state.element.as_mut().unwrap().paint(cx);
return;
}

View File

@@ -960,55 +960,51 @@ impl<'a> WindowContext<'a> {
requested_handler.handler = input_handler;
}
// TODO: Restructure this
let root_view = self.window.root_view.take().unwrap();
self.with_element_context(true, |cx| {
let mut root_element = self.with_element_context(true, |cx| {
cx.with_z_index(0, |cx| {
cx.with_key_dispatch(Some(KeyContext::default()), None, |_, cx| {
// // We need to use cx.cx here so we can utilize borrow splitting
// for (action_type, action_listeners) in &cx.cx.app.global_action_listeners {
// for action_listener in action_listeners.iter().cloned() {
// cx.cx.window.next_frame.dispatch_tree.on_action(
// *action_type,
// Rc::new(
// move |action: &dyn Any, phase, cx: &mut WindowContext<'_>| {
// action_listener(action, phase, cx)
// },
// ),
// )
// }
// }
let available_space = cx.window.viewport_size.map(Into::into);
root_view.draw(Point::default(), available_space, cx);
let mut root_element = root_view.layout(Point::default(), available_space, cx);
root_view.paint(Point::default(), &mut root_element, cx);
root_element
})
})
});
if let Some(active_drag) = self.app.active_drag.take() {
self.with_element_context(true, |cx| {
cx.with_z_index(ACTIVE_DRAG_Z_INDEX, |cx| {
let mut drag_element = None;
let mut tooltip_element = None;
self.with_element_context(true, |cx| {
cx.with_z_index(1, |cx| {
if let Some(active_drag) = cx.app.active_drag.take() {
let offset = cx.mouse_position() - active_drag.cursor_offset;
let available_space =
size(AvailableSpace::MinContent, AvailableSpace::MinContent);
active_drag.view.draw(offset, available_space, cx);
})
});
self.active_drag = Some(active_drag);
} else if let Some(tooltip_request) = self.window.next_frame.tooltip_request.take() {
self.with_element_context(true, |cx| {
cx.with_z_index(1, |cx| {
let mut element = active_drag.view.layout(offset, available_space, cx);
active_drag.view.paint(offset, &mut element, cx);
drag_element = Some(element);
cx.active_drag = Some(active_drag);
} else if let Some(tooltip_request) = cx.window.next_frame.tooltip_request.take() {
let available_space =
size(AvailableSpace::MinContent, AvailableSpace::MinContent);
tooltip_request.tooltip.view.draw(
let mut element = tooltip_request.tooltip.view.layout(
tooltip_request.tooltip.cursor_offset,
available_space,
cx,
);
})
});
self.window.next_frame.tooltip_request = Some(tooltip_request);
}
tooltip_request.tooltip.view.paint(
tooltip_request.tooltip.cursor_offset,
&mut element,
cx,
);
tooltip_element = Some(element);
cx.window.next_frame.tooltip_request = Some(tooltip_request);
}
})
});
assert_eq!(self.window.next_frame.z_index_stack.len(), 0);
self.window.next_frame.next_stacking_order_ids = vec![0];
@@ -1030,36 +1026,33 @@ impl<'a> WindowContext<'a> {
}
}
let available_space = cx.window.viewport_size.map(Into::into);
root_view.draw(Point::default(), available_space, cx);
// let available_space = cx.window.viewport_size.map(Into::into);
// let mut root_element = root_view.layout(Point::default(), available_space, cx);
root_view.paint(Point::default(), &mut root_element, cx);
})
})
});
if let Some(active_drag) = self.app.active_drag.take() {
self.with_element_context(false, |cx| {
cx.with_z_index(ACTIVE_DRAG_Z_INDEX, |cx| {
let offset = cx.mouse_position() - active_drag.cursor_offset;
let available_space =
size(AvailableSpace::MinContent, AvailableSpace::MinContent);
active_drag.view.draw(offset, available_space, cx);
})
});
self.active_drag = Some(active_drag);
} else if let Some(tooltip_request) = self.window.next_frame.tooltip_request.take() {
self.with_element_context(false, |cx| {
cx.with_z_index(1, |cx| {
let available_space =
size(AvailableSpace::MinContent, AvailableSpace::MinContent);
tooltip_request.tooltip.view.draw(
tooltip_request.tooltip.cursor_offset,
available_space,
cx,
);
})
});
self.window.next_frame.tooltip_request = Some(tooltip_request);
}
self.with_element_context(false, |cx| {
cx.with_z_index(1, |cx| {
if let Some(mut drag_element) = drag_element {
if let Some(active_drag) = cx.app.active_drag.take() {
let offset = cx.mouse_position() - active_drag.cursor_offset;
active_drag.view.paint(offset, &mut drag_element, cx);
}
}
if let Some(mut tooltip_element) = tooltip_element {
if let Some(tooltip_request) = cx.window.next_frame.tooltip_request.take() {
tooltip_request.tooltip.view.paint(
tooltip_request.tooltip.cursor_offset,
&mut tooltip_element,
cx,
);
}
}
})
});
self.window.dirty_views.clear();

View File

@@ -168,6 +168,7 @@ pub struct ElementContext<'a> {
#[deref_mut]
pub(crate) cx: WindowContext<'a>,
pub(crate) pre_paint_pass: bool,
pub(crate) in_layout: bool,
}
impl<'a> WindowContext<'a> {
@@ -182,6 +183,7 @@ impl<'a> WindowContext<'a> {
f(&mut ElementContext {
cx: WindowContext::new(self.app, self.window),
pre_paint_pass,
in_layout: false,
})
}
}
@@ -318,6 +320,10 @@ impl<'a> VisualContext for ElementContext<'a> {
}
impl<'a> ElementContext<'a> {
pub(crate) fn in_layout(&mut self, in_layout: bool) {
self.in_layout = in_layout;
}
pub(crate) fn reuse_view(&mut self, next_stacking_order_id: u16) {
let view_id = self.parent_view_id();
let grafted_view_ids = self
@@ -483,6 +489,10 @@ impl<'a> ElementContext<'a> {
/// Called during painting to invoke the given closure in a new stacking context. The given
/// z-index is interpreted relative to the previous call to `stack`.
pub fn with_z_index<R>(&mut self, z_index: u16, f: impl FnOnce(&mut Self) -> R) -> R {
// if self.in_layout {
// panic!("Don't push z-index in layout");
// }
let new_stacking_order_id = post_inc(
self.window_mut()
.next_frame
@@ -1142,6 +1152,10 @@ impl<'a> ElementContext<'a> {
return;
}
// if self.in_layout {
// panic!("Don't add an opaque layer during layout");
// }
let stacking_order = self.window.next_frame.z_index_stack.clone();
let view_id = self.parent_view_id();
let depth_map = &mut self.window.next_frame.depth_map;

View File

@@ -357,10 +357,12 @@ impl Render for SyntaxTreeView {
.size_full()
.track_scroll(self.list_scroll_handle.clone())
.text_bg(cx.theme().colors().background);
let mut list_element = list.into_any_element();
rendered = rendered.child(
canvas(move |bounds, cx| {
list.into_any_element().draw(
list_element.draw(
bounds.origin,
bounds.size.map(AvailableSpace::Definite),
cx,