1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-06-25 16:22:07 +00:00

LibWeb: Use copy-on-write to make cloning a FormattingState much cheaper

Instead of making a full copy of every NodeState when cloning a
FormattingState, we make NodeState ref-counted and implement a basic
copy-on-write mechanism.

FormattingState::get_mutable() now makes a deep copy of the NodeState
when first accessed *if* it is shared with other FormattingStates.
This commit is contained in:
Andreas Kling 2022-02-28 12:32:23 +01:00
parent 0a533da22f
commit aa72ebf7aa
2 changed files with 36 additions and 18 deletions

View file

@ -9,6 +9,23 @@
namespace Web::Layout { 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);
}
return state;
}
FormattingState::NodeState const& FormattingState::get(NodeWithStyleAndBoxModelMetrics const& box) const
{
return *const_cast<FormattingState&>(*this).nodes.ensure(&box, [] { return adopt_ref(*new NodeState); });
}
void FormattingState::commit() void FormattingState::commit()
{ {
for (auto& it : nodes) { for (auto& it : nodes) {

View file

@ -62,30 +62,31 @@ struct FormattingState {
overflow_data = Layout::Box::OverflowData {}; overflow_data = Layout::Box::OverflowData {};
return *overflow_data; return *overflow_data;
} }
// 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(); void commit();
FormattingState clone() const // NOTE: get_mutable() will CoW the NodeState if it's shared with another FormattingContext.
{ NodeState& get_mutable(NodeWithStyleAndBoxModelMetrics const&);
FormattingState new_state;
for (auto& it : nodes) {
new_state.nodes.set(it.key, make<NodeState>(*it.value));
}
return new_state;
}
NodeState& get_mutable(NodeWithStyleAndBoxModelMetrics const& box) // NOTE: get() will not CoW the NodeState.
{ NodeState const& get(NodeWithStyleAndBoxModelMetrics const&) const;
return *nodes.ensure(&box, [] { return make<NodeState>(); });
}
NodeState const& get(NodeWithStyleAndBoxModelMetrics const& box) const HashMap<NodeWithStyleAndBoxModelMetrics const*, NonnullRefPtr<NodeState>> nodes;
{
return const_cast<FormattingState&>(*this).get_mutable(box);
}
HashMap<NodeWithStyleAndBoxModelMetrics const*, NonnullOwnPtr<NodeState>> nodes;
}; };
Gfx::FloatRect absolute_content_rect(Box const&, FormattingState const&); Gfx::FloatRect absolute_content_rect(Box const&, FormattingState const&);