Unverified Commit e2325600 authored by Michael Goderbauer's avatar Michael Goderbauer Committed by GitHub

Do not render any frames when just initializing Bindings (#39535)

parent e26be94e
...@@ -138,14 +138,13 @@ mixin RendererBinding on BindingBase, ServicesBinding, SchedulerBinding, Gesture ...@@ -138,14 +138,13 @@ mixin RendererBinding on BindingBase, ServicesBinding, SchedulerBinding, Gesture
/// Creates a [RenderView] object to be the root of the /// Creates a [RenderView] object to be the root of the
/// [RenderObject] rendering tree, and initializes it so that it /// [RenderObject] rendering tree, and initializes it so that it
/// will be rendered when the engine is next ready to display a /// will be rendered when the next frame is requested.
/// frame.
/// ///
/// Called automatically when the binding is created. /// Called automatically when the binding is created.
void initRenderView() { void initRenderView() {
assert(renderView == null); assert(renderView == null);
renderView = RenderView(configuration: createViewConfiguration(), window: window); renderView = RenderView(configuration: createViewConfiguration(), window: window);
renderView.scheduleInitialFrame(); renderView.prepareInitialFrame();
} }
/// The object that manages state about currently connected mice, for hover /// The object that manages state about currently connected mice, for hover
......
...@@ -74,7 +74,7 @@ class RenderView extends RenderObject with RenderObjectWithChildMixin<RenderBox> ...@@ -74,7 +74,7 @@ class RenderView extends RenderObject with RenderObjectWithChildMixin<RenderBox>
/// The configuration is initially set by the `configuration` argument /// The configuration is initially set by the `configuration` argument
/// passed to the constructor. /// passed to the constructor.
/// ///
/// Always call [scheduleInitialFrame] before changing the configuration. /// Always call [prepareInitialFrame] before changing the configuration.
set configuration(ViewConfiguration value) { set configuration(ViewConfiguration value) {
assert(value != null); assert(value != null);
if (configuration == value) if (configuration == value)
...@@ -110,16 +110,28 @@ class RenderView extends RenderObject with RenderObjectWithChildMixin<RenderBox> ...@@ -110,16 +110,28 @@ class RenderView extends RenderObject with RenderObjectWithChildMixin<RenderBox>
/// Bootstrap the rendering pipeline by scheduling the first frame. /// Bootstrap the rendering pipeline by scheduling the first frame.
/// ///
/// Deprecated. Call [prepareInitialFrame] followed by a call to
/// [PipelineOwner.requestVisualUpdate] on [owner] instead.
@Deprecated('Call prepareInitialFrame followed by owner.requestVisualUpdate() instead.')
void scheduleInitialFrame() {
prepareInitialFrame();
owner.requestVisualUpdate();
}
/// Bootstrap the rendering pipeline by preparing the first frame.
///
/// This should only be called once, and must be called before changing /// This should only be called once, and must be called before changing
/// [configuration]. It is typically called immediately after calling the /// [configuration]. It is typically called immediately after calling the
/// constructor. /// constructor.
void scheduleInitialFrame() { ///
/// This does not actually schedule the first frame. Call
/// [PipelineOwner.requestVisualUpdate] on [owner] to do that.
void prepareInitialFrame() {
assert(owner != null); assert(owner != null);
assert(_rootTransform == null); assert(_rootTransform == null);
scheduleInitialLayout(); scheduleInitialLayout();
scheduleInitialPaint(_updateMatricesAndCreateNewRootLayer()); scheduleInitialPaint(_updateMatricesAndCreateNewRootLayer());
assert(_rootTransform != null); assert(_rootTransform != null);
owner.requestVisualUpdate();
} }
Matrix4 _rootTransform; Matrix4 _rootTransform;
......
...@@ -194,8 +194,6 @@ mixin SchedulerBinding on BindingBase, ServicesBinding { ...@@ -194,8 +194,6 @@ mixin SchedulerBinding on BindingBase, ServicesBinding {
void initInstances() { void initInstances() {
super.initInstances(); super.initInstances();
_instance = this; _instance = this;
window.onBeginFrame = _handleBeginFrame;
window.onDrawFrame = _handleDrawFrame;
SystemChannels.lifecycle.setMessageHandler(_handleLifecycleMessage); SystemChannels.lifecycle.setMessageHandler(_handleLifecycleMessage);
readInitialLifecycleStateFromNativeWindow(); readInitialLifecycleStateFromNativeWindow();
...@@ -656,6 +654,12 @@ mixin SchedulerBinding on BindingBase, ServicesBinding { ...@@ -656,6 +654,12 @@ mixin SchedulerBinding on BindingBase, ServicesBinding {
scheduleFrame(); scheduleFrame();
} }
@protected
void ensureFrameCallbacksRegistered() {
window.onBeginFrame ??= _handleBeginFrame;
window.onDrawFrame ??= _handleDrawFrame;
}
/// Schedules a new frame using [scheduleFrame] if this object is not /// Schedules a new frame using [scheduleFrame] if this object is not
/// currently producing a frame. /// currently producing a frame.
/// ///
...@@ -717,6 +721,7 @@ mixin SchedulerBinding on BindingBase, ServicesBinding { ...@@ -717,6 +721,7 @@ mixin SchedulerBinding on BindingBase, ServicesBinding {
debugPrintStack(label: 'scheduleFrame() called. Current phase is $schedulerPhase.'); debugPrintStack(label: 'scheduleFrame() called. Current phase is $schedulerPhase.');
return true; return true;
}()); }());
ensureFrameCallbacksRegistered();
window.scheduleFrame(); window.scheduleFrame();
_hasScheduledFrame = true; _hasScheduledFrame = true;
} }
......
...@@ -70,6 +70,7 @@ class TestServiceExtensionsBinding extends BindingBase ...@@ -70,6 +70,7 @@ class TestServiceExtensionsBinding extends BindingBase
bool frameScheduled = false; bool frameScheduled = false;
@override @override
void scheduleFrame() { void scheduleFrame() {
ensureFrameCallbacksRegistered();
frameScheduled = true; frameScheduled = true;
} }
Future<void> doFrame() async { Future<void> doFrame() async {
...@@ -126,7 +127,7 @@ void main() { ...@@ -126,7 +127,7 @@ void main() {
final List<String> console = <String>[]; final List<String> console = <String>[];
setUpAll(() async { setUpAll(() async {
binding = TestServiceExtensionsBinding(); binding = TestServiceExtensionsBinding()..scheduleFrame();
expect(binding.frameScheduled, isTrue); expect(binding.frameScheduled, isTrue);
// We need to test this service extension here because the result is true // We need to test this service extension here because the result is true
...@@ -140,7 +141,7 @@ void main() { ...@@ -140,7 +141,7 @@ void main() {
firstFrameResult = await binding.testExtension('didSendFirstFrameRasterizedEvent', <String, String>{}); firstFrameResult = await binding.testExtension('didSendFirstFrameRasterizedEvent', <String, String>{});
expect(firstFrameResult, <String, String>{'enabled': 'false'}); expect(firstFrameResult, <String, String>{'enabled': 'false'});
await binding.doFrame(); // initial frame scheduled by creating the binding await binding.doFrame();
expect(binding.debugDidSendFirstFrameEvent, isTrue); expect(binding.debugDidSendFirstFrameEvent, isTrue);
firstFrameResult = await binding.testExtension('didSendFirstFrameEvent', <String, String>{}); firstFrameResult = await binding.testExtension('didSendFirstFrameEvent', <String, String>{});
......
...@@ -48,7 +48,8 @@ void main() { ...@@ -48,7 +48,8 @@ void main() {
final PipelineOwner pipelineOwner = PipelineOwner(); final PipelineOwner pipelineOwner = PipelineOwner();
renderView.attach(pipelineOwner); renderView.attach(pipelineOwner);
renderView.child = offscreen.root; renderView.child = offscreen.root;
renderView.scheduleInitialFrame(); renderView.prepareInitialFrame();
pipelineOwner.requestVisualUpdate();
// Lay out the onscreen in the default binding // Lay out the onscreen in the default binding
layout(onscreen.root, phase: EnginePhase.paint); layout(onscreen.root, phase: EnginePhase.paint);
expect(onscreen.child.hasSize, isTrue); expect(onscreen.child.hasSize, isTrue);
...@@ -77,7 +78,8 @@ void main() { ...@@ -77,7 +78,8 @@ void main() {
final PipelineOwner pipelineOwner = PipelineOwner(); final PipelineOwner pipelineOwner = PipelineOwner();
renderView.attach(pipelineOwner); renderView.attach(pipelineOwner);
renderView.child = offscreen.root; renderView.child = offscreen.root;
renderView.scheduleInitialFrame(); renderView.prepareInitialFrame();
pipelineOwner.requestVisualUpdate();
// Lay out the offscreen // Lay out the offscreen
pipelineOwner.flushLayout(); pipelineOwner.flushLayout();
expect(offscreen.child.hasSize, isTrue); expect(offscreen.child.hasSize, isTrue);
......
// Copyright 2019 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:ui' show window;
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter/widgets.dart';
void main() {
test('Instantiating WidgetsFlutterBinding does neither schedule a frame nor register frame callbacks', () async {
// Regression test for https://github.com/flutter/flutter/issues/39494.
// Preconditions.
expect(WidgetsBinding.instance, isNull);
expect(window.onBeginFrame, isNull);
expect(window.onDrawFrame, isNull);
// Instantiation does nothing with regards to frame scheduling.
final WidgetsFlutterBinding binding = WidgetsFlutterBinding.ensureInitialized();
expect(binding.hasScheduledFrame, isFalse);
expect(window.onBeginFrame, isNull);
expect(window.onDrawFrame, isNull);
// Frame callbacks are registered lazily when a frame is scheduled.
binding.scheduleFrame();
expect(window.onBeginFrame, isNotNull);
expect(window.onDrawFrame, isNotNull);
});
}
...@@ -23,7 +23,8 @@ class OffscreenRenderView extends RenderView { ...@@ -23,7 +23,8 @@ class OffscreenRenderView extends RenderView {
class OffscreenWidgetTree { class OffscreenWidgetTree {
OffscreenWidgetTree() { OffscreenWidgetTree() {
renderView.attach(pipelineOwner); renderView.attach(pipelineOwner);
renderView.scheduleInitialFrame(); renderView.prepareInitialFrame();
pipelineOwner.requestVisualUpdate();
} }
final RenderView renderView = OffscreenRenderView(); final RenderView renderView = OffscreenRenderView();
......
...@@ -834,8 +834,6 @@ class AutomatedTestWidgetsFlutterBinding extends TestWidgetsFlutterBinding { ...@@ -834,8 +834,6 @@ class AutomatedTestWidgetsFlutterBinding extends TestWidgetsFlutterBinding {
@override @override
void initInstances() { void initInstances() {
super.initInstances(); super.initInstances();
window.onBeginFrame = null;
window.onDrawFrame = null;
_mockFlutterAssets(); _mockFlutterAssets();
} }
...@@ -978,6 +976,13 @@ class AutomatedTestWidgetsFlutterBinding extends TestWidgetsFlutterBinding { ...@@ -978,6 +976,13 @@ class AutomatedTestWidgetsFlutterBinding extends TestWidgetsFlutterBinding {
}); });
} }
@override
void ensureFrameCallbacksRegistered() {
// Leave Window alone, do nothing.
assert(window.onDrawFrame == null);
assert(window.onBeginFrame == null);
}
@override @override
void scheduleWarmUpFrame() { void scheduleWarmUpFrame() {
// We override the default version of this so that the application-startup warm-up frame // We override the default version of this so that the application-startup warm-up frame
...@@ -1361,7 +1366,7 @@ class LiveTestWidgetsFlutterBinding extends TestWidgetsFlutterBinding { ...@@ -1361,7 +1366,7 @@ class LiveTestWidgetsFlutterBinding extends TestWidgetsFlutterBinding {
onNeedPaint: _handleViewNeedsPaint, onNeedPaint: _handleViewNeedsPaint,
window: window, window: window,
); );
renderView.scheduleInitialFrame(); renderView.prepareInitialFrame();
} }
@override @override
......
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