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))
.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 {
@@ -315,6 +325,7 @@ impl Element for TextElement {
None
}
#[profiling::function]
fn request_layout(
&mut self,
_id: Option<&GlobalElementId>,
@@ -326,6 +337,7 @@ impl Element for TextElement {
(cx.request_layout(style, []), ())
}
#[profiling::function]
fn prepaint(
&mut self,
_id: Option<&GlobalElementId>,
@@ -416,6 +428,7 @@ impl Element for TextElement {
}
}
#[profiling::function]
fn paint(
&mut self,
_id: Option<&GlobalElementId>,
@@ -430,12 +443,14 @@ impl Element for TextElement {
ElementInputHandler::new(bounds, self.input.clone()),
);
if let Some(selection) = prepaint.selection.take() {
profiling::scope!("paint_quad selection");
cx.paint_quad(selection)
}
let line = prepaint.line.take().unwrap();
line.paint(bounds.origin, cx.line_height(), cx).unwrap();
if let Some(cursor) = prepaint.cursor.take() {
profiling::scope!("paint_quad cursor");
cx.paint_quad(cursor);
}
self.input.update(cx, |input, _cx| {
@@ -446,6 +461,7 @@ impl Element for TextElement {
}
impl Render for TextInput {
#[profiling::function]
fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
div()
.flex()
@@ -494,13 +510,48 @@ struct InputExample {
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 {
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()
.bg(rgb(0xaaaaaa))
.flex()
.flex_col()
.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())
.children(self.recent_keystrokes.iter().rev().map(|ks| {
format!(

View File

@@ -387,6 +387,7 @@ impl<E: Element> Drawable<E> {
}
}
#[profiling::function]
pub(crate) fn layout_as_root(
&mut self,
available_space: Size<AvailableSpace>,
@@ -503,6 +504,7 @@ impl AnyElement {
}
/// Performs layout for this element within the given available space and returns its size.
#[profiling::function]
pub fn layout_as_root(
&mut self,
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.
#[profiling::function]
pub fn prepaint_as_root(
&mut self,
origin: Point<Pixels>,
@@ -524,7 +527,10 @@ impl AnyElement {
cx: &mut WindowContext,
) {
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);
}
#[profiling::function]
pub fn draw(&mut self, scene: &Scene) {
self.command_encoder.start();
self.atlas.before_frame(&mut self.command_encoder);

View File

@@ -38,6 +38,8 @@ impl LinuxDispatcher {
.map(|i| {
let receiver = background_receiver.clone();
std::thread::spawn(move || {
let thread_name = format!("background-{}", i);
profiling::register_thread!(&thread_name);
for runnable in receiver {
let start = Instant::now();
@@ -55,6 +57,8 @@ impl LinuxDispatcher {
let (timer_sender, timer_channel) = calloop::channel::channel::<TimerAfter>();
let timer_thread = std::thread::spawn(|| {
profiling::register_thread!("timer-thread");
let mut event_loop: EventLoop<()> =
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);
}
#[profiling::function]
fn draw(&self, scene: &Scene) {
let mut inner = self.0.state.borrow_mut();
inner.renderer.draw(scene);

View File

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

View File

@@ -453,17 +453,48 @@ impl Frame {
}
}
#[profiling::function]
pub(crate) fn clear(&mut self) {
self.element_states.clear();
self.accessed_element_states.clear();
self.mouse_listeners.clear();
self.dispatch_tree.clear();
self.scene.clear();
self.input_handlers.clear();
self.tooltip_requests.clear();
self.cursor_styles.clear();
self.hitboxes.clear();
self.deferred_draws.clear();
{
profiling::scope!("element_states clear");
self.element_states.clear();
}
{
profiling::scope!("accessed_element_states clear");
self.accessed_element_states.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 {
@@ -695,6 +726,7 @@ impl Window {
}
}));
platform_window.on_request_frame(Box::new({
profiling::scope!("on_request_frame");
let mut cx = cx.to_async();
let dirty = dirty.clone();
let active = active.clone();
@@ -1429,6 +1461,7 @@ impl<'a> WindowContext<'a> {
.next_frame
.finish(&mut self.window.rendered_frame);
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.;
if percentage >= 80. {
log::warn!("elevated element arena occupation: {}.", percentage);
@@ -1439,37 +1472,47 @@ impl<'a> WindowContext<'a> {
self.window.draw_phase = DrawPhase::Focus;
let previous_focus_path = self.window.rendered_frame.focus_path();
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() {
self.window
.focus_lost_listeners
.clone()
.retain(&(), |listener| listener(self));
}
profiling::scope!("swapping frames");
mem::swap(&mut self.window.rendered_frame, &mut self.window.next_frame);
}
{
profiling::scope!("clearing next frame");
self.window.next_frame.clear();
}
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));
{
profiling::scope!("updating focus path");
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() {
self.window
.focus_lost_listeners
.clone()
.retain(&(), |listener| listener(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();
@@ -1487,6 +1530,7 @@ impl<'a> WindowContext<'a> {
profiling::finish_frame!();
}
#[profiling::function]
fn draw_roots(&mut self) {
self.window.draw_phase = DrawPhase::Prepaint;
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
/// drag handles and other manual painting of elements. This method should only be called during
/// the prepaint phase of element drawing.
#[profiling::function]
pub fn with_absolute_element_offset<R>(
&mut self,
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.
///
/// 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>) {
debug_assert_eq!(
self.window.draw_phase,
@@ -2727,7 +2773,10 @@ impl<'a> WindowContext<'a> {
);
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);
}