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
/// Creates a [RenderView] object to be the root of the
/// [RenderObject] rendering tree, and initializes it so that it
/// will be rendered when the engine is next ready to display a
/// frame.
/// will be rendered when the next frame is requested.
///
/// Called automatically when the binding is created.
void initRenderView() {
assert(renderView == null);
renderView = RenderView(configuration: createViewConfiguration(), window: window);
renderView.scheduleInitialFrame();
renderView.prepareInitialFrame();
}
/// The object that manages state about currently connected mice, for hover
......
......@@ -74,7 +74,7 @@ class RenderView extends RenderObject with RenderObjectWithChildMixin<RenderBox>
/// The configuration is initially set by the `configuration` argument
/// passed to the constructor.
///
/// Always call [scheduleInitialFrame] before changing the configuration.
/// Always call [prepareInitialFrame] before changing the configuration.
set configuration(ViewConfiguration value) {
assert(value != null);
if (configuration == value)
......@@ -110,16 +110,28 @@ class RenderView extends RenderObject with RenderObjectWithChildMixin<RenderBox>
/// 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
/// [configuration]. It is typically called immediately after calling the
/// 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(_rootTransform == null);
scheduleInitialLayout();
scheduleInitialPaint(_updateMatricesAndCreateNewRootLayer());
assert(_rootTransform != null);
owner.requestVisualUpdate();
}
Matrix4 _rootTransform;
......
......@@ -194,8 +194,6 @@ mixin SchedulerBinding on BindingBase, ServicesBinding {
void initInstances() {
super.initInstances();
_instance = this;
window.onBeginFrame = _handleBeginFrame;
window.onDrawFrame = _handleDrawFrame;
SystemChannels.lifecycle.setMessageHandler(_handleLifecycleMessage);
readInitialLifecycleStateFromNativeWindow();
......@@ -656,6 +654,12 @@ mixin SchedulerBinding on BindingBase, ServicesBinding {
scheduleFrame();
}
@protected
void ensureFrameCallbacksRegistered() {
window.onBeginFrame ??= _handleBeginFrame;
window.onDrawFrame ??= _handleDrawFrame;
}
/// Schedules a new frame using [scheduleFrame] if this object is not
/// currently producing a frame.
///
......@@ -717,6 +721,7 @@ mixin SchedulerBinding on BindingBase, ServicesBinding {
debugPrintStack(label: 'scheduleFrame() called. Current phase is $schedulerPhase.');
return true;
}());
ensureFrameCallbacksRegistered();
window.scheduleFrame();
_hasScheduledFrame = true;
}
......
......@@ -70,6 +70,7 @@ class TestServiceExtensionsBinding extends BindingBase
bool frameScheduled = false;
@override
void scheduleFrame() {
ensureFrameCallbacksRegistered();
frameScheduled = true;
}
Future<void> doFrame() async {
......@@ -126,7 +127,7 @@ void main() {
final List<String> console = <String>[];
setUpAll(() async {
binding = TestServiceExtensionsBinding();
binding = TestServiceExtensionsBinding()..scheduleFrame();
expect(binding.frameScheduled, isTrue);
// We need to test this service extension here because the result is true
......@@ -140,7 +141,7 @@ void main() {
firstFrameResult = await binding.testExtension('didSendFirstFrameRasterizedEvent', <String, String>{});
expect(firstFrameResult, <String, String>{'enabled': 'false'});
await binding.doFrame(); // initial frame scheduled by creating the binding
await binding.doFrame();
expect(binding.debugDidSendFirstFrameEvent, isTrue);
firstFrameResult = await binding.testExtension('didSendFirstFrameEvent', <String, String>{});
......
......@@ -48,7 +48,8 @@ void main() {
final PipelineOwner pipelineOwner = PipelineOwner();
renderView.attach(pipelineOwner);
renderView.child = offscreen.root;
renderView.scheduleInitialFrame();
renderView.prepareInitialFrame();
pipelineOwner.requestVisualUpdate();
// Lay out the onscreen in the default binding
layout(onscreen.root, phase: EnginePhase.paint);
expect(onscreen.child.hasSize, isTrue);
......@@ -77,7 +78,8 @@ void main() {
final PipelineOwner pipelineOwner = PipelineOwner();
renderView.attach(pipelineOwner);
renderView.child = offscreen.root;
renderView.scheduleInitialFrame();
renderView.prepareInitialFrame();
pipelineOwner.requestVisualUpdate();
// Lay out the offscreen
pipelineOwner.flushLayout();
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 {
class OffscreenWidgetTree {
OffscreenWidgetTree() {
renderView.attach(pipelineOwner);
renderView.scheduleInitialFrame();
renderView.prepareInitialFrame();
pipelineOwner.requestVisualUpdate();
}
final RenderView renderView = OffscreenRenderView();
......
......@@ -834,8 +834,6 @@ class AutomatedTestWidgetsFlutterBinding extends TestWidgetsFlutterBinding {
@override
void initInstances() {
super.initInstances();
window.onBeginFrame = null;
window.onDrawFrame = null;
_mockFlutterAssets();
}
......@@ -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
void scheduleWarmUpFrame() {
// We override the default version of this so that the application-startup warm-up frame
......@@ -1361,7 +1366,7 @@ class LiveTestWidgetsFlutterBinding extends TestWidgetsFlutterBinding {
onNeedPaint: _handleViewNeedsPaint,
window: window,
);
renderView.scheduleInitialFrame();
renderView.prepareInitialFrame();
}
@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