debug.dart 11.9 KB
Newer Older
Ian Hickson's avatar
Ian Hickson committed
1
// Copyright 2014 The Flutter Authors. All rights reserved.
2 3 4
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

5 6
import 'object.dart';

7
export 'package:flutter/foundation.dart' show debugPrint;
8

9 10 11
// Any changes to this file should be reflected in the debugAssertAllRenderVarsUnset()
// function below.

12
const HSVColor _kDebugDefaultRepaintColor = HSVColor.fromAHSV(0.4, 60.0, 1.0, 1.0);
13

14
/// Causes each RenderBox to paint a box around its bounds, and some extra
Ian Hickson's avatar
Ian Hickson committed
15
/// boxes, such as [RenderPadding], to draw construction lines.
16
///
17
/// The edges of the boxes are painted as a one-pixel-thick `const Color(0xFF00FFFF)` outline.
18
///
19
/// Spacing is painted as a solid `const Color(0x90909090)` area.
20
///
21 22 23
/// Padding is filled in solid `const Color(0x900090FF)`, with the inner edge
/// outlined in `const Color(0xFF0090FF)`, using [debugPaintPadding].
bool debugPaintSizeEnabled = false;
24

Adam Barth's avatar
Adam Barth committed
25
/// Causes each RenderBox to paint a line at each of its baselines.
26
bool debugPaintBaselinesEnabled = false;
Adam Barth's avatar
Adam Barth committed
27 28

/// Causes each Layer to paint a box around its bounds.
29
bool debugPaintLayerBordersEnabled = false;
Adam Barth's avatar
Adam Barth committed
30

31 32 33 34 35 36
/// Causes objects like [RenderPointerListener] to flash while they are being
/// tapped. This can be useful to see how large the hit box is, e.g. when
/// debugging buttons that are harder to hit than expected.
///
/// For details on how to support this in your [RenderBox] subclass, see
/// [RenderBox.debugHandleEvent].
37
bool debugPaintPointersEnabled = false;
38

39
/// Overlay a rotating set of colors when repainting layers in checked mode.
40 41 42 43 44
///
/// See also:
///
///  * [RepaintBoundary], which can be used to contain repaints when unchanged
///    areas are being excessively repainted.
45
bool debugRepaintRainbowEnabled = false;
46

47 48 49
/// Overlay a rotating set of colors when repainting text in checked mode.
bool debugRepaintTextRainbowEnabled = false;

50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85
/// Causes [PhysicalModelLayer]s to paint a red rectangle around themselves if
/// they are overlapping and painted out of order with regard to their elevation.
///
/// Android and iOS will show the last painted layer on top, whereas Fuchsia
/// will show the layer with the highest elevation on top.
///
/// For example, a rectangular elevation at 3.0 that is painted before an
/// overlapping rectangular elevation at 2.0 would render this way on Android
/// and iOS (with fake shadows):
/// ```
/// ┌───────────────────┐
/// │                   │
/// │      3.0          │
/// │            ┌───────────────────┐
/// │            │                   │
/// └────────────│                   │
///              │        2.0        │
///              │                   │
///              └───────────────────┘
/// ```
///
/// But this way on Fuchsia (with real shadows):
/// ```
/// ┌───────────────────┐
/// │                   │
/// │      3.0          │
/// │                   │────────────┐
/// │                   │            │
/// └───────────────────┘            │
///              │         2.0       │
///              │                   │
///              └───────────────────┘
/// ```
///
/// This check helps developers that want a consistent look and feel detect
/// where this inconsistency would occur.
86 87 88 89 90
///
/// This check assumes that elevations on [PhysicalModelLayer] objects have
/// been set to non-null values before the scene is built. If this assumption
/// is violated, the check will throw exceptions. (The scene building would
/// also fail in that case, however.)
91 92
bool debugCheckElevationsEnabled = false;

93
/// The current color to overlay when repainting a layer.
94 95 96 97 98 99 100
///
/// This is used by painting debug code that implements
/// [debugRepaintRainbowEnabled] or [debugRepaintTextRainbowEnabled].
///
/// The value is incremented by [RenderView.compositeFrame] if either of those
/// flags is enabled.
HSVColor debugCurrentRepaintColor = _kDebugDefaultRepaintColor;
101

102
/// Log the call stacks that mark render objects as needing layout.
103 104 105 106 107
///
/// For sanity, this only logs the stack traces of cases where an object is
/// added to the list of nodes needing layout. This avoids printing multiple
/// redundant stack traces as a single [RenderObject.markNeedsLayout] call walks
/// up the tree.
108 109
bool debugPrintMarkNeedsLayoutStacks = false;

110 111 112 113 114 115 116 117 118 119 120 121 122 123
/// Log the call stacks that mark render objects as needing paint.
bool debugPrintMarkNeedsPaintStacks = false;

/// Log the dirty render objects that are laid out each frame.
///
/// Combined with [debugPrintBeginFrameBanner], this allows you to distinguish
/// layouts triggered by the initial mounting of a render tree (e.g. in a call
/// to [runApp]) from the regular layouts triggered by the pipeline.
///
/// Combined with [debugPrintMarkNeedsLayoutStacks], this lets you watch a
/// render object's dirty/clean lifecycle.
///
/// See also:
///
124 125 126 127
///  * [debugProfileLayoutsEnabled], which does something similar for layout
///    but using the timeline view.
///  * [debugProfilePaintsEnabled], which does something similar for painting
///    but using the timeline view.
128 129 130 131 132
///  * [debugPrintRebuildDirtyWidgets], which does something similar for widgets
///    being rebuilt.
///  * The discussion at [RendererBinding.drawFrame].
bool debugPrintLayouts = false;

133
/// Check the intrinsic sizes of each [RenderBox] during layout.
134
///
135 136 137
/// By default this is turned off since these checks are expensive. If you are
/// implementing your own children of [RenderBox] with custom intrinsics, turn
/// this on in your unit tests for additional validations.
138 139
bool debugCheckIntrinsicSizes = false;

140 141 142 143 144 145 146 147 148 149 150 151 152 153 154
/// Adds [dart:developer.Timeline] events for every [RenderObject] layout.
///
/// For details on how to use [dart:developer.Timeline] events in the Dart
/// Observatory to optimize your app, see:
/// <https://fuchsia.googlesource.com/topaz/+/master/shell/docs/performance.md>
///
/// See also:
///
///  * [debugPrintLayouts], which does something similar for layout but using
///    console output.
///  * [debugProfileBuildsEnabled], which does something similar for widgets
///    being rebuilt.
///  * [debugProfilePaintsEnabled], which does something similar for painting.
bool debugProfileLayoutsEnabled = false;

155 156 157 158 159
/// Adds [dart:developer.Timeline] events for every [RenderObject] painted.
///
/// This is only enabled in debug builds. The timing information this exposes is
/// not representative of actual paints. However, it can expose unexpected
/// painting in the timeline.
160
///
Ian Hickson's avatar
Ian Hickson committed
161 162
/// For details on how to use [dart:developer.Timeline] events in the Dart
/// Observatory to optimize your app, see:
163
/// <https://fuchsia.googlesource.com/topaz/+/master/shell/docs/performance.md>
164 165 166
///
/// See also:
///
167 168 169
///  * [debugProfileBuildsEnabled], which does something similar for widgets
///    being rebuilt, and [debugPrintRebuildDirtyWidgets], its console
///    equivalent.
170 171
///  * [debugProfileLayoutsEnabled], which does something similar for layout,
///    and [debugPrintLayouts], its console equivalent.
172
///  * The discussion at [RendererBinding.drawFrame].
173 174
///  * [RepaintBoundary], which can be used to contain repaints when unchanged
///    areas are being excessively repainted.
175 176
bool debugProfilePaintsEnabled = false;

177
/// Signature for [debugOnProfilePaint] implementations.
178
typedef ProfilePaintCallback = void Function(RenderObject renderObject);
179 180 181 182 183 184 185 186 187 188 189 190 191 192 193

/// Callback invoked for every [RenderObject] painted each frame.
///
/// This callback is only invoked in debug builds.
///
/// See also:
///
///  * [debugProfilePaintsEnabled], which does something similar but adds
///    [dart:developer.Timeline] events instead of invoking a callback.
///  * [debugOnRebuildDirtyWidget], which does something similar for widgets
///    being built.
///  * [WidgetInspectorService], which uses the [debugOnProfilePaint]
///    callback to generate aggregate profile statistics describing what paints
///    occurred when the `ext.flutter.inspector.trackRepaintWidgets` service
///    extension is enabled.
194
ProfilePaintCallback? debugOnProfilePaint;
195

196 197
/// Setting to true will cause all clipping effects from the layer tree to be
/// ignored.
198 199
///
/// Can be used to debug whether objects being clipped are painting excessively
200 201 202 203 204 205
/// in clipped areas. Can also be used to check whether excessive use of
/// clipping is affecting performance.
///
/// This will not reduce the number of [Layer] objects created; the compositing
/// strategy is unaffected. It merely causes the clipping layers to be skipped
/// when building the scene.
206 207
bool debugDisableClipLayers = false;

208 209
/// Setting to true will cause all physical modeling effects from the layer
/// tree, such as shadows from elevations, to be ignored.
210 211 212
///
/// Can be used to check whether excessive use of physical models is affecting
/// performance.
213 214 215 216
///
/// This will not reduce the number of [Layer] objects created; the compositing
/// strategy is unaffected. It merely causes the physical shape layers to be
/// skipped when building the scene.
217 218
bool debugDisablePhysicalShapeLayers = false;

219 220
/// Setting to true will cause all opacity effects from the layer tree to be
/// ignored.
221 222
///
/// An optimization to not paint the child at all when opacity is 0 will still
223 224 225 226 227 228 229 230
/// remain.
///
/// Can be used to check whether excessive use of opacity effects is affecting
/// performance.
///
/// This will not reduce the number of [Layer] objects created; the compositing
/// strategy is unaffected. It merely causes the opacity layers to be skipped
/// when building the scene.
231
bool debugDisableOpacityLayers = false;
232

233
void _debugDrawDoubleRect(Canvas canvas, Rect outerRect, Rect innerRect, Color color) {
234
  final Path path = Path()
235 236 237
    ..fillType = PathFillType.evenOdd
    ..addRect(outerRect)
    ..addRect(innerRect);
238
  final Paint paint = Paint()
239 240 241 242
    ..color = color;
  canvas.drawPath(path, paint);
}

243
/// Paint a diagram showing the given area as padding.
244 245 246
///
/// Called by [RenderPadding.debugPaintSize] when [debugPaintSizeEnabled] is
/// true.
247
void debugPaintPadding(Canvas canvas, Rect outerRect, Rect? innerRect, { double outlineWidth = 2.0 }) {
248 249
  assert(() {
    if (innerRect != null && !innerRect.isEmpty) {
250 251
      _debugDrawDoubleRect(canvas, outerRect, innerRect, const Color(0x900090FF));
      _debugDrawDoubleRect(canvas, innerRect.inflate(outlineWidth).intersect(outerRect), innerRect, const Color(0xFF0090FF));
252
    } else {
253
      final Paint paint = Paint()
254
        ..color = const Color(0x90909090);
255 256 257
      canvas.drawRect(outerRect, paint);
    }
    return true;
258
  }());
259 260
}

261 262 263 264 265
/// Returns true if none of the rendering library debug variables have been changed.
///
/// This function is used by the test framework to ensure that debug variables
/// haven't been inadvertently changed.
///
266 267
/// See [the rendering library](rendering/rendering-library.html) for a complete
/// list.
268 269 270 271
///
/// The `debugCheckIntrinsicSizesOverride` argument can be provided to override
/// the expected value for [debugCheckIntrinsicSizes]. (This exists because the
/// test framework itself overrides this value in some cases.)
272
bool debugAssertAllRenderVarsUnset(String reason, { bool debugCheckIntrinsicSizesOverride = false }) {
273 274 275 276 277 278
  assert(() {
    if (debugPaintSizeEnabled ||
        debugPaintBaselinesEnabled ||
        debugPaintLayerBordersEnabled ||
        debugPaintPointersEnabled ||
        debugRepaintRainbowEnabled ||
279
        debugRepaintTextRainbowEnabled ||
280
        debugCurrentRepaintColor != _kDebugDefaultRepaintColor ||
281
        debugPrintMarkNeedsLayoutStacks ||
282 283
        debugPrintMarkNeedsPaintStacks ||
        debugPrintLayouts ||
284
        debugCheckIntrinsicSizes != debugCheckIntrinsicSizesOverride ||
285 286
        debugProfilePaintsEnabled ||
        debugOnProfilePaint != null) {
287
      throw FlutterError(reason);
288 289
    }
    return true;
290
  }());
291 292
  return true;
}