Commit a60db65d authored by Steve Kobes's avatar Steve Kobes Committed by Commit Bot

Track layout jank from composited layer movement.

This ensures that JankTracker cannot be trivially circumvented by
forcing layer promotion of janking content.

Also add a REF check that was missing from crrev.com/587033.

Bug: 581518
Cq-Include-Trybots: luci.chromium.try:linux_layout_tests_slimming_paint_v2;master.tryserver.blink:linux_trusty_blink_rel
Change-Id: I3da0c1986f3b1b033af5f8126459966d1884ef2f
Reviewed-on: https://chromium-review.googlesource.com/1208453
Commit-Queue: Steve Kobes <skobes@chromium.org>
Reviewed-by: default avatarTimothy Dresser <tdresser@chromium.org>
Cr-Commit-Position: refs/heads/master@{#589269}
parent 17957e83
......@@ -51,10 +51,15 @@ static bool EqualWithinMovementThreshold(const FloatPoint& a,
fabs(a.Y() - b.Y()) < threshold_physical_px;
}
static bool SmallerThanRegionGranularity(const LayoutRect& rect,
static bool SmallerThanRegionGranularity(const FloatRect& rect,
float granularity_scale) {
return rect.Width().ToFloat() * granularity_scale < 0.5 ||
rect.Height().ToFloat() * granularity_scale < 0.5;
return rect.Width() * granularity_scale < 0.5 ||
rect.Height() * granularity_scale < 0.5;
}
static const TransformPaintPropertyNode* TransformNodeFor(
LayoutObject& object) {
return object.FirstFragment().LocalBorderBoxProperties().Transform();
}
#ifdef TRACE_JANK_REGIONS
......@@ -82,74 +87,76 @@ JankTracker::JankTracker(LocalFrameView* frame_view)
&JankTracker::TimerFired),
max_distance_(0.0) {}
void JankTracker::NotifyObjectPrePaint(const LayoutObject& object,
const LayoutRect& old_visual_rect,
const PaintLayer& painting_layer) {
if (!IsActive())
return;
LayoutRect new_visual_rect = object.FirstFragment().VisualRect();
if (old_visual_rect.IsEmpty() || new_visual_rect.IsEmpty())
void JankTracker::AccumulateJank(const LayoutObject& source,
const PaintLayer& painting_layer,
FloatRect old_rect,
FloatRect new_rect) {
if (old_rect.IsEmpty() || new_rect.IsEmpty())
return;
if (EqualWithinMovementThreshold(
LogicalStart(FloatRect(old_visual_rect), object),
LogicalStart(FloatRect(new_visual_rect), object), object))
if (EqualWithinMovementThreshold(LogicalStart(old_rect, source),
LogicalStart(new_rect, source), source))
return;
IntRect viewport = frame_view_->GetScrollableArea()->VisibleContentRect();
float scale = RegionGranularityScale(viewport);
if (SmallerThanRegionGranularity(old_visual_rect, scale) &&
SmallerThanRegionGranularity(new_visual_rect, scale))
if (SmallerThanRegionGranularity(old_rect, scale) &&
SmallerThanRegionGranularity(new_rect, scale))
return;
const auto* local_transform = painting_layer.GetLayoutObject()
.FirstFragment()
.LocalBorderBoxProperties()
.Transform();
const auto* ancestor_transform = painting_layer.GetLayoutObject()
.View()
->FirstFragment()
.LocalBorderBoxProperties()
.Transform();
FloatRect old_visual_rect_abs = FloatRect(old_visual_rect);
GeometryMapper::SourceToDestinationRect(local_transform, ancestor_transform,
old_visual_rect_abs);
FloatRect new_visual_rect_abs = FloatRect(new_visual_rect);
GeometryMapper::SourceToDestinationRect(local_transform, ancestor_transform,
new_visual_rect_abs);
// TOOD(crbug.com/842282): Consider tracking a separate jank score for each
// transform space to avoid these local-to-absolute conversions, once we have
// a better idea of how to aggregate multiple scores for a page.
// See review thread of http://crrev.com/c/1046155 for more details.
if (!old_visual_rect_abs.Intersects(viewport) &&
!new_visual_rect_abs.Intersects(viewport))
const auto* local_xform = TransformNodeFor(painting_layer.GetLayoutObject());
const auto* root_xform = TransformNodeFor(*source.View());
GeometryMapper::SourceToDestinationRect(local_xform, root_xform, old_rect);
GeometryMapper::SourceToDestinationRect(local_xform, root_xform, new_rect);
if (!old_rect.Intersects(viewport) && !new_rect.Intersects(viewport))
return;
DVLOG(2) << object.DebugName() << " moved from "
<< old_visual_rect_abs.ToString() << " to "
<< new_visual_rect_abs.ToString();
DVLOG(2) << source.DebugName() << " moved from " << old_rect.ToString()
<< " to " << new_rect.ToString();
max_distance_ =
std::max(max_distance_, GetMoveDistance(old_rect, new_rect, source));
IntRect visible_old_rect = RoundedIntRect(old_rect);
visible_old_rect.Intersect(viewport);
max_distance_ = std::max(
max_distance_,
GetMoveDistance(old_visual_rect_abs, new_visual_rect_abs, object));
IntRect visible_new_rect = RoundedIntRect(new_rect);
visible_new_rect.Intersect(viewport);
IntRect visible_old_visual_rect = RoundedIntRect(old_visual_rect_abs);
visible_old_visual_rect.Intersect(viewport);
visible_old_rect.Scale(scale);
visible_new_rect.Scale(scale);
IntRect visible_new_visual_rect = RoundedIntRect(new_visual_rect_abs);
visible_new_visual_rect.Intersect(viewport);
region_.Unite(Region(visible_old_rect));
region_.Unite(Region(visible_new_rect));
}
void JankTracker::NotifyObjectPrePaint(const LayoutObject& object,
const LayoutRect& old_visual_rect,
const PaintLayer& painting_layer) {
if (!IsActive())
return;
AccumulateJank(object, painting_layer, FloatRect(old_visual_rect),
FloatRect(object.FirstFragment().VisualRect()));
}
void JankTracker::NotifyCompositedLayerMoved(const PaintLayer& paint_layer,
FloatRect old_layer_rect,
FloatRect new_layer_rect) {
if (!IsActive())
return;
visible_old_visual_rect.Scale(scale);
visible_new_visual_rect.Scale(scale);
// Convert to the local transform space, whose origin is the layer's previous
// location because the property trees haven't been updated yet.
FloatPoint transform_parent_offset = -old_layer_rect.Location();
old_layer_rect.MoveBy(transform_parent_offset);
new_layer_rect.MoveBy(transform_parent_offset);
region_.Unite(Region(visible_old_visual_rect));
region_.Unite(Region(visible_new_visual_rect));
AccumulateJank(paint_layer.GetLayoutObject(), paint_layer, old_layer_rect,
new_layer_rect);
}
void JankTracker::NotifyPrePaintFinished() {
......
......@@ -30,6 +30,9 @@ class CORE_EXPORT JankTracker {
void NotifyObjectPrePaint(const LayoutObject& object,
const LayoutRect& old_visual_rect,
const PaintLayer& painting_layer);
void NotifyCompositedLayerMoved(const PaintLayer&,
FloatRect old_layer_rect,
FloatRect new_layer_rect);
void NotifyPrePaintFinished();
void NotifyInput(const WebInputEvent&);
bool IsActive();
......@@ -38,6 +41,10 @@ class CORE_EXPORT JankTracker {
void Dispose() { timer_.Stop(); }
private:
void AccumulateJank(const LayoutObject&,
const PaintLayer&,
FloatRect old_rect,
FloatRect new_rect);
void TimerFired(TimerBase*) {}
std::unique_ptr<TracedValue> PerFrameTraceData(
double jank_fraction,
......
......@@ -11,6 +11,10 @@ namespace blink {
class JankTrackerTest : public RenderingTest {
protected:
void SetUp() override {
RenderingTest::SetUp();
EnableCompositing();
}
LocalFrameView& GetFrameView() { return *GetFrame().View(); }
JankTracker& GetJankTracker() { return GetFrameView().GetJankTracker(); }
......@@ -128,4 +132,39 @@ TEST_F(JankTrackerTest, IgnoreAfterInput) {
EXPECT_EQ(0.0, GetJankTracker().Score());
}
TEST_F(JankTrackerTest, CompositedElementMovement) {
SetBodyInnerHTML(R"HTML(
<style>
#jank {
position: relative;
width: 500px;
height: 200px;
background: yellow;
}
#container {
position: absolute;
width: 400px;
height: 400px;
left: 400px;
top: 100px;
background: #ccc;
}
.tr { will-change: transform; }
</style>
<div id='container' class='tr'>
<div id='space'></div>
<div id='jank' class='tr'></div>
</div>
)HTML");
GetDocument().getElementById("space")->setAttribute(
HTMLNames::styleAttr, AtomicString("height: 100px"));
GetFrameView().UpdateAllLifecyclePhases();
// #jank is 400x200 after viewport intersection with correct application of
// composited #container offset, and 100px lower after janking, so jank score
// is (400 * 300) / (viewport size 800 * 600)
EXPECT_FLOAT_EQ(0.25, GetJankTracker().Score());
}
} // namespace blink
......@@ -140,8 +140,10 @@ WebInputEventResult PageWidgetDelegate::HandleInputEvent(
if (interactive_detector)
interactive_detector->HandleForInputDelay(event);
if (LocalFrameView* view = document->View())
view->GetJankTracker().NotifyInput(event);
if (RuntimeEnabledFeatures::JankTrackingEnabled()) {
if (LocalFrameView* view = document->View())
view->GetJankTracker().NotifyInput(event);
}
}
if (event.GetModifiers() & WebInputEvent::kIsTouchAccessibility &&
......
......@@ -1310,20 +1310,31 @@ void CompositedLayerMapping::UpdateMainGraphicsLayerGeometry(
const IntRect& relative_compositing_bounds,
const IntRect& local_compositing_bounds,
const IntPoint& graphics_layer_parent_location) {
FloatPoint old_position = graphics_layer_->GetPosition();
IntSize old_size = graphics_layer_->Size();
FloatPoint new_position = FloatPoint(relative_compositing_bounds.Location() -
graphics_layer_parent_location);
IntSize new_size = relative_compositing_bounds.Size();
// An iframe's main GraphicsLayer is positioned by the CLM for the <iframe>
// element in the parent frame's DOM.
bool is_iframe_doc = GetLayoutObject().IsLayoutView() &&
!GetLayoutObject().GetFrame()->IsLocalRoot();
if (!is_iframe_doc) {
graphics_layer_->SetPosition(
FloatPoint(relative_compositing_bounds.Location() -
graphics_layer_parent_location));
if (new_position != old_position && !is_iframe_doc) {
graphics_layer_->SetPosition(new_position);
if (RuntimeEnabledFeatures::JankTrackingEnabled()) {
LocalFrameView* frame_view = GetLayoutObject().View()->GetFrameView();
frame_view->GetJankTracker().NotifyCompositedLayerMoved(
OwningLayer(), FloatRect(old_position, FloatSize(old_size)),
FloatRect(new_position, FloatSize(new_size)));
}
}
graphics_layer_->SetOffsetFromLayoutObject(
ToIntSize(local_compositing_bounds.Location()));
if (graphics_layer_->Size() != relative_compositing_bounds.Size())
graphics_layer_->SetSize(relative_compositing_bounds.Size());
if (old_size != new_size)
graphics_layer_->SetSize(new_size);
// m_graphicsLayer is the corresponding GraphicsLayer for this PaintLayer and
// its non-compositing descendants. So, the visibility flag for
......
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