From 3e916982c41d616e0a6dec4a3864e73303a02aa2 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Fri, 2 Oct 2015 14:21:00 -0700 Subject: [PATCH] Allow displaying compositor statistics via a widget Example: import 'package:sky/widgets.dart'; void main() => runApp(new Center(child: new StatisticsOverlay.allEnabled())); --- packages/flutter/lib/src/rendering/layer.dart | 23 +++++++ .../flutter/lib/src/rendering/object.dart | 9 +++ .../lib/src/rendering/statistics_box.dart | 47 +++++++++++++++ .../lib/src/widgets/statistics_overlay.dart | 60 +++++++++++++++++++ packages/flutter/lib/widgets.dart | 1 + 5 files changed, 140 insertions(+) create mode 100644 packages/flutter/lib/src/rendering/statistics_box.dart create mode 100644 packages/flutter/lib/src/widgets/statistics_overlay.dart diff --git a/packages/flutter/lib/src/rendering/layer.dart b/packages/flutter/lib/src/rendering/layer.dart index 49d1e3df58..1e22221d39 100644 --- a/packages/flutter/lib/src/rendering/layer.dart +++ b/packages/flutter/lib/src/rendering/layer.dart @@ -87,6 +87,29 @@ class PictureLayer extends Layer { } +/// A layer that indicates to the compositor that it should display +/// certain statistics within it +class StatisticsLayer extends Layer { + StatisticsLayer({ + Offset offset: Offset.zero, + this.paintBounds, + this.optionsMask + }) : super(offset: offset); + + /// The rectangle in this layer's coodinate system that bounds the recording + Rect paintBounds; + + /// A mask specifying the statistics to display + int optionsMask; + + void addToScene(sky.SceneBuilder builder, Offset layerOffset) { + assert(optionsMask != null); + builder.addStatistics(optionsMask, paintBounds.shift(layerOffset)); + } + +} + + /// A composited layer that has a list of children class ContainerLayer extends Layer { ContainerLayer({ Offset offset: Offset.zero }) : super(offset: offset); diff --git a/packages/flutter/lib/src/rendering/object.dart b/packages/flutter/lib/src/rendering/object.dart index 07cee15b5f..0daaab2bef 100644 --- a/packages/flutter/lib/src/rendering/object.dart +++ b/packages/flutter/lib/src/rendering/object.dart @@ -130,6 +130,15 @@ class PaintingContext { } } + void paintStatistics(int optionsMask, Offset offset, Size size) { + StatisticsLayer statsLayer = new StatisticsLayer( + offset: offset, + paintBounds: new Rect.fromLTWH(0.0, 0.0, size.width, size.height), + optionsMask : optionsMask + ); + _containerLayer.append(statsLayer); + } + // Below we have various variants of the paintChild() method, which // do additional work, such as clipping or transforming, at the same // time as painting the children. diff --git a/packages/flutter/lib/src/rendering/statistics_box.dart b/packages/flutter/lib/src/rendering/statistics_box.dart new file mode 100644 index 0000000000..98780ea889 --- /dev/null +++ b/packages/flutter/lib/src/rendering/statistics_box.dart @@ -0,0 +1,47 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:sky/src/rendering/box.dart'; +import 'package:sky/src/rendering/object.dart'; + +class StatisticsBox extends RenderBox { + + StatisticsBox({int optionsMask: 0}) : _optionsMask = optionsMask; + + int _optionsMask; + int get optionsMask => _optionsMask; + void set optionsMask (int mask) { + if (mask == _optionsMask) { + return; + } + _optionsMask = mask; + markNeedsPaint(); + } + + bool get sizedByParent => true; + + double getMinIntrinsicWidth(BoxConstraints constraints) { + return constraints.minWidth; + } + + double getMaxIntrinsicWidth(BoxConstraints constraints) { + return constraints.maxWidth; + } + + double getMinIntrinsicHeight(BoxConstraints constraints) { + return constraints.minHeight; + } + + double getMaxIntrinsicHeight(BoxConstraints constraints) { + return constraints.maxHeight; + } + + void performResize() { + size = constraints.constrain(Size.infinite); + } + + void paint(PaintingContext context, Offset offset) { + context.paintStatistics(optionsMask, offset, size); + } +} diff --git a/packages/flutter/lib/src/widgets/statistics_overlay.dart b/packages/flutter/lib/src/widgets/statistics_overlay.dart new file mode 100644 index 0000000000..e3d62fc556 --- /dev/null +++ b/packages/flutter/lib/src/widgets/statistics_overlay.dart @@ -0,0 +1,60 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:sky/src/widgets/framework.dart'; +import 'package:sky/src/rendering/statistics_box.dart'; + +/// The options that control whether the statistics overlay displays certain +/// aspects of the compositor +enum StatisticsOption { + /// Display the frame time and FPS of the last frame rendered. This field is + /// updated every frame. + /// + /// This is the time spent by the rasterizer as it tries + /// to convert the layer tree obtained from the widgets into OpenGL commands + /// and tries to flush them onto the screen. When the total time taken by this + /// step exceeds the frame slice, a frame is lost. + displayRasterizerStatistics, + /// Display the rasterizer frame times as they change over a set period of + /// time in the form of a graph. The y axis of the graph denotes the total + /// time spent by the rasterizer as a fraction of the total frame slice. When + /// the bar turns red, a frame is lost. + visualizeRasterizerStatistics, + /// Display the frame time and FPS at which the interface can construct a + /// layer tree for the rasterizer (whose behavior is described above) to + /// consume. + /// + /// This involves all layout, animations, etc. When the total time taken by + /// this step exceeds the frame slice, a frame is lost. + displayEngineStatistics, + /// Display the engine frame times as they change over a set period of time + /// in the form of a graph. The y axis of the graph denotes the total time + /// spent by the eninge as a fraction of the total frame slice. When the bar + /// turns red, a frame is lost. + visualizeEngineStatistics, +} + +class StatisticsOverlay extends LeafRenderObjectWidget { + + /// Create a statistics overlay that only displays specific statistics. The + /// mask is created by shifting 1 by the index of the specific StatisticOption + /// to enable. + StatisticsOverlay({ this.optionsMask, Key key }) : super(key: key); + + /// Create a statistics overaly that displays all available statistics + StatisticsOverlay.allEnabled({ Key key }) : super(key: key), optionsMask = ( + 1 << StatisticsOption.displayRasterizerStatistics.index | + 1 << StatisticsOption.visualizeRasterizerStatistics.index | + 1 << StatisticsOption.displayEngineStatistics.index | + 1 << StatisticsOption.visualizeEngineStatistics.index + ); + + final int optionsMask; + + StatisticsBox createRenderObject() => new StatisticsBox(optionsMask: optionsMask); + + void updateRenderObject(StatisticsBox renderObject, RenderObjectWidget oldWidget) { + renderObject.optionsMask = optionsMask; + } +} diff --git a/packages/flutter/lib/widgets.dart b/packages/flutter/lib/widgets.dart index 3e7f257d57..28e2553441 100644 --- a/packages/flutter/lib/widgets.dart +++ b/packages/flutter/lib/widgets.dart @@ -43,6 +43,7 @@ export 'src/widgets/radio.dart'; export 'src/widgets/raised_button.dart'; export 'src/widgets/scaffold.dart'; export 'src/widgets/scrollable.dart'; +export 'src/widgets/statistics_overlay.dart'; export 'src/widgets/snack_bar.dart'; export 'src/widgets/switch.dart'; export 'src/widgets/tabs.dart';