blob: e63e7e8edaa815b86f22bf7c17f8c8f2e72759cc [file] [log] [blame]
// 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.
import m from 'mithril';
import {raf} from './raf_scheduler';
import {PerfStats, PerfStatsContainer, runningStatStr} from './perf_stats';
export class PerfManager {
private _enabled = false;
readonly containers: PerfStatsContainer[] = [];
get enabled(): boolean {
return this._enabled;
}
set enabled(enabled: boolean) {
this._enabled = enabled;
raf.setPerfStatsEnabled(true);
this.containers.forEach((c) => c.setPerfStatsEnabled(enabled));
}
addContainer(container: PerfStatsContainer): Disposable {
this.containers.push(container);
return {
[Symbol.dispose]: () => {
const i = this.containers.indexOf(container);
this.containers.splice(i, 1);
},
};
}
renderPerfStats(): m.Children {
if (!this._enabled) return;
// The rendering of the perf stats UI is atypical. The main issue is that we
// want to redraw the mithril component even if there is no full DOM redraw
// happening (and we don't want to force redraws as a side effect). So we
// return here just a container and handle its rendering ourselves.
const perfMgr = this;
let removed = false;
return m('.perf-stats', {
oncreate(vnode: m.VnodeDOM) {
const animationFrame = (dom: Element) => {
if (removed) return;
m.render(dom, m(PerfStatsUi, {perfMgr}));
requestAnimationFrame(() => animationFrame(dom));
};
animationFrame(vnode.dom);
},
onremove() {
removed = true;
},
});
}
}
// The mithril component that draws the contents of the perf stats box.
interface PerfStatsUiAttrs {
perfMgr: PerfManager;
}
class PerfStatsUi implements m.ClassComponent<PerfStatsUiAttrs> {
view({attrs}: m.Vnode<PerfStatsUiAttrs>) {
return m(
'.perf-stats',
{},
m('section', this.renderRafSchedulerStats()),
m(
'button.close-button',
{
onclick: () => (attrs.perfMgr.enabled = false),
},
m('i.material-icons', 'close'),
),
attrs.perfMgr.containers.map((c, i) =>
m('section', m('div', `Panel Container ${i + 1}`), c.renderPerfStats()),
),
);
}
renderRafSchedulerStats() {
return m(
'div',
m('div', [
m(
'button',
{onclick: () => raf.scheduleCanvasRedraw()},
'Do Canvas Redraw',
),
' | ',
m(
'button',
{onclick: () => raf.scheduleFullRedraw()},
'Do Full Redraw',
),
]),
m('div', 'Raf Timing ' + '(Total may not add up due to imprecision)'),
m(
'table',
this.statTableHeader(),
this.statTableRow('Actions', raf.perfStats.rafActions),
this.statTableRow('Dom', raf.perfStats.rafDom),
this.statTableRow('Canvas', raf.perfStats.rafCanvas),
this.statTableRow('Total', raf.perfStats.rafTotal),
),
m(
'div',
'Dom redraw: ' +
`Count: ${raf.perfStats.domRedraw.count} | ` +
runningStatStr(raf.perfStats.domRedraw),
),
);
}
statTableHeader() {
return m(
'tr',
m('th', ''),
m('th', 'Last (ms)'),
m('th', 'Avg (ms)'),
m('th', 'Avg-10 (ms)'),
);
}
statTableRow(title: string, stat: PerfStats) {
return m(
'tr',
m('td', title),
m('td', stat.last.toFixed(2)),
m('td', stat.mean.toFixed(2)),
m('td', stat.bufferMean.toFixed(2)),
);
}
}