Commit b5a827bf authored by Ian Hickson's avatar Ian Hickson

Clean up our timeline events. (#3504)

This adds in particular the ability to track the time at which the
framework boots up, and the time at which we are confident we have
completed the first useful frame.
parent 0d7b61f5
...@@ -45,6 +45,8 @@ abstract class BindingBase { ...@@ -45,6 +45,8 @@ abstract class BindingBase {
/// [initServiceExtensions] to have bindings initialize their /// [initServiceExtensions] to have bindings initialize their
/// observatory service extensions, if any. /// observatory service extensions, if any.
BindingBase() { BindingBase() {
developer.Timeline.startSync('Framework initialization');
assert(!_debugInitialized); assert(!_debugInitialized);
initInstances(); initInstances();
assert(_debugInitialized); assert(_debugInitialized);
...@@ -52,6 +54,8 @@ abstract class BindingBase { ...@@ -52,6 +54,8 @@ abstract class BindingBase {
assert(!_debugServiceExtensionsRegistered); assert(!_debugServiceExtensionsRegistered);
initServiceExtensions(); initServiceExtensions();
assert(_debugServiceExtensionsRegistered); assert(_debugServiceExtensionsRegistered);
developer.Timeline.finishSync();
} }
static bool _debugInitialized = false; static bool _debugInitialized = false;
...@@ -116,7 +120,7 @@ abstract class BindingBase { ...@@ -116,7 +120,7 @@ abstract class BindingBase {
/// ///
/// Invokes the `callback` callback when the service extension is /// Invokes the `callback` callback when the service extension is
/// invoked. /// invoked.
void registerSignalServiceExtension({ void registerSignalServiceExtension({
@required String name, @required String name,
@required VoidCallback callback @required VoidCallback callback
}) { }) {
...@@ -143,7 +147,7 @@ abstract class BindingBase { ...@@ -143,7 +147,7 @@ abstract class BindingBase {
/// ///
/// Invokes the `setter` callback with the new value when the /// Invokes the `setter` callback with the new value when the
/// service extension method is invoked with a new value. /// service extension method is invoked with a new value.
void registerBoolServiceExtension({ void registerBoolServiceExtension({
String name, String name,
@required ValueGetter<bool> getter, @required ValueGetter<bool> getter,
@required ValueSetter<bool> setter @required ValueSetter<bool> setter
...@@ -152,7 +156,7 @@ abstract class BindingBase { ...@@ -152,7 +156,7 @@ abstract class BindingBase {
assert(getter != null); assert(getter != null);
assert(setter != null); assert(setter != null);
registerServiceExtension( registerServiceExtension(
name: name, name: name,
callback: (Map<String, String> parameters) async { callback: (Map<String, String> parameters) async {
if (parameters.containsKey('enabled')) if (parameters.containsKey('enabled'))
setter(parameters['enabled'] == 'true'); setter(parameters['enabled'] == 'true');
...@@ -172,7 +176,7 @@ abstract class BindingBase { ...@@ -172,7 +176,7 @@ abstract class BindingBase {
/// ///
/// Invokes the `setter` callback with the new value when the /// Invokes the `setter` callback with the new value when the
/// service extension method is invoked with a new value. /// service extension method is invoked with a new value.
void registerNumericServiceExtension({ void registerNumericServiceExtension({
@required String name, @required String name,
@required ValueGetter<double> getter, @required ValueGetter<double> getter,
@required ValueSetter<double> setter @required ValueSetter<double> setter
...@@ -200,7 +204,7 @@ abstract class BindingBase { ...@@ -200,7 +204,7 @@ abstract class BindingBase {
/// logs. /// logs.
/// ///
/// The returned map will be mutated. /// The returned map will be mutated.
void registerServiceExtension({ void registerServiceExtension({
@required String name, @required String name,
@required ServiceExtensionCallback callback @required ServiceExtensionCallback callback
}) { }) {
......
...@@ -739,7 +739,7 @@ class PipelineOwner { ...@@ -739,7 +739,7 @@ class PipelineOwner {
/// Called as part of the rendering pipeline after [flushLayout] and before /// Called as part of the rendering pipeline after [flushLayout] and before
/// [flushPaint]. /// [flushPaint].
void flushCompositingBits() { void flushCompositingBits() {
Timeline.startSync('Compositing Bits'); Timeline.startSync('Compositing bits');
_nodesNeedingCompositingBitsUpdate.sort((RenderObject a, RenderObject b) => a.depth - b.depth); _nodesNeedingCompositingBitsUpdate.sort((RenderObject a, RenderObject b) => a.depth - b.depth);
for (RenderObject node in _nodesNeedingCompositingBitsUpdate) { for (RenderObject node in _nodesNeedingCompositingBitsUpdate) {
if (node._needsCompositingBitsUpdate && node.owner == this) if (node._needsCompositingBitsUpdate && node.owner == this)
...@@ -797,7 +797,7 @@ class PipelineOwner { ...@@ -797,7 +797,7 @@ class PipelineOwner {
Timeline.finishSync(); Timeline.finishSync();
} }
} }
/// Cause the entire subtree rooted at the given [RenderObject] to /// Cause the entire subtree rooted at the given [RenderObject] to
/// be entirely reprocessed. This is used by development tools when /// be entirely reprocessed. This is used by development tools when
/// the application code has changed, to cause the rendering tree to /// the application code has changed, to cause the rendering tree to
...@@ -1196,7 +1196,7 @@ abstract class RenderObject extends AbstractNode implements HitTestTarget { ...@@ -1196,7 +1196,7 @@ abstract class RenderObject extends AbstractNode implements HitTestTarget {
} }
if (targetFrame != null && targetFrame < stack.length) { if (targetFrame != null && targetFrame < stack.length) {
information.writeln( information.writeln(
'These invalid constraints were provided to $runtimeType\'s method() ' 'These invalid constraints were provided to $runtimeType\'s method() '
'function by the following function, which probably computed the ' 'function by the following function, which probably computed the '
'invalid constraints in question:' 'invalid constraints in question:'
); );
......
...@@ -125,7 +125,7 @@ class RenderView extends RenderObject with RenderObjectWithChildMixin<RenderBox> ...@@ -125,7 +125,7 @@ class RenderView extends RenderObject with RenderObjectWithChildMixin<RenderBox>
/// ///
/// Actually causes the output of the rendering pipeline to appear on screen. /// Actually causes the output of the rendering pipeline to appear on screen.
void compositeFrame() { void compositeFrame() {
Timeline.startSync('Composite'); Timeline.startSync('Compositing');
try { try {
final TransformLayer transformLayer = layer; final TransformLayer transformLayer = layer;
transformLayer.transform = _logicalToDeviceTransform; transformLayer.transform = _logicalToDeviceTransform;
......
...@@ -341,7 +341,7 @@ abstract class SchedulerBinding extends BindingBase { ...@@ -341,7 +341,7 @@ abstract class SchedulerBinding extends BindingBase {
/// typically drive the rendering pipeline, and finally calls the /// typically drive the rendering pipeline, and finally calls the
/// callbacks registered by [addPostFrameCallback]. /// callbacks registered by [addPostFrameCallback].
void handleBeginFrame(Duration rawTimeStamp) { void handleBeginFrame(Duration rawTimeStamp) {
Timeline.startSync('Begin frame'); Timeline.startSync('Frame');
assert(!_debugInFrame); assert(!_debugInFrame);
assert(() { _debugInFrame = true; return true; }); assert(() { _debugInFrame = true; return true; });
Duration timeStamp = new Duration( Duration timeStamp = new Duration(
......
...@@ -164,6 +164,9 @@ class WidgetsAppState<T extends WidgetsApp> extends State<T> implements WidgetsB ...@@ -164,6 +164,9 @@ class WidgetsAppState<T extends WidgetsApp> extends State<T> implements WidgetsB
return new Container(); return new Container();
} }
// TODO(ianh): The following line should not be included in release mode, only in profile and debug modes.
WidgetsBinding.instance.didFirstFrame();
Widget result = new MediaQuery( Widget result = new MediaQuery(
data: new MediaQueryData.fromWindow(ui.window), data: new MediaQueryData.fromWindow(ui.window),
child: new LocaleQuery( child: new LocaleQuery(
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
import 'dart:developer' as developer;
import 'dart:ui' as ui show window; import 'dart:ui' as ui show window;
import 'dart:ui' show AppLifecycleState, Locale; import 'dart:ui' show AppLifecycleState, Locale;
...@@ -171,11 +172,27 @@ abstract class WidgetsBinding extends BindingBase implements GestureBinding, Ren ...@@ -171,11 +172,27 @@ abstract class WidgetsBinding extends BindingBase implements GestureBinding, Ren
observer.didChangeAppLifecycleState(state); observer.didChangeAppLifecycleState(state);
} }
bool _didFirstFrame = false;
bool _reportFirstFrame = true;
/// Tell the framework that the first useful frame has been completed.
///
/// This is used by [WidgetsApp] to report the first frame.
// remove this once we've fixed https://github.com/flutter/flutter/issues/1865
void didFirstFrame() {
_didFirstFrame = true;
}
@override @override
void beginFrame() { void beginFrame() {
buildOwner.buildDirtyElements(); buildOwner.buildDirtyElements();
super.beginFrame(); super.beginFrame();
buildOwner.finalizeTree(); buildOwner.finalizeTree();
// TODO(ianh): Following code should not be included in release mode, only profile and debug mode
if (_reportFirstFrame && _didFirstFrame) {
developer.Timeline.instantSync('Widgets completed first useful frame');
_reportFirstFrame = false;
}
} }
/// The [Element] that is at the root of the hierarchy (and which wraps the /// The [Element] that is at the root of the hierarchy (and which wraps the
......
...@@ -788,13 +788,15 @@ class BuildOwner { ...@@ -788,13 +788,15 @@ class BuildOwner {
/// After the current call stack unwinds, a microtask that notifies listeners /// After the current call stack unwinds, a microtask that notifies listeners
/// about changes to global keys will run. /// about changes to global keys will run.
void finalizeTree() { void finalizeTree() {
Timeline.startSync('Finalize tree');
lockState(() { lockState(() {
_inactiveElements._unmountAll(); _inactiveElements._unmountAll();
}, context: 'while finalizing the widget tree'); }, context: 'while finalizing the widget tree');
assert(GlobalKey._debugCheckForDuplicates); assert(GlobalKey._debugCheckForDuplicates);
scheduleMicrotask(GlobalKey._notifyListeners); scheduleMicrotask(GlobalKey._notifyListeners);
Timeline.finishSync();
} }
/// Cause the entire subtree rooted at the given [Element] to /// Cause the entire subtree rooted at the given [Element] to
/// be entirely rebuilt. This is used by development tools when /// be entirely rebuilt. This is used by development tools when
/// the application code has changed, to cause the widget tree to /// the application code has changed, to cause the widget tree to
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment