Report low FPS to analytics
Change-Id: I43e90294b16b48f717cfa7eaca18fec7c0bb488d
This commit is contained in:
@@ -0,0 +1,61 @@
|
|||||||
|
/**
|
||||||
|
* @license
|
||||||
|
* Copyright (C) 2016 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
(function() {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const JANK_SLEEP_TIME_MS = 1000;
|
||||||
|
|
||||||
|
const GrJankDetector = {
|
||||||
|
// Slowdowns counter.
|
||||||
|
jank: 0,
|
||||||
|
fps: 0,
|
||||||
|
_lastFrameTime: 0,
|
||||||
|
|
||||||
|
start() {
|
||||||
|
this._requestAnimationFrame(this._detect.bind(this));
|
||||||
|
},
|
||||||
|
|
||||||
|
_requestAnimationFrame(callback) {
|
||||||
|
window.requestAnimationFrame(callback);
|
||||||
|
},
|
||||||
|
|
||||||
|
_detect(now) {
|
||||||
|
if (this._lastFrameTime === 0) {
|
||||||
|
this._lastFrameTime = now;
|
||||||
|
this.fps = 0;
|
||||||
|
this._requestAnimationFrame(this._detect.bind(this));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const fpsNow = 1000/(now - this._lastFrameTime);
|
||||||
|
this._lastFrameTime = now;
|
||||||
|
// Calculate moving average within last 3 measurements.
|
||||||
|
this.fps = this.fps === 0 ? fpsNow : ((this.fps * 2 + fpsNow) / 3);
|
||||||
|
if (this.fps > 10) {
|
||||||
|
this._requestAnimationFrame(this._detect.bind(this));
|
||||||
|
} else {
|
||||||
|
this.jank++;
|
||||||
|
console.warn('JANK', this.jank);
|
||||||
|
this._lastFrameTime = 0;
|
||||||
|
window.setTimeout(
|
||||||
|
() => this._requestAnimationFrame(this._detect.bind(this)),
|
||||||
|
JANK_SLEEP_TIME_MS);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
window.GrJankDetector = GrJankDetector;
|
||||||
|
})();
|
@@ -0,0 +1,78 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<!--
|
||||||
|
@license
|
||||||
|
Copyright (C) 2018 The Android Open Source Project
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
|
||||||
|
<title>gr-jank-detector</title>
|
||||||
|
|
||||||
|
<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
|
||||||
|
<script src="../../../bower_components/web-component-tester/browser.js"></script>
|
||||||
|
<link rel="import" href="../../../test/common-test-setup.html"/>
|
||||||
|
|
||||||
|
<script src="gr-jank-detector.js"></script>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
suite('gr-jank-detector tests', () => {
|
||||||
|
let sandbox;
|
||||||
|
let clock;
|
||||||
|
let instance;
|
||||||
|
|
||||||
|
const NOW_TIME = 100;
|
||||||
|
|
||||||
|
setup(() => {
|
||||||
|
sandbox = sinon.sandbox.create();
|
||||||
|
clock = sinon.useFakeTimers(NOW_TIME);
|
||||||
|
instance = GrJankDetector;
|
||||||
|
instance._lastFrameTime = 0;
|
||||||
|
sandbox.stub(instance, '_requestAnimationFrame');
|
||||||
|
});
|
||||||
|
|
||||||
|
teardown(() => {
|
||||||
|
sandbox.restore();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('start() installs frame callback', () => {
|
||||||
|
sandbox.stub(instance, '_detect');
|
||||||
|
instance._requestAnimationFrame.callsArg(0);
|
||||||
|
instance.start();
|
||||||
|
assert.isTrue(instance._detect.calledOnce);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('measures fps', () => {
|
||||||
|
instance._detect(10);
|
||||||
|
instance._detect(30);
|
||||||
|
assert.equal(instance.fps, 50);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('detects jank', () => {
|
||||||
|
let now = 10;
|
||||||
|
instance._detect(now);
|
||||||
|
const fastFrame = () => instance._detect(now += 20);
|
||||||
|
const slowFrame = () => instance._detect(now += 300);
|
||||||
|
fastFrame();
|
||||||
|
assert.equal(instance.jank, 0);
|
||||||
|
_.times(4, slowFrame);
|
||||||
|
assert.equal(instance.jank, 0);
|
||||||
|
instance._requestAnimationFrame.reset();
|
||||||
|
slowFrame();
|
||||||
|
assert.equal(instance.jank, 1);
|
||||||
|
assert.isFalse(instance._requestAnimationFrame.called);
|
||||||
|
clock.tick(1000);
|
||||||
|
assert.isTrue(instance._requestAnimationFrame.called);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
@@ -19,5 +19,6 @@ limitations under the License.
|
|||||||
<link rel="import" href="../../shared/gr-js-api-interface/gr-js-api-interface.html">
|
<link rel="import" href="../../shared/gr-js-api-interface/gr-js-api-interface.html">
|
||||||
|
|
||||||
<dom-module id="gr-reporting">
|
<dom-module id="gr-reporting">
|
||||||
|
<script src="gr-jank-detector.js"></script>
|
||||||
<script src="gr-reporting.js"></script>
|
<script src="gr-reporting.js"></script>
|
||||||
</dom-module>
|
</dom-module>
|
||||||
|
@@ -48,6 +48,14 @@
|
|||||||
STARTED_HIDDEN: 'hidden',
|
STARTED_HIDDEN: 'hidden',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Frame rate related constants.
|
||||||
|
const JANK = {
|
||||||
|
TYPE: 'lifecycle',
|
||||||
|
CATEGORY: 'UI Latency',
|
||||||
|
// Reported events - alphabetize below.
|
||||||
|
COUNT: 'Jank count',
|
||||||
|
};
|
||||||
|
|
||||||
// Navigation reporting constants.
|
// Navigation reporting constants.
|
||||||
const NAVIGATION = {
|
const NAVIGATION = {
|
||||||
TYPE: 'nav-report',
|
TYPE: 'nav-report',
|
||||||
@@ -118,6 +126,8 @@
|
|||||||
};
|
};
|
||||||
catchErrors();
|
catchErrors();
|
||||||
|
|
||||||
|
GrJankDetector.start();
|
||||||
|
|
||||||
const GrReporting = Polymer({
|
const GrReporting = Polymer({
|
||||||
is: 'gr-reporting',
|
is: 'gr-reporting',
|
||||||
|
|
||||||
@@ -206,6 +216,11 @@
|
|||||||
},
|
},
|
||||||
|
|
||||||
beforeLocationChanged() {
|
beforeLocationChanged() {
|
||||||
|
if (GrJankDetector.jank > 0) {
|
||||||
|
this.reporter(
|
||||||
|
JANK.TYPE, JANK.CATEGORY, JANK.COUNT, GrJankDetector.jank);
|
||||||
|
GrJankDetector.jank = 0;
|
||||||
|
}
|
||||||
for (const prop of Object.keys(this._baselines)) {
|
for (const prop of Object.keys(this._baselines)) {
|
||||||
delete this._baselines[prop];
|
delete this._baselines[prop];
|
||||||
}
|
}
|
||||||
|
@@ -93,7 +93,11 @@ limitations under the License.
|
|||||||
test('beforeLocationChanged', () => {
|
test('beforeLocationChanged', () => {
|
||||||
element._baselines['garbage'] = 'monster';
|
element._baselines['garbage'] = 'monster';
|
||||||
sandbox.stub(element, 'time');
|
sandbox.stub(element, 'time');
|
||||||
|
GrJankDetector.jank = 42;
|
||||||
element.beforeLocationChanged();
|
element.beforeLocationChanged();
|
||||||
|
assert.equal(GrJankDetector.jank, 0);
|
||||||
|
assert.isTrue(element.reporter.calledWithExactly(
|
||||||
|
'lifecycle', 'UI Latency', 'Jank count', 42));
|
||||||
assert.isTrue(element.time.calledWithExactly('DashboardDisplayed'));
|
assert.isTrue(element.time.calledWithExactly('DashboardDisplayed'));
|
||||||
assert.isTrue(element.time.calledWithExactly('ChangeDisplayed'));
|
assert.isTrue(element.time.calledWithExactly('ChangeDisplayed'));
|
||||||
assert.isTrue(element.time.calledWithExactly('DiffViewDisplayed'));
|
assert.isTrue(element.time.calledWithExactly('DiffViewDisplayed'));
|
||||||
|
@@ -88,6 +88,7 @@ limitations under the License.
|
|||||||
'core/gr-error-manager/gr-error-manager_test.html',
|
'core/gr-error-manager/gr-error-manager_test.html',
|
||||||
'core/gr-main-header/gr-main-header_test.html',
|
'core/gr-main-header/gr-main-header_test.html',
|
||||||
'core/gr-navigation/gr-navigation_test.html',
|
'core/gr-navigation/gr-navigation_test.html',
|
||||||
|
'core/gr-reporting/gr-jank-detector_test.html',
|
||||||
'core/gr-reporting/gr-reporting_test.html',
|
'core/gr-reporting/gr-reporting_test.html',
|
||||||
'core/gr-router/gr-router_test.html',
|
'core/gr-router/gr-router_test.html',
|
||||||
'core/gr-search-bar/gr-search-bar_test.html',
|
'core/gr-search-bar/gr-search-bar_test.html',
|
||||||
|
Reference in New Issue
Block a user