From 12c6ac78e20904431372dec1e81f896807c9f35a Mon Sep 17 00:00:00 2001 From: Aliaksandr Kalenik Date: Fri, 7 Mar 2025 20:37:04 +0100 Subject: [PATCH] LibWeb: Cache intrinsic sizes across layout runs This change moves intrinsic sizes cache from LayoutState, which is local to current layout run, to layout nodes, so it could be reused between layout runs. This optimization is possible because we can guarantee that these measurements will remain unchanged unless the style of the element or any of its descendants changes. For now, invalidation is implemented simply by resetting cache on whole ancestors chain once we figured that element needs layout update. The case when layout is invalidated by DOM's structural changes is covered by layout tree invalidation that drops intrinsic sizes cache along with layout nodes. I measured improvement on couple websites: - Mail list on GMail 28ms -> 6ms - GitHub large code page 47ms -> 36ms - Discord chat history 15ms -> 8ms (Time does not include `commit()`) --- .../LibWeb/Animations/KeyframeEffect.cpp | 2 +- Libraries/LibWeb/DOM/CharacterData.cpp | 2 +- Libraries/LibWeb/DOM/Document.cpp | 34 +++++------ Libraries/LibWeb/DOM/Document.h | 24 -------- Libraries/LibWeb/DOM/Node.cpp | 23 +++++++- Libraries/LibWeb/DOM/Node.h | 26 +++++++++ Libraries/LibWeb/HTML/HTMLImageElement.cpp | 4 +- Libraries/LibWeb/HTML/HTMLVideoElement.cpp | 2 +- Libraries/LibWeb/HTML/Navigable.cpp | 4 +- Libraries/LibWeb/Layout/Box.h | 17 ++++++ Libraries/LibWeb/Layout/FormattingContext.cpp | 58 +++++++------------ Libraries/LibWeb/Layout/LayoutState.h | 12 ---- Libraries/LibWeb/SVG/SVGImageElement.cpp | 2 +- 13 files changed, 108 insertions(+), 102 deletions(-) diff --git a/Libraries/LibWeb/Animations/KeyframeEffect.cpp b/Libraries/LibWeb/Animations/KeyframeEffect.cpp index 3a776e4a136..543a827cd22 100644 --- a/Libraries/LibWeb/Animations/KeyframeEffect.cpp +++ b/Libraries/LibWeb/Animations/KeyframeEffect.cpp @@ -962,7 +962,7 @@ void KeyframeEffect::update_computed_properties() } if (invalidation.relayout) - document.set_needs_layout(DOM::SetNeedsLayoutReason::KeyframeEffect); + target->set_needs_layout_update(DOM::SetNeedsLayoutReason::KeyframeEffect); if (invalidation.rebuild_layout_tree) document.invalidate_layout_tree(DOM::InvalidateLayoutTreeReason::KeyframeEffect); if (invalidation.repaint) { diff --git a/Libraries/LibWeb/DOM/CharacterData.cpp b/Libraries/LibWeb/DOM/CharacterData.cpp index 0c008293277..663d3b1b428 100644 --- a/Libraries/LibWeb/DOM/CharacterData.cpp +++ b/Libraries/LibWeb/DOM/CharacterData.cpp @@ -147,7 +147,7 @@ WebIDL::ExceptionOr CharacterData::replace_data(size_t offset, size_t coun static_cast(*layout_node).invalidate_text_for_rendering(); // We also need to relayout. - document().set_needs_layout(SetNeedsLayoutReason::CharacterDataReplaceData); + set_needs_layout_update(SetNeedsLayoutReason::CharacterDataReplaceData); } document().bump_character_data_version(); diff --git a/Libraries/LibWeb/DOM/Document.cpp b/Libraries/LibWeb/DOM/Document.cpp index 011429087b2..97418b439ca 100644 --- a/Libraries/LibWeb/DOM/Document.cpp +++ b/Libraries/LibWeb/DOM/Document.cpp @@ -1211,20 +1211,6 @@ Optional Document::encoding_parse_and_serialize_url(StringView url) cons return parsed_url->serialize(); } -void Document::set_needs_layout(SetNeedsLayoutReason reason) -{ - if (m_needs_layout) - return; - if constexpr (UPDATE_LAYOUT_DEBUG) { - // NOTE: We check some conditions here to avoid debug spam in documents that don't do layout. - auto navigable = this->navigable(); - if (m_layout_root && navigable && navigable->active_document() == this) - dbgln_if(UPDATE_LAYOUT_DEBUG, "NEED LAYOUT {}", to_string(reason)); - } - m_needs_layout = true; - schedule_layout_update(); -} - void Document::invalidate_layout_tree(InvalidateLayoutTreeReason reason) { if (m_layout_root) @@ -1299,7 +1285,7 @@ void Document::update_layout(UpdateLayoutReason reason) update_style(); - if (!m_needs_layout && m_layout_root) + if (!m_needs_layout_update && m_layout_root) return; // NOTE: If this is a document hosting