From 72f1903518b637c4e9719451a501c3cb045fa548 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Wed, 18 Oct 2023 23:19:06 -0700 Subject: [PATCH 1/3] Impl --- lib/ui/platform_dispatcher.dart | 52 +++++++++++++++++++++++++++++++++ lib/ui/window.dart | 13 ++++++++- 2 files changed, 64 insertions(+), 1 deletion(-) diff --git a/lib/ui/platform_dispatcher.dart b/lib/ui/platform_dispatcher.dart index 0bd387270ba13..66e68b88be56c 100644 --- a/lib/ui/platform_dispatcher.dart +++ b/lib/ui/platform_dispatcher.dart @@ -308,6 +308,28 @@ class PlatformDispatcher { _invoke(onMetricsChanged, _onMetricsChangedZone); } + // A debug-only variable that stores the [FlutterView]s for which + // [FlutterView.render] has already been called during the current + // [onBeginFrame]/[onDrawFrame] callback sequence. + // + // It is null outside the scope of those callbacks indicating that calls to + // [FlutterView.render] must be ignored. Furthermore, if a given [FlutterView] + // is already present in this set when its [FlutterView.render] is called + // again, that call must be ignored as a duplicate. + // + // Between [onBeginFrame] and [onDrawFrame] the properties value is + // temporarily stored in `_renderedViewsBetweenCallbacks` so that it survives + // the gap between the two callbacks. + // + // In release build, this variable is null, and therefore the calling rule is + // not enforced. This is because the check might hurt cold startup delay; + // see https://github.com/flutter/engine/pull/46919. + Set? _debugRenderedViews; + // A debug-only variable that temporarily stores the `_renderedViews` value + // between `_beginFrame` and `_drawFrame`. + // + // In release build, this variable is null. + Set? _debugRenderedViewsBetweenCallbacks; /// A callback invoked when any view begins a frame. /// @@ -329,11 +351,26 @@ class PlatformDispatcher { // Called from the engine, via hooks.dart void _beginFrame(int microseconds) { + assert(_debugRenderedViews == null); + assert(_debugRenderedViewsBetweenCallbacks == null); + assert(() { + _debugRenderedViews = {}; + return true; + }()); + _invoke1( onBeginFrame, _onBeginFrameZone, Duration(microseconds: microseconds), ); + + assert(_debugRenderedViews != null); + assert(_debugRenderedViewsBetweenCallbacks == null); + assert(() { + _debugRenderedViewsBetweenCallbacks = _debugRenderedViews; + _debugRenderedViews = null; + return true; + }()); } /// A callback that is invoked for each frame after [onBeginFrame] has @@ -351,7 +388,22 @@ class PlatformDispatcher { // Called from the engine, via hooks.dart void _drawFrame() { + assert(_debugRenderedViews == null); + assert(_debugRenderedViewsBetweenCallbacks != null); + assert(() { + _debugRenderedViews = _debugRenderedViewsBetweenCallbacks; + _debugRenderedViewsBetweenCallbacks = null; + return true; + }()); + _invoke(onDrawFrame, _onDrawFrameZone); + + assert(_debugRenderedViews != null); + assert(_debugRenderedViewsBetweenCallbacks == null); + assert(() { + _debugRenderedViews = null; + return true; + }()); } /// A callback that is invoked when pointer data is available. diff --git a/lib/ui/window.dart b/lib/ui/window.dart index 26a258cfa96c1..b7881a02fe8ca 100644 --- a/lib/ui/window.dart +++ b/lib/ui/window.dart @@ -353,7 +353,18 @@ class FlutterView { /// scheduling of frames. /// * [RendererBinding], the Flutter framework class which manages layout and /// painting. - void render(Scene scene) => _render(scene as _NativeScene); + void render(Scene scene) { + // Duplicated calls or calls outside of onBeginFrame/onDrawFrame (indicated + // by _renderedViews being null) are ignored. See _debugRenderedViews. + bool invalidRender = false; + assert(() { + invalidRender = platformDispatcher._debugRenderedViews?.add(this) ?? true; + return true; + }()); + if (!invalidRender) { + _render(scene as _NativeScene); + } + } @Native)>(symbol: 'PlatformConfigurationNativeApi::Render') external static void _render(_NativeScene scene); From 36adaa04b24a9c945b1902a8f1ef6d1f2adf1969 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Thu, 19 Oct 2023 13:06:39 -0700 Subject: [PATCH 2/3] Fix tests --- lib/ui/window.dart | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/ui/window.dart b/lib/ui/window.dart index b7881a02fe8ca..92771211ad0a0 100644 --- a/lib/ui/window.dart +++ b/lib/ui/window.dart @@ -356,12 +356,12 @@ class FlutterView { void render(Scene scene) { // Duplicated calls or calls outside of onBeginFrame/onDrawFrame (indicated // by _renderedViews being null) are ignored. See _debugRenderedViews. - bool invalidRender = false; + bool validRender = true; assert(() { - invalidRender = platformDispatcher._debugRenderedViews?.add(this) ?? true; + validRender = platformDispatcher._debugRenderedViews?.add(this) ?? false; return true; }()); - if (!invalidRender) { + if (validRender) { _render(scene as _NativeScene); } } From 9cf6632f9668d10480c46fff053db0a9b0f8b129 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Thu, 19 Oct 2023 13:09:41 -0700 Subject: [PATCH 3/3] Fix comment --- lib/ui/window.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/ui/window.dart b/lib/ui/window.dart index 92771211ad0a0..be761e548bb12 100644 --- a/lib/ui/window.dart +++ b/lib/ui/window.dart @@ -355,7 +355,7 @@ class FlutterView { /// painting. void render(Scene scene) { // Duplicated calls or calls outside of onBeginFrame/onDrawFrame (indicated - // by _renderedViews being null) are ignored. See _debugRenderedViews. + // by _debugRenderedViews being null) are ignored. See _debugRenderedViews. bool validRender = true; assert(() { validRender = platformDispatcher._debugRenderedViews?.add(this) ?? false;