Commit cb47ebe2 authored by Koji Ishii's avatar Koji Ishii Committed by Commit Bot

[LayoutNG] Reduce paint tree traversal for floats

This patch reduces tree traversals in NGBoxFragmentPainter
when there are floating objects.

When a PaintLayer has floating objects, PaintPhase::kFloat
runs tree traversals twice, once for regular children and
another for floating children.

This patch adds HasFloatingDescendants() to
NGPhysicalContainerFragment to optimize the traversal. With
this change, we will no longer traverse normal children when
PaintPhase::kFloat, and skip traversing descendants that
don't have floating objects.

Bug: 936024
Change-Id: I1df35b788501dd39660e781b03ed76af1190e7f0
Reviewed-on: https://chromium-review.googlesource.com/c/1490355
Commit-Queue: Koji Ishii <kojii@chromium.org>
Reviewed-by: default avatarEmil A Eklund <eae@chromium.org>
Reviewed-by: default avatarXianzhu Wang <wangxianzhu@chromium.org>
Reviewed-by: default avatarIan Kilpatrick <ikilpatrick@chromium.org>
Cr-Commit-Position: refs/heads/master@{#636204}
parent 4707457c
......@@ -115,6 +115,16 @@ NGContainerFragmentBuilder& NGContainerFragmentBuilder::AddChild(
}
}
// Compute |has_floating_descendants_| to optimize tree traversal in paint.
if (!has_floating_descendants_) {
if (child->IsFloating()) {
has_floating_descendants_ = true;
} else if (child->IsContainer() && !child->IsBlockFormattingContextRoot() &&
ToNGPhysicalContainerFragment(*child).HasFloatingDescendants()) {
has_floating_descendants_ = true;
}
}
if (!IsParallelWritingMode(child->Style().GetWritingMode(),
Style().GetWritingMode()))
has_orthogonal_flow_roots_ = true;
......
......@@ -149,6 +149,8 @@ class CORE_EXPORT NGContainerFragmentBuilder : public NGFragmentBuilder {
}
bool IsPushedByFloats() const { return is_pushed_by_floats_; }
bool HasFloatingDescendants() const { return has_floating_descendants_; }
NGContainerFragmentBuilder& ResetAdjoiningFloatTypes() {
adjoining_floats_ = kFloatTypeNone;
return *this;
......@@ -239,6 +241,7 @@ class CORE_EXPORT NGContainerFragmentBuilder : public NGFragmentBuilder {
bool is_pushed_by_floats_ = false;
bool is_old_layout_root_ = false;
bool has_floating_descendants_ = false;
bool has_orthogonal_flow_roots_ = false;
bool has_child_that_depends_on_percentage_block_size_ = false;
bool has_block_fragmentation_ = false;
......
......@@ -16,8 +16,76 @@ class NGPhysicalBoxFragmentTest : public NGLayoutTest {
return *ToLayoutBlockFlow(GetDocument().body()->GetLayoutObject())
->CurrentFragment();
}
const NGPhysicalBoxFragment& GetPhysicalBoxFragmentByElementId(
const char* id) {
LayoutBlockFlow* layout_object =
ToLayoutBlockFlow(GetLayoutObjectByElementId(id));
DCHECK(layout_object);
const NGPhysicalBoxFragment* fragment = layout_object->CurrentFragment();
DCHECK(fragment);
return *fragment;
}
};
TEST_F(NGPhysicalBoxFragmentTest, FloatingDescendantsInlineChlidren) {
SetBodyInnerHTML(R"HTML(
<div id="hasfloats">
text
<div style="float: left"></div>
</div>
<div id="nofloats">
text
</div>
)HTML");
const NGPhysicalBoxFragment& has_floats =
GetPhysicalBoxFragmentByElementId("hasfloats");
EXPECT_TRUE(has_floats.HasFloatingDescendants());
const NGPhysicalBoxFragment& no_floats =
GetPhysicalBoxFragmentByElementId("nofloats");
EXPECT_FALSE(no_floats.HasFloatingDescendants());
}
TEST_F(NGPhysicalBoxFragmentTest, FloatingDescendantsBlockChlidren) {
SetBodyInnerHTML(R"HTML(
<div id="hasfloats">
<div></div>
<div style="float: left"></div>
</div>
<div id="nofloats">
<div></div>
</div>
)HTML");
const NGPhysicalBoxFragment& has_floats =
GetPhysicalBoxFragmentByElementId("hasfloats");
EXPECT_TRUE(has_floats.HasFloatingDescendants());
const NGPhysicalBoxFragment& no_floats =
GetPhysicalBoxFragmentByElementId("nofloats");
EXPECT_FALSE(no_floats.HasFloatingDescendants());
}
// HasFloatingDescendants() should be set for each inline formatting context and
// should not be propagated across inline formatting context.
TEST_F(NGPhysicalBoxFragmentTest, FloatingDescendantsInlineBlock) {
SetBodyInnerHTML(R"HTML(
<div id="nofloats">
text
<span id="hasfloats" style="display: inline-block">
<div style="float: left"></div>
</span>
</div>
)HTML");
const NGPhysicalBoxFragment& has_floats =
GetPhysicalBoxFragmentByElementId("hasfloats");
EXPECT_TRUE(has_floats.HasFloatingDescendants());
const NGPhysicalBoxFragment& no_floats =
GetPhysicalBoxFragmentByElementId("nofloats");
EXPECT_FALSE(no_floats.HasFloatingDescendants());
}
// TODO(layout-dev): Design more straightforward way to ensure old layout
// instead of using |contenteditable|.
......
......@@ -35,6 +35,8 @@ NGPhysicalContainerFragment::NGPhysicalContainerFragment(
unsigned sub_type)
: NGPhysicalFragment(builder, type, sub_type),
num_children_(builder->children_.size()) {
has_floating_descendants_ = builder->HasFloatingDescendants();
DCHECK_EQ(builder->children_.size(), builder->offsets_.size());
// Because flexible arrays need to be the last member in a class, we need to
// have the buffer passed as a constructor argument and have the actual
......
......@@ -50,6 +50,8 @@ class CORE_EXPORT NGPhysicalContainerFragment : public NGPhysicalFragment {
const LayoutPoint& additional_offset,
NGOutlineType outline_type) const;
bool HasFloatingDescendants() const { return has_floating_descendants_; }
protected:
// block_or_line_writing_mode is used for converting the child offsets.
NGPhysicalContainerFragment(NGContainerFragmentBuilder*,
......
......@@ -254,6 +254,7 @@ NGPhysicalFragment::NGPhysicalFragment(NGFragmentBuilder* builder,
type_(type),
sub_type_(sub_type),
style_variant_((unsigned)builder->style_variant_),
has_floating_descendants_(false),
is_fieldset_container_(false),
is_old_layout_root_(false) {}
......@@ -269,6 +270,7 @@ NGPhysicalFragment::NGPhysicalFragment(LayoutObject* layout_object,
type_(type),
sub_type_(sub_type),
style_variant_((unsigned)style_variant),
has_floating_descendants_(false),
is_fieldset_container_(false),
is_old_layout_root_(false) {}
......
......@@ -256,6 +256,10 @@ class CORE_EXPORT NGPhysicalFragment
const unsigned sub_type_ : 3; // NGBoxType, NGTextType, or NGLineBoxType
const unsigned style_variant_ : 2; // NGStyleVariant
// The following bitfield is only to be used by NGPhysicalContainerFragment
// (it's defined here to save memory, since that class has no bitfields).
unsigned has_floating_descendants_ : 1;
// The following bitfield is only to be used by NGPhysicalLineBoxFragment
// (it's defined here to save memory, since that class has no bitfields).
unsigned base_direction_ : 1; // TextDirection
......
......@@ -233,6 +233,7 @@ void NGBoxFragmentPainter::PaintObject(
const LayoutPoint& paint_offset,
bool suppress_box_decoration_background) {
const PaintPhase paint_phase = paint_info.phase;
const NGPhysicalBoxFragment& physical_box_fragment = PhysicalFragment();
const ComputedStyle& style = box_fragment_.Style();
bool is_visible = style.Visibility() == EVisibility::kVisible;
......@@ -245,7 +246,7 @@ void NGBoxFragmentPainter::PaintObject(
PaintBoxDecorationBackground(paint_info, paint_offset);
if (NGFragmentPainter::ShouldRecordHitTestData(paint_info,
PhysicalFragment()))
physical_box_fragment))
RecordHitTestData(paint_info, paint_offset);
// Record the scroll hit test after the background so background squashing
......@@ -268,16 +269,22 @@ void NGBoxFragmentPainter::PaintObject(
}
if (paint_phase != PaintPhase::kSelfOutlineOnly) {
if (PhysicalFragment().ChildrenInline()) {
if (PhysicalFragment().IsBlockFlow())
PaintBlockFlowContents(paint_info, paint_offset);
else
PaintInlineChildren(box_fragment_.Children(), paint_info, paint_offset);
if (physical_box_fragment.ChildrenInline()) {
if (paint_phase != PaintPhase::kFloat) {
if (physical_box_fragment.IsBlockFlow()) {
PaintBlockFlowContents(paint_info, paint_offset);
} else {
PaintInlineChildren(box_fragment_.Children(), paint_info,
paint_offset);
}
}
if (paint_phase == PaintPhase::kFloat ||
paint_phase == PaintPhase::kSelection ||
paint_phase == PaintPhase::kTextClip)
PaintFloats(paint_info);
paint_phase == PaintPhase::kTextClip) {
if (physical_box_fragment.HasFloatingDescendants())
PaintFloats(paint_info);
}
} else {
PaintBlockChildren(paint_info);
}
......@@ -390,13 +397,19 @@ void NGBoxFragmentPainter::PaintFloatingChildren(
// we're more stable.
ObjectPainter(*child->GetLayoutObject())
.PaintAllPhasesAtomically(paint_info);
} else {
PaintFloatingChildren(child->Children(), paint_info);
continue;
}
if (const NGPhysicalContainerFragment* child_container =
ToNGPhysicalContainerFragmentOrNull(&fragment)) {
if (child_container->HasFloatingDescendants())
PaintFloatingChildren(child->Children(), paint_info);
}
}
}
void NGBoxFragmentPainter::PaintFloats(const PaintInfo& paint_info) {
DCHECK(PhysicalFragment().HasFloatingDescendants());
// TODO(eae): The legacy paint code currently handles most floats, if they can
// be painted by PaintNG BlockFlowPainter::PaintFloats will then call
// NGBlockFlowPainter::Paint on each float.
......
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