Compare commits

...

1 Commits

Author SHA1 Message Date
Thorsten Ball
1ac6a2f5c2 linux: add profling annotations & extend example 2024-07-10 13:08:51 +02:00
7 changed files with 162 additions and 43 deletions

View File

@@ -193,6 +193,16 @@ impl TextInput {
.find_map(|(idx, _)| (idx > offset).then_some(idx)) .find_map(|(idx, _)| (idx > offset).then_some(idx))
.unwrap_or(self.content.len()) .unwrap_or(self.content.len())
} }
fn reset(&mut self) {
self.content = "".into();
self.selected_range = 0..0;
self.selection_reversed = false;
self.marked_range = None;
self.last_layout = None;
self.last_bounds = None;
self.is_selecting = false;
}
} }
impl ViewInputHandler for TextInput { impl ViewInputHandler for TextInput {
@@ -315,6 +325,7 @@ impl Element for TextElement {
None None
} }
#[profiling::function]
fn request_layout( fn request_layout(
&mut self, &mut self,
_id: Option<&GlobalElementId>, _id: Option<&GlobalElementId>,
@@ -326,6 +337,7 @@ impl Element for TextElement {
(cx.request_layout(style, []), ()) (cx.request_layout(style, []), ())
} }
#[profiling::function]
fn prepaint( fn prepaint(
&mut self, &mut self,
_id: Option<&GlobalElementId>, _id: Option<&GlobalElementId>,
@@ -416,6 +428,7 @@ impl Element for TextElement {
} }
} }
#[profiling::function]
fn paint( fn paint(
&mut self, &mut self,
_id: Option<&GlobalElementId>, _id: Option<&GlobalElementId>,
@@ -430,12 +443,14 @@ impl Element for TextElement {
ElementInputHandler::new(bounds, self.input.clone()), ElementInputHandler::new(bounds, self.input.clone()),
); );
if let Some(selection) = prepaint.selection.take() { if let Some(selection) = prepaint.selection.take() {
profiling::scope!("paint_quad selection");
cx.paint_quad(selection) cx.paint_quad(selection)
} }
let line = prepaint.line.take().unwrap(); let line = prepaint.line.take().unwrap();
line.paint(bounds.origin, cx.line_height(), cx).unwrap(); line.paint(bounds.origin, cx.line_height(), cx).unwrap();
if let Some(cursor) = prepaint.cursor.take() { if let Some(cursor) = prepaint.cursor.take() {
profiling::scope!("paint_quad cursor");
cx.paint_quad(cursor); cx.paint_quad(cursor);
} }
self.input.update(cx, |input, _cx| { self.input.update(cx, |input, _cx| {
@@ -446,6 +461,7 @@ impl Element for TextElement {
} }
impl Render for TextInput { impl Render for TextInput {
#[profiling::function]
fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement { fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
div() div()
.flex() .flex()
@@ -494,13 +510,48 @@ struct InputExample {
recent_keystrokes: Vec<Keystroke>, recent_keystrokes: Vec<Keystroke>,
} }
impl InputExample {
fn on_reset_click(&mut self, _: &MouseUpEvent, cx: &mut ViewContext<Self>) {
self.recent_keystrokes.clear();
self.text_input
.update(cx, |text_input, _cx| text_input.reset());
cx.notify();
}
}
impl Render for InputExample { impl Render for InputExample {
fn render(&mut self, _: &mut ViewContext<Self>) -> impl IntoElement { #[profiling::function]
fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
let num_keystrokes = self.recent_keystrokes.len();
div() div()
.bg(rgb(0xaaaaaa)) .bg(rgb(0xaaaaaa))
.flex() .flex()
.flex_col() .flex_col()
.size_full() .size_full()
.child(
div()
.bg(white())
.border_b_1()
.border_color(black())
.flex()
.flex_row()
.justify_between()
.child(format!("Keystrokes: {}", num_keystrokes))
.child(
div()
.border_1()
.border_color(black())
.px_2()
.bg(yellow())
.child("Reset")
.hover(|style| {
style
.bg(yellow().blend(opaque_grey(0.5, 0.5)))
.cursor_pointer()
})
.on_mouse_up(MouseButton::Left, cx.listener(Self::on_reset_click)),
),
)
.child(self.text_input.clone()) .child(self.text_input.clone())
.children(self.recent_keystrokes.iter().rev().map(|ks| { .children(self.recent_keystrokes.iter().rev().map(|ks| {
format!( format!(

View File

@@ -387,6 +387,7 @@ impl<E: Element> Drawable<E> {
} }
} }
#[profiling::function]
pub(crate) fn layout_as_root( pub(crate) fn layout_as_root(
&mut self, &mut self,
available_space: Size<AvailableSpace>, available_space: Size<AvailableSpace>,
@@ -503,6 +504,7 @@ impl AnyElement {
} }
/// Performs layout for this element within the given available space and returns its size. /// Performs layout for this element within the given available space and returns its size.
#[profiling::function]
pub fn layout_as_root( pub fn layout_as_root(
&mut self, &mut self,
available_space: Size<AvailableSpace>, available_space: Size<AvailableSpace>,
@@ -517,6 +519,7 @@ impl AnyElement {
} }
/// Performs layout on this element in the available space, then prepaints it at the given absolute origin. /// Performs layout on this element in the available space, then prepaints it at the given absolute origin.
#[profiling::function]
pub fn prepaint_as_root( pub fn prepaint_as_root(
&mut self, &mut self,
origin: Point<Pixels>, origin: Point<Pixels>,
@@ -524,7 +527,10 @@ impl AnyElement {
cx: &mut WindowContext, cx: &mut WindowContext,
) { ) {
self.layout_as_root(available_space, cx); self.layout_as_root(available_space, cx);
cx.with_absolute_element_offset(origin, |cx| self.0.prepaint(cx)); cx.with_absolute_element_offset(origin, |cx| {
profiling::scope!("with_absolute_element_offset prepaint");
self.0.prepaint(cx)
});
} }
} }

View File

@@ -524,6 +524,7 @@ impl BladeRenderer {
self.gpu.destroy_command_encoder(&mut self.command_encoder); self.gpu.destroy_command_encoder(&mut self.command_encoder);
} }
#[profiling::function]
pub fn draw(&mut self, scene: &Scene) { pub fn draw(&mut self, scene: &Scene) {
self.command_encoder.start(); self.command_encoder.start();
self.atlas.before_frame(&mut self.command_encoder); self.atlas.before_frame(&mut self.command_encoder);

View File

@@ -38,6 +38,8 @@ impl LinuxDispatcher {
.map(|i| { .map(|i| {
let receiver = background_receiver.clone(); let receiver = background_receiver.clone();
std::thread::spawn(move || { std::thread::spawn(move || {
let thread_name = format!("background-{}", i);
profiling::register_thread!(&thread_name);
for runnable in receiver { for runnable in receiver {
let start = Instant::now(); let start = Instant::now();
@@ -55,6 +57,8 @@ impl LinuxDispatcher {
let (timer_sender, timer_channel) = calloop::channel::channel::<TimerAfter>(); let (timer_sender, timer_channel) = calloop::channel::channel::<TimerAfter>();
let timer_thread = std::thread::spawn(|| { let timer_thread = std::thread::spawn(|| {
profiling::register_thread!("timer-thread");
let mut event_loop: EventLoop<()> = let mut event_loop: EventLoop<()> =
EventLoop::try_new().expect("Failed to initialize timer loop!"); EventLoop::try_new().expect("Failed to initialize timer loop!");

View File

@@ -1182,6 +1182,7 @@ impl PlatformWindow for X11Window {
self.0.callbacks.borrow_mut().appearance_changed = Some(callback); self.0.callbacks.borrow_mut().appearance_changed = Some(callback);
} }
#[profiling::function]
fn draw(&self, scene: &Scene) { fn draw(&self, scene: &Scene) {
let mut inner = self.0.state.borrow_mut(); let mut inner = self.0.state.borrow_mut();
inner.renderer.draw(scene); inner.renderer.draw(scene);

View File

@@ -142,6 +142,7 @@ impl TaffyLayoutEngine {
Ok(edges) Ok(edges)
} }
#[profiling::function]
pub fn compute_layout( pub fn compute_layout(
&mut self, &mut self,
id: LayoutId, id: LayoutId,
@@ -161,6 +162,7 @@ impl TaffyLayoutEngine {
// //
if !self.computed_layouts.insert(id) { if !self.computed_layouts.insert(id) {
profiling::scope!("compute layout stack extension");
let mut stack = SmallVec::<[LayoutId; 64]>::new(); let mut stack = SmallVec::<[LayoutId; 64]>::new();
stack.push(id); stack.push(id);
while let Some(id) = stack.pop() { while let Some(id) = stack.pop() {
@@ -181,6 +183,8 @@ impl TaffyLayoutEngine {
id.into(), id.into(),
available_space.into(), available_space.into(),
|known_dimensions, available_space, node_id, _context| { |known_dimensions, available_space, node_id, _context| {
profiling::scope!("measure function");
let Some(measure) = self.nodes_to_measure.get_mut(&node_id.into()) else { let Some(measure) = self.nodes_to_measure.get_mut(&node_id.into()) else {
return taffy::geometry::Size::default(); return taffy::geometry::Size::default();
}; };
@@ -190,7 +194,10 @@ impl TaffyLayoutEngine {
height: known_dimensions.height.map(Pixels), height: known_dimensions.height.map(Pixels),
}; };
measure(known_dimensions, available_space.into(), cx).into() {
profiling::scope!("calling measure");
measure(known_dimensions, available_space.into(), cx).into()
}
}, },
) )
.expect(EXPECT_MESSAGE); .expect(EXPECT_MESSAGE);

View File

@@ -453,17 +453,48 @@ impl Frame {
} }
} }
#[profiling::function]
pub(crate) fn clear(&mut self) { pub(crate) fn clear(&mut self) {
self.element_states.clear(); {
self.accessed_element_states.clear(); profiling::scope!("element_states clear");
self.mouse_listeners.clear(); self.element_states.clear();
self.dispatch_tree.clear(); }
self.scene.clear(); {
self.input_handlers.clear(); profiling::scope!("accessed_element_states clear");
self.tooltip_requests.clear(); self.accessed_element_states.clear();
self.cursor_styles.clear(); }
self.hitboxes.clear(); {
self.deferred_draws.clear(); profiling::scope!("mouse_listeners clear");
self.mouse_listeners.clear();
}
{
profiling::scope!("dispatch_tree clear");
self.dispatch_tree.clear();
}
{
profiling::scope!("scene clear");
self.scene.clear();
}
{
profiling::scope!("input handlers clear");
self.input_handlers.clear();
}
{
profiling::scope!("tooltip_requests clear");
self.tooltip_requests.clear();
}
{
profiling::scope!("cursor styles clear");
self.cursor_styles.clear();
}
{
profiling::scope!("hitboxes clear");
self.hitboxes.clear();
}
{
profiling::scope!("deferred draws clear");
self.deferred_draws.clear();
}
} }
pub(crate) fn hit_test(&self, position: Point<Pixels>) -> HitTest { pub(crate) fn hit_test(&self, position: Point<Pixels>) -> HitTest {
@@ -695,6 +726,7 @@ impl Window {
} }
})); }));
platform_window.on_request_frame(Box::new({ platform_window.on_request_frame(Box::new({
profiling::scope!("on_request_frame");
let mut cx = cx.to_async(); let mut cx = cx.to_async();
let dirty = dirty.clone(); let dirty = dirty.clone();
let active = active.clone(); let active = active.clone();
@@ -1429,6 +1461,7 @@ impl<'a> WindowContext<'a> {
.next_frame .next_frame
.finish(&mut self.window.rendered_frame); .finish(&mut self.window.rendered_frame);
ELEMENT_ARENA.with_borrow_mut(|element_arena| { ELEMENT_ARENA.with_borrow_mut(|element_arena| {
profiling::scope!("element area clear");
let percentage = (element_arena.len() as f32 / element_arena.capacity() as f32) * 100.; let percentage = (element_arena.len() as f32 / element_arena.capacity() as f32) * 100.;
if percentage >= 80. { if percentage >= 80. {
log::warn!("elevated element arena occupation: {}.", percentage); log::warn!("elevated element arena occupation: {}.", percentage);
@@ -1439,37 +1472,47 @@ impl<'a> WindowContext<'a> {
self.window.draw_phase = DrawPhase::Focus; self.window.draw_phase = DrawPhase::Focus;
let previous_focus_path = self.window.rendered_frame.focus_path(); let previous_focus_path = self.window.rendered_frame.focus_path();
let previous_window_active = self.window.rendered_frame.window_active; let previous_window_active = self.window.rendered_frame.window_active;
mem::swap(&mut self.window.rendered_frame, &mut self.window.next_frame);
self.window.next_frame.clear();
let current_focus_path = self.window.rendered_frame.focus_path();
let current_window_active = self.window.rendered_frame.window_active;
if previous_focus_path != current_focus_path
|| previous_window_active != current_window_active
{ {
if !previous_focus_path.is_empty() && current_focus_path.is_empty() { profiling::scope!("swapping frames");
self.window mem::swap(&mut self.window.rendered_frame, &mut self.window.next_frame);
.focus_lost_listeners }
.clone() {
.retain(&(), |listener| listener(self)); profiling::scope!("clearing next frame");
} self.window.next_frame.clear();
}
let event = WindowFocusEvent { {
previous_focus_path: if previous_window_active { profiling::scope!("updating focus path");
previous_focus_path let current_focus_path = self.window.rendered_frame.focus_path();
} else { let current_window_active = self.window.rendered_frame.window_active;
Default::default()
}, if previous_focus_path != current_focus_path
current_focus_path: if current_window_active { || previous_window_active != current_window_active
current_focus_path {
} else { if !previous_focus_path.is_empty() && current_focus_path.is_empty() {
Default::default() self.window
}, .focus_lost_listeners
}; .clone()
self.window .retain(&(), |listener| listener(self));
.focus_listeners }
.clone()
.retain(&(), |listener| listener(&event, self)); let event = WindowFocusEvent {
previous_focus_path: if previous_window_active {
previous_focus_path
} else {
Default::default()
},
current_focus_path: if current_window_active {
current_focus_path
} else {
Default::default()
},
};
self.window
.focus_listeners
.clone()
.retain(&(), |listener| listener(&event, self));
}
} }
self.reset_cursor_style(); self.reset_cursor_style();
@@ -1487,6 +1530,7 @@ impl<'a> WindowContext<'a> {
profiling::finish_frame!(); profiling::finish_frame!();
} }
#[profiling::function]
fn draw_roots(&mut self) { fn draw_roots(&mut self) {
self.window.draw_phase = DrawPhase::Prepaint; self.window.draw_phase = DrawPhase::Prepaint;
self.window.tooltip_bounds.take(); self.window.tooltip_bounds.take();
@@ -1854,6 +1898,7 @@ impl<'a> WindowContext<'a> {
/// Updates the global element offset based on the given offset. This is used to implement /// Updates the global element offset based on the given offset. This is used to implement
/// drag handles and other manual painting of elements. This method should only be called during /// drag handles and other manual painting of elements. This method should only be called during
/// the prepaint phase of element drawing. /// the prepaint phase of element drawing.
#[profiling::function]
pub fn with_absolute_element_offset<R>( pub fn with_absolute_element_offset<R>(
&mut self, &mut self,
offset: Point<Pixels>, offset: Point<Pixels>,
@@ -2719,6 +2764,7 @@ impl<'a> WindowContext<'a> {
/// After calling it, you can request the bounds of the given layout node id or any descendant. /// After calling it, you can request the bounds of the given layout node id or any descendant.
/// ///
/// This method should only be called as part of the prepaint phase of element drawing. /// This method should only be called as part of the prepaint phase of element drawing.
#[profiling::function]
pub fn compute_layout(&mut self, layout_id: LayoutId, available_space: Size<AvailableSpace>) { pub fn compute_layout(&mut self, layout_id: LayoutId, available_space: Size<AvailableSpace>) {
debug_assert_eq!( debug_assert_eq!(
self.window.draw_phase, self.window.draw_phase,
@@ -2727,7 +2773,10 @@ impl<'a> WindowContext<'a> {
); );
let mut layout_engine = self.window.layout_engine.take().unwrap(); let mut layout_engine = self.window.layout_engine.take().unwrap();
layout_engine.compute_layout(layout_id, available_space, self); {
profiling::scope!("layout_engine compute_layout");
layout_engine.compute_layout(layout_id, available_space, self);
}
self.window.layout_engine = Some(layout_engine); self.window.layout_engine = Some(layout_engine);
} }