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 | |
5 | import 'package:flutter/foundation.dart'; |
6 | |
7 | import 'box.dart'; |
8 | import 'layer.dart'; |
9 | import 'object.dart'; |
10 | |
11 | /// The options that control whether the performance overlay displays certain |
12 | /// aspects of the compositor. |
13 | enum 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. |
62 | class 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 | |