diff options
author | Andreas Kling <kling@serenityos.org> | 2022-03-12 16:33:11 +0100 |
---|---|---|
committer | Andreas Kling <kling@serenityos.org> | 2022-03-13 00:04:51 +0100 |
commit | 515db5fc1b3516d21c96c49f994868a937afecd8 (patch) | |
tree | 4170946e904f275be9845a2bfa898d7c59632c00 /Userland/Libraries | |
parent | b6097cf7249cbe000645f9aa05255074d4faaccb (diff) | |
download | serenity-515db5fc1b3516d21c96c49f994868a937afecd8.zip |
LibWeb: Make Layout::FormattingState copies shallow
Previously, each NodeState in a FormattingState was shared with the
parent FormattingState, but the HashMap of NodeState had to be copied
when making FormattingState copies.
This patch makes copying instant by keeping a pointer to the parent
FormattingState instead. When fetching immutable state via get(), we may
now return a reference to a NodeState owned by a parent FormattingState.
get_mutable() will copy any NodeState found in the ancestor chain before
making a brand new one.
Diffstat (limited to 'Userland/Libraries')
4 files changed, 40 insertions, 30 deletions
diff --git a/Userland/Libraries/LibWeb/Layout/FlexFormattingContext.cpp b/Userland/Libraries/LibWeb/Layout/FlexFormattingContext.cpp index 7c584398d1..e011062780 100644 --- a/Userland/Libraries/LibWeb/Layout/FlexFormattingContext.cpp +++ b/Userland/Libraries/LibWeb/Layout/FlexFormattingContext.cpp @@ -440,7 +440,7 @@ float FlexFormattingContext::calculate_indefinite_main_size(FlexItem const& item if (has_definite_cross_size(item.box)) { // For indefinite main sizes, we perform a throwaway layout and then measure it. - auto throwaway_state = m_state; + FormattingState throwaway_state(&m_state); auto& box_state = throwaway_state.get_mutable(item.box); // Item has definite cross size, layout with that as the used cross size. @@ -470,7 +470,7 @@ float FlexFormattingContext::calculate_indefinite_main_size(FlexItem const& item // then layout with that and see what height comes out of it. float fit_content_cross_size = calculate_fit_content_width(item.box, m_available_space->cross); - auto throwaway_state = m_state; + FormattingState throwaway_state(&m_state); auto& box_state = throwaway_state.get_mutable(item.box); // Item has definite cross size, layout with that as the used cross size. @@ -814,7 +814,7 @@ void FlexFormattingContext::determine_hypothetical_cross_size_of_item(FlexItem& if (has_definite_main_size(item.box)) { // For indefinite cross sizes, we perform a throwaway layout and then measure it. - auto throwaway_state = m_state; + FormattingState throwaway_state(&m_state); auto& box_state = throwaway_state.get_mutable(item.box); // Item has definite main size, layout with that as the used main size. @@ -842,7 +842,7 @@ void FlexFormattingContext::determine_hypothetical_cross_size_of_item(FlexItem& else fit_content_main_size = calculate_fit_content_height(item.box, m_available_space->main); - auto throwaway_state = m_state; + FormattingState throwaway_state(&m_state); auto& box_state = throwaway_state.get_mutable(item.box); // Item has definite main size, layout with that as the used main size. diff --git a/Userland/Libraries/LibWeb/Layout/FormattingContext.cpp b/Userland/Libraries/LibWeb/Layout/FormattingContext.cpp index 3baf0a60b0..03cf9bc5cf 100644 --- a/Userland/Libraries/LibWeb/Layout/FormattingContext.cpp +++ b/Userland/Libraries/LibWeb/Layout/FormattingContext.cpp @@ -824,7 +824,7 @@ FormattingState::IntrinsicSizes FormattingContext::calculate_intrinsic_sizes(Lay auto& cached_box_sizes = m_state.intrinsic_sizes.ensure(&box); auto const& containing_block = *box.containing_block(); { - auto throwaway_state = m_state; + FormattingState throwaway_state(&m_state); throwaway_state.get_mutable(containing_block).content_width = INFINITY; throwaway_state.get_mutable(containing_block).content_height = INFINITY; auto independent_formatting_context = const_cast<FormattingContext*>(this)->create_independent_formatting_context_if_needed(throwaway_state, box); @@ -836,7 +836,7 @@ FormattingState::IntrinsicSizes FormattingContext::calculate_intrinsic_sizes(Lay } { - auto throwaway_state = m_state; + FormattingState throwaway_state(&m_state); throwaway_state.get_mutable(containing_block).content_width = 0; throwaway_state.get_mutable(containing_block).content_height = 0; auto independent_formatting_context = const_cast<FormattingContext*>(this)->create_independent_formatting_context_if_needed(throwaway_state, box); diff --git a/Userland/Libraries/LibWeb/Layout/FormattingState.cpp b/Userland/Libraries/LibWeb/Layout/FormattingState.cpp index 7b70b43628..eeb8b6d37e 100644 --- a/Userland/Libraries/LibWeb/Layout/FormattingState.cpp +++ b/Userland/Libraries/LibWeb/Layout/FormattingState.cpp @@ -12,23 +12,39 @@ namespace Web::Layout { FormattingState::NodeState& FormattingState::get_mutable(NodeWithStyleAndBoxModelMetrics const& box) { - auto state = nodes.ensure(&box, [] { return adopt_ref(*new NodeState); }); - // CoW if ref_count > 2 (1 for the entry in `this->nodes`, 1 for the `state` local in this function) - if (state->ref_count > 2) { - state = adopt_ref(*new NodeState { *state }); - state->ref_count = 1; - nodes.set(&box, state); + if (auto it = nodes.find(&box); it != nodes.end()) + return *it->value; + + for (auto* ancestor = m_parent; ancestor; ancestor = ancestor->m_parent) { + if (auto it = ancestor->nodes.find(&box); it != ancestor->nodes.end()) { + auto cow_node_state = adopt_own(*new NodeState(*it->value)); + auto* cow_node_state_ptr = cow_node_state.ptr(); + nodes.set(&box, move(cow_node_state)); + return *cow_node_state_ptr; + } } - return state; + + return *nodes.ensure(&box, [] { return adopt_own(*new NodeState); }); } FormattingState::NodeState const& FormattingState::get(NodeWithStyleAndBoxModelMetrics const& box) const { - return *const_cast<FormattingState&>(*this).nodes.ensure(&box, [] { return adopt_ref(*new NodeState); }); + if (auto it = nodes.find(&box); it != nodes.end()) + return *it->value; + + for (auto* ancestor = m_parent; ancestor; ancestor = ancestor->m_parent) { + if (auto it = ancestor->nodes.find(&box); it != ancestor->nodes.end()) + return *it->value; + } + + return *const_cast<FormattingState&>(*this).nodes.ensure(&box, [] { return adopt_own(*new NodeState); }); } void FormattingState::commit() { + // Only the top-level FormattingState should ever be committed. + VERIFY(!m_parent); + HashTable<Layout::TextNode*> text_nodes; for (auto& it : nodes) { diff --git a/Userland/Libraries/LibWeb/Layout/FormattingState.h b/Userland/Libraries/LibWeb/Layout/FormattingState.h index 2983e2f89c..0598d49e0f 100644 --- a/Userland/Libraries/LibWeb/Layout/FormattingState.h +++ b/Userland/Libraries/LibWeb/Layout/FormattingState.h @@ -15,6 +15,12 @@ namespace Web::Layout { struct FormattingState { + FormattingState() { } + explicit FormattingState(FormattingState const* parent) + : m_parent(parent) + { + } + struct NodeState { float content_width { 0 }; float content_height { 0 }; @@ -65,31 +71,17 @@ struct FormattingState { } Optional<LineBoxFragmentCoordinate> containing_line_box_fragment; - - // NOTE: NodeState is ref-counted and accessed via copy-on-write helpers below. - size_t ref_count { 1 }; - void ref() - { - VERIFY(ref_count); - ++ref_count; - } - void unref() - { - VERIFY(ref_count); - if (!--ref_count) - delete this; - } }; void commit(); - // NOTE: get_mutable() will CoW the NodeState if it's shared with another FormattingContext. + // NOTE: get_mutable() will CoW the NodeState if it's inherited from an ancestor state; NodeState& get_mutable(NodeWithStyleAndBoxModelMetrics const&); // NOTE: get() will not CoW the NodeState. NodeState const& get(NodeWithStyleAndBoxModelMetrics const&) const; - HashMap<NodeWithStyleAndBoxModelMetrics const*, NonnullRefPtr<NodeState>> nodes; + HashMap<NodeWithStyleAndBoxModelMetrics const*, NonnullOwnPtr<NodeState>> nodes; // We cache intrinsic sizes once determined, as they will not change over the course of a full layout. // This avoids computing them several times while performing flex layout. @@ -98,6 +90,8 @@ struct FormattingState { Gfx::FloatSize max_content_size; }; HashMap<NodeWithStyleAndBoxModelMetrics const*, IntrinsicSizes> mutable intrinsic_sizes; + + FormattingState const* m_parent { nullptr }; }; Gfx::FloatRect absolute_content_rect(Box const&, FormattingState const&); |