1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-03 00:42:14 +00:00

LibWeb: Optimize scroll offset and clip state recalculation

In this commit we have optimized the handling of scroll offsets and
clip rectangles to improve performance. Previously, the process
involved multiple full traversals of the paintable tree before each
repaint, which was highly inefficient, especially on pages with a
large number of paintables. The steps were:

1. Traverse the paintable tree to identify all boxes with scrollable or
   clipped overflow.
2. Gather the accumulated scroll offset or clip rectangle for each box.
3. Perform another traversal to apply the corresponding scroll offset
   and clip rectangle to each paintable.

To address this, we've adopted a new strategy that separates the
assignment of the scroll/clip frame from the refresh of accumulated
scroll offsets and clip rectangles, thus reducing the workload:

1. Post-relayout: Identify all boxes with overflow and link each
   paintable to the state of its containing scroll/clip frame.
2. Pre-repaint: Update the clip rectangle and scroll offset only in the
   previously identified boxes.

This adjustment ensures that the costly tree traversals are only
necessary after a relayout, substantially decreasing the amount of work
required before each repaint.
This commit is contained in:
Aliaksandr Kalenik 2024-02-08 17:30:07 +01:00 committed by Andreas Kling
parent fc40d35012
commit 76d1536307
8 changed files with 189 additions and 104 deletions

View file

@ -2108,21 +2108,24 @@ void Navigable::paint(Painting::RecordingPainter& recording_painter, PaintConfig
document->update_paint_and_hit_testing_properties_if_needed();
HashMap<Painting::PaintableBox const*, Painting::ViewportPaintable::ScrollFrame> scroll_frames;
auto& viewport_paintable = *document->paintable();
// NOTE: We only need to refresh the scroll state for traversables because they are responsible
// for tracking the state of all nested navigables.
if (is_traversable()) {
document->paintable()->assign_scroll_frame_ids(scroll_frames);
document->paintable()->assign_clip_rectangles();
viewport_paintable.refresh_scroll_state();
viewport_paintable.refresh_clip_state();
}
document->paintable()->paint_all_phases(context);
viewport_paintable.paint_all_phases(context);
// FIXME: Support scrollable frames inside iframes.
if (is_traversable()) {
Vector<Gfx::IntPoint> scroll_offsets_by_frame_id;
scroll_offsets_by_frame_id.resize(scroll_frames.size());
for (auto [_, scrollable_frame] : scroll_frames) {
auto scroll_offset = context.rounded_device_point(scrollable_frame.offset).to_type<int>();
scroll_offsets_by_frame_id[scrollable_frame.id] = scroll_offset;
scroll_offsets_by_frame_id.resize(viewport_paintable.scroll_state.size());
for (auto [_, scrollable_frame] : viewport_paintable.scroll_state) {
auto scroll_offset = context.rounded_device_point(scrollable_frame->offset).to_type<int>();
scroll_offsets_by_frame_id[scrollable_frame->id] = scroll_offset;
}
recording_painter.apply_scroll_offsets(scroll_offsets_by_frame_id);
}