[CP] Revert "Reland: Enforce the rule of calling FlutterView.Render (#4530… (#47075)
b/306089854
Co-authored-by: Tong Mu <dkwingsmt@users.noreply.github.com>
diff --git a/lib/ui/fixtures/ui_test.dart b/lib/ui/fixtures/ui_test.dart
index 3cc93a7..0aff177 100644
--- a/lib/ui/fixtures/ui_test.dart
+++ b/lib/ui/fixtures/ui_test.dart
@@ -1076,45 +1076,3 @@
Object? arg20,
Object? arg21,
]);
-
-Scene _createRedBoxScene(Size size) {
- final SceneBuilder builder = SceneBuilder();
- builder.pushOffset(0.0, 0.0);
- final Paint paint = Paint()
- ..color = Color.fromARGB(255, 255, 0, 0)
- ..style = PaintingStyle.fill;
- final PictureRecorder baseRecorder = PictureRecorder();
- final Canvas canvas = Canvas(baseRecorder);
- canvas.drawRect(Rect.fromLTRB(0.0, 0.0, size.width, size.height), paint);
- final Picture picture = baseRecorder.endRecording();
- builder.addPicture(Offset(0.0, 0.0), picture);
- builder.pop();
- return builder.build();
-}
-
-@pragma('vm:entry-point')
-void incorrectImmediateRender() {
- PlatformDispatcher.instance.views.first.render(_createRedBoxScene(Size(2, 2)));
- _finish();
- // Don't schedule a frame here. This test only checks if the
- // [FlutterView.render] call is propagated to PlatformConfiguration.render
- // and thus doesn't need anything from `Animator` or `Engine`, which,
- // besides, are not even created in the native side at all.
-}
-
-@pragma('vm:entry-point')
-void incorrectDoubleRender() {
- PlatformDispatcher.instance.onBeginFrame = (Duration value) {
- PlatformDispatcher.instance.views.first.render(_createRedBoxScene(Size(2, 2)));
- PlatformDispatcher.instance.views.first.render(_createRedBoxScene(Size(3, 3)));
- };
- PlatformDispatcher.instance.onDrawFrame = () {
- PlatformDispatcher.instance.views.first.render(_createRedBoxScene(Size(4, 4)));
- PlatformDispatcher.instance.views.first.render(_createRedBoxScene(Size(5, 5)));
- };
- _finish();
- // Don't schedule a frame here. This test only checks if the
- // [FlutterView.render] call is propagated to PlatformConfiguration.render
- // and thus doesn't need anything from `Animator` or `Engine`, which,
- // besides, are not even created in the native side at all.
-}
diff --git a/lib/ui/platform_dispatcher.dart b/lib/ui/platform_dispatcher.dart
index 5f42aa4..0bd3872 100644
--- a/lib/ui/platform_dispatcher.dart
+++ b/lib/ui/platform_dispatcher.dart
@@ -308,21 +308,6 @@
_invoke(onMetricsChanged, _onMetricsChangedZone);
}
- // [FlutterView]s for which [FlutterView.render] has already been called
- // during the current [onBeginFrame]/[onDrawFrame] callback sequence.
- //
- // The field 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.
- Set<FlutterView>? _renderedViews;
- // Temporary storage of the `_renderedViews` value between `_beginFrame` and
- // `_drawFrame`.
- Set<FlutterView>? _renderedViewsBetweenCallbacks;
/// A callback invoked when any view begins a frame.
///
@@ -344,20 +329,11 @@
// Called from the engine, via hooks.dart
void _beginFrame(int microseconds) {
- assert(_renderedViews == null);
- assert(_renderedViewsBetweenCallbacks == null);
-
- _renderedViews = <FlutterView>{};
_invoke1<Duration>(
onBeginFrame,
_onBeginFrameZone,
Duration(microseconds: microseconds),
);
- _renderedViewsBetweenCallbacks = _renderedViews;
- _renderedViews = null;
-
- assert(_renderedViews == null);
- assert(_renderedViewsBetweenCallbacks != null);
}
/// A callback that is invoked for each frame after [onBeginFrame] has
@@ -375,16 +351,7 @@
// Called from the engine, via hooks.dart
void _drawFrame() {
- assert(_renderedViews == null);
- assert(_renderedViewsBetweenCallbacks != null);
-
- _renderedViews = _renderedViewsBetweenCallbacks;
- _renderedViewsBetweenCallbacks = null;
_invoke(onDrawFrame, _onDrawFrameZone);
- _renderedViews = null;
-
- assert(_renderedViews == null);
- assert(_renderedViewsBetweenCallbacks == null);
}
/// A callback that is invoked when pointer data is available.
diff --git a/lib/ui/window.dart b/lib/ui/window.dart
index 286e148..26a258c 100644
--- a/lib/ui/window.dart
+++ b/lib/ui/window.dart
@@ -327,21 +327,14 @@
/// Updates the view's rendering on the GPU with the newly provided [Scene].
///
- /// ## Requirement for calling this method
- ///
- /// This method must be called within the synchronous scope of the
+ /// This function must be called within the scope of the
/// [PlatformDispatcher.onBeginFrame] or [PlatformDispatcher.onDrawFrame]
- /// callbacks. Calls out of this scope will be ignored. To use this method,
- /// create a callback that calls this method instead, and assign it to either
- /// of the fields above; then schedule a frame, which is done typically with
- /// [PlatformDispatcher.scheduleFrame]. Also, make sure the callback does not
- /// have `await` before the `FlutterWindow.render` call.
+ /// callbacks being invoked.
///
- /// Additionally, this method can only be called once for each view during a
- /// single [PlatformDispatcher.onBeginFrame]/[PlatformDispatcher.onDrawFrame]
- /// callback sequence. Duplicate calls will be ignored in production.
- ///
- /// ## How to record a scene
+ /// If this function is called a second time during a single
+ /// [PlatformDispatcher.onBeginFrame]/[PlatformDispatcher.onDrawFrame]
+ /// callback sequence or called outside the scope of those callbacks, the call
+ /// will be ignored.
///
/// To record graphical operations, first create a [PictureRecorder], then
/// construct a [Canvas], passing that [PictureRecorder] to its constructor.
@@ -360,14 +353,7 @@
/// scheduling of frames.
/// * [RendererBinding], the Flutter framework class which manages layout and
/// painting.
- void render(Scene scene) {
- if (platformDispatcher._renderedViews?.add(this) != true) {
- // Duplicated calls or calls outside of onBeginFrame/onDrawFrame
- // (indicated by _renderedViews being null) are ignored, as documented.
- return;
- }
- _render(scene as _NativeScene);
- }
+ void render(Scene scene) => _render(scene as _NativeScene);
@Native<Void Function(Pointer<Void>)>(symbol: 'PlatformConfigurationNativeApi::Render')
external static void _render(_NativeScene scene);
diff --git a/lib/ui/window/platform_configuration_unittests.cc b/lib/ui/window/platform_configuration_unittests.cc
index 1fa8377..7410cae 100644
--- a/lib/ui/window/platform_configuration_unittests.cc
+++ b/lib/ui/window/platform_configuration_unittests.cc
@@ -15,171 +15,10 @@
#include "flutter/shell/common/shell_test.h"
#include "flutter/shell/common/thread_host.h"
#include "flutter/testing/testing.h"
-#include "gmock/gmock.h"
namespace flutter {
-
-namespace {
-
-static constexpr int64_t kImplicitViewId = 0;
-
-static void PostSync(const fml::RefPtr<fml::TaskRunner>& task_runner,
- const fml::closure& task) {
- fml::AutoResetWaitableEvent latch;
- fml::TaskRunner::RunNowOrPostTask(task_runner, [&latch, &task] {
- task();
- latch.Signal();
- });
- latch.Wait();
-}
-
-class MockRuntimeDelegate : public RuntimeDelegate {
- public:
- MOCK_METHOD(std::string, DefaultRouteName, (), (override));
- MOCK_METHOD(void, ScheduleFrame, (bool), (override));
- MOCK_METHOD(void,
- Render,
- (std::unique_ptr<flutter::LayerTree>, float),
- (override));
- MOCK_METHOD(void,
- UpdateSemantics,
- (SemanticsNodeUpdates, CustomAccessibilityActionUpdates),
- (override));
- MOCK_METHOD(void,
- HandlePlatformMessage,
- (std::unique_ptr<PlatformMessage>),
- (override));
- MOCK_METHOD(FontCollection&, GetFontCollection, (), (override));
- MOCK_METHOD(std::shared_ptr<AssetManager>, GetAssetManager, (), (override));
- MOCK_METHOD(void, OnRootIsolateCreated, (), (override));
- MOCK_METHOD(void,
- UpdateIsolateDescription,
- (const std::string, int64_t),
- (override));
- MOCK_METHOD(void, SetNeedsReportTimings, (bool), (override));
- MOCK_METHOD(std::unique_ptr<std::vector<std::string>>,
- ComputePlatformResolvedLocale,
- (const std::vector<std::string>&),
- (override));
- MOCK_METHOD(void, RequestDartDeferredLibrary, (intptr_t), (override));
- MOCK_METHOD(std::weak_ptr<PlatformMessageHandler>,
- GetPlatformMessageHandler,
- (),
- (const, override));
- MOCK_METHOD(void, SendChannelUpdate, (std::string, bool), (override));
- MOCK_METHOD(double,
- GetScaledFontSize,
- (double font_size, int configuration_id),
- (const, override));
-};
-
-class MockPlatformMessageHandler : public PlatformMessageHandler {
- public:
- MOCK_METHOD(void,
- HandlePlatformMessage,
- (std::unique_ptr<PlatformMessage> message),
- (override));
- MOCK_METHOD(bool,
- DoesHandlePlatformMessageOnPlatformThread,
- (),
- (const, override));
- MOCK_METHOD(void,
- InvokePlatformMessageResponseCallback,
- (int response_id, std::unique_ptr<fml::Mapping> mapping),
- (override));
- MOCK_METHOD(void,
- InvokePlatformMessageEmptyResponseCallback,
- (int response_id),
- (override));
-};
-
-// A class that can launch a RuntimeController with the specified
-// RuntimeDelegate.
-//
-// To use this class, contruct this class with Create, call LaunchRootIsolate,
-// and use the controller with ControllerTaskSync().
-class RuntimeControllerContext {
- public:
- using ControllerCallback = std::function<void(RuntimeController&)>;
-
- [[nodiscard]] static std::unique_ptr<RuntimeControllerContext> Create(
- Settings settings, //
- const TaskRunners& task_runners, //
- RuntimeDelegate& client) {
- auto [vm, isolate_snapshot] = Shell::InferVmInitDataFromSettings(settings);
- FML_CHECK(vm) << "Must be able to initialize the VM.";
- // Construct the class with `new` because `make_unique` has no access to the
- // private constructor.
- RuntimeControllerContext* raw_pointer = new RuntimeControllerContext(
- settings, task_runners, client, std::move(vm), isolate_snapshot);
- return std::unique_ptr<RuntimeControllerContext>(raw_pointer);
- }
-
- ~RuntimeControllerContext() {
- PostSync(task_runners_.GetUITaskRunner(),
- [&]() { runtime_controller_.reset(); });
- }
-
- // Launch the root isolate. The post_launch callback will be executed in the
- // same UI task, which can be used to create initial views.
- void LaunchRootIsolate(RunConfiguration& configuration,
- ControllerCallback post_launch) {
- PostSync(task_runners_.GetUITaskRunner(), [&]() {
- bool launch_success = runtime_controller_->LaunchRootIsolate(
- settings_, //
- []() {}, //
- configuration.GetEntrypoint(), //
- configuration.GetEntrypointLibrary(), //
- configuration.GetEntrypointArgs(), //
- configuration.TakeIsolateConfiguration()); //
- ASSERT_TRUE(launch_success);
- post_launch(*runtime_controller_);
- });
- }
-
- // Run a task that operates the RuntimeController on the UI thread, and wait
- // for the task to end.
- void ControllerTaskSync(ControllerCallback task) {
- ASSERT_TRUE(runtime_controller_);
- ASSERT_TRUE(task);
- PostSync(task_runners_.GetUITaskRunner(),
- [&]() { task(*runtime_controller_); });
- }
-
- private:
- RuntimeControllerContext(const Settings& settings,
- const TaskRunners& task_runners,
- RuntimeDelegate& client,
- DartVMRef vm,
- fml::RefPtr<const DartSnapshot> isolate_snapshot)
- : settings_(settings),
- task_runners_(task_runners),
- isolate_snapshot_(std::move(isolate_snapshot)),
- vm_(std::move(vm)),
- runtime_controller_(std::make_unique<RuntimeController>(
- client,
- &vm_,
- std::move(isolate_snapshot_),
- settings.idle_notification_callback, // idle notification callback
- flutter::PlatformData(), // platform data
- settings.isolate_create_callback, // isolate create callback
- settings.isolate_shutdown_callback, // isolate shutdown callback
- settings.persistent_isolate_data, // persistent isolate data
- UIDartState::Context{task_runners})) {}
-
- Settings settings_;
- TaskRunners task_runners_;
- fml::RefPtr<const DartSnapshot> isolate_snapshot_;
- DartVMRef vm_;
- std::unique_ptr<RuntimeController> runtime_controller_;
-};
-} // namespace
-
namespace testing {
-using ::testing::_;
-using ::testing::Return;
-
class PlatformConfigurationTest : public ShellTest {};
TEST_F(PlatformConfigurationTest, Initialization) {
@@ -493,84 +332,5 @@
DestroyShell(std::move(shell), task_runners);
}
-TEST_F(PlatformConfigurationTest, OutOfScopeRenderCallsAreIgnored) {
- Settings settings = CreateSettingsForFixture();
- TaskRunners task_runners = GetTaskRunnersForFixture();
-
- MockRuntimeDelegate client;
- auto platform_message_handler =
- std::make_shared<MockPlatformMessageHandler>();
- EXPECT_CALL(client, GetPlatformMessageHandler)
- .WillOnce(Return(platform_message_handler));
- // Render should not be called.
- EXPECT_CALL(client, Render).Times(0);
-
- auto finish_latch = std::make_shared<fml::AutoResetWaitableEvent>();
- auto finish = [finish_latch](Dart_NativeArguments args) {
- finish_latch->Signal();
- };
- AddNativeCallback("Finish", CREATE_NATIVE_ENTRY(finish));
-
- auto runtime_controller_context =
- RuntimeControllerContext::Create(settings, task_runners, client);
-
- auto configuration = RunConfiguration::InferFromSettings(settings);
- configuration.SetEntrypoint("incorrectImmediateRender");
- runtime_controller_context->LaunchRootIsolate(
- configuration, [](RuntimeController& runtime_controller) {
- runtime_controller.AddView(
- kImplicitViewId,
- ViewportMetrics(
- /*pixel_ratio=*/1.0, /*width=*/20, /*height=*/20,
- /*touch_slop=*/2, /*display_id=*/0));
- });
-
- // Wait for the Dart main function to end.
- finish_latch->Wait();
-}
-
-TEST_F(PlatformConfigurationTest, DuplicateRenderCallsAreIgnored) {
- Settings settings = CreateSettingsForFixture();
- TaskRunners task_runners = GetTaskRunnersForFixture();
-
- MockRuntimeDelegate client;
- auto platform_message_handler =
- std::make_shared<MockPlatformMessageHandler>();
- EXPECT_CALL(client, GetPlatformMessageHandler)
- .WillOnce(Return(platform_message_handler));
- // Render should only be called once, because the second call is ignored.
- EXPECT_CALL(client, Render).Times(1);
-
- auto finish_latch = std::make_shared<fml::AutoResetWaitableEvent>();
- auto finish = [finish_latch](Dart_NativeArguments args) {
- finish_latch->Signal();
- };
- AddNativeCallback("Finish", CREATE_NATIVE_ENTRY(finish));
-
- auto runtime_controller_context =
- RuntimeControllerContext::Create(settings, task_runners, client);
-
- auto configuration = RunConfiguration::InferFromSettings(settings);
- configuration.SetEntrypoint("incorrectDoubleRender");
- runtime_controller_context->LaunchRootIsolate(
- configuration, [](RuntimeController& runtime_controller) {
- runtime_controller.AddView(
- kImplicitViewId,
- ViewportMetrics(
- /*pixel_ratio=*/1.0, /*width=*/20, /*height=*/20,
- /*touch_slop=*/2, /*display_id=*/0));
- });
-
- // Wait for the Dart main function to end.
- finish_latch->Wait();
-
- // This call synchronously calls PlatformDispatcher's handleBeginFrame and
- // handleDrawFrame. Therefore it doesn't have to wait for latches.
- runtime_controller_context->ControllerTaskSync(
- [](RuntimeController& runtime_controller) {
- runtime_controller.BeginFrame(fml::TimePoint::Now(), 0);
- });
-}
-
} // namespace testing
} // namespace flutter
diff --git a/shell/common/shell.cc b/shell/common/shell.cc
index a6ba487..a7f7c9a 100644
--- a/shell/common/shell.cc
+++ b/shell/common/shell.cc
@@ -144,23 +144,6 @@
} // namespace
-std::pair<DartVMRef, fml::RefPtr<const DartSnapshot>>
-Shell::InferVmInitDataFromSettings(Settings& settings) {
- // Always use the `vm_snapshot` and `isolate_snapshot` provided by the
- // settings to launch the VM. If the VM is already running, the snapshot
- // arguments are ignored.
- auto vm_snapshot = DartSnapshot::VMSnapshotFromSettings(settings);
- auto isolate_snapshot = DartSnapshot::IsolateSnapshotFromSettings(settings);
- auto vm = DartVMRef::Create(settings, vm_snapshot, isolate_snapshot);
-
- // If the settings did not specify an `isolate_snapshot`, fall back to the
- // one the VM was launched with.
- if (!isolate_snapshot) {
- isolate_snapshot = vm->GetVMData()->GetIsolateSnapshot();
- }
- return {std::move(vm), isolate_snapshot};
-}
-
std::unique_ptr<Shell> Shell::Create(
const PlatformData& platform_data,
const TaskRunners& task_runners,
@@ -173,7 +156,19 @@
TRACE_EVENT0("flutter", "Shell::Create");
- auto [vm, isolate_snapshot] = InferVmInitDataFromSettings(settings);
+ // Always use the `vm_snapshot` and `isolate_snapshot` provided by the
+ // settings to launch the VM. If the VM is already running, the snapshot
+ // arguments are ignored.
+ auto vm_snapshot = DartSnapshot::VMSnapshotFromSettings(settings);
+ auto isolate_snapshot = DartSnapshot::IsolateSnapshotFromSettings(settings);
+ auto vm = DartVMRef::Create(settings, vm_snapshot, isolate_snapshot);
+ FML_CHECK(vm) << "Must be able to initialize the VM.";
+
+ // If the settings did not specify an `isolate_snapshot`, fall back to the
+ // one the VM was launched with.
+ if (!isolate_snapshot) {
+ isolate_snapshot = vm->GetVMData()->GetIsolateSnapshot();
+ }
auto resource_cache_limit_calculator =
std::make_shared<ResourceCacheLimitCalculator>(
settings.resource_cache_max_bytes_threshold);
diff --git a/shell/common/shell.h b/shell/common/shell.h
index 133fecc..039eb65 100644
--- a/shell/common/shell.h
+++ b/shell/common/shell.h
@@ -438,16 +438,6 @@
const std::shared_ptr<fml::ConcurrentTaskRunner>
GetConcurrentWorkerTaskRunner() const;
- // Infer the VM ref and the isolate snapshot based on the settings.
- //
- // If the VM is already running, the settings are ignored, but the returned
- // isolate snapshot always prioritize what is specified by the settings, and
- // falls back to the one VM was launched with.
- //
- // This function is what Shell::Create uses to infer snapshot settings.
- static std::pair<DartVMRef, fml::RefPtr<const DartSnapshot>>
- InferVmInitDataFromSettings(Settings& settings);
-
private:
using ServiceProtocolHandler =
std::function<bool(const ServiceProtocol::Handler::ServiceProtocolMap&,
diff --git a/testing/dart/platform_view_test.dart b/testing/dart/platform_view_test.dart
index 7d467f8..146c865 100644
--- a/testing/dart/platform_view_test.dart
+++ b/testing/dart/platform_view_test.dart
@@ -10,13 +10,10 @@
test('PlatformView layers do not emit errors from tester', () async {
final SceneBuilder builder = SceneBuilder();
builder.addPlatformView(1);
+ final Scene scene = builder.build();
- PlatformDispatcher.instance.onBeginFrame = (Duration duration) {
- final Scene scene = builder.build();
- PlatformDispatcher.instance.implicitView!.render(scene);
- scene.dispose();
- };
- PlatformDispatcher.instance.scheduleFrame();
+ PlatformDispatcher.instance.implicitView!.render(scene);
+ scene.dispose();
// Test harness asserts that this does not emit an error from the shell logs.
});
}