Commit 63b48821 authored by Yoshifumi Inoue's avatar Yoshifumi Inoue Committed by Commit Bot

[FragmentItem] Introduce next for same layout object cache in NGFragmentItem

This patch introduces next for same layout object cache in |NGFragmentItem| to
make following functions faster:
 - NGInlineCursor::NextForSameLayoutObject()
 - ItemsForLayoutObject::Iterator::operator++()

Once we utilize |NGInlineCursor| for all |LayoutObject::FirstInlineFragment()|
usages[1], following patch will make following functions to utilize cache:
 - NGInlineCursor::MoveTo(const LayoutObject&)
 - ItemsForLayoutObject constructr


[1] https://bit.ly/2MSgvxN List of FirstInlineFragment() usages

Bug: 982194
Change-Id: Id34e08b0b87d9471058ee5899b0fbe6e591883f3
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1930370
Commit-Queue: Yoshifumi Inoue <yosin@chromium.org>
Reviewed-by: default avatarKoji Ishii <kojii@chromium.org>
Auto-Submit: Yoshifumi Inoue <yosin@chromium.org>
Cr-Commit-Position: refs/heads/master@{#719897}
parent 82d010e0
......@@ -2339,9 +2339,25 @@ void LayoutBox::DirtyLineBoxes(bool full_layout) {
void LayoutBox::SetFirstInlineFragment(NGPaintFragment* fragment) {
CHECK(IsInLayoutNGInlineFormattingContext()) << *this;
// TODO(yosin): Once we remove |NGPaintFragment|, we should get rid of
// |!fragment|.
DCHECK(!fragment || !RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled());
first_paint_fragment_ = fragment;
}
void LayoutBox::ClearFirstInlineFragmentItemIndex() {
CHECK(IsInLayoutNGInlineFormattingContext()) << *this;
DCHECK(RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled());
first_fragment_item_index_ = 0u;
}
void LayoutBox::SetFirstInlineFragmentItemIndex(wtf_size_t index) {
CHECK(IsInLayoutNGInlineFormattingContext()) << *this;
DCHECK(RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled());
DCHECK_NE(index, 0u);
first_fragment_item_index_ = index;
}
void LayoutBox::InLayoutNGInlineFormattingContextWillChange(bool new_value) {
DeleteLineBoxWrapper();
......
......@@ -943,6 +943,9 @@ class CORE_EXPORT LayoutBox : public LayoutBoxModelObject {
NGPaintFragment* FirstInlineFragment() const final;
void SetFirstInlineFragment(NGPaintFragment*) final;
wtf_size_t FirstInlineFragmentItemIndex() const final;
void ClearFirstInlineFragmentItemIndex() final;
void SetFirstInlineFragmentItemIndex(wtf_size_t) final;
void SetCachedLayoutResult(const NGLayoutResult&, const NGBreakToken*);
void ClearCachedLayoutResult();
......@@ -1876,6 +1879,10 @@ class CORE_EXPORT LayoutBox : public LayoutBoxModelObject {
// atomic inline elements. Valid only when
// IsInLayoutNGInlineFormattingContext().
NGPaintFragment* first_paint_fragment_;
// The index of the first fragment item associated with this object in
// |NGFragmentItems::Items()|. Zero means there are no such item.
// Valid only when IsInLayoutNGInlineFormattingContext().
wtf_size_t first_fragment_item_index_;
};
std::unique_ptr<LayoutBoxRareData> rare_data_;
......@@ -1960,8 +1967,20 @@ inline void LayoutBox::SetInlineBoxWrapper(InlineBox* box_wrapper) {
}
inline NGPaintFragment* LayoutBox::FirstInlineFragment() const {
return IsInLayoutNGInlineFormattingContext() ? first_paint_fragment_
: nullptr;
if (!IsInLayoutNGInlineFormattingContext())
return nullptr;
// TODO(yosin): Once we replace all usage of |FirstInlineFragment()| to
// |NGInlineCursor|, we should change this to |DCHECK()|.
if (RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled())
return nullptr;
return first_paint_fragment_;
}
inline wtf_size_t LayoutBox::FirstInlineFragmentItemIndex() const {
if (!IsInLayoutNGInlineFormattingContext())
return 0u;
DCHECK(RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled());
return first_fragment_item_index_;
}
inline bool LayoutBox::IsForcedFragmentainerBreakValue(
......
......@@ -144,10 +144,26 @@ void LayoutInline::DeleteLineBoxes() {
}
void LayoutInline::SetFirstInlineFragment(NGPaintFragment* fragment) {
CHECK(IsInLayoutNGInlineFormattingContext());
CHECK(IsInLayoutNGInlineFormattingContext()) << *this;
// TODO(yosin): Once we remove |NGPaintFragment|, we should get rid of
// |!fragment|.
DCHECK(!fragment || !RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled());
first_paint_fragment_ = fragment;
}
void LayoutInline::ClearFirstInlineFragmentItemIndex() {
CHECK(IsInLayoutNGInlineFormattingContext()) << *this;
DCHECK(RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled());
first_fragment_item_index_ = 0u;
}
void LayoutInline::SetFirstInlineFragmentItemIndex(wtf_size_t index) {
CHECK(IsInLayoutNGInlineFormattingContext()) << *this;
DCHECK(RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled());
DCHECK_NE(index, 0u);
first_fragment_item_index_ = index;
}
void LayoutInline::InLayoutNGInlineFormattingContextWillChange(bool new_value) {
DeleteLineBoxes();
......
......@@ -174,6 +174,9 @@ class CORE_EXPORT LayoutInline : public LayoutBoxModelObject {
NGPaintFragment* FirstInlineFragment() const final;
void SetFirstInlineFragment(NGPaintFragment*) final;
wtf_size_t FirstInlineFragmentItemIndex() const final;
void ClearFirstInlineFragmentItemIndex() final;
void SetFirstInlineFragmentItemIndex(wtf_size_t) final;
// Return true if this inline doesn't occur on any lines, i.e. when it creates
// no fragments.
......@@ -412,6 +415,10 @@ class CORE_EXPORT LayoutInline : public LayoutBoxModelObject {
// The first fragment of inline boxes associated with this object.
// Valid only when IsInLayoutNGInlineFormattingContext().
NGPaintFragment* first_paint_fragment_;
// The index of the first fragment item associated with this object in
// |NGFragmentItems::Items()|. Zero means there are no such item.
// Valid only when IsInLayoutNGInlineFormattingContext().
wtf_size_t first_fragment_item_index_;
};
};
......@@ -421,8 +428,20 @@ inline LineBoxList* LayoutInline::MutableLineBoxes() {
}
inline NGPaintFragment* LayoutInline::FirstInlineFragment() const {
return IsInLayoutNGInlineFormattingContext() ? first_paint_fragment_
: nullptr;
if (!IsInLayoutNGInlineFormattingContext())
return nullptr;
// TODO(yosin): Once we replace all usage of |FirstInlineFragment()| to
// |NGInlineCursor|, we should change this to |DCHECK()|.
if (RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled())
return nullptr;
return first_paint_fragment_;
}
inline wtf_size_t LayoutInline::FirstInlineFragmentItemIndex() const {
if (!IsInLayoutNGInlineFormattingContext())
return 0u;
DCHECK(RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled());
return first_fragment_item_index_;
}
DEFINE_LAYOUT_OBJECT_TYPE_CASTS(LayoutInline, IsLayoutInline());
......
......@@ -1379,6 +1379,9 @@ class CORE_EXPORT LayoutObject : public ImageResourceObserver,
void SetIsInLayoutNGInlineFormattingContext(bool);
virtual NGPaintFragment* FirstInlineFragment() const { return nullptr; }
virtual void SetFirstInlineFragment(NGPaintFragment*) {}
virtual wtf_size_t FirstInlineFragmentItemIndex() const { return 0u; }
virtual void ClearFirstInlineFragmentItemIndex() {}
virtual void SetFirstInlineFragmentItemIndex(wtf_size_t) {}
void SetForceLegacyLayout() { bitfields_.SetForceLegacyLayout(true); }
void SetHasBoxDecorationBackground(bool);
......
......@@ -270,6 +270,10 @@ void LayoutText::DeleteTextBoxes() {
void LayoutText::SetFirstInlineFragment(NGPaintFragment* first_fragment) {
CHECK(IsInLayoutNGInlineFormattingContext());
// TODO(yosin): Once we remove |NGPaintFragment|, we should get rid of
// |!fragment|.
DCHECK(!first_fragment ||
!RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled());
// TODO(layout-dev): Because We should call |WillDestroy()| once for
// associated fragments, when you reuse fragments, you should construct
// NGAbstractInlineTextBox for them.
......@@ -281,6 +285,20 @@ void LayoutText::SetFirstInlineFragment(NGPaintFragment* first_fragment) {
first_paint_fragment_ = first_fragment;
}
void LayoutText::ClearFirstInlineFragmentItemIndex() {
CHECK(IsInLayoutNGInlineFormattingContext()) << *this;
DCHECK(RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled());
first_fragment_item_index_ = 0u;
}
void LayoutText::SetFirstInlineFragmentItemIndex(wtf_size_t index) {
CHECK(IsInLayoutNGInlineFormattingContext());
// TODO(yosin): Call |NGAbstractInlineTextBox::WillDestroy()|.
DCHECK(RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled());
DCHECK_NE(index, 0u);
first_fragment_item_index_ = index;
}
void LayoutText::InLayoutNGInlineFormattingContextWillChange(bool new_value) {
DeleteTextBoxes();
......
......@@ -99,6 +99,9 @@ class CORE_EXPORT LayoutText : public LayoutObject {
NGPaintFragment* FirstInlineFragment() const final;
void SetFirstInlineFragment(NGPaintFragment*) final;
wtf_size_t FirstInlineFragmentItemIndex() const final;
void ClearFirstInlineFragmentItemIndex() final;
void SetFirstInlineFragmentItemIndex(wtf_size_t) final;
const String& GetText() const { return text_; }
virtual unsigned TextStartOffset() const { return 0; }
......@@ -455,6 +458,10 @@ class CORE_EXPORT LayoutText : public LayoutObject {
// The first fragment of text boxes associated with this object.
// Valid only when IsInLayoutNGInlineFormattingContext().
NGPaintFragment* first_paint_fragment_;
// The index of the first fragment item associated with this object in
// |NGFragmentItems::Items()|. Zero means there are no such item.
// Valid only when IsInLayoutNGInlineFormattingContext().
wtf_size_t first_fragment_item_index_;
};
DOMNodeId node_id_ = kInvalidDOMNodeId;
};
......@@ -465,8 +472,20 @@ inline InlineTextBoxList& LayoutText::MutableTextBoxes() {
}
inline NGPaintFragment* LayoutText::FirstInlineFragment() const {
return IsInLayoutNGInlineFormattingContext() ? first_paint_fragment_
: nullptr;
if (!IsInLayoutNGInlineFormattingContext())
return nullptr;
// TODO(yosin): Once we replace all usage of |FirstInlineFragment()| to
// |NGInlineCursor|, we should change this to |DCHECK()|.
if (RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled())
return nullptr;
return first_paint_fragment_;
}
inline wtf_size_t LayoutText::FirstInlineFragmentItemIndex() const {
if (!IsInLayoutNGInlineFormattingContext())
return 0u;
DCHECK(RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled());
return first_fragment_item_index_;
}
inline UChar LayoutText::UncheckedCharacterAt(unsigned i) const {
......
......@@ -347,17 +347,26 @@ void NGFragmentItem::RecalcInkOverflow(
}
}
void NGFragmentItem::SetDeltaToNextForSameLayoutObject(wtf_size_t delta) {
DCHECK_NE(delta, 0u);
delta_to_next_for_same_layout_object_ = delta;
}
NGFragmentItem::ItemsForLayoutObject NGFragmentItem::ItemsFor(
const LayoutObject& layout_object) {
DCHECK(layout_object.IsInLayoutNGInlineFormattingContext());
DCHECK(layout_object.IsText() || layout_object.IsLayoutInline() ||
(layout_object.IsBox() && layout_object.IsInline()));
// TODO(kojii): This is a hot function needed by paint and several other
// operations. Make this fast, by not iterating.
if (const LayoutBlockFlow* block_flow =
layout_object.RootInlineFormattingContext()) {
if (const NGPhysicalBoxFragment* fragment = block_flow->CurrentFragment()) {
if (wtf_size_t index = layout_object.FirstInlineFragmentItemIndex()) {
const auto& items = fragment->Items()->Items();
return ItemsForLayoutObject(items, index, items[index].get());
}
// TODO(yosin): Once we update all usages of |FirstInlineFragment()|,
// we should get rid of below code.
if (const NGFragmentItems* items = fragment->Items()) {
for (unsigned i = 0; i < items->Items().size(); ++i) {
const NGFragmentItem* item = items->Items()[i].get();
......@@ -377,13 +386,12 @@ NGFragmentItem::ItemsForLayoutObject::Iterator::operator++() {
// operations. Make this fast, by not iterating.
if (!current_)
return *this;
const LayoutObject* current_layout_object = current_->GetLayoutObject();
while (++index_ < items_->size()) {
current_ = (*items_)[index_].get();
if (current_->GetLayoutObject() == current_layout_object)
return *this;
if (!current_->delta_to_next_for_same_layout_object_) {
current_ = nullptr;
return *this;
}
current_ = nullptr;
index_ += current_->delta_to_next_for_same_layout_object_;
current_ = (*items_)[index_].get();
return *this;
}
......
......@@ -101,6 +101,11 @@ class CORE_EXPORT NGFragmentItem : public DisplayItemClient {
Node* GetNode() const { return layout_object_->GetNode(); }
bool HasSameParent(const NGFragmentItem& other) const;
wtf_size_t DeltaToNextForSameLayoutObject() const {
return delta_to_next_for_same_layout_object_;
}
void SetDeltaToNextForSameLayoutObject(wtf_size_t delta);
const PhysicalRect& Rect() const { return rect_; }
const PhysicalOffset& Offset() const { return rect_.offset; }
const PhysicalSize& Size() const { return rect_.size; }
......@@ -356,7 +361,7 @@ class CORE_EXPORT NGFragmentItem : public DisplayItemClient {
std::unique_ptr<NGInkOverflow> ink_overflow_;
// Item index delta to the next item for the same |LayoutObject|.
// wtf_size_t delta_to_next_for_same_layout_object_ = 0;
wtf_size_t delta_to_next_for_same_layout_object_ = 0;
// Note: We should not add |bidi_level_| because it is used only for layout.
unsigned type_ : 2; // ItemType
......
......@@ -193,7 +193,33 @@ void NGFragmentItemsBuilder::ToFragmentItems(WritingMode writing_mode,
const PhysicalSize& outer_size,
void* data) {
ConvertToPhysical(writing_mode, direction, outer_size);
AssociateNextForSameLayoutObject();
new (data) NGFragmentItems(this);
}
void NGFragmentItemsBuilder::AssociateNextForSameLayoutObject() {
DCHECK(items_.IsEmpty() || items_[0]->Type() == NGFragmentItem::kLine);
HashMap<const LayoutObject*, wtf_size_t> last_fragment_map;
for (wtf_size_t index = 1u; index < items_.size(); ++index) {
const NGFragmentItem& item = *items_[index];
if (item.Type() == NGFragmentItem::kLine)
continue;
LayoutObject* const layout_object = item.GetMutableLayoutObject();
DCHECK(layout_object->IsInLayoutNGInlineFormattingContext()) << item;
auto insert_result = last_fragment_map.insert(layout_object, index);
if (insert_result.is_new_entry) {
// TDOO(yosin): Once we update all |LayoutObject::FirstInlineFragment()|,
// we should enable below.
// layout_object->SetFirstInlineFragmentItemIndex(index);
continue;
}
const wtf_size_t last_index = insert_result.stored_value->value;
insert_result.stored_value->value = index;
DCHECK_GT(last_index, 0u) << item;
DCHECK_LT(last_index, items_.size());
DCHECK_LT(last_index, index);
items_[last_index]->SetDeltaToNextForSameLayoutObject(index - last_index);
}
}
} // namespace blink
......@@ -73,6 +73,8 @@ class CORE_EXPORT NGFragmentItemsBuilder {
TextDirection direction,
const PhysicalSize& outer_size);
void AssociateNextForSameLayoutObject();
Vector<std::unique_ptr<NGFragmentItem>> items_;
Vector<LogicalOffset> offsets_;
String text_content_;
......
......@@ -629,7 +629,9 @@ void NGInlineCursor::MakeNull() {
void NGInlineCursor::InternalMoveTo(const LayoutObject& layout_object) {
DCHECK(layout_object.IsInLayoutNGInlineFormattingContext());
// If this cursor is rootless, find the root of the inline formatting context.
bool had_root = true;
if (!HasRoot()) {
had_root = false;
const LayoutBlockFlow& root = *layout_object.RootInlineFormattingContext();
DCHECK(&root);
SetRoot(root);
......@@ -645,10 +647,30 @@ void NGInlineCursor::InternalMoveTo(const LayoutObject& layout_object) {
}
}
if (fragment_items_) {
item_iter_ = items_.begin();
while (current_item_ && CurrentLayoutObject() != &layout_object)
MoveToNextItem();
return;
const wtf_size_t index = layout_object.FirstInlineFragmentItemIndex();
if (!index) {
// TODO(yosin): Once we update all |LayoutObject::FirstInlineFragment()|
// clients, we should replace to |return MakeNull()|
item_iter_ = items_.begin();
while (current_item_ && CurrentLayoutObject() != &layout_object)
MoveToNextItem();
return;
}
DCHECK_LT(index, items_.size());
if (!had_root)
return MoveToItem(items_.begin() + index);
// Map |index| in |NGFragmentItems| to index of |items_|.
const LayoutBlockFlow& block_flow =
*layout_object.RootInlineFormattingContext();
const auto items =
ItemsSpan(block_flow.CurrentFragment()->Items()->Items());
// Note: We use address instead of iterator because we can't compare
// iterators in different span. See |base::CheckedContiguousIterator<T>|.
const ptrdiff_t adjusted_index =
&*(items.begin() + index) - &*items_.begin();
DCHECK_GE(adjusted_index, 0);
DCHECK_LT(static_cast<size_t>(adjusted_index), items_.size());
return MoveToItem(items_.begin() + adjusted_index);
}
if (root_paint_fragment_) {
const auto fragments = NGPaintFragment::InlineFragmentsFor(&layout_object);
......@@ -810,12 +832,10 @@ void NGInlineCursor::MoveToNextForSameLayoutObject() {
return MakeNull();
}
if (current_item_) {
const LayoutObject* const layout_object = CurrentLayoutObject();
DCHECK(layout_object);
do {
MoveToNextItem();
} while (current_item_ && CurrentLayoutObject() != layout_object);
return;
const wtf_size_t delta = current_item_->DeltaToNextForSameLayoutObject();
if (delta == 0u)
return MakeNull();
return MoveToItem(item_iter_ + delta);
}
}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment