From 18fc23b3d61c8d7230ce381613a3ccc64c7cb4c0 Mon Sep 17 00:00:00 2001 From: Aliaksandr Kalenik Date: Mon, 12 Aug 2024 00:49:32 +0200 Subject: [PATCH] LibWeb: Reuse display list across repaints ...if only the scroll offset is updated. Currently, on any document with a large amount of content, the process of building a display list is often more expensive than its rasterization. This is because the amount of work required to build a display list is proportional to the size of the paintable tree, whereas rasterization only occurs for the portion visible in the viewport. This change is the first step toward improving this process by caching the display list across repaints when neither style nor layout requires invalidation. This means that repainting while scrolling becomes significantly less expensive, as we only need to reapply the scroll offsets to the existing display list. The performance improvement is especially visible on pages like https://ziglang.org/documentation/master/ or https://www.w3.org/TR/css-grid-2/ --- Userland/Libraries/LibWeb/DOM/Document.cpp | 30 ++++++++++++++++++- Userland/Libraries/LibWeb/DOM/Document.h | 7 +++++ Userland/Libraries/LibWeb/DOM/Range.cpp | 1 + .../LibWeb/HTML/CanvasRenderingContext2D.cpp | 1 + Userland/Libraries/LibWeb/HTML/Navigable.h | 1 + .../LibWeb/HTML/TraversableNavigable.cpp | 7 +++++ 6 files changed, 46 insertions(+), 1 deletion(-) diff --git a/Userland/Libraries/LibWeb/DOM/Document.cpp b/Userland/Libraries/LibWeb/DOM/Document.cpp index d6aa370a9fc..863cda59be8 100644 --- a/Userland/Libraries/LibWeb/DOM/Document.cpp +++ b/Userland/Libraries/LibWeb/DOM/Document.cpp @@ -380,9 +380,11 @@ Document::Document(JS::Realm& realm, const URL::URL& url, TemporaryDocumentForFr if (!node) return; - if (auto navigable = this->navigable(); !navigable || !navigable->is_focused()) + auto navigable = this->navigable(); + if (!navigable || !navigable->is_focused()) return; + node->document().invalidate_display_list(); node->document().update_layout(); if (node->paintable()) { @@ -1112,6 +1114,8 @@ void Document::update_layout() if (m_created_for_appropriate_template_contents) return; + invalidate_display_list(); + auto* document_element = this->document_element(); auto viewport_rect = navigable->viewport_rect(); @@ -1250,6 +1254,9 @@ void Document::update_style() style_computer().reset_ancestor_filter(); auto invalidation = update_style_recursively(*this, style_computer()); + if (!invalidation.is_none()) { + invalidate_display_list(); + } if (invalidation.rebuild_layout_tree) { invalidate_layout(); } else { @@ -1266,6 +1273,8 @@ void Document::update_animated_style_if_needed() if (!m_needs_animated_style_update) return; + invalidate_display_list(); + for (auto& timeline : m_associated_animation_timelines) { for (auto& animation : timeline->associated_animations()) { if (auto effect = animation->effect(); effect && effect->target()) @@ -5393,8 +5402,24 @@ void Document::set_needs_display(CSSPixelRect const&) } } +void Document::invalidate_display_list() +{ + m_cached_display_list.clear(); + + auto navigable = this->navigable(); + if (!navigable) + return; + + if (navigable->container()) { + navigable->container()->document().invalidate_display_list(); + } +} + RefPtr Document::record_display_list(PaintConfig config) { + if (m_cached_display_list && m_cached_display_list_paint_config == config) + return m_cached_display_list; + auto display_list = Painting::DisplayList::create(); Painting::DisplayListRecorder display_list_recorder(display_list); @@ -5436,6 +5461,9 @@ RefPtr Document::record_display_list(PaintConfig config) m_needs_repaint = false; + m_cached_display_list = display_list; + m_cached_display_list_paint_config = config; + return display_list; } diff --git a/Userland/Libraries/LibWeb/DOM/Document.h b/Userland/Libraries/LibWeb/DOM/Document.h index 6b2d1bff0de..95938817fa1 100644 --- a/Userland/Libraries/LibWeb/DOM/Document.h +++ b/Userland/Libraries/LibWeb/DOM/Document.h @@ -708,9 +708,13 @@ public: bool should_show_line_box_borders { false }; bool has_focus { false }; Optional canvas_fill_rect {}; + + bool operator==(PaintConfig const& other) const = default; }; RefPtr record_display_list(PaintConfig); + void invalidate_display_list(); + protected: virtual void initialize(JS::Realm&) override; virtual void visit_edges(Cell::Visitor&) override; @@ -986,6 +990,9 @@ private: WeakPtr m_cached_navigable; bool m_needs_repaint { false }; + + Optional m_cached_display_list_paint_config; + RefPtr m_cached_display_list; }; template<> diff --git a/Userland/Libraries/LibWeb/DOM/Range.cpp b/Userland/Libraries/LibWeb/DOM/Range.cpp index 0a4ab926f1d..bf05321ed63 100644 --- a/Userland/Libraries/LibWeb/DOM/Range.cpp +++ b/Userland/Libraries/LibWeb/DOM/Range.cpp @@ -98,6 +98,7 @@ void Range::update_associated_selection() { if (auto* viewport = m_start_container->document().paintable()) { viewport->recompute_selection_states(); + m_start_container->document().invalidate_display_list(); viewport->set_needs_display(); } diff --git a/Userland/Libraries/LibWeb/HTML/CanvasRenderingContext2D.cpp b/Userland/Libraries/LibWeb/HTML/CanvasRenderingContext2D.cpp index 083ac2621ad..484892256d3 100644 --- a/Userland/Libraries/LibWeb/HTML/CanvasRenderingContext2D.cpp +++ b/Userland/Libraries/LibWeb/HTML/CanvasRenderingContext2D.cpp @@ -186,6 +186,7 @@ Gfx::Painter* CanvasRenderingContext2D::painter() if (!canvas_element().bitmap()) { if (!canvas_element().create_bitmap()) return nullptr; + canvas_element().document().invalidate_display_list(); m_painter = make(*canvas_element().bitmap()); } return m_painter.ptr(); diff --git a/Userland/Libraries/LibWeb/HTML/Navigable.h b/Userland/Libraries/LibWeb/HTML/Navigable.h index 3745f171e96..96d4e313d0b 100644 --- a/Userland/Libraries/LibWeb/HTML/Navigable.h +++ b/Userland/Libraries/LibWeb/HTML/Navigable.h @@ -22,6 +22,7 @@ #include #include #include +#include #include #include diff --git a/Userland/Libraries/LibWeb/HTML/TraversableNavigable.cpp b/Userland/Libraries/LibWeb/HTML/TraversableNavigable.cpp index 63d87817b6a..ad966cef041 100644 --- a/Userland/Libraries/LibWeb/HTML/TraversableNavigable.cpp +++ b/Userland/Libraries/LibWeb/HTML/TraversableNavigable.cpp @@ -16,6 +16,7 @@ #include #include #include +#include #include namespace Web::HTML { @@ -1193,6 +1194,12 @@ void TraversableNavigable::paint(DevicePixelRect const& content_rect, Painting:: if (!document) return; + for (auto& navigable : all_navigables()) { + if (auto active_document = navigable->active_document(); active_document && active_document->paintable()) { + active_document->paintable()->refresh_scroll_state(); + } + } + DOM::Document::PaintConfig paint_config; paint_config.paint_overlay = paint_options.paint_overlay == PaintOptions::PaintOverlay::Yes; paint_config.should_show_line_box_borders = paint_options.should_show_line_box_borders;