Skip to content

Commit

Permalink
Add UMA metrics Renderer4.MainThread(Gesture|Wheel)ScrollReason2
Browse files Browse the repository at this point in the history
They deprecate Renderer4.MainThread(Gesture|Wheel)ScrollReason with
which we were unable to calculate the percentage of scrolls without
any main-thread reason or with a particular reason because each scroll
can have multiple main-thread scrolling reasons.

In the new metrics, we report "Scrolling on main (any reason)". With
this and "Not scrolling on main (no reason)" we can know the total
count of scrolls and calculate the percentage of non-main-thread
scrolls and main-thread scrolls for a particular reason.

Change-Id: Ide9bfd087b32231ad9e99413e5e14fe7ac1d72a3
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3583502
Reviewed-by: Robert Flack <flackr@chromium.org>
Reviewed-by: Philip Rogers <pdr@chromium.org>
Reviewed-by: Stephen Chenney <schenney@chromium.org>
Commit-Queue: Xianzhu Wang <wangxianzhu@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1028419}
  • Loading branch information
wangxianzhu authored and Chromium LUCI CQ committed Jul 26, 2022
1 parent 06de955 commit 6fe8309
Show file tree
Hide file tree
Showing 8 changed files with 223 additions and 208 deletions.
12 changes: 12 additions & 0 deletions cc/input/main_thread_scrolling_reason.cc
Original file line number Diff line number Diff line change
Expand Up @@ -60,4 +60,16 @@ void MainThreadScrollingReason::AddToTracedValue(
traced_value.EndArray();
}

int MainThreadScrollingReason::BucketIndexForTesting(uint32_t reason) {
// These two values are already bucket indices.
DCHECK_NE(reason, kNotScrollingOnMain);
DCHECK_NE(reason, kScrollingOnMainForAnyReason);

int index = 0;
while (reason >>= 1)
++index;
DCHECK_NE(index, 0);
return index;
}

} // namespace cc
39 changes: 23 additions & 16 deletions cc/input/main_thread_scrolling_reason.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,42 +28,47 @@ struct CC_EXPORT MainThreadScrollingReason {
enum : uint32_t {
kNotScrollingOnMain = 0,

// This is used only to report the histogram of main thread scrolling for
// any reason below. It's a histogram bucket index instead of a bit.
kScrollingOnMainForAnyReason = 1,

// This enum simultaneously defines actual bitmask values and indices into
// the bitmask, but kNotScrollingMain is recorded in the histograms as
// value 0, so the 0th bit should never be used.
// the bitmask (which are the numbers after "1 << " below, used as the
// histogram bucket indices), but value 0 and 1 are used as the histogram
// bucket indices for kNotScrollingMain and kScrollingOnMainForAnyReason,
// respectively, so the 0th bit and the 1st bit should never be used.
// See also blink::RecordScrollReasonsMetric().

// Non-transient scrolling reasons. These are set on the ScrollNode.
kHasBackgroundAttachmentFixedObjects = 1 << 1,
kHasBackgroundAttachmentFixedObjects = 1 << 2,
kThreadedScrollingDisabled = 1 << 3,
kPopupNoThreadedInput = 1 << 26,
kPopupNoThreadedInput = 1 << 4,

// Style-related scrolling on main reasons. Subpixel (LCD) text rendering
// requires blending glyphs with the background at a specific screen
// position; transparency and transforms break this.
// These are only reported by the main-thread scroll gesture event codepath.
// After scroll unification, we report kNoScrollingLayer instead.
kNonCompositedReasonsFirst = 18,
kNotOpaqueForTextAndLCDText = 1 << 19,
kCantPaintScrollingBackgroundAndLCDText = 1 << 20,
kNonCompositedReasonsLast = 23,
kNotOpaqueForTextAndLCDText = 1 << 5,
kCantPaintScrollingBackgroundAndLCDText = 1 << 6,

// Transient scrolling reasons. These are computed for each scroll gesture.
// When computed inside ScrollBegin, these prevent the InputHandler from
// reporting a status with SCROLL_ON_IMPL_THREAD. In other cases, the
// InputHandler is scrolling "on impl", but we report a transient main
// thread scrolling reason to UMA when we determine that some other aspect
// of handling the scroll has been (or will be) blocked on the main thread.
kScrollbarScrolling = 1 << 4,
kNonFastScrollableRegion = 1 << 6,
kFailedHitTest = 1 << 8,
kNoScrollingLayer = 1 << 9,
kNotScrollable = 1 << 10,
kScrollbarScrolling = 1 << 7,
kNonFastScrollableRegion = 1 << 8,
kFailedHitTest = 1 << 9,
kNoScrollingLayer = 1 << 10,
kNotScrollable = 1 << 11,
kNonInvertibleTransform = 1 << 12,
kWheelEventHandlerRegion = 1 << 24,
kTouchEventHandlerRegion = 1 << 25,
kWheelEventHandlerRegion = 1 << 13,
kTouchEventHandlerRegion = 1 << 14,

kMainThreadScrollingReasonLast = 26,
// For blink::RecordScrollReasonsMetric() to know the number of used bits.
kMainThreadScrollingReasonLast = 14,
};

static const uint32_t kNonCompositedReasons =
Expand Down Expand Up @@ -94,6 +99,8 @@ struct CC_EXPORT MainThreadScrollingReason {
return (reasons & kNonCompositedReasons) != 0;
}

static int BucketIndexForTesting(uint32_t reason);

static std::string AsText(uint32_t reasons);
static void AddToTracedValue(uint32_t reasons,
base::trace_event::TracedValue&);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,13 +103,6 @@ class MainThreadScrollingReasonsTest : public PaintTestConfigurations,
->GetScrollableArea();
}

base::Bucket BucketForReason(uint32_t reason) {
uint32_t bucket = 0;
while (reason >>= 1)
bucket++;
return base::Bucket(base::HistogramBase::Sample(bucket), 1);
}

protected:
String base_url_;
frame_test_helpers::WebViewHelper helper_;
Expand Down Expand Up @@ -286,8 +279,17 @@ TEST_P(MainThreadScrollingReasonsTest, ReportBackgroundAttachmentFixed) {
uint32_t expected_reason =
cc::MainThreadScrollingReason::kHasBackgroundAttachmentFixedObjects;
EXPECT_THAT(
histogram_tester.GetAllSamples("Renderer4.MainThreadGestureScrollReason"),
testing::ElementsAre(BucketForReason(expected_reason)));
histogram_tester.GetAllSamples(
"Renderer4.MainThreadGestureScrollReason2"),
testing::ElementsAre(
base::Bucket(
base::HistogramBase::Sample(
cc::MainThreadScrollingReason::kScrollingOnMainForAnyReason),
1),
base::Bucket(base::HistogramBase::Sample(
cc::MainThreadScrollingReason::BucketIndexForTesting(
expected_reason)),
1)));
}

// Upon resizing the content size, the main thread scrolling reason
Expand Down
132 changes: 70 additions & 62 deletions third_party/blink/renderer/core/page/scrolling/scroll_metrics_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -15,24 +15,22 @@
#include "third_party/blink/renderer/platform/testing/testing_platform_support.h"
#include "third_party/blink/renderer/platform/testing/unit_test_helpers.h"

#define EXPECT_WHEEL_BUCKET(reason, count) \
histogram_tester.ExpectBucketCount( \
"Renderer4.MainThreadWheelScrollReason", \
GetBucketIndex(cc::MainThreadScrollingReason::reason), count);

#define EXPECT_TOUCH_BUCKET(reason, count) \
histogram_tester.ExpectBucketCount( \
"Renderer4.MainThreadGestureScrollReason", \
GetBucketIndex(cc::MainThreadScrollingReason::reason), count);

#define EXPECT_WHEEL_TOTAL(count) \
histogram_tester.ExpectTotalCount("Renderer4.MainThreadWheelScrollReason", \
count);
#define EXPECT_WHEEL_BUCKET(index, count) \
histogram_tester.ExpectBucketCount("Renderer4.MainThreadWheelScrollReason2", \
index, count);

#define EXPECT_TOUCH_BUCKET(index, count) \
histogram_tester.ExpectBucketCount( \
"Renderer4.MainThreadGestureScrollReason2", index, count);

#define EXPECT_TOUCH_TOTAL(count) \
histogram_tester.ExpectTotalCount("Renderer4.MainThreadGestureScrollReason", \
#define EXPECT_WHEEL_TOTAL(count) \
histogram_tester.ExpectTotalCount("Renderer4.MainThreadWheelScrollReason2", \
count);

#define EXPECT_TOUCH_TOTAL(count) \
histogram_tester.ExpectTotalCount( \
"Renderer4.MainThreadGestureScrollReason2", count);

namespace blink {

namespace {
Expand All @@ -46,12 +44,6 @@ class ScrollMetricsTest : public SimTest {
}
};

class NonCompositedMainThreadScrollingReasonRecordTest
: public ScrollMetricsTest {
protected:
int GetBucketIndex(uint32_t reason);
};

class ScrollBeginEventBuilder : public WebGestureEvent {
public:
ScrollBeginEventBuilder(gfx::PointF position,
Expand Down Expand Up @@ -88,13 +80,8 @@ class ScrollEndEventBuilder : public WebGestureEvent {
}
};

int NonCompositedMainThreadScrollingReasonRecordTest::GetBucketIndex(
uint32_t reason) {
int index = 0;
while (reason >>= 1)
++index;
DCHECK_NE(index, 0);
return index;
int BucketIndex(uint32_t reason) {
return cc::MainThreadScrollingReason::BucketIndexForTesting(reason);
}

void ScrollMetricsTest::Scroll(Element* element,
Expand All @@ -120,10 +107,13 @@ void ScrollMetricsTest::SetUpHtml(const char* html_content) {
LoadURL("https://example.com/test.html");
request.Complete(html_content);
Compositor().BeginFrame();

GetDocument().View()->SetParentVisible(true);
GetDocument().View()->SetSelfVisible(true);
UpdateAllLifecyclePhases();
}

TEST_F(NonCompositedMainThreadScrollingReasonRecordTest,
TouchAndWheelGeneralTest) {
TEST_F(ScrollMetricsTest, TouchAndWheelGeneralTest) {
SetUpHtml(R"HTML(
<style>
.box { overflow:scroll; width: 100px; height: 100px; }
Expand All @@ -134,27 +124,37 @@ TEST_F(NonCompositedMainThreadScrollingReasonRecordTest,
</div>
)HTML");

UpdateAllLifecyclePhases();

Element* box = GetDocument().getElementById("box");
HistogramTester histogram_tester;

// Test touch scroll.
Scroll(box, WebGestureDevice::kTouchscreen);
EXPECT_TOUCH_BUCKET(kNotOpaqueForTextAndLCDText, 1);
EXPECT_TOUCH_BUCKET(
BucketIndex(cc::MainThreadScrollingReason::kNotOpaqueForTextAndLCDText),
1);
EXPECT_TOUCH_BUCKET(
cc::MainThreadScrollingReason::kScrollingOnMainForAnyReason, 1);
EXPECT_TOUCH_TOTAL(2);

Scroll(box, WebGestureDevice::kTouchscreen);
EXPECT_TOUCH_BUCKET(kNotOpaqueForTextAndLCDText, 2);
EXPECT_TOUCH_TOTAL(2);
EXPECT_TOUCH_BUCKET(
BucketIndex(cc::MainThreadScrollingReason::kNotOpaqueForTextAndLCDText),
2);
EXPECT_TOUCH_BUCKET(
cc::MainThreadScrollingReason::kScrollingOnMainForAnyReason, 2);
EXPECT_TOUCH_TOTAL(4);

// Test wheel scroll.
Scroll(box, WebGestureDevice::kTouchpad);
EXPECT_WHEEL_BUCKET(kNotOpaqueForTextAndLCDText, 1);
EXPECT_WHEEL_TOTAL(1);
EXPECT_WHEEL_BUCKET(
BucketIndex(cc::MainThreadScrollingReason::kNotOpaqueForTextAndLCDText),
1);
EXPECT_WHEEL_BUCKET(
cc::MainThreadScrollingReason::kScrollingOnMainForAnyReason, 1);
EXPECT_WHEEL_TOTAL(2);
}

TEST_F(NonCompositedMainThreadScrollingReasonRecordTest,
CompositedScrollableAreaTest) {
TEST_F(ScrollMetricsTest, CompositedScrollableAreaTest) {
SetUpHtml(R"HTML(
<style>
.box { overflow:scroll; width: 100px; height: 100px; }
Expand All @@ -166,29 +166,31 @@ TEST_F(NonCompositedMainThreadScrollingReasonRecordTest,
</div>
)HTML");

GetDocument().View()->SetParentVisible(true);
GetDocument().View()->SetSelfVisible(true);
UpdateAllLifecyclePhases();

Element* box = GetDocument().getElementById("box");
HistogramTester histogram_tester;

Scroll(box, WebGestureDevice::kTouchpad);
EXPECT_WHEEL_BUCKET(kNotOpaqueForTextAndLCDText, 1);
EXPECT_WHEEL_TOTAL(1);
EXPECT_WHEEL_BUCKET(
BucketIndex(cc::MainThreadScrollingReason::kNotOpaqueForTextAndLCDText),
1);
EXPECT_WHEEL_BUCKET(
cc::MainThreadScrollingReason::kScrollingOnMainForAnyReason, 1);
EXPECT_WHEEL_TOTAL(2);

box->setAttribute("class", "composited transform box");
UpdateAllLifecyclePhases();
Scroll(box, WebGestureDevice::kTouchpad);
EXPECT_FALSE(To<LayoutBox>(box->GetLayoutObject())
->GetScrollableArea()
->GetNonCompositedMainThreadScrollingReasons());
EXPECT_WHEEL_BUCKET(kNotOpaqueForTextAndLCDText, 1);
EXPECT_WHEEL_TOTAL(1);
EXPECT_WHEEL_BUCKET(
BucketIndex(cc::MainThreadScrollingReason::kNotOpaqueForTextAndLCDText),
1);
// EXPECT_WHEEL_BUCKET(cc::MainThreadScrollingReason::kNotScrollingOnMain, 1);
EXPECT_WHEEL_TOTAL(2);
}

TEST_F(NonCompositedMainThreadScrollingReasonRecordTest,
NotScrollableAreaTest) {
TEST_F(ScrollMetricsTest, NotScrollableAreaTest) {
SetUpHtml(R"HTML(
<style>.box { overflow:scroll; width: 100px; height: 100px; }
.hidden { overflow: hidden; }
Expand All @@ -199,23 +201,29 @@ TEST_F(NonCompositedMainThreadScrollingReasonRecordTest,
</div>
)HTML");

UpdateAllLifecyclePhases();

Element* box = GetDocument().getElementById("box");
HistogramTester histogram_tester;

Scroll(box, WebGestureDevice::kTouchpad);
EXPECT_WHEEL_BUCKET(kNotOpaqueForTextAndLCDText, 1);
EXPECT_WHEEL_TOTAL(1);
EXPECT_WHEEL_BUCKET(
BucketIndex(cc::MainThreadScrollingReason::kNotOpaqueForTextAndLCDText),
1);
EXPECT_WHEEL_BUCKET(
cc::MainThreadScrollingReason::kScrollingOnMainForAnyReason, 1);
EXPECT_WHEEL_TOTAL(2);

box->setAttribute("class", "hidden transform box");
UpdateAllLifecyclePhases();
Scroll(box, WebGestureDevice::kTouchpad);
EXPECT_WHEEL_BUCKET(kNotOpaqueForTextAndLCDText, 1);
EXPECT_WHEEL_TOTAL(1);
EXPECT_WHEEL_BUCKET(
BucketIndex(cc::MainThreadScrollingReason::kNotOpaqueForTextAndLCDText),
1);
EXPECT_WHEEL_BUCKET(
cc::MainThreadScrollingReason::kScrollingOnMainForAnyReason, 1);
EXPECT_WHEEL_TOTAL(2);
}

TEST_F(NonCompositedMainThreadScrollingReasonRecordTest, NestedScrollersTest) {
TEST_F(ScrollMetricsTest, NestedScrollersTest) {
SetUpHtml(R"HTML(
<style>
.container { overflow:scroll; width: 200px; height: 200px; }
Expand All @@ -236,19 +244,19 @@ TEST_F(NonCompositedMainThreadScrollingReasonRecordTest, NestedScrollersTest) {
</div>
)HTML");

GetDocument().View()->SetParentVisible(true);
GetDocument().View()->SetSelfVisible(true);
UpdateAllLifecyclePhases();

Element* box = GetDocument().getElementById("inner");
HistogramTester histogram_tester;

Scroll(box, WebGestureDevice::kTouchpad);
// Scrolling the inner box will gather reasons from the scrolling chain. The
// inner box itself has no reason because it's composited. Other scrollable
// areas from the chain have corresponding reasons.
EXPECT_WHEEL_BUCKET(kNotOpaqueForTextAndLCDText, 1);
EXPECT_WHEEL_TOTAL(1);
EXPECT_WHEEL_BUCKET(
BucketIndex(cc::MainThreadScrollingReason::kNotOpaqueForTextAndLCDText),
1);
EXPECT_WHEEL_BUCKET(
cc::MainThreadScrollingReason::kScrollingOnMainForAnyReason, 1);
EXPECT_WHEEL_TOTAL(2);
}

} // namespace
Expand Down

0 comments on commit 6fe8309

Please sign in to comment.