mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-08-11 02:29:21 +00:00
LibWeb: Defer invalidation sets processing until update_style()
Some checks are pending
CI / macOS, arm64, Sanitizer, Clang (push) Waiting to run
CI / Linux, x86_64, Fuzzers, Clang (push) Waiting to run
CI / Linux, x86_64, Sanitizer, GNU (push) Waiting to run
CI / Linux, x86_64, Sanitizer, Clang (push) Waiting to run
Package the js repl as a binary artifact / Linux, arm64 (push) Waiting to run
Package the js repl as a binary artifact / macOS, arm64 (push) Waiting to run
Package the js repl as a binary artifact / Linux, x86_64 (push) Waiting to run
Run test262 and test-wasm / run_and_update_results (push) Waiting to run
Lint Code / lint (push) Waiting to run
Label PRs with merge conflicts / auto-labeler (push) Waiting to run
Push notes / build (push) Waiting to run
Some checks are pending
CI / macOS, arm64, Sanitizer, Clang (push) Waiting to run
CI / Linux, x86_64, Fuzzers, Clang (push) Waiting to run
CI / Linux, x86_64, Sanitizer, GNU (push) Waiting to run
CI / Linux, x86_64, Sanitizer, Clang (push) Waiting to run
Package the js repl as a binary artifact / Linux, arm64 (push) Waiting to run
Package the js repl as a binary artifact / macOS, arm64 (push) Waiting to run
Package the js repl as a binary artifact / Linux, x86_64 (push) Waiting to run
Run test262 and test-wasm / run_and_update_results (push) Waiting to run
Lint Code / lint (push) Waiting to run
Label PRs with merge conflicts / auto-labeler (push) Waiting to run
Push notes / build (push) Waiting to run
This change converts `Node::invalidate_style()` (invalidation sets overload) from eagerly doing tree traversal that marks elements affected by invalidation set to instead adding "pending invalidation sets" into `StyleInvalidator`, processing of which is deferred until the next `update_style()`. By doing that we sometimes substantially reduce amount of work done performing tree traversal that marks elements for style recalculation. Improves performance on Discord, were according to my measurements we were previously spending 20% of time in style invalidation, but now it's down to <1%.
This commit is contained in:
parent
8cbe27b2f9
commit
b0ef7f4427
Notes:
github-actions[bot]
2025-07-16 22:44:27 +00:00
Author: https://github.com/kalenikaliaksandr
Commit: b0ef7f4427
Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/5474
6 changed files with 82 additions and 18 deletions
|
@ -1508,10 +1508,10 @@ void Document::update_style()
|
|||
|
||||
invalidate_style_of_elements_affected_by_has();
|
||||
|
||||
if (!needs_full_style_update() && !needs_style_update() && !child_needs_style_update())
|
||||
if (!m_style_invalidator->has_pending_invalidations() && !needs_full_style_update() && !needs_style_update() && !child_needs_style_update())
|
||||
return;
|
||||
|
||||
m_style_invalidator->perform_pending_style_invalidations(*this, false);
|
||||
m_style_invalidator->invalidate(*this);
|
||||
|
||||
// NOTE: If this is a document hosting <template> contents, style update is unnecessary.
|
||||
if (m_created_for_appropriate_template_contents)
|
||||
|
|
|
@ -903,6 +903,8 @@ public:
|
|||
|
||||
String dump_display_list();
|
||||
|
||||
StyleInvalidator& style_invalidator() { return m_style_invalidator; }
|
||||
|
||||
protected:
|
||||
virtual void initialize(JS::Realm&) override;
|
||||
virtual void visit_edges(Cell::Visitor&) override;
|
||||
|
|
|
@ -36,6 +36,7 @@
|
|||
#include <LibWeb/DOM/Range.h>
|
||||
#include <LibWeb/DOM/ShadowRoot.h>
|
||||
#include <LibWeb/DOM/StaticNodeList.h>
|
||||
#include <LibWeb/DOM/StyleInvalidator.h>
|
||||
#include <LibWeb/DOM/XMLDocument.h>
|
||||
#include <LibWeb/HTML/CustomElements/CustomElementReactionNames.h>
|
||||
#include <LibWeb/HTML/HTMLAnchorElement.h>
|
||||
|
@ -521,20 +522,7 @@ void Node::invalidate_style(StyleInvalidationReason reason, Vector<CSS::Invalida
|
|||
return;
|
||||
}
|
||||
|
||||
for_each_shadow_including_inclusive_descendant([&](Node& node) {
|
||||
if (!node.is_element())
|
||||
return TraversalDecision::Continue;
|
||||
auto& element = static_cast<Element&>(node);
|
||||
bool needs_style_recalculation = false;
|
||||
if (element.includes_properties_from_invalidation_set(invalidation_set)) {
|
||||
needs_style_recalculation = true;
|
||||
} else if (options.invalidate_elements_that_use_css_custom_properties && element.style_uses_css_custom_properties()) {
|
||||
needs_style_recalculation = true;
|
||||
}
|
||||
if (needs_style_recalculation)
|
||||
element.set_needs_style_update(true);
|
||||
return TraversalDecision::Continue;
|
||||
});
|
||||
document().style_invalidator().add_pending_invalidation(*this, move(invalidation_set), options.invalidate_elements_that_use_css_custom_properties);
|
||||
}
|
||||
|
||||
String Node::child_text_content() const
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibWeb/CSS/InvalidationSet.h>
|
||||
#include <LibWeb/DOM/Element.h>
|
||||
#include <LibWeb/DOM/Node.h>
|
||||
#include <LibWeb/DOM/ShadowRoot.h>
|
||||
|
@ -13,8 +14,32 @@ namespace Web::DOM {
|
|||
|
||||
GC_DEFINE_ALLOCATOR(StyleInvalidator);
|
||||
|
||||
// This function makes a full pass over the entire DOM and converts "entire subtree needs style update"
|
||||
// into "needs style update" for each inclusive descendant where it's found.
|
||||
void StyleInvalidator::visit_edges(Cell::Visitor& visitor)
|
||||
{
|
||||
Base::visit_edges(visitor);
|
||||
visitor.visit(m_pending_invalidations);
|
||||
}
|
||||
|
||||
void StyleInvalidator::invalidate(Node& node)
|
||||
{
|
||||
perform_pending_style_invalidations(node, false);
|
||||
m_pending_invalidations.clear();
|
||||
}
|
||||
|
||||
void StyleInvalidator::add_pending_invalidation(GC::Ref<Node> node, CSS::InvalidationSet&& invalidation_set, bool invalidate_elements_that_use_css_custom_properties)
|
||||
{
|
||||
auto& pending_invalidation = m_pending_invalidations.ensure(node, [&] {
|
||||
return PendingInvalidation {};
|
||||
});
|
||||
pending_invalidation.invalidation_set.include_all_from(invalidation_set);
|
||||
if (invalidate_elements_that_use_css_custom_properties) {
|
||||
pending_invalidation.invalidate_elements_that_use_css_custom_properties = true;
|
||||
}
|
||||
}
|
||||
|
||||
// This function makes a full pass over the entire DOM and:
|
||||
// - converts "entire subtree needs style update" into "needs style update" for each inclusive descendant where it's found.
|
||||
// - marks nodes included into pending invalidation sets as "needs style update"
|
||||
void StyleInvalidator::perform_pending_style_invalidations(Node& node, bool invalidate_entire_subtree)
|
||||
{
|
||||
invalidate_entire_subtree |= node.entire_subtree_needs_style_update();
|
||||
|
@ -25,6 +50,37 @@ void StyleInvalidator::perform_pending_style_invalidations(Node& node, bool inva
|
|||
node.set_child_needs_style_update(true);
|
||||
}
|
||||
|
||||
auto previous_subtree_invalidations_sets_size = m_subtree_invalidation_sets.size();
|
||||
auto previous_invalidate_elements_that_use_css_custom_properties = m_invalidate_elements_that_use_css_custom_properties;
|
||||
ScopeGuard restore_state = [this, previous_subtree_invalidations_sets_size, previous_invalidate_elements_that_use_css_custom_properties] {
|
||||
m_subtree_invalidation_sets.shrink(previous_subtree_invalidations_sets_size);
|
||||
m_invalidate_elements_that_use_css_custom_properties = previous_invalidate_elements_that_use_css_custom_properties;
|
||||
};
|
||||
|
||||
if (!invalidate_entire_subtree) {
|
||||
auto pending_invalidation = m_pending_invalidations.get(node);
|
||||
if (pending_invalidation.has_value()) {
|
||||
m_subtree_invalidation_sets.append(pending_invalidation->invalidation_set);
|
||||
m_invalidate_elements_that_use_css_custom_properties = m_invalidate_elements_that_use_css_custom_properties || pending_invalidation->invalidate_elements_that_use_css_custom_properties;
|
||||
}
|
||||
|
||||
auto affected_by_invalidation_sets_or_invalidation_flags = [this](Element const& element) {
|
||||
if (m_invalidate_elements_that_use_css_custom_properties && element.style_uses_css_custom_properties()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
for (auto& invalidation_set : m_subtree_invalidation_sets) {
|
||||
if (element.includes_properties_from_invalidation_set(invalidation_set))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
if (auto* element = as_if<Element>(node); element && affected_by_invalidation_sets_or_invalidation_flags(*element)) {
|
||||
node.set_needs_style_update(true);
|
||||
}
|
||||
}
|
||||
|
||||
for (auto* child = node.first_child(); child; child = child->next_sibling()) {
|
||||
perform_pending_style_invalidations(*child, invalidate_entire_subtree);
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#pragma once
|
||||
|
||||
#include <LibGC/CellAllocator.h>
|
||||
#include <LibWeb/CSS/InvalidationSet.h>
|
||||
#include <LibWeb/Forward.h>
|
||||
|
||||
namespace Web::DOM {
|
||||
|
@ -16,7 +17,23 @@ class StyleInvalidator : public GC::Cell {
|
|||
GC_DECLARE_ALLOCATOR(StyleInvalidator);
|
||||
|
||||
public:
|
||||
void invalidate(Node& node);
|
||||
void add_pending_invalidation(GC::Ref<Node>, CSS::InvalidationSet&&, bool invalidate_elements_that_use_css_custom_properties);
|
||||
bool has_pending_invalidations() const { return !m_pending_invalidations.is_empty(); }
|
||||
|
||||
virtual void visit_edges(Cell::Visitor& visitor) override;
|
||||
|
||||
private:
|
||||
void perform_pending_style_invalidations(Node& node, bool invalidate_entire_subtree);
|
||||
|
||||
struct PendingInvalidation {
|
||||
bool invalidate_elements_that_use_css_custom_properties { false };
|
||||
CSS::InvalidationSet invalidation_set;
|
||||
};
|
||||
HashMap<GC::Ref<Node>, PendingInvalidation> m_pending_invalidations;
|
||||
|
||||
Vector<CSS::InvalidationSet> m_subtree_invalidation_sets;
|
||||
bool m_invalidate_elements_that_use_css_custom_properties { false };
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -268,6 +268,7 @@ class GuaranteedInvalidStyleValue;
|
|||
class ImageStyleValue;
|
||||
class IntegerOrCalculated;
|
||||
class IntegerStyleValue;
|
||||
class InvalidationSet;
|
||||
class Length;
|
||||
class LengthBox;
|
||||
class LengthOrCalculated;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue