From dfbd8c6a8a72cfd79be2f87d04540b9e53a386f0 Mon Sep 17 00:00:00 2001 From: Adam Barth Date: Thu, 19 Nov 2015 14:45:40 -0800 Subject: [PATCH] Add debugDumpLayerTree to dump the layer tree To help debugging issues with the layer tree. --- examples/stocks/lib/stock_home.dart | 2 +- .../flutter/lib/src/rendering/binding.dart | 5 ++ packages/flutter/lib/src/rendering/debug.dart | 8 ++ packages/flutter/lib/src/rendering/layer.dart | 74 ++++++++++++++++++- .../flutter/lib/src/rendering/object.dart | 16 +++- .../flutter/lib/src/rendering/proxy_box.dart | 5 +- 6 files changed, 101 insertions(+), 9 deletions(-) diff --git a/examples/stocks/lib/stock_home.dart b/examples/stocks/lib/stock_home.dart index f07267ce7b..d523c5ada1 100644 --- a/examples/stocks/lib/stock_home.dart +++ b/examples/stocks/lib/stock_home.dart @@ -107,7 +107,7 @@ class StockHomeState extends State { ), new DrawerItem( icon: 'device/dvr', - onPressed: () { debugDumpApp(); debugDumpRenderTree(); }, + onPressed: () { debugDumpApp(); debugDumpRenderTree(); debugDumpLayerTree(); }, child: new Text('Dump App to Console') ), new DrawerDivider(), diff --git a/packages/flutter/lib/src/rendering/binding.dart b/packages/flutter/lib/src/rendering/binding.dart index b3d9cfb704..bc11243bdc 100644 --- a/packages/flutter/lib/src/rendering/binding.dart +++ b/packages/flutter/lib/src/rendering/binding.dart @@ -292,3 +292,8 @@ class FlutterBinding extends HitTestTarget { void debugDumpRenderTree() { debugPrint(FlutterBinding.instance.renderView.toStringDeep()); } + +/// Prints a textual representation of the entire layer tree +void debugDumpLayerTree() { + debugPrint(FlutterBinding.instance.renderView.layer.toStringDeep()); +} diff --git a/packages/flutter/lib/src/rendering/debug.dart b/packages/flutter/lib/src/rendering/debug.dart index 28cec70fee..4a6cc2fd4f 100644 --- a/packages/flutter/lib/src/rendering/debug.dart +++ b/packages/flutter/lib/src/rendering/debug.dart @@ -6,6 +6,8 @@ import 'dart:ui' as ui; import 'dart:async'; import 'dart:collection'; +import 'package:vector_math/vector_math_64.dart'; + /// Causes each RenderBox to paint a box around its bounds. bool debugPaintSizeEnabled = false; @@ -36,6 +38,12 @@ int debugPaintPointersColorValue = 0x00BBBB; /// The color to use when painting RenderError boxes in checked mode. ui.Color debugErrorBoxColor = const ui.Color(0xFFFF0000); +List debugDescribeTransform(Matrix4 transform) { + List matrix = transform.toString().split('\n').map((String s) => ' $s').toList(); + matrix.removeLast(); + return matrix; +} + /// Prints a message to the console, which you can access using the "flutter" /// tool's "logs" command ("flutter logs"). /// diff --git a/packages/flutter/lib/src/rendering/layer.dart b/packages/flutter/lib/src/rendering/layer.dart index 3da0d16087..a58c7d3250 100644 --- a/packages/flutter/lib/src/rendering/layer.dart +++ b/packages/flutter/lib/src/rendering/layer.dart @@ -7,6 +7,7 @@ import 'dart:ui' as ui; import 'package:vector_math/vector_math_64.dart'; import 'basic_types.dart'; +import 'debug.dart'; export 'basic_types.dart'; @@ -66,6 +67,31 @@ abstract class Layer { /// The layerOffset is the accumulated offset of this layer's parent from the /// origin of the builder's coordinate system. void addToScene(ui.SceneBuilder builder, Offset layerOffset); + + String toString() => '$runtimeType'; + + dynamic debugOwner; + + String toStringDeep([String prefixLineOne = '', String prefixOtherLines = '']) { + String result = '$prefixLineOne$this\n'; + final String childrenDescription = debugDescribeChildren(prefixOtherLines); + final String settingsPrefix = childrenDescription != '' ? '$prefixOtherLines \u2502 ' : '$prefixOtherLines '; + List settings = []; + debugDescribeSettings(settings); + result += settings.map((String setting) => "$settingsPrefix$setting\n").join(); + if (childrenDescription == '') + result += '$prefixOtherLines\n'; + result += childrenDescription; + return result; + } + + void debugDescribeSettings(List settings) { + if (debugOwner != null) + settings.add('owner: $debugOwner'); + settings.add('offset: $offset'); + } + + String debugDescribeChildren(String prefix) => ''; } /// A composited layer containing a [Picture] @@ -88,6 +114,10 @@ class PictureLayer extends Layer { builder.addPicture(offset + layerOffset, picture, paintBounds); } + void debugDescribeSettings(List settings) { + super.debugDescribeSettings(settings); + settings.add('paintBounds: $paintBounds'); + } } /// A layer that indicates to the compositor that it should display @@ -113,7 +143,6 @@ class StatisticsLayer extends Layer { builder.addStatistics(optionsMask, paintBounds.shift(layerOffset)); builder.setRasterizerTracingThreshold(rasterizerThreshold); } - } @@ -210,6 +239,23 @@ class ContainerLayer extends Layer { } } + String debugDescribeChildren(String prefix) { + String result = '$prefix \u2502\n'; + if (_firstChild != null) { + Layer child = _firstChild; + int count = 1; + while (child != _lastChild) { + result += '${child.toStringDeep("$prefix \u251C\u2500child $count: ", "$prefix \u2502")}'; + count += 1; + child = child._nextSibling; + } + if (child != null) { + assert(child == _lastChild); + result += '${child.toStringDeep("$prefix \u2514\u2500child $count: ", "$prefix ")}'; + } + } + return result; + } } /// A composite layer that clips its children using a rectangle @@ -227,6 +273,10 @@ class ClipRectLayer extends ContainerLayer { builder.pop(); } + void debugDescribeSettings(List settings) { + super.debugDescribeSettings(settings); + settings.add('clipRect: $clipRect'); + } } /// A composite layer that clips its children using a rounded rectangle @@ -248,6 +298,11 @@ class ClipRRectLayer extends ContainerLayer { builder.pop(); } + void debugDescribeSettings(List settings) { + super.debugDescribeSettings(settings); + settings.add('bounds: $bounds'); + settings.add('clipRRect: $clipRRect'); + } } /// A composite layer that clips its children using a path @@ -269,6 +324,11 @@ class ClipPathLayer extends ContainerLayer { builder.pop(); } + void debugDescribeSettings(List settings) { + super.debugDescribeSettings(settings); + settings.add('bounds: $bounds'); + settings.add('clipPath: $clipPath'); + } } /// A composited layer that applies a transformation matrix to its children @@ -285,6 +345,12 @@ class TransformLayer extends ContainerLayer { addChildrenToScene(builder, Offset.zero); builder.pop(); } + + void debugDescribeSettings(List settings) { + super.debugDescribeSettings(settings); + settings.add('transform:'); + settings.addAll(debugDescribeTransform(transform)); + } } /// A composited layer that makes its children partially transparent @@ -306,4 +372,10 @@ class OpacityLayer extends ContainerLayer { addChildrenToScene(builder, offset + layerOffset); builder.pop(); } + + void debugDescribeSettings(List settings) { + super.debugDescribeSettings(settings); + settings.add('bounds: $bounds'); + settings.add('alpha: $alpha'); + } } diff --git a/packages/flutter/lib/src/rendering/object.dart b/packages/flutter/lib/src/rendering/object.dart index 0fa06b6341..0689a78b8d 100644 --- a/packages/flutter/lib/src/rendering/object.dart +++ b/packages/flutter/lib/src/rendering/object.dart @@ -74,6 +74,10 @@ class PaintingContext { assert(child.needsPaint); child._layer ??= new ContainerLayer(); child._layer.removeAllChildren(); + assert(() { + child._layer.debugOwner = child.debugOwner ?? child.runtimeType; + return true; + }); PaintingContext childContext = new PaintingContext._(child._layer, child.paintBounds); child._paintWithContext(childContext, Offset.zero); childContext._stopRecordingIfNeeded(); @@ -104,6 +108,10 @@ class PaintingContext { } else { assert(child._layer != null); child._layer.detach(); + assert(() { + child._layer.debugOwner = child.debugOwner ?? child.runtimeType; + return true; + }); } _appendLayer(child._layer, offset); } @@ -435,7 +443,7 @@ abstract class RenderObject extends AbstractNode implements HitTestTarget { /// Override in subclasses with children and call the visitor for each child void visitChildren(RenderObjectVisitor visitor) { } - dynamic debugOwner = ''; + dynamic debugOwner; void _debugReportException(String method, dynamic exception, StackTrace stack) { debugPrint('-- EXCEPTION --'); debugPrint('The following exception was raised during $method():'); @@ -443,7 +451,7 @@ abstract class RenderObject extends AbstractNode implements HitTestTarget { debugPrint('Stack trace:'); debugPrint('$stack'); debugPrint('The following RenderObject was being processed when the exception was fired:\n${this}'); - if (debugOwner != '') + if (debugOwner != null) debugPrint('That RenderObject had the following owner:\n$debugOwner'); if (debugRenderingExceptionHandler != null) debugRenderingExceptionHandler(this, method, exception, stack); @@ -1094,10 +1102,10 @@ abstract class RenderObject extends AbstractNode implements HitTestTarget { /// per string. Subclasses should override this to have their information /// included in toStringDeep(). void debugDescribeSettings(List settings) { + if (debugOwner != null) + settings.add('owner: $debugOwner'); settings.add('parentData: $parentData'); settings.add('constraints: $constraints'); - if (debugOwner != '') - settings.add('owner: $debugOwner'); } /// Returns a string describing the current node's descendants. Each line of diff --git a/packages/flutter/lib/src/rendering/proxy_box.dart b/packages/flutter/lib/src/rendering/proxy_box.dart index b4f19a5030..324adb72bb 100644 --- a/packages/flutter/lib/src/rendering/proxy_box.dart +++ b/packages/flutter/lib/src/rendering/proxy_box.dart @@ -10,6 +10,7 @@ import 'package:flutter/gestures.dart'; import 'package:vector_math/vector_math_64.dart'; import 'box.dart'; +import 'debug.dart'; import 'object.dart'; export 'package:flutter/src/painting/box_painter.dart'; @@ -875,10 +876,8 @@ class RenderTransform extends RenderProxyBox { void debugDescribeSettings(List settings) { super.debugDescribeSettings(settings); - List matrix = _transform.toString().split('\n').map((String s) => ' $s').toList(); - matrix.removeLast(); settings.add('transform matrix:'); - settings.addAll(matrix); + settings.addAll(debugDescribeTransform(_transform)); settings.add('origin: $origin'); settings.add('alignment: $alignment'); }