1// Copyright 2014 The Flutter Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5import 'package:flutter/foundation.dart';
6
7import 'box.dart';
8import 'layer.dart';
9import 'object.dart';
10
11/// The options that control whether the performance overlay displays certain
12/// aspects of the compositor.
13enum PerformanceOverlayOption {
14 // these must be in the order needed for their index values to match the
15 // constants in //engine/src/sky/compositor/performance_overlay_layer.h
16
17 /// Display the frame time and FPS of the last frame rendered. This field is
18 /// updated every frame.
19 ///
20 /// This is the time spent by the rasterizer as it tries
21 /// to convert the layer tree obtained from the widgets into OpenGL commands
22 /// and tries to flush them onto the screen. When the total time taken by this
23 /// step exceeds the frame slice, a frame is lost.
24 displayRasterizerStatistics,
25
26 /// Display the rasterizer frame times as they change over a set period of
27 /// time in the form of a graph. The y axis of the graph denotes the total
28 /// time spent by the rasterizer as a fraction of the total frame slice. When
29 /// the bar turns red, a frame is lost.
30 visualizeRasterizerStatistics,
31
32 /// Display the frame time and FPS at which the interface can construct a
33 /// layer tree for the rasterizer (whose behavior is described above) to
34 /// consume.
35 ///
36 /// This involves all layout, animations, etc. When the total time taken by
37 /// this step exceeds the frame slice, a frame is lost.
38 displayEngineStatistics,
39
40 /// Display the engine frame times as they change over a set period of time
41 /// in the form of a graph. The y axis of the graph denotes the total time
42 /// spent by the engine as a fraction of the total frame slice. When the bar
43 /// turns red, a frame is lost.
44 visualizeEngineStatistics,
45}
46
47/// Displays performance statistics.
48///
49/// The overlay shows two time series. The first shows how much time was
50/// required on this thread to produce each frame. The second shows how much
51/// time was required on the raster thread (formerly known as the GPU thread)
52/// to produce each frame. Ideally, both these values would be less than
53/// the total frame budget for the hardware on which the app is running.
54/// For example, if the hardware has a screen that updates at 60 Hz, each
55/// thread should ideally spend less than 16ms producing each frame.
56/// This ideal condition is indicated by a green vertical line for each thread.
57/// Otherwise, the performance overlay shows a red vertical line.
58///
59/// The simplest way to show the performance overlay is to set
60/// [MaterialApp.showPerformanceOverlay] or [WidgetsApp.showPerformanceOverlay]
61/// to true.
62class RenderPerformanceOverlay extends RenderBox {
63 /// Creates a performance overlay render object.
64 RenderPerformanceOverlay({
65 int optionsMask = 0,
66 int rasterizerThreshold = 0,
67 bool checkerboardRasterCacheImages = false,
68 bool checkerboardOffscreenLayers = false,
69 }) : _optionsMask = optionsMask,
70 _rasterizerThreshold = rasterizerThreshold,
71 _checkerboardRasterCacheImages = checkerboardRasterCacheImages,
72 _checkerboardOffscreenLayers = checkerboardOffscreenLayers;
73
74 /// The mask is created by shifting 1 by the index of the specific
75 /// [PerformanceOverlayOption] to enable.
76 int get optionsMask => _optionsMask;
77 int _optionsMask;
78 set optionsMask(int value) {
79 if (value == _optionsMask) {
80 return;
81 }
82 _optionsMask = value;
83 markNeedsPaint();
84 }
85
86 /// The rasterizer threshold is an integer specifying the number of frame
87 /// intervals that the rasterizer must miss before it decides that the frame
88 /// is suitable for capturing an SkPicture trace for further analysis.
89 int get rasterizerThreshold => _rasterizerThreshold;
90 int _rasterizerThreshold;
91 set rasterizerThreshold(int value) {
92 if (value == _rasterizerThreshold) {
93 return;
94 }
95 _rasterizerThreshold = value;
96 markNeedsPaint();
97 }
98
99 /// Whether the raster cache should checkerboard cached entries.
100 bool get checkerboardRasterCacheImages => _checkerboardRasterCacheImages;
101 bool _checkerboardRasterCacheImages;
102 set checkerboardRasterCacheImages(bool value) {
103 if (value == _checkerboardRasterCacheImages) {
104 return;
105 }
106 _checkerboardRasterCacheImages = value;
107 markNeedsPaint();
108 }
109
110 /// Whether the compositor should checkerboard layers rendered to offscreen bitmaps.
111 bool get checkerboardOffscreenLayers => _checkerboardOffscreenLayers;
112 bool _checkerboardOffscreenLayers;
113 set checkerboardOffscreenLayers(bool value) {
114 if (value == _checkerboardOffscreenLayers) {
115 return;
116 }
117 _checkerboardOffscreenLayers = value;
118 markNeedsPaint();
119 }
120
121 @override
122 bool get sizedByParent => true;
123
124 @override
125 bool get alwaysNeedsCompositing => true;
126
127 @override
128 double computeMinIntrinsicWidth(double height) {
129 return 0.0;
130 }
131
132 @override
133 double computeMaxIntrinsicWidth(double height) {
134 return 0.0;
135 }
136
137 double get _intrinsicHeight {
138 const double kDefaultGraphHeight = 80.0;
139 double result = 0.0;
140 if ((optionsMask | (1 << PerformanceOverlayOption.displayRasterizerStatistics.index) > 0) ||
141 (optionsMask | (1 << PerformanceOverlayOption.visualizeRasterizerStatistics.index) > 0)) {
142 result += kDefaultGraphHeight;
143 }
144 if ((optionsMask | (1 << PerformanceOverlayOption.displayEngineStatistics.index) > 0) ||
145 (optionsMask | (1 << PerformanceOverlayOption.visualizeEngineStatistics.index) > 0)) {
146 result += kDefaultGraphHeight;
147 }
148 return result;
149 }
150
151 @override
152 double computeMinIntrinsicHeight(double width) {
153 return _intrinsicHeight;
154 }
155
156 @override
157 double computeMaxIntrinsicHeight(double width) {
158 return _intrinsicHeight;
159 }
160
161 @override
162 @protected
163 Size computeDryLayout(covariant BoxConstraints constraints) {
164 return constraints.constrain(Size(double.infinity, _intrinsicHeight));
165 }
166
167 @override
168 void paint(PaintingContext context, Offset offset) {
169 assert(needsCompositing);
170 context.addLayer(PerformanceOverlayLayer(
171 overlayRect: Rect.fromLTWH(offset.dx, offset.dy, size.width, size.height),
172 optionsMask: optionsMask,
173 rasterizerThreshold: rasterizerThreshold,
174 checkerboardRasterCacheImages: checkerboardRasterCacheImages,
175 checkerboardOffscreenLayers: checkerboardOffscreenLayers,
176 ));
177 }
178}
179