From 783c867312ee950f6223d9b73d74bd7443c276af Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Tue, 20 Mar 2018 18:49:46 -0700 Subject: [PATCH 1/2] Updates in flutter/shell for the shell refactor (Patch 9) This changes is part of a large patch for easier review. Try the whole patch in one go by checking out https://github.com/chinmaygarde/flutter_engine/tree/shell directly. This patch contains the following changes: * Fairly mechanical removal of singletons used to access the task runners referneces for various thread specific tasks. These task runners can be queried on the shell. * Engine, Animator, PlatformView and Rasterizer use a delegation mechanism which the shell implmenets to access various resources that used to be singletons. There are significant opportunities to cleanup this code in the future. * Engines can be launched with a separate RunConfiguration instead of having various overrides for each variant. * Added documentation to some switches that were missing the same. * Remove platform specific impelemtations of flutter_tester in favor of one executable target for all platforms. * Update various platform specific backend to account for the new Shell and PlatformView API. --- shell/common/BUILD.gn | 43 +- shell/common/animator.cc | 81 +- shell/common/animator.h | 37 +- shell/common/engine.cc | 589 +++------- shell/common/engine.h | 153 +-- shell/common/io_manager.cc | 71 ++ shell/common/io_manager.h | 53 + shell/common/isolate_configuration.cc | 144 +++ shell/common/isolate_configuration.h | 51 + shell/common/null_platform_view.cc | 37 - shell/common/null_platform_view.h | 39 - shell/common/null_rasterizer.cc | 67 -- shell/common/null_rasterizer.h | 51 - shell/common/platform_view.cc | 182 +-- shell/common/platform_view.h | 117 +- .../common/platform_view_service_protocol.cc | 447 -------- shell/common/platform_view_service_protocol.h | 85 -- shell/common/rasterizer.cc | 197 ++++ shell/common/rasterizer.h | 60 +- shell/common/run_configuration.cc | 80 ++ shell/common/run_configuration.h | 56 + shell/common/shell.cc | 1008 ++++++++++++----- shell/common/shell.h | 254 +++-- shell/common/shell_unittests.cc | 133 +++ shell/common/surface.cc | 28 +- shell/common/surface.h | 14 +- shell/common/switches.cc | 121 ++ shell/common/switches.h | 37 +- shell/common/thread_host.cc | 38 + shell/common/thread_host.h | 43 + shell/common/tracing_controller.cc | 51 - shell/common/tracing_controller.h | 34 - shell/common/vsync_waiter.cc | 32 + shell/common/vsync_waiter.h | 21 +- shell/common/vsync_waiter_fallback.cc | 24 +- shell/common/vsync_waiter_fallback.h | 13 +- shell/gpu/BUILD.gn | 2 - shell/gpu/gpu_rasterizer.cc | 168 --- shell/gpu/gpu_rasterizer.h | 68 -- shell/gpu/gpu_surface_gl.cc | 11 +- shell/gpu/gpu_surface_gl.h | 5 +- shell/gpu/gpu_surface_software.cc | 17 +- shell/gpu/gpu_surface_software.h | 7 +- shell/gpu/gpu_surface_vulkan.h | 5 +- shell/platform/BUILD.gn | 3 +- shell/platform/android/BUILD.gn | 16 +- shell/platform/android/android_context_gl.cc | 24 +- shell/platform/android/android_context_gl.h | 1 - .../android/android_external_texture_gl.cc | 6 +- .../platform/android/android_shell_holder.cc | 173 +++ shell/platform/android/android_shell_holder.h | 59 + shell/platform/android/android_surface.cc | 24 + shell/platform/android/android_surface.h | 7 +- shell/platform/android/android_surface_gl.cc | 37 +- shell/platform/android/android_surface_gl.h | 24 +- .../android/android_surface_software.cc | 14 +- .../android/android_surface_software.h | 26 +- .../android/android_surface_vulkan.cc | 15 +- .../platform/android/android_surface_vulkan.h | 13 +- shell/platform/android/apk_asset_provider.cc | 29 +- shell/platform/android/apk_asset_provider.h | 27 +- shell/platform/android/flutter_main.cc | 75 +- shell/platform/android/flutter_main.h | 27 +- shell/platform/android/library_loader.cc | 2 +- .../platform_message_response_android.cc | 62 + .../platform_message_response_android.h | 39 + .../platform/android/platform_view_android.cc | 590 ++-------- .../platform/android/platform_view_android.h | 98 +- .../android/platform_view_android_jni.cc | 416 +++++-- .../platform/android/vsync_waiter_android.cc | 100 +- shell/platform/android/vsync_waiter_android.h | 15 +- shell/platform/darwin/BUILD.gn | 12 +- shell/platform/darwin/common/BUILD.gn | 13 +- shell/platform/darwin/common/command_line.h | 17 + shell/platform/darwin/common/command_line.mm | 21 + shell/platform/darwin/common/platform_mac.h | 20 - shell/platform/darwin/common/platform_mac.mm | 160 --- .../darwin/common/process_info_mac.cc | 36 - .../platform/darwin/common/process_info_mac.h | 36 - shell/platform/darwin/desktop/BUILD.gn | 22 +- shell/platform/darwin/desktop/Info.plist | 8 +- .../darwin/desktop/flutter_app_delegate.h | 14 - .../darwin/desktop/flutter_app_delegate.m | 15 - .../darwin/desktop/flutter_application.mm | 8 - ...ation.h => flutter_application_delegate.h} | 11 +- .../desktop/flutter_application_delegate.mm | 80 ++ shell/platform/darwin/desktop/flutter_mac.xib | 695 ------------ .../platform/darwin/desktop/flutter_window.h | 6 +- .../platform/darwin/desktop/flutter_window.mm | 201 +++- shell/platform/darwin/desktop/main_mac.mm | 33 +- .../darwin/desktop/platform_view_mac.h | 27 +- .../darwin/desktop/platform_view_mac.mm | 110 +- .../darwin/desktop/vsync_waiter_mac.cc | 16 +- .../darwin/desktop/vsync_waiter_mac.h | 10 +- shell/platform/darwin/ios/BUILD.gn | 13 +- .../framework/Source/FlutterDartProject.mm | 405 +++---- .../Source/FlutterDartProject_Internal.h | 23 +- .../ios/framework/Source/FlutterDartSource.h | 30 - .../ios/framework/Source/FlutterDartSource.mm | 100 -- .../Source/FlutterHeadlessDartRunner.mm | 88 +- .../darwin/ios/framework/Source/FlutterView.h | 6 + .../ios/framework/Source/FlutterView.mm | 103 +- .../framework/Source/FlutterViewController.mm | 331 +++--- .../Source/FlutterViewController_Internal.h | 17 + .../framework/Source/accessibility_bridge.mm | 4 +- .../ios/framework/Source/flutter_main_ios.h | 18 - .../ios/framework/Source/flutter_main_ios.mm | 25 - .../Source/platform_message_response_darwin.h | 50 + .../platform_message_response_darwin.mm | 11 + .../Source/platform_message_router.h | 5 +- .../Source/platform_message_router.mm | 15 +- .../ios/framework/Source/vsync_waiter_ios.h | 19 +- .../ios/framework/Source/vsync_waiter_ios.mm | 115 +- .../darwin/ios/ios_external_texture_gl.mm | 11 +- shell/platform/darwin/ios/ios_gl_context.h | 2 +- shell/platform/darwin/ios/ios_gl_context.mm | 60 +- shell/platform/darwin/ios/ios_surface.h | 19 +- shell/platform/darwin/ios/ios_surface.mm | 31 +- shell/platform/darwin/ios/ios_surface_gl.h | 3 +- shell/platform/darwin/ios/ios_surface_gl.mm | 5 +- .../darwin/ios/ios_surface_software.h | 11 +- .../darwin/ios/ios_surface_software.mm | 9 +- shell/platform/darwin/ios/platform_view_ios.h | 93 +- .../platform/darwin/ios/platform_view_ios.mm | 146 +-- shell/platform/embedder/BUILD.gn | 8 +- shell/platform/embedder/embedder.cc | 240 ++-- shell/platform/embedder/embedder_engine.cc | 115 ++ shell/platform/embedder/embedder_engine.h | 53 + .../embedder/platform_view_embedder.cc | 55 +- .../embedder/platform_view_embedder.h | 29 +- shell/platform/linux/BUILD.gn | 23 - shell/platform/linux/main_linux.cc | 142 --- shell/testing/BUILD.gn | 24 +- shell/testing/platform_view_test.cc | 32 - shell/testing/platform_view_test.h | 38 - shell/testing/test_runner.cc | 48 - shell/testing/test_runner.h | 42 - shell/testing/tester_main.cc | 235 ++++ shell/testing/testing.cc | 24 - shell/testing/testing.h | 16 - 140 files changed, 5239 insertions(+), 5920 deletions(-) create mode 100644 shell/common/io_manager.cc create mode 100644 shell/common/io_manager.h create mode 100644 shell/common/isolate_configuration.cc create mode 100644 shell/common/isolate_configuration.h delete mode 100644 shell/common/null_platform_view.cc delete mode 100644 shell/common/null_platform_view.h delete mode 100644 shell/common/null_rasterizer.cc delete mode 100644 shell/common/null_rasterizer.h delete mode 100644 shell/common/platform_view_service_protocol.cc delete mode 100644 shell/common/platform_view_service_protocol.h create mode 100644 shell/common/run_configuration.cc create mode 100644 shell/common/run_configuration.h create mode 100644 shell/common/shell_unittests.cc create mode 100644 shell/common/thread_host.cc create mode 100644 shell/common/thread_host.h delete mode 100644 shell/common/tracing_controller.cc delete mode 100644 shell/common/tracing_controller.h delete mode 100644 shell/gpu/gpu_rasterizer.cc delete mode 100644 shell/gpu/gpu_rasterizer.h create mode 100644 shell/platform/android/android_shell_holder.cc create mode 100644 shell/platform/android/android_shell_holder.h create mode 100644 shell/platform/android/platform_message_response_android.cc create mode 100644 shell/platform/android/platform_message_response_android.h create mode 100644 shell/platform/darwin/common/command_line.h create mode 100644 shell/platform/darwin/common/command_line.mm delete mode 100644 shell/platform/darwin/common/platform_mac.h delete mode 100644 shell/platform/darwin/common/platform_mac.mm delete mode 100644 shell/platform/darwin/common/process_info_mac.cc delete mode 100644 shell/platform/darwin/common/process_info_mac.h delete mode 100644 shell/platform/darwin/desktop/flutter_app_delegate.h delete mode 100644 shell/platform/darwin/desktop/flutter_app_delegate.m delete mode 100644 shell/platform/darwin/desktop/flutter_application.mm rename shell/platform/darwin/desktop/{flutter_application.h => flutter_application_delegate.h} (65%) create mode 100644 shell/platform/darwin/desktop/flutter_application_delegate.mm delete mode 100644 shell/platform/darwin/desktop/flutter_mac.xib delete mode 100644 shell/platform/darwin/ios/framework/Source/FlutterDartSource.h delete mode 100644 shell/platform/darwin/ios/framework/Source/FlutterDartSource.mm create mode 100644 shell/platform/darwin/ios/framework/Source/FlutterViewController_Internal.h delete mode 100644 shell/platform/darwin/ios/framework/Source/flutter_main_ios.h delete mode 100644 shell/platform/darwin/ios/framework/Source/flutter_main_ios.mm create mode 100644 shell/platform/darwin/ios/framework/Source/platform_message_response_darwin.h create mode 100644 shell/platform/darwin/ios/framework/Source/platform_message_response_darwin.mm create mode 100644 shell/platform/embedder/embedder_engine.cc create mode 100644 shell/platform/embedder/embedder_engine.h delete mode 100644 shell/platform/linux/BUILD.gn delete mode 100644 shell/platform/linux/main_linux.cc delete mode 100644 shell/testing/platform_view_test.cc delete mode 100644 shell/testing/platform_view_test.h delete mode 100644 shell/testing/test_runner.cc delete mode 100644 shell/testing/test_runner.h create mode 100644 shell/testing/tester_main.cc delete mode 100644 shell/testing/testing.cc delete mode 100644 shell/testing/testing.h diff --git a/shell/common/BUILD.gn b/shell/common/BUILD.gn index f9e25a3c8d6d7..25edcfa23bd85 100644 --- a/shell/common/BUILD.gn +++ b/shell/common/BUILD.gn @@ -2,6 +2,8 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. +import("$flutter_root/testing/testing.gni") + # Template to generate a dart embedder resource.cc file. # Required invoker inputs: # String output (name of output file) @@ -61,18 +63,18 @@ source_set("common") { "animator.h", "engine.cc", "engine.h", - "null_platform_view.cc", - "null_platform_view.h", - "null_rasterizer.cc", - "null_rasterizer.h", + "io_manager.cc", + "io_manager.h", + "isolate_configuration.cc", + "isolate_configuration.h", "picture_serializer.cc", "picture_serializer.h", "platform_view.cc", "platform_view.h", - "platform_view_service_protocol.cc", - "platform_view_service_protocol.h", "rasterizer.cc", "rasterizer.h", + "run_configuration.cc", + "run_configuration.h", "shell.cc", "shell.h", "skia_event_tracer_impl.cc", @@ -81,8 +83,8 @@ source_set("common") { "surface.h", "switches.cc", "switches.h", - "tracing_controller.cc", - "tracing_controller.h", + "thread_host.cc", + "thread_host.h", "vsync_waiter.cc", "vsync_waiter.h", "vsync_waiter_fallback.cc", @@ -90,8 +92,6 @@ source_set("common") { ] deps = [ - "//third_party/dart/runtime:dart_api", - "//third_party/dart/runtime/platform:libdart_platform", "$flutter_root/assets", "$flutter_root/common", "$flutter_root/flow", @@ -99,10 +99,13 @@ source_set("common") { "$flutter_root/glue", "$flutter_root/lib/ui", "$flutter_root/runtime", + "$flutter_root/sky/engine/platform", "$flutter_root/sky/engine/wtf", "$flutter_root/synchronization", "$flutter_root/third_party/txt", "//garnet/public/lib/fxl", + "//third_party/dart/runtime:dart_api", + "//third_party/dart/runtime/platform:libdart_platform", "//third_party/rapidjson", "//third_party/skia", "//third_party/skia:gpu", @@ -112,7 +115,23 @@ source_set("common") { "//topaz/lib/tonic", ] - public_configs = [ - "$flutter_root:config", + public_configs = [ "$flutter_root:config" ] +} + +executable("shell_unittests") { + testonly = true + + sources = [ + "shell_unittests.cc", + ] + deps = [ + ":common", + "$flutter_root/fml", + "$flutter_root/lib/snapshot", + "$flutter_root/testing", + "//garnet/public/lib/fxl", + "//third_party/dart/runtime:libdart_jit", + "//third_party/skia", + "//topaz/lib/tonic", ] } diff --git a/shell/common/animator.cc b/shell/common/animator.cc index d5679a0160a86..f463ee7cf2dd2 100644 --- a/shell/common/animator.cc +++ b/shell/common/animator.cc @@ -4,19 +4,18 @@ #include "flutter/shell/common/animator.h" -#include "flutter/common/threads.h" -#include "flutter/fml/trace_event.h" +#include "flutter/glue/trace_event.h" #include "lib/fxl/time/stopwatch.h" #include "third_party/dart/runtime/include/dart_tools_api.h" namespace shell { -Animator::Animator(fml::WeakPtr rasterizer, - VsyncWaiter* waiter, - Engine* engine) - : rasterizer_(rasterizer), - waiter_(waiter), - engine_(engine), +Animator::Animator(Delegate& delegate, + blink::TaskRunners task_runners, + std::unique_ptr waiter) + : delegate_(delegate), + task_runners_(std::move(task_runners)), + waiter_(std::move(waiter)), last_begin_frame_time_(), dart_frame_deadline_(0), layer_tree_pipeline_(fxl::MakeRefCounted(2)), @@ -28,7 +27,11 @@ Animator::Animator(fml::WeakPtr rasterizer, dimension_change_pending_(false), weak_factory_(this) {} -Animator::~Animator() = default; +Animator::~Animator() { + // TODO(chinmaygarde): Remove after + // https://github.com/flutter/flutter/issues/13692 is fixed. + pending_frame_semaphore_.Signal(); +} void Animator::Stop() { paused_ = true; @@ -79,7 +82,6 @@ void Animator::BeginFrame(fxl::TimePoint frame_start_time, // If we still don't have valid continuation, the pipeline is currently // full because the consumer is being too slow. Try again at the next // frame interval. - TRACE_EVENT_INSTANT0("flutter", "ConsumerSlowDefer"); RequestFrame(); return; } @@ -94,13 +96,13 @@ void Animator::BeginFrame(fxl::TimePoint frame_start_time, { TRACE_EVENT2("flutter", "Framework Workload", "mode", "basic", "frame", FrameParity()); - engine_->BeginFrame(last_begin_frame_time_); + delegate_.OnAnimatorBeginFrame(*this, last_begin_frame_time_); } if (!frame_scheduled_) { // We don't have another frame pending, so we're waiting on user input // or I/O. Allow the Dart VM 100 ms. - engine_->NotifyIdle(dart_frame_deadline_ + 100000); + delegate_.OnAnimatorNotifyIdle(*this, dart_frame_deadline_ + 100000); } } @@ -120,15 +122,7 @@ void Animator::Render(std::unique_ptr layer_tree) { // Commit the pending continuation. producer_continuation_.Complete(std::move(layer_tree)); - blink::Threads::Gpu()->PostTask([ - rasterizer = rasterizer_, pipeline = layer_tree_pipeline_, - frame_id = FrameParity() - ]() { - if (!rasterizer.get()) - return; - TRACE_EVENT2("flutter", "GPU Workload", "mode", "basic", "frame", frame_id); - rasterizer->Draw(pipeline); - }); + delegate_.OnAnimatorDraw(*this, layer_tree_pipeline_); } bool Animator::CanReuseLastLayerTree() { @@ -137,10 +131,7 @@ bool Animator::CanReuseLastLayerTree() { void Animator::DrawLastLayerTree() { pending_frame_semaphore_.Signal(); - blink::Threads::Gpu()->PostTask([rasterizer = rasterizer_]() { - if (rasterizer.get()) - rasterizer->DrawLastLayerTree(); - }); + delegate_.OnAnimatorDrawLastLayerTree(*this); } void Animator::RequestFrame(bool regenerate_layer_tree) { @@ -164,31 +155,31 @@ void Animator::RequestFrame(bool regenerate_layer_tree) { // started an expensive operation right after posting this message however. // To support that, we need edge triggered wakes on VSync. - blink::Threads::UI()->PostTask( - [ self = weak_factory_.GetWeakPtr(), frame_number = frame_number_ ]() { - if (!self.get()) { - return; - } - TRACE_EVENT_ASYNC_BEGIN0("flutter", "Frame Request Pending", - frame_number); - self->AwaitVSync(); - }); + task_runners_.GetUITaskRunner()->PostTask([self = weak_factory_.GetWeakPtr(), + frame_number = frame_number_]() { + if (!self.get()) { + return; + } + TRACE_EVENT_ASYNC_BEGIN0("flutter", "Frame Request Pending", frame_number); + self->AwaitVSync(); + }); frame_scheduled_ = true; } void Animator::AwaitVSync() { - waiter_->AsyncWaitForVsync([self = weak_factory_.GetWeakPtr()]( - fxl::TimePoint frame_start_time, fxl::TimePoint frame_target_time) { - if (self) { - if (self->CanReuseLastLayerTree()) { - self->DrawLastLayerTree(); - } else { - self->BeginFrame(frame_start_time, frame_target_time); - } - } - }); + waiter_->AsyncWaitForVsync( + [self = weak_factory_.GetWeakPtr()](fxl::TimePoint frame_start_time, + fxl::TimePoint frame_target_time) { + if (self) { + if (self->CanReuseLastLayerTree()) { + self->DrawLastLayerTree(); + } else { + self->BeginFrame(frame_start_time, frame_target_time); + } + } + }); - engine_->NotifyIdle(dart_frame_deadline_); + delegate_.OnAnimatorNotifyIdle(*this, dart_frame_deadline_); } } // namespace shell diff --git a/shell/common/animator.h b/shell/common/animator.h index bc2ee21441b29..53b2ac6884158 100644 --- a/shell/common/animator.h +++ b/shell/common/animator.h @@ -5,7 +5,7 @@ #ifndef FLUTTER_SHELL_COMMON_ANIMATOR_H_ #define FLUTTER_SHELL_COMMON_ANIMATOR_H_ -#include "flutter/shell/common/engine.h" +#include "flutter/common/task_runners.h" #include "flutter/shell/common/rasterizer.h" #include "flutter/shell/common/vsync_waiter.h" #include "flutter/synchronization/pipeline.h" @@ -16,17 +16,28 @@ namespace shell { -class Animator { +class Animator final { public: - Animator(fml::WeakPtr rasterizer, - VsyncWaiter* waiter, - Engine* engine); + class Delegate { + public: + virtual void OnAnimatorBeginFrame(const Animator& animator, + fxl::TimePoint frame_time) = 0; - ~Animator(); + virtual void OnAnimatorNotifyIdle(const Animator& animator, + int64_t deadline) = 0; + + virtual void OnAnimatorDraw( + const Animator& animator, + fxl::RefPtr> pipeline) = 0; + + virtual void OnAnimatorDrawLastLayerTree(const Animator& animator) = 0; + }; - void set_rasterizer(fml::WeakPtr rasterizer) { - rasterizer_ = rasterizer; - } + Animator(Delegate& delegate, + blink::TaskRunners task_runners, + std::unique_ptr waiter); + + ~Animator(); void RequestFrame(bool regenerate_layer_tree = true); @@ -51,9 +62,9 @@ class Animator { const char* FrameParity(); - fml::WeakPtr rasterizer_; - VsyncWaiter* waiter_; - Engine* engine_; + Delegate& delegate_; + blink::TaskRunners task_runners_; + std::unique_ptr waiter_; fxl::TimePoint last_begin_frame_time_; int64_t dart_frame_deadline_; @@ -67,7 +78,7 @@ class Animator { bool dimension_change_pending_; SkISize last_layer_tree_size_; - fml::WeakPtrFactory weak_factory_; + fxl::WeakPtrFactory weak_factory_; FXL_DISALLOW_COPY_AND_ASSIGN(Animator); }; diff --git a/shell/common/engine.cc b/shell/common/engine.cc index 3629b729c29f7..ecfeb649c9652 100644 --- a/shell/common/engine.cc +++ b/shell/common/engine.cc @@ -4,47 +4,20 @@ #include "flutter/shell/common/engine.h" -#if OS(WIN) -#include -#include -#define access _access -#define R_OK 0x4 - -#ifndef S_ISDIR -#define S_ISDIR(mode) (((mode)&S_IFMT) == S_IFDIR) -#endif - -#ifndef S_ISREG -#define S_ISREG(mode) (((mode)&S_IFMT) == S_IFREG) -#endif - -#else -#include -#include -#include -#endif // OS(WIN) - -#include -#include #include #include -#include "flutter/assets/directory_asset_bundle.h" -#include "flutter/assets/unzipper_provider.h" -#include "flutter/assets/zip_asset_store.h" -#include "flutter/assets/asset_provider.h" #include "flutter/common/settings.h" -#include "flutter/common/threads.h" #include "flutter/glue/trace_event.h" #include "flutter/lib/snapshot/snapshot.h" #include "flutter/lib/ui/text/font_collection.h" #include "flutter/runtime/asset_font_selector.h" -#include "flutter/runtime/dart_controller.h" -#include "flutter/runtime/dart_init.h" -#include "flutter/runtime/runtime_init.h" +#include "flutter/runtime/platform_impl.h" #include "flutter/runtime/test_font_selector.h" #include "flutter/shell/common/animator.h" #include "flutter/shell/common/platform_view.h" +#include "flutter/shell/common/shell.h" +#include "flutter/sky/engine/platform/fonts/FontFallbackList.h" #include "flutter/sky/engine/public/web/Sky.h" #include "lib/fxl/files/eintr_wrapper.h" #include "lib/fxl/files/file.h" @@ -56,379 +29,205 @@ #include "third_party/skia/include/core/SkPictureRecorder.h" namespace shell { -namespace { - -constexpr char kAssetChannel[] = "flutter/assets"; -constexpr char kLifecycleChannel[] = "flutter/lifecycle"; -constexpr char kNavigationChannel[] = "flutter/navigation"; -constexpr char kLocalizationChannel[] = "flutter/localization"; -constexpr char kSettingsChannel[] = "flutter/settings"; - -#if OS(WIN) -void FindAndReplaceInPlace(std::string& str, - const std::string& findStr, - const std::string& replaceStr) { - size_t pos = 0; - while ((pos = str.find(findStr, pos)) != std::string::npos) { - str.replace(pos, findStr.length(), replaceStr); - pos += replaceStr.length(); - } -} -#endif -std::string SanitizePath(const std::string& path) { -#if OS(WIN) - std::string sanitized = path; - FindAndReplaceInPlace(sanitized, "\\\\", "/"); - if ((sanitized.length() > 2) && (sanitized[1] == ':')) { - // Path begins with a drive letter. - sanitized = '/' + sanitized; +static constexpr char kAssetChannel[] = "flutter/assets"; +static constexpr char kLifecycleChannel[] = "flutter/lifecycle"; +static constexpr char kNavigationChannel[] = "flutter/navigation"; +static constexpr char kLocalizationChannel[] = "flutter/localization"; +static constexpr char kSettingsChannel[] = "flutter/settings"; + +Engine::Engine(Delegate& delegate, + const blink::DartVM& vm, + blink::TaskRunners task_runners, + blink::Settings settings, + std::unique_ptr animator, + fml::WeakPtr resource_context, + fxl::RefPtr unref_queue) + : delegate_(delegate), + settings_(std::move(settings)), + animator_(std::move(animator)), + legacy_sky_platform_(settings_.using_blink ? new blink::PlatformImpl() + : nullptr), + load_script_error_(tonic::kNoError), + activity_running_(false), + have_surface_(false), + weak_factory_(this) { + weak_prototype_ = weak_factory_.GetWeakPtr(); + + if (legacy_sky_platform_) { + // TODO: Remove this legacy call along with the platform. This is what makes + // the engine unable to run from multiple threads in the legacy + // configuration. The microtask handler are still in this initializer still. + // So care must be taken to remove them from the legacy platform + // initializer. + blink::InitEngine(legacy_sky_platform_.get()); } - return sanitized; -#else - return path; -#endif -} -bool PathExists(const std::string& path) { - return access(path.c_str(), R_OK) == 0; + // Runtime controller is initialized here because it takes a reference to this + // object as its delegate. The delegate may be called in the constructor and + // we want to be fully initilazed by that point. + runtime_controller_ = std::make_unique( + *this, // runtime delegate + &vm, // VM + std::move(task_runners), // task runners + std::move(resource_context), // resource context + std::move(unref_queue) // skia unref queue + ); } -std::string FindPackagesPath(const std::string& main_dart) { - std::string directory = files::GetDirectoryName(main_dart); - std::string packages_path = directory + "/.packages"; - if (!PathExists(packages_path)) { - directory = files::GetDirectoryName(directory); - packages_path = directory + "/.packages"; - if (!PathExists(packages_path)) - packages_path = std::string(); +Engine::~Engine() { + if (legacy_sky_platform_) { + blink::ShutdownEngine(/* legacy_sky_platform_ */); } - return packages_path; } -std::string GetScriptUriFromPath(const std::string& path) { - return "file://" + SanitizePath(path); +fml::WeakPtr Engine::GetWeakPtr() const { + return weak_prototype_; } -} // namespace +bool Engine::UpdateAssetManager( + fxl::RefPtr new_asset_manager) { + if (asset_manager_ == new_asset_manager) { + return false; + } -Engine::Engine(PlatformView* platform_view) - : platform_view_(platform_view->GetWeakPtr()), - animator_(std::make_unique( - platform_view->rasterizer().GetWeakRasterizerPtr(), - platform_view->GetVsyncWaiter(), - this)), - load_script_error_(tonic::kNoError), - user_settings_data_("{}"), - activity_running_(false), - have_surface_(false), - weak_factory_(this) {} + asset_manager_ = new_asset_manager; -Engine::~Engine() {} + if (!asset_manager_) { + return false; + } -void Engine::set_rasterizer(fml::WeakPtr rasterizer) { - animator_->set_rasterizer(rasterizer); -} + if (settings_.using_blink) { + // Using blink as the text engine. + blink::FontFallbackList::SetUseTestFonts(settings_.use_test_fonts); + } else { + // Using libTXT as the text engine. + if (settings_.use_test_fonts) { + blink::FontCollection::ForProcess().RegisterTestFonts(); + } else { + blink::FontCollection::ForProcess().RegisterFonts(*asset_manager_.get()); + } + } -fml::WeakPtr Engine::GetWeakPtr() { - return weak_factory_.GetWeakPtr(); + return true; } -#if !FLUTTER_AOT -#elif OS(IOS) -#elif OS(ANDROID) -// TODO(bkonyi): do we even get here for Windows? -static const uint8_t* MemMapSnapshot(const std::string& aot_snapshot_path, - const std::string& default_file_name, - const std::string& settings_file_name, - bool executable) { - std::string asset_path; - if (settings_file_name.empty()) { - asset_path = aot_snapshot_path + "/" + default_file_name; - } else { - asset_path = aot_snapshot_path + "/" + settings_file_name; +bool Engine::Restart(RunConfiguration configuration) { + TRACE_EVENT0("flutter", "Engine::Restart"); + if (!configuration.IsValid()) { + FXL_LOG(ERROR) << "Engine run configuration was invalid."; + return false; } + runtime_controller_ = runtime_controller_->Clone(); + UpdateAssetManager(nullptr); + return Run(std::move(configuration)); +} -#if OS(WIN) - HANDLE file_handle_ = - CreateFileA(reinterpret_cast(path.c_str()), GENERIC_READ, - FILE_SHARE_READ, nullptr, OPEN_EXISTING, - FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS, nullptr); - - if (file_handle_ == INVALID_HANDLE_VALUE) { - return; +bool Engine::Run(RunConfiguration configuration) { + if (!configuration.IsValid()) { + FXL_LOG(ERROR) << "Engine run configuration was invalid."; + return false; } - size_ = GetFileSize(file_handle_, nullptr); - if (size_ == INVALID_FILE_SIZE) { - size_ = 0; - return; + if (!PrepareAndLaunchIsolate(std::move(configuration))) { + return false; } - int mapping_flags = executable ? PAGE_EXECUTE_READ : PAGE_READONLY; - mapping_handle_ = CreateFileMapping(file_handle_, nullptr, mapping_flags, 0, - size_, nullptr); + auto isolate = runtime_controller_->GetRootIsolate(); - CloseHandle(file_handle_); + bool isolate_running = + isolate && isolate->GetPhase() == blink::DartIsolate::Phase::Running; - if (mapping_handle_ == INVALID_HANDLE_VALUE) { - return; - } + if (isolate_running) { + tonic::DartState::Scope scope(isolate.get()); - int access_flags = FILE_MAP_READ; - if (executable) { - access_flags |= FILE_MAP_EXECUTE; - } - auto mapping = MapViewOfFile(mapping_handle_, access_flags, 0, 0, size_); + if (settings_.root_isolate_create_callback) { + settings_.root_isolate_create_callback(); + } - if (mapping == INVALID_HANDLE_VALUE) { - CloseHandle(mapping_handle_); - mapping_handle_ = INVALID_HANDLE_VALUE; - return; - } + if (settings_.root_isolate_shutdown_callback) { + isolate->AddIsolateShutdownCallback( + settings_.root_isolate_shutdown_callback); + } - void* symbol = static_cast(mapping); - if (symbol == NULL) { - return nullptr; - } -#else - struct stat info; - if (stat(asset_path.c_str(), &info) < 0) { - return nullptr; + // Blink uses a per isolate font selector. + if (settings_.using_blink) { + if (settings_.use_test_fonts) { + blink::TestFontSelector::Install(); + } else { + blink::AssetFontSelector::Install(asset_manager_); + } + } } - int64_t asset_size = info.st_size; - fxl::UniqueFD fd(HANDLE_EINTR(open(asset_path.c_str(), O_RDONLY))); - if (fd.get() == -1) { - return nullptr; - } + return isolate_running; +} - int mmap_flags = PROT_READ; - if (executable) - mmap_flags |= PROT_EXEC; +bool Engine::PrepareAndLaunchIsolate(RunConfiguration configuration) { + TRACE_EVENT0("flutter", "Engine::PrepareAndLaunchIsolate"); - void* symbol = mmap(NULL, asset_size, mmap_flags, MAP_PRIVATE, fd.get(), 0); - if (symbol == MAP_FAILED) { - return nullptr; - } -#endif - return reinterpret_cast(symbol); -} -#endif - -static const uint8_t* default_isolate_snapshot_data = nullptr; -static const uint8_t* default_isolate_snapshot_instr = nullptr; - -void Engine::Init(const std::string& bundle_path) { - const uint8_t* vm_snapshot_data; - const uint8_t* vm_snapshot_instr; -#if !FLUTTER_AOT - vm_snapshot_data = ::kDartVmSnapshotData; - vm_snapshot_instr = ::kDartVmSnapshotInstructions; - default_isolate_snapshot_data = ::kDartIsolateCoreSnapshotData; - default_isolate_snapshot_instr = ::kDartIsolateCoreSnapshotInstructions; -#elif OS(IOS) - const char* kDartApplicationLibraryPath = "App.framework/App"; - const char* application_library_path = kDartApplicationLibraryPath; - const blink::Settings& settings = blink::Settings::Get(); - const std::string& application_library_path_setting = - settings.application_library_path; - if (!application_library_path_setting.empty()) { - application_library_path = application_library_path_setting.c_str(); - } - dlerror(); // clear previous errors on thread - void* library_handle = dlopen(application_library_path, RTLD_NOW); - const char* err = dlerror(); - if (err != nullptr) { - FXL_LOG(FATAL) << "dlopen failed: " << err; - } - vm_snapshot_data = reinterpret_cast( - dlsym(library_handle, "kDartVmSnapshotData")); - vm_snapshot_instr = reinterpret_cast( - dlsym(library_handle, "kDartVmSnapshotInstructions")); - default_isolate_snapshot_data = reinterpret_cast( - dlsym(library_handle, "kDartIsolateSnapshotData")); - default_isolate_snapshot_instr = reinterpret_cast( - dlsym(library_handle, "kDartIsolateSnapshotInstructions")); -#elif OS(ANDROID) || OS(WIN) - const blink::Settings& settings = blink::Settings::Get(); - const std::string& aot_shared_library_path = settings.aot_shared_library_path; - const std::string& aot_snapshot_path = settings.aot_snapshot_path; - - if (!aot_shared_library_path.empty()) { - FXL_CHECK(aot_snapshot_path.empty()); - dlerror(); // clear previous errors on thread - void* library_handle = dlopen(aot_shared_library_path.c_str(), RTLD_NOW); - const char* err = dlerror(); - if (err != nullptr) { - FXL_LOG(FATAL) << "dlopen failed: " << err; - } - vm_snapshot_data = reinterpret_cast( - dlsym(library_handle, "_kDartVmSnapshotData")); - vm_snapshot_instr = reinterpret_cast( - dlsym(library_handle, "_kDartVmSnapshotInstructions")); - default_isolate_snapshot_data = reinterpret_cast( - dlsym(library_handle, "_kDartIsolateSnapshotData")); - default_isolate_snapshot_instr = reinterpret_cast( - dlsym(library_handle, "_kDartIsolateSnapshotInstructions")); - } else { - FXL_CHECK(!aot_snapshot_path.empty()); - vm_snapshot_data = - MemMapSnapshot(aot_snapshot_path, "vm_snapshot_data", - settings.aot_vm_snapshot_data_filename, false); - vm_snapshot_instr = - MemMapSnapshot(aot_snapshot_path, "vm_snapshot_instr", - settings.aot_vm_snapshot_instr_filename, true); - default_isolate_snapshot_data = - MemMapSnapshot(aot_snapshot_path, "isolate_snapshot_data", - settings.aot_isolate_snapshot_data_filename, false); - default_isolate_snapshot_instr = - MemMapSnapshot(aot_snapshot_path, "isolate_snapshot_instr", - settings.aot_isolate_snapshot_instr_filename, true); - } -#else -#error Unknown OS -#endif - blink::InitRuntime(vm_snapshot_data, vm_snapshot_instr, - default_isolate_snapshot_data, - default_isolate_snapshot_instr, bundle_path); -} - -const std::string Engine::main_entrypoint_ = "main"; - -void Engine::RunBundle(const std::string& bundle_path, - const std::string& entrypoint, - bool reuse_runtime_controller) { - TRACE_EVENT0("flutter", "Engine::RunBundle"); - ConfigureAssetBundle(bundle_path); - DoRunBundle(GetScriptUriFromPath(bundle_path), entrypoint, - reuse_runtime_controller); -} - -void Engine::DoRunBundle(const std::string& script_uri, - const std::string& entrypoint, - bool reuse_runtime_controller) { - ConfigureRuntime(script_uri, reuse_runtime_controller); - if (blink::IsRunningPrecompiledCode()) { - runtime_->dart_controller()->RunFromPrecompiledSnapshot(entrypoint); - } else { - std::vector kernel; - if (GetAssetAsBuffer(blink::kKernelAssetKey, &kernel)) { - runtime_->dart_controller()->RunFromKernel(kernel, entrypoint); - return; - } - std::vector snapshot; - if (!GetAssetAsBuffer(blink::kSnapshotAssetKey, &snapshot)) - return; - runtime_->dart_controller()->RunFromScriptSnapshot( - snapshot.data(), snapshot.size(), entrypoint); + UpdateAssetManager(configuration.GetAssetManager()); + + auto isolate_configuration = configuration.TakeIsolateConfiguration(); + + auto isolate = runtime_controller_->GetRootIsolate(); + + if (!isolate_configuration->PrepareIsolate(isolate)) { + FXL_DLOG(ERROR) << "Could not prepare to run the isolate."; + return false; } -} -// TODO(jsimmons): merge this with RunBundle -void Engine::RunBundleWithAssets( - fxl::RefPtr asset_provider, - const std::string& bundle_path, - const std::string& entrypoint, - bool reuse_runtime_controller) { - TRACE_EVENT0("flutter", "Engine::RunBundleWithAssets"); - asset_provider_ = asset_provider; - DoRunBundle(GetScriptUriFromPath(bundle_path), entrypoint, - reuse_runtime_controller); -} - -void Engine::RunBundleAndSource(const std::string& bundle_path, - const std::string& main, - const std::string& packages, - bool reuse_runtime_controller) { - TRACE_EVENT0("flutter", "Engine::RunBundleAndSource"); - FXL_CHECK(!blink::IsRunningPrecompiledCode()) - << "Cannot run from source in a precompiled build."; - std::string packages_path = packages; - if (packages_path.empty()) - packages_path = FindPackagesPath(main); - - if (!bundle_path.empty()) - ConfigureAssetBundle(bundle_path); - - ConfigureRuntime(main, reuse_runtime_controller); - - if (blink::GetKernelPlatformBinary() != nullptr) { - std::vector kernel; - if (!files::ReadFileToVector(main, &kernel)) { - load_script_error_ = tonic::kUnknownErrorType; - } - load_script_error_ = runtime_->dart_controller()->RunFromKernel(kernel); - } else { - load_script_error_ = - runtime_->dart_controller()->RunFromSource(main, packages_path); + if (!isolate->Run(configuration.GetEntrypoint())) { + FXL_DLOG(ERROR) << "Could not run the isolate."; + return false; } + + return true; } void Engine::BeginFrame(fxl::TimePoint frame_time) { TRACE_EVENT0("flutter", "Engine::BeginFrame"); - if (runtime_) - runtime_->BeginFrame(frame_time); + runtime_controller_->BeginFrame(frame_time); } void Engine::NotifyIdle(int64_t deadline) { TRACE_EVENT0("flutter", "Engine::NotifyIdle"); - if (runtime_) - runtime_->NotifyIdle(deadline); + runtime_controller_->NotifyIdle(deadline); } -void Engine::RunFromSource(const std::string& main, - const std::string& packages, - const std::string& bundle_path) { - RunBundleAndSource(bundle_path, main, packages); -} - -void Engine::SetAssetBundlePath(const std::string& bundle_path) { - TRACE_EVENT0("flutter", "Engine::SetAssetBundlePath"); - ConfigureAssetBundle(bundle_path); +std::pair Engine::GetUIIsolateReturnCode() { + return runtime_controller_->GetRootIsolateReturnCode(); } Dart_Port Engine::GetUIIsolateMainPort() { - if (!runtime_) - return ILLEGAL_PORT; - return runtime_->GetMainPort(); + return runtime_controller_->GetMainPort(); } std::string Engine::GetUIIsolateName() { - if (!runtime_) { - return ""; - } - return runtime_->GetIsolateName(); + return runtime_controller_->GetIsolateName(); } bool Engine::UIIsolateHasLivePorts() { - if (!runtime_) - return false; - return runtime_->HasLivePorts(); + return runtime_controller_->HasLivePorts(); } tonic::DartErrorHandleType Engine::GetUIIsolateLastError() { - if (!runtime_) - return tonic::kNoError; - return runtime_->GetLastError(); + return runtime_controller_->GetLastError(); } tonic::DartErrorHandleType Engine::GetLoadScriptError() { return load_script_error_; } -void Engine::OnOutputSurfaceCreated(const fxl::Closure& gpu_continuation) { - blink::Threads::Gpu()->PostTask(gpu_continuation); +void Engine::OnOutputSurfaceCreated() { have_surface_ = true; StartAnimatorIfPossible(); - if (runtime_) - ScheduleFrame(); + ScheduleFrame(); } -void Engine::OnOutputSurfaceDestroyed(const fxl::Closure& gpu_continuation) { +void Engine::OnOutputSurfaceDestroyed() { have_surface_ = false; StopAnimator(); - blink::Threads::Gpu()->PostTask(gpu_continuation); } void Engine::SetViewportMetrics(const blink::ViewportMetrics& metrics) { @@ -436,8 +235,7 @@ void Engine::SetViewportMetrics(const blink::ViewportMetrics& metrics) { viewport_metrics_.physical_height != metrics.physical_height || viewport_metrics_.physical_width != metrics.physical_width; viewport_metrics_ = metrics; - if (runtime_) - runtime_->SetViewportMetrics(viewport_metrics_); + runtime_controller_->SetViewportMetrics(viewport_metrics_); if (animator_) { if (dimensions_changed) animator_->SetDimensionChangePending(); @@ -459,8 +257,7 @@ void Engine::DispatchPlatformMessage( return; } - if (runtime_) { - runtime_->DispatchPlatformMessage(std::move(message)); + if (runtime_controller_->DispatchPlatformMessage(std::move(message))) { return; } @@ -493,7 +290,6 @@ bool Engine::HandleLifecyclePlatformMessage(blink::PlatformMessage* message) { bool Engine::HandleNavigationPlatformMessage( fxl::RefPtr message) { - FXL_DCHECK(!runtime_); const auto& data = message->data(); rapidjson::Document document; @@ -532,99 +328,33 @@ bool Engine::HandleLocalizationPlatformMessage( if (!language.IsString() || !country.IsString()) return false; - language_code_ = language.GetString(); - country_code_ = country.GetString(); - if (runtime_) - runtime_->SetLocale(language_code_, country_code_); - return true; + return runtime_controller_->SetLocale(language.GetString(), + country.GetString()); } void Engine::HandleSettingsPlatformMessage(blink::PlatformMessage* message) { const auto& data = message->data(); std::string jsonData(reinterpret_cast(data.data()), data.size()); - user_settings_data_ = jsonData; - if (runtime_) { - runtime_->SetUserSettingsData(user_settings_data_); - if (have_surface_) - ScheduleFrame(); + if (runtime_controller_->SetUserSettingsData(std::move(jsonData)) && + have_surface_) { + ScheduleFrame(); } } -void Engine::DispatchPointerDataPacket(const PointerDataPacket& packet) { - if (runtime_) - runtime_->DispatchPointerDataPacket(packet); +void Engine::DispatchPointerDataPacket(const blink::PointerDataPacket& packet) { + runtime_controller_->DispatchPointerDataPacket(packet); } void Engine::DispatchSemanticsAction(int id, blink::SemanticsAction action, std::vector args) { - if (runtime_) - runtime_->DispatchSemanticsAction(id, action, std::move(args)); + runtime_controller_->DispatchSemanticsAction(id, action, std::move(args)); } void Engine::SetSemanticsEnabled(bool enabled) { - semantics_enabled_ = enabled; - if (runtime_) - runtime_->SetSemanticsEnabled(semantics_enabled_); -} - -void Engine::ConfigureAssetBundle(const std::string& path) { - asset_provider_ = fxl::MakeRefCounted(path); - - struct stat stat_result = {}; - - // TODO(abarth): We should reset directory_asset_bundle_, but that might break - // custom font loading in hot reload. - - if (::stat(path.c_str(), &stat_result) != 0) { - FXL_LOG(INFO) << "Could not configure asset bundle at path: " << path; - return; - } - - std::string flx_path; - if (S_ISDIR(stat_result.st_mode)) { - flx_path = files::GetDirectoryName(path) + "/app.flx"; - } else if (S_ISREG(stat_result.st_mode)) { - flx_path = path; - } - - if (PathExists(flx_path)) { - asset_store_ = fxl::MakeRefCounted( - blink::GetUnzipperProviderForPath(flx_path)); - } + runtime_controller_->SetSemanticsEnabled(enabled); } -void Engine::ConfigureRuntime(const std::string& script_uri, - bool reuse_runtime_controller) { - if (runtime_ && reuse_runtime_controller) { - return; - } - runtime_ = blink::RuntimeController::Create(this); - runtime_->CreateDartController(std::move(script_uri), - default_isolate_snapshot_data, - default_isolate_snapshot_instr); - runtime_->SetViewportMetrics(viewport_metrics_); - runtime_->SetLocale(language_code_, country_code_); - runtime_->SetUserSettingsData(user_settings_data_); - runtime_->SetSemanticsEnabled(semantics_enabled_); -} - -void Engine::DidCreateMainIsolate(Dart_Isolate isolate) { - if (blink::Settings::Get().use_test_fonts) { - blink::TestFontSelector::Install(); - if (!blink::Settings::Get().using_blink) - blink::FontCollection::ForProcess().RegisterTestFonts(); - } else if (asset_provider_) { - blink::AssetFontSelector::Install(asset_provider_); - if (!blink::Settings::Get().using_blink) { - blink::FontCollection::ForProcess().RegisterFontsFromAssetProvider( - asset_provider_); - } - } -} - -void Engine::DidCreateSecondaryIsolate(Dart_Isolate isolate) {} - void Engine::StopAnimator() { animator_->Stop(); } @@ -659,49 +389,34 @@ void Engine::Render(std::unique_ptr layer_tree) { } void Engine::UpdateSemantics(blink::SemanticsNodeUpdates update) { - blink::Threads::Platform()->PostTask(fxl::MakeCopyable([ - platform_view = platform_view_.lock(), update = std::move(update) - ]() mutable { - if (platform_view) - platform_view->UpdateSemantics(std::move(update)); - })); + delegate_.OnEngineUpdateSemantics(*this, std::move(update)); } void Engine::HandlePlatformMessage( fxl::RefPtr message) { if (message->channel() == kAssetChannel) { HandleAssetPlatformMessage(std::move(message)); - return; + } else { + delegate_.OnEngineHandlePlatformMessage(*this, std::move(message)); } - blink::Threads::Platform()->PostTask([ - platform_view = platform_view_.lock(), message = std::move(message) - ]() mutable { - if (platform_view) - platform_view->HandlePlatformMessage(std::move(message)); - }); } void Engine::HandleAssetPlatformMessage( fxl::RefPtr message) { fxl::RefPtr response = message->response(); - if (!response) + if (!response) { return; + } const auto& data = message->data(); std::string asset_name(reinterpret_cast(data.data()), data.size()); + std::vector asset_data; - if (GetAssetAsBuffer(asset_name, &asset_data)) { + if (asset_manager_ && asset_manager_->GetAsBuffer(asset_name, &asset_data)) { response->Complete(std::move(asset_data)); } else { response->CompleteEmpty(); } } -bool Engine::GetAssetAsBuffer(const std::string& name, - std::vector* data) { - return ((asset_provider_ && - asset_provider_->GetAsBuffer(name, data)) || - (asset_store_ && asset_store_->GetAsBuffer(name, data))); -} - } // namespace shell diff --git a/shell/common/engine.h b/shell/common/engine.h index a0c5183338a43..ede442ee3e8a4 100644 --- a/shell/common/engine.h +++ b/shell/common/engine.h @@ -5,131 +5,144 @@ #ifndef SHELL_COMMON_ENGINE_H_ #define SHELL_COMMON_ENGINE_H_ -#include "flutter/assets/zip_asset_store.h" -#include "flutter/assets/asset_provider.h" +#include +#include + +#include "flutter/assets/asset_manager.h" +#include "flutter/common/task_runners.h" +#include "flutter/lib/ui/semantics/semantics_node.h" #include "flutter/lib/ui/window/platform_message.h" #include "flutter/lib/ui/window/viewport_metrics.h" +#include "flutter/runtime/dart_vm.h" +#include "flutter/runtime/platform_impl.h" #include "flutter/runtime/runtime_controller.h" #include "flutter/runtime/runtime_delegate.h" +#include "flutter/shell/common/animator.h" #include "flutter/shell/common/rasterizer.h" +#include "flutter/shell/common/run_configuration.h" #include "lib/fxl/macros.h" #include "lib/fxl/memory/weak_ptr.h" #include "third_party/skia/include/core/SkPicture.h" -namespace blink { -class DirectoryAssetBundle; -class ZipAssetBundle; -} // namespace blink - namespace shell { -class PlatformView; -class Animator; -using PointerDataPacket = blink::PointerDataPacket; -class Engine : public blink::RuntimeDelegate { +class Engine final : public blink::RuntimeDelegate { public: - explicit Engine(PlatformView* platform_view); + class Delegate { + public: + virtual void OnEngineUpdateSemantics( + const Engine& engine, + blink::SemanticsNodeUpdates update) = 0; + + virtual void OnEngineHandlePlatformMessage( + const Engine& engine, + fxl::RefPtr message) = 0; + }; + + Engine(Delegate& delegate, + const blink::DartVM& vm, + blink::TaskRunners task_runners, + blink::Settings settings, + std::unique_ptr animator, + fml::WeakPtr resource_context, + fxl::RefPtr unref_queue); ~Engine() override; - fml::WeakPtr GetWeakPtr(); - - static void Init(const std::string& bundle_path); + fml::WeakPtr GetWeakPtr() const; - void RunBundle(const std::string& bundle_path, - const std::string& entrypoint = main_entrypoint_, - bool reuse_runtime_controller = false); + FXL_WARN_UNUSED_RESULT + bool Run(RunConfiguration configuration); - // Uses the given provider to locate assets. - void RunBundleWithAssets(fxl::RefPtr asset_provider, - const std::string& bundle_path, - const std::string& entrypoint = main_entrypoint_, - bool reuse_runtime_controller = false); + // Used to "cold reload" a running application where the shell (along with the + // platform view and its rasterizer bindings) remains the same but the root + // isolate is torn down and restarted with the new configuration. Only used in + // the development workflow. + FXL_WARN_UNUSED_RESULT + bool Restart(RunConfiguration configuration); - // Uses the given source code instead of looking inside the bundle for the - // source code. - void RunBundleAndSource(const std::string& bundle_path, - const std::string& main, - const std::string& packages, - bool reuse_runtime_controller = false); + bool UpdateAssetManager(fxl::RefPtr asset_manager); void BeginFrame(fxl::TimePoint frame_time); - void NotifyIdle(int64_t deadline); - void RunFromSource(const std::string& main, - const std::string& packages, - const std::string& bundle); - void SetAssetBundlePath(const std::string& bundle_path); + void NotifyIdle(int64_t deadline); Dart_Port GetUIIsolateMainPort(); + std::string GetUIIsolateName(); + bool UIIsolateHasLivePorts(); + tonic::DartErrorHandleType GetUIIsolateLastError(); + tonic::DartErrorHandleType GetLoadScriptError(); - void OnOutputSurfaceCreated(const fxl::Closure& gpu_continuation); - void OnOutputSurfaceDestroyed(const fxl::Closure& gpu_continuation); + std::pair GetUIIsolateReturnCode(); + + void OnOutputSurfaceCreated(); + + void OnOutputSurfaceDestroyed(); + void SetViewportMetrics(const blink::ViewportMetrics& metrics); + void DispatchPlatformMessage(fxl::RefPtr message); - void DispatchPointerDataPacket(const PointerDataPacket& packet); + + void DispatchPointerDataPacket(const blink::PointerDataPacket& packet); + void DispatchSemanticsAction(int id, blink::SemanticsAction action, std::vector args); + void SetSemanticsEnabled(bool enabled); - void ScheduleFrame(bool regenerate_layer_tree = true) override; - void set_rasterizer(fml::WeakPtr rasterizer); + void ScheduleFrame(bool regenerate_layer_tree = true) override; private: - // RuntimeDelegate methods: + Engine::Delegate& delegate_; + const blink::Settings settings_; + std::unique_ptr animator_; + std::unique_ptr runtime_controller_; + std::unique_ptr legacy_sky_platform_; + tonic::DartErrorHandleType load_script_error_; + std::string initial_route_; + blink::ViewportMetrics viewport_metrics_; + fxl::RefPtr asset_manager_; + bool activity_running_; + bool have_surface_; + fml::WeakPtr weak_prototype_; + fml::WeakPtrFactory weak_factory_; + + // |blink::RuntimeDelegate| std::string DefaultRouteName() override; + + // |blink::RuntimeDelegate| void Render(std::unique_ptr layer_tree) override; + + // |blink::RuntimeDelegate| void UpdateSemantics(blink::SemanticsNodeUpdates update) override; + + // |blink::RuntimeDelegate| void HandlePlatformMessage( fxl::RefPtr message) override; - void DidCreateMainIsolate(Dart_Isolate isolate) override; - void DidCreateSecondaryIsolate(Dart_Isolate isolate) override; void StopAnimator(); - void StartAnimatorIfPossible(); - - void DoRunBundle(const std::string& script_uri, - const std::string& entrypoint, - bool reuse_runtime_controller); - void ConfigureAssetBundle(const std::string& path); - void ConfigureRuntime(const std::string& script_uri, - bool reuse_runtime_controller = false); + void StartAnimatorIfPossible(); bool HandleLifecyclePlatformMessage(blink::PlatformMessage* message); + bool HandleNavigationPlatformMessage( fxl::RefPtr message); + bool HandleLocalizationPlatformMessage(blink::PlatformMessage* message); + void HandleSettingsPlatformMessage(blink::PlatformMessage* message); void HandleAssetPlatformMessage(fxl::RefPtr message); - bool GetAssetAsBuffer(const std::string& name, std::vector* data); - static const std::string main_entrypoint_; + bool GetAssetAsBuffer(const std::string& name, std::vector* data); - fxl::RefPtr asset_provider_; - std::weak_ptr platform_view_; - std::unique_ptr animator_; - std::unique_ptr runtime_; - tonic::DartErrorHandleType load_script_error_; - std::string initial_route_; - blink::ViewportMetrics viewport_metrics_; - std::string language_code_; - std::string country_code_; - std::string user_settings_data_; - bool semantics_enabled_ = false; - // TODO(zarah): Remove usage of asset_store_ once app.flx is removed. - fxl::RefPtr asset_store_; - fxl::RefPtr directory_asset_bundle_; - // TODO(eseidel): This should move into an AnimatorStateMachine. - bool activity_running_; - bool have_surface_; - fml::WeakPtrFactory weak_factory_; + bool PrepareAndLaunchIsolate(RunConfiguration configuration); FXL_DISALLOW_COPY_AND_ASSIGN(Engine); }; diff --git a/shell/common/io_manager.cc b/shell/common/io_manager.cc new file mode 100644 index 0000000000000..72d49dff91c99 --- /dev/null +++ b/shell/common/io_manager.cc @@ -0,0 +1,71 @@ +// Copyright 2017 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. + +#include "flutter/shell/common/io_manager.h" + +#include "flutter/fml/message_loop.h" + +namespace shell { + +sk_sp IOManager::CreateCompatibleResourceLoadingContext( + GrBackend backend, + GrBackendContext backend_context) { + GrContextOptions options = {}; + + // There is currently a bug with doing GPU YUV to RGB conversions on the IO + // thread. The necessary work isn't being flushed or synchronized with the + // other threads correctly, so the textures end up blank. For now, suppress + // that feature, which will cause texture uploads to do CPU YUV conversion. + options.fDisableGpuYUVConversion = true; + + sk_sp context( + GrContext::Create(backend, backend_context, options)); + + if (context) { + // Do not cache textures created by the image decoder. These textures + // should be deleted when they are no longer referenced by an SkImage. + context->setResourceCacheLimits(0, 0); + } + + return context; +} + +IOManager::IOManager(sk_sp resource_context, + fxl::RefPtr unref_queue_task_runner) + : resource_context_(std::move(resource_context)), + resource_context_weak_factory_( + resource_context_ ? std::make_unique>( + resource_context_.get()) + : nullptr), + unref_queue_(fxl::MakeRefCounted( + std::move(unref_queue_task_runner), + fxl::TimeDelta::FromMilliseconds(250))), + weak_factory_(this) { + if (!resource_context_) { + FXL_DLOG(WARNING) << "The IO manager was initialized without a resource " + "context. Async texture uploads will be disabled. " + "Expect performance degradation."; + } + + if (resource_context_weak_factory_) { + resource_context_weak_prototype_ = + resource_context_weak_factory_->GetWeakPtr(); + } +} + +IOManager::~IOManager() { + // Last chance to drain the IO queue as the platform side reference to the + // underlying OpenGL context may be going away. + unref_queue_->Drain(); +} + +fml::WeakPtr IOManager::GetResourceContext() const { + return resource_context_weak_prototype_; +} + +fxl::RefPtr IOManager::GetSkiaUnrefQueue() const { + return unref_queue_; +} + +} // namespace shell diff --git a/shell/common/io_manager.h b/shell/common/io_manager.h new file mode 100644 index 0000000000000..7c9dab00cc225 --- /dev/null +++ b/shell/common/io_manager.h @@ -0,0 +1,53 @@ +// Copyright 2017 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. + +#ifndef FLUTTER_SHELL_COMMON_IO_MANAGER_H_ +#define FLUTTER_SHELL_COMMON_IO_MANAGER_H_ + +#include + +#include "flutter/flow/skia_gpu_object.h" +#include "flutter/fml/memory/weak_ptr.h" +#include "lib/fxl/macros.h" +#include "lib/fxl/memory/weak_ptr.h" +#include "third_party/skia/include/gpu/GrContext.h" + +namespace shell { + +class IOManager { + public: + // Convenience methods for platforms to create a GrContext used to supply to + // the IOManager. The platforms may create the context themselves if they so + // desire. + static sk_sp CreateCompatibleResourceLoadingContext( + GrBackend backend, + GrBackendContext backend_context); + + IOManager(sk_sp resource_context, + fxl::RefPtr unref_queue_task_runner); + + ~IOManager(); + + fml::WeakPtr GetResourceContext() const; + + fxl::RefPtr GetSkiaUnrefQueue() const; + + private: + // Resource context management. + sk_sp resource_context_; + fml::WeakPtr resource_context_weak_prototype_; + std::unique_ptr> + resource_context_weak_factory_; + + // Unref queue management. + fxl::RefPtr unref_queue_; + + fml::WeakPtrFactory weak_factory_; + + FXL_DISALLOW_COPY_AND_ASSIGN(IOManager); +}; + +} // namespace shell + +#endif // FLUTTER_SHELL_COMMON_IO_MANAGER_H_ diff --git a/shell/common/isolate_configuration.cc b/shell/common/isolate_configuration.cc new file mode 100644 index 0000000000000..fd330e544ceb5 --- /dev/null +++ b/shell/common/isolate_configuration.cc @@ -0,0 +1,144 @@ +// Copyright 2018 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. + +#include "flutter/shell/common/isolate_configuration.h" + +#include "flutter/runtime/dart_vm.h" + +namespace shell { + +IsolateConfiguration::IsolateConfiguration() = default; + +IsolateConfiguration::~IsolateConfiguration() = default; + +bool IsolateConfiguration::PrepareIsolate( + fml::WeakPtr isolate) { + if (!isolate) { + return false; + } + + if (isolate->GetPhase() != blink::DartIsolate::Phase::LibrariesSetup) { + FXL_DLOG(ERROR) + << "Isolate was in incorrect phase to be prepared for running."; + return false; + } + + return DoPrepareIsolate(*isolate); +} + +class PrecompiledIsolateConfiguration final : public IsolateConfiguration { + public: + PrecompiledIsolateConfiguration() = default; + + // |shell::IsolateConfiguration| + bool DoPrepareIsolate(blink::DartIsolate& isolate) override { + if (!blink::DartVM::IsRunningPrecompiledCode()) { + return false; + } + return isolate.PrepareForRunningFromPrecompiledCode(); + } + + private: + FXL_DISALLOW_COPY_AND_ASSIGN(PrecompiledIsolateConfiguration); +}; + +class SnapshotIsolateConfiguration : public IsolateConfiguration { + public: + SnapshotIsolateConfiguration(std::unique_ptr snapshot) + : snapshot_(std::move(snapshot)) {} + + // |shell::IsolateConfiguration| + bool DoPrepareIsolate(blink::DartIsolate& isolate) override { + if (blink::DartVM::IsRunningPrecompiledCode()) { + return false; + } + return isolate.PrepareForRunningFromSnapshot(std::move(snapshot_)); + } + + private: + std::unique_ptr snapshot_; + + FXL_DISALLOW_COPY_AND_ASSIGN(SnapshotIsolateConfiguration); +}; + +class SourceIsolateConfiguration final : public IsolateConfiguration { + public: + SourceIsolateConfiguration(std::string main_path, std::string packages_path) + : main_path_(std::move(main_path)), + packages_path_(std::move(packages_path)) {} + + // |shell::IsolateConfiguration| + bool DoPrepareIsolate(blink::DartIsolate& isolate) override { + if (blink::DartVM::IsRunningPrecompiledCode()) { + return false; + } + return isolate.PrepareForRunningFromSource(std::move(main_path_), + std::move(packages_path_)); + } + + private: + std::string main_path_; + std::string packages_path_; + + FXL_DISALLOW_COPY_AND_ASSIGN(SourceIsolateConfiguration); +}; + +std::unique_ptr IsolateConfiguration::InferFromSettings( + const blink::Settings& settings, + fxl::RefPtr asset_manager) { + // Running in AOT mode. + if (blink::DartVM::IsRunningPrecompiledCode()) { + return CreateForPrecompiledCode(); + } + + // Run from sources. + { + const auto& main = settings.main_dart_file_path; + const auto& packages = settings.packages_file_path; + if (main.size() != 0 && packages.size() != 0) { + return CreateForSource(std::move(main), std::move(packages)); + } + } + + // Running from kernel snapshot. + { + std::vector kernel; + if (asset_manager && + asset_manager->GetAsBuffer(settings.application_kernel_path, &kernel)) { + return CreateForSnapshot( + std::make_unique(std::move(kernel))); + } + } + + // Running from script snapshot. + { + std::vector script_snapshot; + if (asset_manager && asset_manager->GetAsBuffer( + settings.script_snapshot_path, &script_snapshot)) { + return CreateForSnapshot( + std::make_unique(std::move(script_snapshot))); + } + } + + return nullptr; +} + +std::unique_ptr +IsolateConfiguration::CreateForPrecompiledCode() { + return std::make_unique(); +} + +std::unique_ptr IsolateConfiguration::CreateForSnapshot( + std::unique_ptr snapshot) { + return std::make_unique(std::move(snapshot)); +} + +std::unique_ptr IsolateConfiguration::CreateForSource( + std::string main_path, + std::string packages_path) { + return std::make_unique(std::move(main_path), + std::move(packages_path)); +} + +} // namespace shell diff --git a/shell/common/isolate_configuration.h b/shell/common/isolate_configuration.h new file mode 100644 index 0000000000000..82d06dac621b2 --- /dev/null +++ b/shell/common/isolate_configuration.h @@ -0,0 +1,51 @@ +// Copyright 2018 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. + +#ifndef FLUTTER_SHELL_COMMON_ISOLATE_CONFIGURATION_H_ +#define FLUTTER_SHELL_COMMON_ISOLATE_CONFIGURATION_H_ + +#include +#include + +#include "flutter/assets/asset_manager.h" +#include "flutter/assets/asset_resolver.h" +#include "flutter/common/settings.h" +#include "flutter/fml/mapping.h" +#include "flutter/fml/memory/weak_ptr.h" +#include "flutter/runtime/dart_isolate.h" +#include "lib/fxl/macros.h" + +namespace shell { + +class IsolateConfiguration { + public: + static std::unique_ptr InferFromSettings( + const blink::Settings& settings, + fxl::RefPtr asset_manager); + + static std::unique_ptr CreateForPrecompiledCode(); + + static std::unique_ptr CreateForSnapshot( + std::unique_ptr snapshot); + + static std::unique_ptr CreateForSource( + std::string main_path, + std::string packages_path); + + IsolateConfiguration(); + + virtual ~IsolateConfiguration(); + + bool PrepareIsolate(fml::WeakPtr isolate); + + protected: + virtual bool DoPrepareIsolate(blink::DartIsolate& isolate) = 0; + + private: + FXL_DISALLOW_COPY_AND_ASSIGN(IsolateConfiguration); +}; + +} // namespace shell + +#endif // FLUTTER_SHELL_COMMON_ISOLATE_CONFIGURATION_H_ diff --git a/shell/common/null_platform_view.cc b/shell/common/null_platform_view.cc deleted file mode 100644 index 49fdf5a935b80..0000000000000 --- a/shell/common/null_platform_view.cc +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "flutter/shell/common/null_platform_view.h" - -#include "flutter/shell/common/null_rasterizer.h" -#include "flutter/shell/common/shell.h" - -namespace shell { - -NullPlatformView::NullPlatformView() - : PlatformView(std::make_unique()), weak_factory_(this) {} - -void NullPlatformView::Attach() { - CreateEngine(); -} - -NullPlatformView::~NullPlatformView() = default; - -fxl::WeakPtr NullPlatformView::GetWeakPtr() { - return weak_factory_.GetWeakPtr(); -} - -bool NullPlatformView::ResourceContextMakeCurrent() { - return false; -} - -// Hot-reload of the null platform view is not supported. -void NullPlatformView::RunFromSource(const std::string& assets_directory, - const std::string& main, - const std::string& packages) {} - -void NullPlatformView::SetAssetBundlePath(const std::string& assets_directory) { -} - -} // namespace shell diff --git a/shell/common/null_platform_view.h b/shell/common/null_platform_view.h deleted file mode 100644 index eb23d67b48a7c..0000000000000 --- a/shell/common/null_platform_view.h +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef COMMON_NULL_PLATFORM_VIEW_H_ -#define COMMON_NULL_PLATFORM_VIEW_H_ - -#include "flutter/shell/common/platform_view.h" -#include "lib/fxl/macros.h" -#include "lib/fxl/memory/weak_ptr.h" - -namespace shell { - -class NullPlatformView : public PlatformView { - public: - NullPlatformView(); - - ~NullPlatformView(); - - fxl::WeakPtr GetWeakPtr(); - - virtual void Attach() override; - - bool ResourceContextMakeCurrent() override; - - void RunFromSource(const std::string& assets_directory, - const std::string& main, - const std::string& packages) override; - void SetAssetBundlePath(const std::string& assets_directory) override; - - private: - fxl::WeakPtrFactory weak_factory_; - - FXL_DISALLOW_COPY_AND_ASSIGN(NullPlatformView); -}; - -} // namespace shell - -#endif // COMMON_NULL_PLATFORM_VIEW_H_ diff --git a/shell/common/null_rasterizer.cc b/shell/common/null_rasterizer.cc deleted file mode 100644 index 81efdd11d4970..0000000000000 --- a/shell/common/null_rasterizer.cc +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "flutter/shell/common/null_rasterizer.h" - -namespace shell { - -NullRasterizer::NullRasterizer() : weak_factory_(this) {} - -void NullRasterizer::Setup( - std::unique_ptr surface_or_null, - fxl::Closure rasterizer_continuation, - fxl::AutoResetWaitableEvent* setup_completion_event) { - surface_ = std::move(surface_or_null); - rasterizer_continuation(); - setup_completion_event->Signal(); -} - -void NullRasterizer::Teardown( - fxl::AutoResetWaitableEvent* teardown_completion_event) { - if (surface_) { - surface_.reset(); - } - teardown_completion_event->Signal(); -} - -fml::WeakPtr NullRasterizer::GetWeakRasterizerPtr() { - return weak_factory_.GetWeakPtr(); -} - -flow::LayerTree* NullRasterizer::GetLastLayerTree() { - return nullptr; -} - -void NullRasterizer::DrawLastLayerTree() { - // Null rasterizer. Nothing to do. -} - -flow::TextureRegistry& NullRasterizer::GetTextureRegistry() { - return *texture_registry_; -} - -void NullRasterizer::Clear(SkColor color, const SkISize& size) { - // Null rasterizer. Nothing to do. -} - -void NullRasterizer::Draw( - fxl::RefPtr> pipeline) { - FXL_ALLOW_UNUSED_LOCAL( - pipeline->Consume([](std::unique_ptr) { - // Drop the layer tree on the floor. We only need the pipeline empty so - // that frame requests are not deferred indefinitely due to - // backpressure. - })); -} - -void NullRasterizer::AddNextFrameCallback(fxl::Closure nextFrameCallback) { - // Null rasterizer. Nothing to do. -} - -void NullRasterizer::SetTextureRegistry( - flow::TextureRegistry* textureRegistry) { - texture_registry_ = textureRegistry; -} - -} // namespace shell diff --git a/shell/common/null_rasterizer.h b/shell/common/null_rasterizer.h deleted file mode 100644 index 8558a3c3accaa..0000000000000 --- a/shell/common/null_rasterizer.h +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef FLUTTER_SHELL_COMMON_NULL_RASTERIZER_H_ -#define FLUTTER_SHELL_COMMON_NULL_RASTERIZER_H_ - -#include "flutter/shell/common/rasterizer.h" -#include "lib/fxl/macros.h" -#include "lib/fxl/memory/weak_ptr.h" - -namespace shell { - -class NullRasterizer : public Rasterizer { - public: - NullRasterizer(); - - void Setup(std::unique_ptr surface_or_null, - fxl::Closure rasterizer_continuation, - fxl::AutoResetWaitableEvent* setup_completion_event) override; - - void Teardown( - fxl::AutoResetWaitableEvent* teardown_completion_event) override; - - void Clear(SkColor color, const SkISize& size) override; - - fml::WeakPtr GetWeakRasterizerPtr() override; - - flow::LayerTree* GetLastLayerTree() override; - - void DrawLastLayerTree() override; - - flow::TextureRegistry& GetTextureRegistry() override; - - void Draw(fxl::RefPtr> pipeline) override; - - void AddNextFrameCallback(fxl::Closure nextFrameCallback) override; - - void SetTextureRegistry(flow::TextureRegistry* textureRegistry) override; - - private: - std::unique_ptr surface_; - fml::WeakPtrFactory weak_factory_; - flow::TextureRegistry* texture_registry_; - - FXL_DISALLOW_COPY_AND_ASSIGN(NullRasterizer); -}; - -} // namespace shell - -#endif // FLUTTER_SHELL_COMMON_NULL_RASTERIZER_H_ diff --git a/shell/common/platform_view.cc b/shell/common/platform_view.cc index 2d5715a1733bd..b2bfce051a5bf 100644 --- a/shell/common/platform_view.cc +++ b/shell/common/platform_view.cc @@ -6,124 +6,74 @@ #include -#include "flutter/common/threads.h" -#include "flutter/lib/ui/painting/resource_context.h" #include "flutter/shell/common/rasterizer.h" +#include "flutter/shell/common/shell.h" #include "flutter/shell/common/vsync_waiter_fallback.h" #include "lib/fxl/functional/make_copyable.h" +#include "lib/fxl/synchronization/waitable_event.h" #include "third_party/skia/include/gpu/GrContextOptions.h" #include "third_party/skia/include/gpu/gl/GrGLInterface.h" namespace shell { -PlatformView::PlatformView(std::unique_ptr rasterizer) - : rasterizer_(std::move(rasterizer)), size_(SkISize::Make(0, 0)) { - rasterizer_->SetTextureRegistry(&texture_registry_); - Shell::Shared().AddPlatformView(this); +PlatformView::PlatformView(Delegate& delegate, blink::TaskRunners task_runners) + : delegate_(delegate), + task_runners_(std::move(task_runners)), + size_(SkISize::Make(0, 0)), + weak_factory_(this) { + weak_prototype_ = weak_factory_.GetWeakPtr(); } -PlatformView::~PlatformView() { - Shell::Shared().RemovePlatformView(this); +PlatformView::~PlatformView() = default; - Rasterizer* rasterizer = rasterizer_.release(); - blink::Threads::Gpu()->PostTask([rasterizer]() { delete rasterizer; }); - - Engine* engine = engine_.release(); - blink::Threads::UI()->PostTask([engine]() { delete engine; }); -} - -void PlatformView::SetRasterizer(std::unique_ptr rasterizer) { - Rasterizer* r = rasterizer_.release(); - blink::Threads::Gpu()->PostTask([r]() { delete r; }); - rasterizer_ = std::move(rasterizer); - rasterizer_->SetTextureRegistry(&texture_registry_); - engine_->set_rasterizer(rasterizer_->GetWeakRasterizerPtr()); -} - -void PlatformView::CreateEngine() { - engine_.reset(new Engine(this)); +std::unique_ptr PlatformView::CreateVSyncWaiter() { + FXL_DLOG(WARNING) + << "This platform does not provide a Vsync waiter implementation. A " + "simple timer based fallback is being used."; + return std::make_unique(task_runners_); } void PlatformView::DispatchPlatformMessage( fxl::RefPtr message) { - blink::Threads::UI()->PostTask( - [engine = engine_->GetWeakPtr(), message = std::move(message)] { - if (engine) { - engine->DispatchPlatformMessage(message); - } - }); + delegate_.OnPlatformViewDispatchPlatformMessage(*this, std::move(message)); +} + +void PlatformView::DispatchPointerDataPacket( + std::unique_ptr packet) { + delegate_.OnPlatformViewDispatchPointerDataPacket(*this, std::move(packet)); } void PlatformView::DispatchSemanticsAction(int32_t id, blink::SemanticsAction action, std::vector args) { - blink::Threads::UI()->PostTask( - [engine = engine_->GetWeakPtr(), id, action, args = std::move(args)] { - if (engine) { - engine->DispatchSemanticsAction( - id, static_cast(action), std::move(args)); - } - }); + delegate_.OnPlatformViewDispatchSemanticsAction(*this, id, action, + std::move(args)); } void PlatformView::SetSemanticsEnabled(bool enabled) { - blink::Threads::UI()->PostTask([engine = engine_->GetWeakPtr(), enabled] { - if (engine) - engine->SetSemanticsEnabled(enabled); - }); + delegate_.OnPlatformViewSetSemanticsEnabled(*this, enabled); } -void PlatformView::NotifyCreated(std::unique_ptr surface) { - NotifyCreated(std::move(surface), []() {}); +void PlatformView::SetViewportMetrics(const blink::ViewportMetrics& metrics) { + delegate_.OnPlatformViewSetViewportMetrics(*this, metrics); } -void PlatformView::NotifyCreated(std::unique_ptr surface, - fxl::Closure caller_continuation) { - fxl::AutoResetWaitableEvent latch; - - auto ui_continuation = fxl::MakeCopyable([this, // - surface = std::move(surface), // - caller_continuation, // - &latch]() mutable { - auto gpu_continuation = fxl::MakeCopyable([this, // - surface = std::move(surface), // - caller_continuation, // - &latch]() mutable { - // Runs on the GPU Thread. So does the Caller Continuation. - rasterizer_->Setup(std::move(surface), caller_continuation, &latch); - }); - // Runs on the UI Thread. - engine_->OnOutputSurfaceCreated(std::move(gpu_continuation)); - }); - - // Runs on the Platform Thread. - blink::Threads::UI()->PostTask(std::move(ui_continuation)); - - latch.Wait(); +void PlatformView::NotifyCreated() { + delegate_.OnPlatformViewCreated(*this, CreateRenderingSurface()); } void PlatformView::NotifyDestroyed() { - fxl::AutoResetWaitableEvent latch; - - auto engine_continuation = [this, &latch]() { - rasterizer_->Teardown(&latch); - }; - - blink::Threads::UI()->PostTask([this, engine_continuation]() { - engine_->OnOutputSurfaceDestroyed(engine_continuation); - }); - - latch.Wait(); + delegate_.OnPlatformViewDestroyed(*this); } -std::weak_ptr PlatformView::GetWeakPtr() { - return shared_from_this(); +sk_sp PlatformView::CreateResourceContext() const { + FXL_DLOG(WARNING) << "This platform does not setup the resource " + "context on the IO thread for async texture uploads."; + return nullptr; } -VsyncWaiter* PlatformView::GetVsyncWaiter() { - if (!vsync_waiter_) - vsync_waiter_ = std::make_unique(); - return vsync_waiter_.get(); +fml::WeakPtr PlatformView::GetWeakPtr() const { + return weak_prototype_; } void PlatformView::UpdateSemantics(blink::SemanticsNodeUpdates update) {} @@ -135,71 +85,31 @@ void PlatformView::HandlePlatformMessage( } void PlatformView::RegisterTexture(std::shared_ptr texture) { - ASSERT_IS_PLATFORM_THREAD - blink::Threads::Gpu()->PostTask([this, texture]() { - rasterizer_->GetTextureRegistry().RegisterTexture(texture); - }); + delegate_.OnPlatformViewRegisterTexture(*this, std::move(texture)); } void PlatformView::UnregisterTexture(int64_t texture_id) { - ASSERT_IS_PLATFORM_THREAD - blink::Threads::Gpu()->PostTask([this, texture_id]() { - rasterizer_->GetTextureRegistry().UnregisterTexture(texture_id); - }); + delegate_.OnPlatformViewUnregisterTexture(*this, texture_id); } void PlatformView::MarkTextureFrameAvailable(int64_t texture_id) { - ASSERT_IS_PLATFORM_THREAD - blink::Threads::UI()->PostTask([this]() { engine_->ScheduleFrame(false); }); + delegate_.OnPlatformViewMarkTextureFrameAvailable(*this, texture_id); } -void PlatformView::SetupResourceContextOnIOThread() { - fxl::AutoResetWaitableEvent latch; - - blink::Threads::IO()->PostTask( - [this, &latch]() { SetupResourceContextOnIOThreadPerform(&latch); }); - - latch.Wait(); +std::unique_ptr PlatformView::CreateRenderingSurface() { + // We have a default implementation because tests create a platform view but + // never a rendering surface. + FXL_DCHECK(false) << "This platform does not provide a rendering surface but " + "it was notified of surface rendering surface creation."; + return nullptr; } -void PlatformView::SetupResourceContextOnIOThreadPerform( - fxl::AutoResetWaitableEvent* latch) { - std::unique_ptr resourceContext = - blink::ResourceContext::Acquire(); - if (resourceContext->Get() != nullptr) { - // The resource context was already setup. This could happen if platforms - // try to setup a context multiple times, or, if there are multiple platform - // views. In any case, there is nothing else to do. So just signal the - // latch. - latch->Signal(); +void PlatformView::SetNextFrameCallback(fxl::Closure closure) { + if (!closure) { return; } - bool current = ResourceContextMakeCurrent(); - - if (!current) { - FXL_DLOG(WARNING) - << "WARNING: Could not setup a context on the resource loader."; - latch->Signal(); - return; - } - - GrContextOptions options; - // There is currently a bug with doing GPU YUV to RGB conversions on the IO - // thread. The necessary work isn't being flushed or synchronized with the - // other threads correctly, so the textures end up blank. For now, suppress - // that feature, which will cause texture uploads to do CPU YUV conversion. - options.fDisableGpuYUVConversion = true; - - blink::ResourceContext::Set( - GrContext::MakeGL(GrGLMakeNativeInterface(), options)); - - // Do not cache textures created by the image decoder. These textures should - // be deleted when they are no longer referenced by an SkImage. - if (resourceContext->Get()) - resourceContext->Get()->setResourceCacheLimits(0, 0); - - latch->Signal(); + delegate_.OnPlatformViewSetNextFrameCallback(*this, std::move(closure)); } } // namespace shell diff --git a/shell/common/platform_view.h b/shell/common/platform_view.h index 66b1fcae10c79..eda08714f5a8a 100644 --- a/shell/common/platform_view.h +++ b/shell/common/platform_view.h @@ -7,63 +7,104 @@ #include +#include "flutter/common/task_runners.h" #include "flutter/flow/texture.h" +#include "flutter/fml/memory/weak_ptr.h" #include "flutter/lib/ui/semantics/semantics_node.h" -#include "flutter/shell/common/engine.h" -#include "flutter/shell/common/shell.h" +#include "flutter/lib/ui/window/platform_message.h" +#include "flutter/lib/ui/window/pointer_data_packet.h" +#include "flutter/lib/ui/window/viewport_metrics.h" #include "flutter/shell/common/surface.h" #include "flutter/shell/common/vsync_waiter.h" #include "lib/fxl/macros.h" -#include "lib/fxl/memory/weak_ptr.h" -#include "lib/fxl/synchronization/waitable_event.h" #include "third_party/skia/include/core/SkSize.h" #include "third_party/skia/include/gpu/GrContext.h" namespace shell { -class Rasterizer; +class Shell; -class PlatformView : public std::enable_shared_from_this { +class PlatformView { public: - struct SurfaceConfig { - uint8_t red_bits = 8; - uint8_t green_bits = 8; - uint8_t blue_bits = 8; - uint8_t alpha_bits = 8; - uint8_t depth_bits = 0; - uint8_t stencil_bits = 0; + class Delegate { + public: + virtual void OnPlatformViewCreated(const PlatformView& view, + std::unique_ptr surface) = 0; + + virtual void OnPlatformViewDestroyed(const PlatformView& view) = 0; + + virtual void OnPlatformViewSetNextFrameCallback(const PlatformView& view, + fxl::Closure closure) = 0; + + virtual void OnPlatformViewSetViewportMetrics( + const PlatformView& view, + const blink::ViewportMetrics& metrics) = 0; + + virtual void OnPlatformViewDispatchPlatformMessage( + const PlatformView& view, + fxl::RefPtr message) = 0; + + virtual void OnPlatformViewDispatchPointerDataPacket( + const PlatformView& view, + std::unique_ptr packet) = 0; + + virtual void OnPlatformViewDispatchSemanticsAction( + const PlatformView& view, + int32_t id, + blink::SemanticsAction action, + std::vector args) = 0; + + virtual void OnPlatformViewSetSemanticsEnabled(const PlatformView& view, + bool enabled) = 0; + + virtual void OnPlatformViewRegisterTexture( + const PlatformView& view, + std::shared_ptr texture) = 0; + + virtual void OnPlatformViewUnregisterTexture(const PlatformView& view, + int64_t texture_id) = 0; + + virtual void OnPlatformViewMarkTextureFrameAvailable( + const PlatformView& view, + int64_t texture_id) = 0; }; - void SetupResourceContextOnIOThread(); + explicit PlatformView(Delegate& delegate, blink::TaskRunners task_runners); virtual ~PlatformView(); - virtual void Attach() = 0; + virtual std::unique_ptr CreateVSyncWaiter(); void DispatchPlatformMessage(fxl::RefPtr message); + void DispatchSemanticsAction(int32_t id, blink::SemanticsAction action, std::vector args); - void SetSemanticsEnabled(bool enabled); - void NotifyCreated(std::unique_ptr surface); + virtual void SetSemanticsEnabled(bool enabled); - void NotifyCreated(std::unique_ptr surface, - fxl::Closure continuation); + void SetViewportMetrics(const blink::ViewportMetrics& metrics); - void NotifyDestroyed(); + void NotifyCreated(); - std::weak_ptr GetWeakPtr(); + virtual void NotifyDestroyed(); - // The VsyncWaiter will live at least as long as the PlatformView. - virtual VsyncWaiter* GetVsyncWaiter(); + // Unlike all other methods on the platform view, this one may be called on a + // non-platform task runner. + virtual sk_sp CreateResourceContext() const; - virtual bool ResourceContextMakeCurrent() = 0; + fml::WeakPtr GetWeakPtr() const; virtual void UpdateSemantics(blink::SemanticsNodeUpdates update); + virtual void HandlePlatformMessage( fxl::RefPtr message); + void SetNextFrameCallback(fxl::Closure closure); + + void DispatchPointerDataPacket( + std::unique_ptr packet); + // Called once per texture, on the platform thread. void RegisterTexture(std::shared_ptr texture); @@ -73,32 +114,16 @@ class PlatformView : public std::enable_shared_from_this { // Called once per texture update (e.g. video frame), on the platform thread. virtual void MarkTextureFrameAvailable(int64_t texture_id); - void SetRasterizer(std::unique_ptr rasterizer); - - Rasterizer& rasterizer() { return *rasterizer_; } - Engine& engine() { return *engine_; } - - virtual void RunFromSource(const std::string& assets_directory, - const std::string& main, - const std::string& packages) = 0; - - virtual void SetAssetBundlePath(const std::string& assets_directory) = 0; - protected: - explicit PlatformView(std::unique_ptr rasterizer); - - void CreateEngine(); - - void SetupResourceContextOnIOThreadPerform( - fxl::AutoResetWaitableEvent* event); - - SurfaceConfig surface_config_; - std::unique_ptr rasterizer_; - flow::TextureRegistry texture_registry_; - std::unique_ptr engine_; + PlatformView::Delegate& delegate_; + const blink::TaskRunners task_runners_; std::unique_ptr vsync_waiter_; SkISize size_; + fml::WeakPtr weak_prototype_; + fml::WeakPtrFactory weak_factory_; + + virtual std::unique_ptr CreateRenderingSurface(); private: FXL_DISALLOW_COPY_AND_ASSIGN(PlatformView); diff --git a/shell/common/platform_view_service_protocol.cc b/shell/common/platform_view_service_protocol.cc deleted file mode 100644 index 8785c4812e202..0000000000000 --- a/shell/common/platform_view_service_protocol.cc +++ /dev/null @@ -1,447 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "flutter/shell/common/platform_view_service_protocol.h" - -#include - -#include -#include - -#include "flutter/common/threads.h" -#include "flutter/shell/common/picture_serializer.h" -#include "flutter/shell/common/rasterizer.h" -#include "flutter/shell/common/shell.h" -#include "lib/fxl/memory/weak_ptr.h" -#include "third_party/skia/include/core/SkSurface.h" -#include "third_party/skia/src/utils/SkBase64.h" - -namespace shell { -namespace { - -constexpr char kViewIdPrefx[] = "_flutterView/"; -constexpr size_t kViewIdPrefxLength = sizeof(kViewIdPrefx) - 1; - -static intptr_t KeyIndex(const char** param_keys, - intptr_t num_params, - const char* key) { - if (param_keys == NULL) { - return -1; - } - for (intptr_t i = 0; i < num_params; i++) { - if (strcmp(param_keys[i], key) == 0) { - return i; - } - } - return -1; -} - -static const char* ValueForKey(const char** param_keys, - const char** param_values, - intptr_t num_params, - const char* key) { - intptr_t index = KeyIndex(param_keys, num_params, key); - if (index < 0) { - return NULL; - } - return param_values[index]; -} - -static bool ErrorMissingParameter(const char** json_object, const char* name) { - const intptr_t kInvalidParams = -32602; - std::stringstream response; - response << "{\"code\":" << std::to_string(kInvalidParams) << ","; - response << "\"message\":\"Invalid params\","; - response << "\"data\": {\"details\": \"" << name << "\"}}"; - *json_object = strdup(response.str().c_str()); - return false; -} - -static bool ErrorBadParameter(const char** json_object, - const char* name, - const char* value) { - const intptr_t kInvalidParams = -32602; - std::stringstream response; - response << "{\"code\":" << std::to_string(kInvalidParams) << ","; - response << "\"message\":\"Invalid params\","; - response << "\"data\": {\"details\": \"parameter: " << name << " has a bad "; - response << "value: " << value << "\"}}"; - *json_object = strdup(response.str().c_str()); - return false; -} - -static bool ErrorUnknownView(const char** json_object, const char* view_id) { - const intptr_t kInvalidParams = -32602; - std::stringstream response; - response << "{\"code\":" << std::to_string(kInvalidParams) << ","; - response << "\"message\":\"Invalid params\","; - response << "\"data\": {\"details\": \"view not found: " << view_id << "\"}}"; - *json_object = strdup(response.str().c_str()); - return false; -} - -static bool ErrorServer(const char** json_object, const char* message) { - const intptr_t kServerError = -32000; - std::stringstream response; - response << "{\"code\":" << std::to_string(kServerError) << ","; - response << "\"message\":\"" << message << "\"}"; - *json_object = strdup(response.str().c_str()); - return false; -} - -static void AppendIsolateRef(std::stringstream* stream, - int64_t main_port, - const std::string name) { - *stream << "{\"type\":\"@Isolate\",\"fixedId\":true,\"id\":\"isolates/"; - *stream << main_port << "\",\"name\":\"" << name << "\","; - *stream << "\"number\":\"" << main_port << "\"}"; -} - -static void AppendFlutterView(std::stringstream* stream, - uintptr_t view_id, - int64_t isolate_id, - const std::string isolate_name) { - *stream << "{\"type\":\"FlutterView\", \"id\": \"" << kViewIdPrefx << "0x" - << std::hex << view_id << std::dec << "\""; - if (isolate_id != ILLEGAL_PORT) { - // Append the isolate (if it exists). - *stream << "," - << "\"isolate\":"; - AppendIsolateRef(stream, isolate_id, isolate_name); - } - *stream << "}"; -} - -} // namespace - -void PlatformViewServiceProtocol::RegisterHook(bool running_precompiled_code) { - // Listing of FlutterViews. - Dart_RegisterRootServiceRequestCallback(kListViewsExtensionName, &ListViews, - nullptr); - // Screenshot. - Dart_RegisterRootServiceRequestCallback(kScreenshotExtensionName, &Screenshot, - nullptr); - - // SkPicture Screenshot. - Dart_RegisterRootServiceRequestCallback(kScreenshotSkpExtensionName, - &ScreenshotSkp, nullptr); - - // The following set of service protocol extensions require debug build - if (running_precompiled_code) { - return; - } - Dart_RegisterRootServiceRequestCallback(kRunInViewExtensionName, &RunInView, - nullptr); - Dart_RegisterRootServiceRequestCallback(kSetAssetBundlePathExtensionName, - &SetAssetBundlePath, nullptr); - // [benchmark helper] Wait for the UI Thread to idle. - Dart_RegisterRootServiceRequestCallback(kFlushUIThreadTasksExtensionName, - &FlushUIThreadTasks, nullptr); -} - -const char* PlatformViewServiceProtocol::kRunInViewExtensionName = - "_flutter.runInView"; - -bool PlatformViewServiceProtocol::RunInView(const char* method, - const char** param_keys, - const char** param_values, - intptr_t num_params, - void* user_data, - const char** json_object) { - const char* view_id = - ValueForKey(param_keys, param_values, num_params, "viewId"); - const char* asset_directory = - ValueForKey(param_keys, param_values, num_params, "assetDirectory"); - const char* main_script = - ValueForKey(param_keys, param_values, num_params, "mainScript"); - const char* packages_file = - ValueForKey(param_keys, param_values, num_params, "packagesFile"); - if (view_id == NULL) { - return ErrorMissingParameter(json_object, "viewId"); - } - if (strncmp(view_id, kViewIdPrefx, kViewIdPrefxLength) != 0) { - return ErrorBadParameter(json_object, "viewId", view_id); - } - if (asset_directory == NULL) { - return ErrorMissingParameter(json_object, "assetDirectory"); - } - if (main_script == NULL) { - return ErrorMissingParameter(json_object, "mainScript"); - } - if (packages_file == NULL) { - return ErrorMissingParameter(json_object, "packagesFile"); - } - - // Convert the actual flutter view hex id into a number. - uintptr_t view_id_as_num = - std::stoull((view_id + kViewIdPrefxLength), nullptr, 16); - - // Ask the Shell to run this script in the specified view. This will run a - // task on the UI thread before returning. - Shell& shell = Shell::Shared(); - bool view_existed = false; - Dart_Port main_port = ILLEGAL_PORT; - std::string isolate_name; - shell.RunInPlatformView(view_id_as_num, main_script, packages_file, - asset_directory, &view_existed, &main_port, - &isolate_name); - - if (!view_existed) { - // If the view did not exist this request has definitely failed. - return ErrorUnknownView(json_object, view_id); - } - - // The view existed and the isolate was created. Success. - std::stringstream response; - response << "{\"type\":\"Success\"," - << "\"view\":"; - AppendFlutterView(&response, view_id_as_num, main_port, isolate_name); - response << "}"; - *json_object = strdup(response.str().c_str()); - return true; -} - -const char* PlatformViewServiceProtocol::kListViewsExtensionName = - "_flutter.listViews"; - -bool PlatformViewServiceProtocol::ListViews(const char* method, - const char** param_keys, - const char** param_values, - intptr_t num_params, - void* user_data, - const char** json_object) { - std::stringstream response; - response << "{\"type\":\"FlutterViewList\",\"views\":["; - bool prefix_comma = false; - Shell::Shared().IteratePlatformViews( - [&response, &prefix_comma](PlatformView* view) -> bool { - if (prefix_comma) { - response << ','; - } else { - prefix_comma = true; - } - AppendFlutterView(&response, reinterpret_cast(view), - view->engine().GetUIIsolateMainPort(), - view->engine().GetUIIsolateName()); - return true; - }); - response << "]}"; - // Copy the response. - *json_object = strdup(response.str().c_str()); - return true; -} - -const char* PlatformViewServiceProtocol::kScreenshotExtensionName = - "_flutter.screenshot"; - -static sk_sp EncodeBitmapAsPNG(const SkBitmap& bitmap) { - return SkEncodeBitmap(bitmap, SkEncodedImageFormat::kPNG, 100); -} - -static fml::WeakPtr GetRandomRasterizer() { - fml::WeakPtr rasterizer; - Shell::Shared().IteratePlatformViews( - [&rasterizer](PlatformView* view) -> bool { - rasterizer = view->rasterizer().GetWeakRasterizerPtr(); - // We just grab the first rasterizer so there is no need to iterate - // further. - return false; - }); - return rasterizer; -} - -bool PlatformViewServiceProtocol::Screenshot(const char* method, - const char** param_keys, - const char** param_values, - intptr_t num_params, - void* user_data, - const char** json_object) { - fxl::AutoResetWaitableEvent latch; - SkBitmap bitmap; - blink::Threads::Gpu()->PostTask([&latch, &bitmap]() { - ScreenshotGpuTask(&bitmap); - latch.Signal(); - }); - - latch.Wait(); - - sk_sp png(EncodeBitmapAsPNG(bitmap)); - - if (!png) - return ErrorServer(json_object, "can not encode screenshot"); - - size_t b64_size = SkBase64::Encode(png->data(), png->size(), nullptr); - SkAutoTMalloc b64_data(b64_size); - SkBase64::Encode(png->data(), png->size(), b64_data.get()); - - std::stringstream response; - response << "{\"type\":\"Screenshot\"," - << "\"screenshot\":\"" << std::string{b64_data.get(), b64_size} - << "\"}"; - *json_object = strdup(response.str().c_str()); - return true; -} - -void PlatformViewServiceProtocol::ScreenshotGpuTask(SkBitmap* bitmap) { - auto rasterizer = GetRandomRasterizer(); - - if (!rasterizer) - return; - - flow::LayerTree* layer_tree = rasterizer->GetLastLayerTree(); - if (layer_tree == nullptr) - return; - - const SkISize& frame_size = layer_tree->frame_size(); - if (!bitmap->tryAllocN32Pixels(frame_size.width(), frame_size.height())) - return; - - sk_sp surface = SkSurface::MakeRasterDirect( - bitmap->info(), bitmap->getPixels(), bitmap->rowBytes()); - - flow::CompositorContext compositor_context(nullptr); - SkCanvas* canvas = surface->getCanvas(); - flow::CompositorContext::ScopedFrame frame = - compositor_context.AcquireFrame(nullptr, canvas, false); - - canvas->clear(SK_ColorBLACK); - layer_tree->Raster(frame); - canvas->flush(); -} - -const char* PlatformViewServiceProtocol::kScreenshotSkpExtensionName = - "_flutter.screenshotSkp"; - -bool PlatformViewServiceProtocol::ScreenshotSkp(const char* method, - const char** param_keys, - const char** param_values, - intptr_t num_params, - void* user_data, - const char** json_object) { - fxl::AutoResetWaitableEvent latch; - sk_sp picture; - blink::Threads::Gpu()->PostTask([&latch, &picture]() { - picture = ScreenshotSkpGpuTask(); - latch.Signal(); - }); - - latch.Wait(); - - sk_sp skp_data = picture->serialize(); - - size_t b64_size = - SkBase64::Encode(skp_data->data(), skp_data->size(), nullptr); - SkAutoTMalloc b64_data(b64_size); - SkBase64::Encode(skp_data->data(), skp_data->size(), b64_data.get()); - - std::stringstream response; - response << "{\"type\":\"ScreenshotSkp\"," - << "\"skp\":\"" << std::string{b64_data.get(), b64_size} << "\"}"; - *json_object = strdup(response.str().c_str()); - return true; -} - -sk_sp PlatformViewServiceProtocol::ScreenshotSkpGpuTask() { - auto rasterizer = GetRandomRasterizer(); - - if (!rasterizer) - return nullptr; - - flow::LayerTree* layer_tree = rasterizer->GetLastLayerTree(); - if (layer_tree == nullptr) - return nullptr; - - SkPictureRecorder recorder; - recorder.beginRecording(SkRect::MakeWH(layer_tree->frame_size().width(), - layer_tree->frame_size().height())); - - flow::CompositorContext compositor_context(nullptr); - flow::CompositorContext::ScopedFrame frame = compositor_context.AcquireFrame( - nullptr, recorder.getRecordingCanvas(), false); - layer_tree->Raster(frame); - - return recorder.finishRecordingAsPicture(); -} - -const char* PlatformViewServiceProtocol::kFlushUIThreadTasksExtensionName = - "_flutter.flushUIThreadTasks"; - -// This API should not be invoked by production code. -// It can potentially starve the service isolate if the main isolate pauses -// at a breakpoint or is in an infinite loop. -// -// It should be invoked from the VM Service and and blocks it until UI thread -// tasks are processed. -bool PlatformViewServiceProtocol::FlushUIThreadTasks(const char* method, - const char** param_keys, - const char** param_values, - intptr_t num_params, - void* user_data, - const char** json_object) { - fxl::AutoResetWaitableEvent latch; - blink::Threads::UI()->PostTask([&latch]() { - // This task is empty because we just need to synchronize this RPC with the - // UI Thread - latch.Signal(); - }); - - latch.Wait(); - - *json_object = strdup("{\"type\":\"Success\"}"); - return true; -} - -const char* PlatformViewServiceProtocol::kSetAssetBundlePathExtensionName = - "_flutter.setAssetBundlePath"; - -bool PlatformViewServiceProtocol::SetAssetBundlePath(const char* method, - const char** param_keys, - const char** param_values, - intptr_t num_params, - void* user_data, - const char** json_object) { - const char* view_id = - ValueForKey(param_keys, param_values, num_params, "viewId"); - if (view_id == nullptr) { - return ErrorMissingParameter(json_object, "viewId"); - } - if (strncmp(view_id, kViewIdPrefx, kViewIdPrefxLength) != 0) { - return ErrorBadParameter(json_object, "viewId", view_id); - } - const char* asset_directory = - ValueForKey(param_keys, param_values, num_params, "assetDirectory"); - if (asset_directory == nullptr) { - return ErrorMissingParameter(json_object, "assetDirectory"); - } - - // Convert the actual flutter view hex id into a number. - uintptr_t view_id_as_num = - std::stoull((view_id + kViewIdPrefxLength), nullptr, 16); - - // Ask the Shell to update asset bundle path in the specified view. - // This will run a task on the UI thread before returning. - Shell& shell = Shell::Shared(); - bool view_existed = false; - Dart_Port main_port = ILLEGAL_PORT; - std::string isolate_name; - shell.SetAssetBundlePathInPlatformView(view_id_as_num, asset_directory, - &view_existed, &main_port, - &isolate_name); - - if (!view_existed) { - // If the view did not exist this request has definitely failed. - return ErrorUnknownView(json_object, view_id); - } - - // The view existed and the isolate was created. Success. - std::stringstream response; - response << "{\"type\":\"Success\"," - << "\"view\":"; - AppendFlutterView(&response, view_id_as_num, main_port, isolate_name); - response << "}"; - *json_object = strdup(response.str().c_str()); - return true; -} - -} // namespace shell diff --git a/shell/common/platform_view_service_protocol.h b/shell/common/platform_view_service_protocol.h deleted file mode 100644 index b7f74b56a08ba..0000000000000 --- a/shell/common/platform_view_service_protocol.h +++ /dev/null @@ -1,85 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef SHELL_COMMON_VIEW_SERVICE_PROTOCOL_H_ -#define SHELL_COMMON_VIEW_SERVICE_PROTOCOL_H_ - -#include - -#include "flutter/shell/common/platform_view.h" -#include "lib/fxl/synchronization/waitable_event.h" -#include "third_party/dart/runtime/include/dart_tools_api.h" -#include "third_party/skia/include/core/SkBitmap.h" - -namespace shell { - -class PlatformViewServiceProtocol { - public: - static void RegisterHook(bool running_precompiled_code); - - private: - static const char* kRunInViewExtensionName; - // It should be invoked from the VM Service and and blocks it until previous - // UI thread tasks are processed. - static bool RunInView(const char* method, - const char** param_keys, - const char** param_values, - intptr_t num_params, - void* user_data, - const char** json_object); - - static const char* kListViewsExtensionName; - static bool ListViews(const char* method, - const char** param_keys, - const char** param_values, - intptr_t num_params, - void* user_data, - const char** json_object); - - static const char* kScreenshotExtensionName; - // It should be invoked from the VM Service and and blocks it until previous - // GPU thread tasks are processed. - static bool Screenshot(const char* method, - const char** param_keys, - const char** param_values, - intptr_t num_params, - void* user_data, - const char** json_object); - static void ScreenshotGpuTask(SkBitmap* bitmap); - - static const char* kScreenshotSkpExtensionName; - static bool ScreenshotSkp(const char* method, - const char** param_keys, - const char** param_values, - intptr_t num_params, - void* user_data, - const char** json_object); - static sk_sp ScreenshotSkpGpuTask(); - - // This API should not be invoked by production code. - // It can potentially starve the service isolate if the main isolate pauses - // at a breakpoint or is in an infinite loop. - // - // It should be invoked from the VM Service and and blocks it until previous - // GPU thread tasks are processed. - static const char* kFlushUIThreadTasksExtensionName; - static bool FlushUIThreadTasks(const char* method, - const char** param_keys, - const char** param_values, - intptr_t num_params, - void* user_data, - const char** json_object); - - static const char* kSetAssetBundlePathExtensionName; - static bool SetAssetBundlePath(const char* method, - const char** param_keys, - const char** param_values, - intptr_t num_params, - void* user_data, - const char** json_object); -}; - -} // namespace shell - -#endif // SHELL_COMMON_VIEW_SERVICE_PROTOCOL_H_ diff --git a/shell/common/rasterizer.cc b/shell/common/rasterizer.cc index 905ee6285c5d3..b116f31b5ba00 100644 --- a/shell/common/rasterizer.cc +++ b/shell/common/rasterizer.cc @@ -4,8 +4,205 @@ #include "flutter/shell/common/rasterizer.h" +#include + +#include "third_party/skia/include/core/SkEncodedImageFormat.h" +#include "third_party/skia/include/core/SkImageEncoder.h" +#include "third_party/skia/include/core/SkPictureRecorder.h" +#include "third_party/skia/include/core/SkSurface.h" +#include "third_party/skia/src/utils/SkBase64.h" + namespace shell { +Rasterizer::Rasterizer(blink::TaskRunners task_runners) + : task_runners_(std::move(task_runners)), weak_factory_(this) { + weak_prototype_ = weak_factory_.GetWeakPtr(); +} + Rasterizer::~Rasterizer() = default; +fml::WeakPtr Rasterizer::GetWeakPtr() const { + return weak_prototype_; +} + +void Rasterizer::Setup(std::unique_ptr surface) { + surface_ = std::move(surface); +} + +void Rasterizer::Teardown() { + surface_.reset(); + last_layer_tree_.reset(); +} + +flow::TextureRegistry* Rasterizer::GetTextureRegistry() { + if (!surface_) { + return nullptr; + } + + return &(surface_->GetCompositorContext().texture_registry()); +} + +flow::LayerTree* Rasterizer::GetLastLayerTree() { + return last_layer_tree_.get(); +} + +void Rasterizer::DrawLastLayerTree() { + if (!last_layer_tree_ || !surface_) { + return; + } + DrawToSurface(*last_layer_tree_); +} + +void Rasterizer::Draw( + fxl::RefPtr> pipeline) { + TRACE_EVENT0("flutter", "GPURasterizer::Draw"); + + flutter::Pipeline::Consumer consumer = + std::bind(&Rasterizer::DoDraw, this, std::placeholders::_1); + + // Consume as many pipeline items as possible. But yield the event loop + // between successive tries. + switch (pipeline->Consume(consumer)) { + case flutter::PipelineConsumeResult::MoreAvailable: { + task_runners_.GetGPUTaskRunner()->PostTask( + [weak_this = weak_factory_.GetWeakPtr(), pipeline]() { + if (weak_this) { + weak_this->Draw(pipeline); + } + }); + break; + } + default: + break; + } +} + +void Rasterizer::DoDraw(std::unique_ptr layer_tree) { + if (!layer_tree || !surface_) { + return; + } + + if (DrawToSurface(*layer_tree)) { + last_layer_tree_ = std::move(layer_tree); + } +} + +bool Rasterizer::DrawToSurface(flow::LayerTree& layer_tree) { + FXL_DCHECK(surface_); + + auto frame = surface_->AcquireFrame(layer_tree.frame_size()); + + if (frame == nullptr) { + return false; + } + + auto& compositor_context = surface_->GetCompositorContext(); + + // There is no way for the compositor to know how long the layer tree + // construction took. Fortunately, the layer tree does. Grab that time + // for instrumentation. + compositor_context.engine_time().SetLapTime(layer_tree.construction_time()); + + auto compositor_frame = compositor_context.AcquireFrame( + surface_->GetContext(), frame->SkiaCanvas(), true); + + if (compositor_frame && compositor_frame->Raster(layer_tree, false)) { + frame->Submit(); + FireNextFrameCallbackIfPresent(); + return true; + } + + return false; +} + +static sk_sp ScreenshotLayerTreeAsPicture(flow::LayerTree* tree) { + FXL_DCHECK(tree != nullptr); + SkPictureRecorder recorder; + recorder.beginRecording( + SkRect::MakeWH(tree->frame_size().width(), tree->frame_size().height())); + + flow::CompositorContext compositor_context; + auto frame = compositor_context.AcquireFrame( + nullptr, recorder.getRecordingCanvas(), false); + + frame->Raster(*tree, true); + + return recorder.finishRecordingAsPicture(); +} + +static sk_sp ScreenshotLayerTreeAsImage(flow::LayerTree* tree, + bool compressed) { + const SkISize& frame_size = tree->frame_size(); + SkBitmap bitmap; + if (!bitmap.tryAllocN32Pixels(frame_size.width(), frame_size.height())) { + return nullptr; + } + auto bitmap_surface = SkSurface::MakeRasterDirect( + bitmap.info(), bitmap.getPixels(), bitmap.rowBytes()); + flow::CompositorContext compositor_context; + auto canvas = bitmap_surface->getCanvas(); + auto frame = compositor_context.AcquireFrame(nullptr, canvas, false); + canvas->clear(SK_ColorBLACK); + frame->Raster(*tree, true); + canvas->flush(); + if (compressed) { + return SkEncodeBitmap(bitmap, SkEncodedImageFormat::kPNG, 100); + } else { + return SkData::MakeWithCopy(bitmap.getPixels(), bitmap.computeByteSize()); + } + return nullptr; +} + +Rasterizer::Screenshot Rasterizer::ScreenshotLastLayerTree( + Rasterizer::ScreenshotType type, + bool base64_encode) { + auto layer_tree = GetLastLayerTree(); + if (layer_tree == nullptr) { + FXL_DLOG(INFO) << "Last layer tree was null when screenshotting."; + return {nullptr, SkISize::MakeEmpty()}; + } + + sk_sp data = nullptr; + + switch (type) { + case ScreenshotType::SkiaPicture: + data = ScreenshotLayerTreeAsPicture(layer_tree)->serialize(); + break; + case ScreenshotType::UncompressedImage: + data = ScreenshotLayerTreeAsImage(layer_tree, false); + break; + case ScreenshotType::CompressedImage: + data = ScreenshotLayerTreeAsImage(layer_tree, true); + break; + } + + if (data == nullptr) { + FXL_DLOG(INFO) << "Sceenshot data will null."; + return {nullptr, SkISize::MakeEmpty()}; + } + + if (base64_encode) { + size_t b64_size = SkBase64::Encode(data->data(), data->size(), nullptr); + auto b64_data = SkData::MakeUninitialized(b64_size); + SkBase64::Encode(data->data(), data->size(), b64_data->writable_data()); + return {b64_data, layer_tree->frame_size()}; + } + + return {data, layer_tree->frame_size()}; +} + +void Rasterizer::SetNextFrameCallback(fxl::Closure callback) { + next_frame_callback_ = callback; +} + +void Rasterizer::FireNextFrameCallbackIfPresent() { + if (!next_frame_callback_) { + return; + } + // It is safe for the callback to set a new callback. + auto callback = next_frame_callback_; + next_frame_callback_ = nullptr; + callback(); +} + } // namespace shell diff --git a/shell/common/rasterizer.h b/shell/common/rasterizer.h index 6f45f49d8178a..8cef262db4381 100644 --- a/shell/common/rasterizer.h +++ b/shell/common/rasterizer.h @@ -7,6 +7,7 @@ #include +#include "flutter/common/task_runners.h" #include "flutter/flow/layers/layer_tree.h" #include "flutter/fml/memory/weak_ptr.h" #include "flutter/shell/common/surface.h" @@ -16,34 +17,59 @@ namespace shell { -class Rasterizer { +class Rasterizer final { public: - virtual ~Rasterizer(); + Rasterizer(blink::TaskRunners task_runners); - virtual void Setup(std::unique_ptr surface_or_null, - fxl::Closure rasterizer_continuation, - fxl::AutoResetWaitableEvent* setup_completion_event) = 0; + ~Rasterizer(); - virtual void Teardown( - fxl::AutoResetWaitableEvent* teardown_completion_event) = 0; + void Setup(std::unique_ptr surface); - virtual void Clear(SkColor color, const SkISize& size) = 0; + void Teardown(); - virtual fml::WeakPtr GetWeakRasterizerPtr() = 0; + fml::WeakPtr GetWeakPtr() const; - virtual flow::LayerTree* GetLastLayerTree() = 0; + flow::LayerTree* GetLastLayerTree(); - virtual void DrawLastLayerTree() = 0; + void DrawLastLayerTree(); - virtual flow::TextureRegistry& GetTextureRegistry() = 0; + flow::TextureRegistry* GetTextureRegistry(); - virtual void Draw( - fxl::RefPtr> pipeline) = 0; + void Draw(fxl::RefPtr> pipeline); - // Set a callback to be called once when the next frame is drawn. - virtual void AddNextFrameCallback(fxl::Closure nextFrameCallback) = 0; + enum class ScreenshotType { + SkiaPicture, + UncompressedImage, // In kN32_SkColorType format + CompressedImage, + }; - virtual void SetTextureRegistry(flow::TextureRegistry* textureRegistry) = 0; + struct Screenshot { + sk_sp data; + SkISize frame_size = SkISize::MakeEmpty(); + }; + + Screenshot ScreenshotLastLayerTree(ScreenshotType type, bool base64_encode); + + // Sets a callback that will be executed after the next frame is submitted to + // the surface on the GPU task runner. + void SetNextFrameCallback(fxl::Closure callback); + + private: + blink::TaskRunners task_runners_; + std::unique_ptr surface_; + std::unique_ptr compositor_context_; + std::unique_ptr last_layer_tree_; + fxl::Closure next_frame_callback_; + fml::WeakPtr weak_prototype_; + fml::WeakPtrFactory weak_factory_; + + void DoDraw(std::unique_ptr layer_tree); + + bool DrawToSurface(flow::LayerTree& layer_tree); + + void FireNextFrameCallbackIfPresent(); + + FXL_DISALLOW_COPY_AND_ASSIGN(Rasterizer); }; } // namespace shell diff --git a/shell/common/run_configuration.cc b/shell/common/run_configuration.cc new file mode 100644 index 0000000000000..8bb69e8436f0f --- /dev/null +++ b/shell/common/run_configuration.cc @@ -0,0 +1,80 @@ +// Copyright 2017 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. + +#include "flutter/shell/common/run_configuration.h" + +#include + +#include "flutter/assets/directory_asset_bundle.h" +#include "flutter/assets/zip_asset_store.h" +#include "flutter/fml/file.h" +#include "flutter/runtime/dart_vm.h" + +namespace shell { + +RunConfiguration RunConfiguration::InferFromSettings( + const blink::Settings& settings) { + auto asset_manager = fxl::MakeRefCounted(); + + asset_manager->PushBack(std::make_unique( + fml::Duplicate(settings.assets_dir))); + + asset_manager->PushBack( + std::make_unique(fml::OpenFile( + settings.assets_path.c_str(), fml::OpenPermission::kRead, true))); + + asset_manager->PushBack( + std::make_unique(settings.flx_path)); + + return {IsolateConfiguration::InferFromSettings(settings, asset_manager), + asset_manager}; +} + +RunConfiguration::RunConfiguration( + std::unique_ptr configuration) + : RunConfiguration(std::move(configuration), + fxl::MakeRefCounted()) {} + +RunConfiguration::RunConfiguration( + std::unique_ptr configuration, + fxl::RefPtr asset_manager) + : run_configuration_(std::move(configuration)), + asset_manager_(std::move(asset_manager)) {} + +RunConfiguration::RunConfiguration(RunConfiguration&&) = default; + +RunConfiguration::~RunConfiguration() = default; + +bool RunConfiguration::IsValid() const { + return asset_manager_ && run_configuration_; +} + +bool RunConfiguration::AddAssetResolver( + std::unique_ptr resolver) { + if (!resolver || !resolver->IsValid()) { + return false; + } + + asset_manager_->PushBack(std::move(resolver)); + return true; +} + +void RunConfiguration::SetEntrypoint(std::string entrypoint) { + entrypoint_ = std::move(entrypoint); +} + +fxl::RefPtr RunConfiguration::GetAssetManager() const { + return asset_manager_; +} + +const std::string& RunConfiguration::GetEntrypoint() const { + return entrypoint_; +} + +std::unique_ptr +RunConfiguration::TakeIsolateConfiguration() { + return std::move(run_configuration_); +} + +} // namespace shell diff --git a/shell/common/run_configuration.h b/shell/common/run_configuration.h new file mode 100644 index 0000000000000..f3995622cb204 --- /dev/null +++ b/shell/common/run_configuration.h @@ -0,0 +1,56 @@ +// Copyright 2017 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. + +#ifndef FLUTTER_SHELL_COMMON_RUN_CONFIGURATION_H_ +#define FLUTTER_SHELL_COMMON_RUN_CONFIGURATION_H_ + +#include +#include + +#include "flutter/assets/asset_manager.h" +#include "flutter/assets/asset_resolver.h" +#include "flutter/common/settings.h" +#include "flutter/fml/mapping.h" +#include "flutter/shell/common/isolate_configuration.h" +#include "lib/fxl/files/unique_fd.h" +#include "lib/fxl/macros.h" + +namespace shell { + +class RunConfiguration { + public: + static RunConfiguration InferFromSettings(const blink::Settings& settings); + + RunConfiguration(std::unique_ptr configuration); + + RunConfiguration(std::unique_ptr configuration, + fxl::RefPtr asset_manager); + + RunConfiguration(RunConfiguration&&); + + ~RunConfiguration(); + + bool IsValid() const; + + bool AddAssetResolver(std::unique_ptr resolver); + + void SetEntrypoint(std::string entrypoint); + + fxl::RefPtr GetAssetManager() const; + + const std::string& GetEntrypoint() const; + + std::unique_ptr TakeIsolateConfiguration(); + + private: + std::unique_ptr run_configuration_; + fxl::RefPtr asset_manager_; + std::string entrypoint_ = "main"; + + FXL_DISALLOW_COPY_AND_ASSIGN(RunConfiguration); +}; + +} // namespace shell + +#endif // FLUTTER_SHELL_COMMON_RUN_CONFIGURATION_H_ diff --git a/shell/common/shell.cc b/shell/common/shell.cc index 14f8fee84bb5f..4bbc795f33f06 100644 --- a/shell/common/shell.cc +++ b/shell/common/shell.cc @@ -2,362 +2,840 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#define RAPIDJSON_HAS_STDSTRING 1 + #include "flutter/shell/common/shell.h" -#include #include #include #include -#include "flutter/common/settings.h" -#include "flutter/common/threads.h" +#include "flutter/assets/directory_asset_bundle.h" +#include "flutter/fml/file.h" #include "flutter/fml/icu_util.h" #include "flutter/fml/message_loop.h" -#include "flutter/fml/trace_event.h" -#include "flutter/runtime/dart_init.h" +#include "flutter/glue/trace_event.h" +#include "flutter/runtime/dart_vm.h" #include "flutter/shell/common/engine.h" -#include "flutter/shell/common/platform_view_service_protocol.h" #include "flutter/shell/common/skia_event_tracer_impl.h" #include "flutter/shell/common/switches.h" +#include "flutter/shell/common/vsync_waiter.h" #include "lib/fxl/files/unique_fd.h" +#include "lib/fxl/functional/make_copyable.h" +#include "lib/fxl/logging.h" #include "third_party/dart/runtime/include/dart_tools_api.h" #include "third_party/skia/include/core/SkGraphics.h" namespace shell { -namespace { -static Shell* g_shell = nullptr; +std::unique_ptr Shell::CreateShellOnPlatformThread( + blink::TaskRunners task_runners, + blink::Settings settings, + Shell::CreateCallback on_create_platform_view, + Shell::CreateCallback on_create_rasterizer) { + if (!task_runners.IsValid()) { + return nullptr; + } -template -bool GetSwitchValue(const fxl::CommandLine& command_line, - Switch sw, - T* result) { - std::string switch_string; + auto shell = std::unique_ptr(new Shell(task_runners, settings)); - if (!command_line.GetOptionValue(FlagForSwitch(sw), &switch_string)) { - return false; + // Create the platform view on the platform thread (this thread). + auto platform_view = on_create_platform_view(*shell.get()); + if (!platform_view || !platform_view->GetWeakPtr()) { + return nullptr; } - std::stringstream stream(switch_string); - T value = 0; - if (stream >> value) { - *result = value; - return true; + // Ask the platform view for the vsync waiter. This will be used by the engine + // to create the animator. + auto vsync_waiter = platform_view->CreateVSyncWaiter(); + if (!vsync_waiter) { + return nullptr; } - return false; + // Create the IO manager on the IO thread. The IO manager must be initialized + // first because it has state that the other subsystems depend on. It must + // first be booted and the necessary references obtained to initialize the + // other subsystems. + fxl::AutoResetWaitableEvent io_latch; + std::unique_ptr io_manager; + fml::WeakPtr resource_context; + fxl::RefPtr unref_queue; + auto io_task_runner = shell->GetTaskRunners().GetIOTaskRunner(); + fml::TaskRunner::RunNowOrPostTask( + io_task_runner, + [&io_latch, // + &io_manager, // + &resource_context, // + &unref_queue, // + &platform_view, // + io_task_runner // + ]() { + io_manager = std::make_unique( + platform_view->CreateResourceContext(), io_task_runner); + resource_context = io_manager->GetResourceContext(); + unref_queue = io_manager->GetSkiaUnrefQueue(); + io_latch.Signal(); + }); + io_latch.Wait(); + + // Create the rasterizer on the GPU thread. + fxl::AutoResetWaitableEvent gpu_latch; + std::unique_ptr rasterizer; + fml::TaskRunner::RunNowOrPostTask( + task_runners.GetGPUTaskRunner(), [&gpu_latch, // + &rasterizer, // + on_create_rasterizer, // + shell = shell.get() // + ]() { + if (auto new_rasterizer = on_create_rasterizer(*shell)) { + rasterizer = std::move(new_rasterizer); + } + gpu_latch.Signal(); + }); + + // Create the engine on the UI thread. + fxl::AutoResetWaitableEvent ui_latch; + std::unique_ptr engine; + fml::TaskRunner::RunNowOrPostTask( + shell->GetTaskRunners().GetUITaskRunner(), + fxl::MakeCopyable([&ui_latch, // + &engine, // + shell = shell.get(), // + vsync_waiter = std::move(vsync_waiter), // + resource_context = std::move(resource_context), // + unref_queue = std::move(unref_queue) // + ]() mutable { + const auto& task_runners = shell->GetTaskRunners(); + + // The animator is owned by the UI thread but it gets its vsync pulses + // from the platform. + auto animator = std::make_unique(*shell, task_runners, + std::move(vsync_waiter)); + + engine = std::make_unique(*shell, // + shell->GetDartVM(), // + task_runners, // + shell->GetSettings(), // + std::move(animator), // + std::move(resource_context), // + std::move(unref_queue) // + ); + ui_latch.Signal(); + })); + + gpu_latch.Wait(); + ui_latch.Wait(); + // We are already on the platform thread. So there is no platform latch to + // wait on. + + if (!shell->Setup(std::move(platform_view), // + std::move(engine), // + std::move(rasterizer), // + std::move(io_manager)) // + ) { + return nullptr; + } + + return shell; } -} // namespace +std::unique_ptr Shell::Create( + blink::TaskRunners task_runners, + blink::Settings settings, + Shell::CreateCallback on_create_platform_view, + Shell::CreateCallback on_create_rasterizer) { + if (!task_runners.IsValid() || !on_create_platform_view || + !on_create_rasterizer) { + return nullptr; + } -Shell::Shell(fxl::CommandLine command_line) - : command_line_(std::move(command_line)) { - FXL_DCHECK(!g_shell); + fxl::AutoResetWaitableEvent latch; + std::unique_ptr shell; + fml::TaskRunner::RunNowOrPostTask( + task_runners.GetPlatformTaskRunner(), + [&latch, &shell, task_runners = std::move(task_runners), settings, + on_create_platform_view, on_create_rasterizer]() { + shell = CreateShellOnPlatformThread(std::move(task_runners), settings, + on_create_platform_view, + on_create_rasterizer); + latch.Signal(); + }); + latch.Wait(); + return shell; +} - gpu_thread_.reset(new fml::Thread("gpu_thread")); - ui_thread_.reset(new fml::Thread("ui_thread")); - io_thread_.reset(new fml::Thread("io_thread")); +Shell::Shell(blink::TaskRunners task_runners, blink::Settings settings) + : task_runners_(std::move(task_runners)), + settings_(std::move(settings)), + vm_(blink::DartVM::ForProcess(settings_)) { + FXL_DCHECK(task_runners_.IsValid()); + FXL_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread()); + + if (settings_.icu_data_path.size() != 0) { + fml::icu::InitializeICU(settings_.icu_data_path); + } else { + FXL_DLOG(WARNING) << "Skipping ICU initialization in the shell."; + } - // Since we are not using fml::Thread, we need to initialize the message loop - // manually. - fml::MessageLoop::EnsureInitializedForCurrentThread(); - blink::Threads threads(fml::MessageLoop::GetCurrent().GetTaskRunner(), - gpu_thread_->GetTaskRunner(), - ui_thread_->GetTaskRunner(), - io_thread_->GetTaskRunner()); - blink::Threads::Set(threads); + if (settings_.trace_skia) { + InitSkiaEventTracer(settings_.trace_skia); + } - blink::Threads::Gpu()->PostTask([this]() { InitGpuThread(); }); - blink::Threads::UI()->PostTask([this]() { InitUIThread(); }); + if (!settings_.skia_deterministic_rendering_on_cpu) { + SkGraphics::Init(); + } else { + FXL_DLOG(INFO) << "Skia deterministic rendering is enabled."; + } - blink::SetRegisterNativeServiceProtocolExtensionHook( - PlatformViewServiceProtocol::RegisterHook); + // Install service protocol handlers. + + service_protocol_handlers_[blink::ServiceProtocol::kScreenshotExtensionName + .ToString()] = { + task_runners_.GetGPUTaskRunner(), + std::bind(&Shell::OnServiceProtocolScreenshot, this, + std::placeholders::_1, std::placeholders::_2)}; + service_protocol_handlers_[blink::ServiceProtocol::kScreenshotSkpExtensionName + .ToString()] = { + task_runners_.GetGPUTaskRunner(), + std::bind(&Shell::OnServiceProtocolScreenshotSKP, this, + std::placeholders::_1, std::placeholders::_2)}; + service_protocol_handlers_[blink::ServiceProtocol::kRunInViewExtensionName + .ToString()] = { + task_runners_.GetUITaskRunner(), + std::bind(&Shell::OnServiceProtocolRunInView, this, std::placeholders::_1, + std::placeholders::_2)}; + service_protocol_handlers_ + [blink::ServiceProtocol::kFlushUIThreadTasksExtensionName.ToString()] = { + task_runners_.GetUITaskRunner(), + std::bind(&Shell::OnServiceProtocolFlushUIThreadTasks, this, + std::placeholders::_1, std::placeholders::_2)}; + service_protocol_handlers_ + [blink::ServiceProtocol::kSetAssetBundlePathExtensionName.ToString()] = { + task_runners_.GetUITaskRunner(), + std::bind(&Shell::OnServiceProtocolSetAssetBundlePath, this, + std::placeholders::_1, std::placeholders::_2)}; } -Shell::~Shell() {} +Shell::~Shell() { + if (auto vm = blink::DartVM::ForProcessIfInitialized()) { + vm->GetServiceProtocol().RemoveHandler(this); + } -void Shell::InitStandalone(fxl::CommandLine command_line, - std::string icu_data_path, - std::string application_library_path, - std::string bundle_path) { - TRACE_EVENT0("flutter", "Shell::InitStandalone"); + fxl::AutoResetWaitableEvent ui_latch, gpu_latch, platform_latch, io_latch; + + fml::TaskRunner::RunNowOrPostTask( + task_runners_.GetUITaskRunner(), + fxl::MakeCopyable([engine = std::move(engine_), &ui_latch]() mutable { + engine.reset(); + ui_latch.Signal(); + })); + ui_latch.Wait(); + + fml::TaskRunner::RunNowOrPostTask( + task_runners_.GetGPUTaskRunner(), + fxl::MakeCopyable( + [rasterizer = std::move(rasterizer_), &gpu_latch]() mutable { + rasterizer.reset(); + gpu_latch.Signal(); + })); + gpu_latch.Wait(); + + fml::TaskRunner::RunNowOrPostTask( + task_runners_.GetIOTaskRunner(), + fxl::MakeCopyable( + [io_manager = std::move(io_manager_), &io_latch]() mutable { + io_manager.reset(); + io_latch.Signal(); + })); + + io_latch.Wait(); + + // The platform view must go last because it may be holding onto platform side + // counterparts to resources owned by subsystems running on other threads. For + // example, the NSOpenGLContext on the Mac. + fml::TaskRunner::RunNowOrPostTask( + task_runners_.GetPlatformTaskRunner(), + fxl::MakeCopyable([platform_view = std::move(platform_view_), + &platform_latch]() mutable { + platform_view.reset(); + platform_latch.Signal(); + })); + platform_latch.Wait(); +} - fml::icu::InitializeICU(icu_data_path); +bool Shell::IsSetup() const { + return is_setup_; +} - if (!command_line.HasOption( - FlagForSwitch(Switch::SkiaDeterministicRendering))) - SkGraphics::Init(); +bool Shell::Setup(std::unique_ptr platform_view, + std::unique_ptr engine, + std::unique_ptr rasterizer, + std::unique_ptr io_manager) { + if (is_setup_) { + return false; + } + + if (!platform_view || !engine || !rasterizer || !io_manager) { + return false; + } - blink::Settings settings; - settings.application_library_path = application_library_path; + platform_view_ = std::move(platform_view); + engine_ = std::move(engine); + rasterizer_ = std::move(rasterizer); + io_manager_ = std::move(io_manager); - // Enable Observatory - settings.enable_observatory = - !command_line.HasOption(FlagForSwitch(Switch::DisableObservatory)); + is_setup_ = true; - // Set Observatory Port - if (command_line.HasOption(FlagForSwitch(Switch::DeviceObservatoryPort))) { - if (!GetSwitchValue(command_line, Switch::DeviceObservatoryPort, - &settings.observatory_port)) { - FXL_LOG(INFO) - << "Observatory port specified was malformed. Will default to " - << settings.observatory_port; - } + if (auto vm = blink::DartVM::ForProcessIfInitialized()) { + vm->GetServiceProtocol().AddHandler(this); } - // Checked mode overrides. - settings.dart_non_checked_mode = - command_line.HasOption(FlagForSwitch(Switch::DartNonCheckedMode)); + return true; +} + +const blink::Settings& Shell::GetSettings() const { + return settings_; +} + +const blink::TaskRunners& Shell::GetTaskRunners() const { + return task_runners_; +} + +fml::WeakPtr Shell::GetRasterizer() { + FXL_DCHECK(is_setup_); + return rasterizer_->GetWeakPtr(); +} - settings.ipv6 = command_line.HasOption(FlagForSwitch(Switch::IPv6)); +fml::WeakPtr Shell::GetEngine() { + FXL_DCHECK(is_setup_); + return engine_->GetWeakPtr(); +} - settings.start_paused = - command_line.HasOption(FlagForSwitch(Switch::StartPaused)); +fml::WeakPtr Shell::GetPlatformView() { + FXL_DCHECK(is_setup_); + return platform_view_->GetWeakPtr(); +} + +const blink::DartVM& Shell::GetDartVM() const { + return *vm_; +} - settings.enable_dart_profiling = - command_line.HasOption(FlagForSwitch(Switch::EnableDartProfiling)); +// |shell::PlatformView::Delegate| +void Shell::OnPlatformViewCreated(const PlatformView& view, + std::unique_ptr surface) { + FXL_DCHECK(is_setup_); + FXL_DCHECK(&view == platform_view_.get()); + FXL_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread()); - settings.enable_software_rendering = - command_line.HasOption(FlagForSwitch(Switch::EnableSoftwareRendering)); + // Note: + // This is a synchronous operation because certain platforms depend on + // setup/suspension of all activities that may be interacting with the GPU in + // a synchronous fashion. + + fxl::AutoResetWaitableEvent latch; + auto gpu_task = fxl::MakeCopyable([rasterizer = rasterizer_->GetWeakPtr(), // + surface = std::move(surface), // + &latch]() mutable { + if (rasterizer) { + rasterizer->Setup(std::move(surface)); + } + // Step 2: All done. Signal the latch that the platform thread is waiting + // on. + latch.Signal(); + }); - settings.using_blink = - command_line.HasOption(FlagForSwitch(Switch::EnableBlink)); + auto ui_task = [engine = engine_->GetWeakPtr(), // + gpu_task_runner = task_runners_.GetGPUTaskRunner(), // + gpu_task // + ] { + if (engine) { + engine->OnOutputSurfaceCreated(); + } + // Step 1: Next, tell the GPU thread that it should create a surface for its + // rasterizer. + fml::TaskRunner::RunNowOrPostTask(gpu_task_runner, gpu_task); + }; + + // Step 0: Post a task onto the UI thread to tell the engine that it has an + // output surface. + fml::TaskRunner::RunNowOrPostTask(task_runners_.GetUITaskRunner(), ui_task); + latch.Wait(); +} - settings.endless_trace_buffer = - command_line.HasOption(FlagForSwitch(Switch::EndlessTraceBuffer)); +// |shell::PlatformView::Delegate| +void Shell::OnPlatformViewDestroyed(const PlatformView& view) { + FXL_DCHECK(is_setup_); + FXL_DCHECK(&view == platform_view_.get()); + FXL_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread()); - settings.trace_startup = - command_line.HasOption(FlagForSwitch(Switch::TraceStartup)); + // Note: + // This is a synchronous operation because certain platforms depend on + // setup/suspension of all activities that may be interacting with the GPU in + // a synchronous fashion. - command_line.GetOptionValue(FlagForSwitch(Switch::AotSnapshotPath), - &settings.aot_snapshot_path); + fxl::AutoResetWaitableEvent latch; - command_line.GetOptionValue(FlagForSwitch(Switch::AotVmSnapshotData), - &settings.aot_vm_snapshot_data_filename); + auto gpu_task = [rasterizer = rasterizer_->GetWeakPtr(), &latch]() { + if (rasterizer) { + rasterizer->Teardown(); + } + // Step 2: All done. Signal the latch that the platform thread is waiting + // on. + latch.Signal(); + }; + + auto ui_task = [engine = engine_->GetWeakPtr(), + gpu_task_runner = task_runners_.GetGPUTaskRunner(), + gpu_task]() { + if (engine) { + engine->OnOutputSurfaceDestroyed(); + } + // Step 1: Next, tell the GPU thread that its rasterizer should suspend + // access to the underlying surface. + fml::TaskRunner::RunNowOrPostTask(gpu_task_runner, gpu_task); + }; + + // Step 0: Post a task onto the UI thread to tell the engine that its output + // surface is about to go away. + fml::TaskRunner::RunNowOrPostTask(task_runners_.GetUITaskRunner(), ui_task); + latch.Wait(); +} - command_line.GetOptionValue(FlagForSwitch(Switch::AotVmSnapshotInstructions), - &settings.aot_vm_snapshot_instr_filename); +// |shell::PlatformView::Delegate| +void Shell::OnPlatformViewSetViewportMetrics( + const PlatformView& view, + const blink::ViewportMetrics& metrics) { + FXL_DCHECK(is_setup_); + FXL_DCHECK(&view == platform_view_.get()); + FXL_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread()); + + task_runners_.GetUITaskRunner()->PostTask( + [engine = engine_->GetWeakPtr(), metrics]() { + if (engine) { + engine->SetViewportMetrics(metrics); + } + }); +} - command_line.GetOptionValue(FlagForSwitch(Switch::AotIsolateSnapshotData), - &settings.aot_isolate_snapshot_data_filename); +// |shell::PlatformView::Delegate| +void Shell::OnPlatformViewDispatchPlatformMessage( + const PlatformView& view, + fxl::RefPtr message) { + FXL_DCHECK(is_setup_); + FXL_DCHECK(&view == platform_view_.get()); + FXL_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread()); + + task_runners_.GetUITaskRunner()->PostTask( + [engine = engine_->GetWeakPtr(), message = std::move(message)] { + if (engine) { + engine->DispatchPlatformMessage(std::move(message)); + } + }); +} - command_line.GetOptionValue(FlagForSwitch(Switch::AotSharedLibraryPath), - &settings.aot_shared_library_path); +// |shell::PlatformView::Delegate| +void Shell::OnPlatformViewDispatchPointerDataPacket( + const PlatformView& view, + std::unique_ptr packet) { + FXL_DCHECK(is_setup_); + FXL_DCHECK(&view == platform_view_.get()); + FXL_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread()); + task_runners_.GetUITaskRunner()->PostTask(fxl::MakeCopyable( + [engine = engine_->GetWeakPtr(), packet = std::move(packet)] { + if (engine) { + engine->DispatchPointerDataPacket(*packet); + } + })); +} - command_line.GetOptionValue( - FlagForSwitch(Switch::AotIsolateSnapshotInstructions), - &settings.aot_isolate_snapshot_instr_filename); +// |shell::PlatformView::Delegate| +void Shell::OnPlatformViewDispatchSemanticsAction(const PlatformView& view, + int32_t id, + blink::SemanticsAction action, + std::vector args) { + FXL_DCHECK(is_setup_); + FXL_DCHECK(&view == platform_view_.get()); + FXL_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread()); + + task_runners_.GetUITaskRunner()->PostTask( + [engine = engine_->GetWeakPtr(), id, action, args = std::move(args)] { + if (engine) { + engine->DispatchSemanticsAction(id, action, std::move(args)); + } + }); +} - command_line.GetOptionValue(FlagForSwitch(Switch::CacheDirPath), - &settings.temp_directory_path); +// |shell::PlatformView::Delegate| +void Shell::OnPlatformViewSetSemanticsEnabled(const PlatformView& view, + bool enabled) { + FXL_DCHECK(is_setup_); + FXL_DCHECK(&view == platform_view_.get()); + FXL_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread()); + + task_runners_.GetUITaskRunner()->PostTask( + [engine = engine_->GetWeakPtr(), enabled] { + if (engine) { + engine->SetSemanticsEnabled(enabled); + } + }); +} - settings.use_test_fonts = - command_line.HasOption(FlagForSwitch(Switch::UseTestFonts)); +// |shell::PlatformView::Delegate| +void Shell::OnPlatformViewRegisterTexture( + const PlatformView& view, + std::shared_ptr texture) { + FXL_DCHECK(is_setup_); + FXL_DCHECK(&view == platform_view_.get()); + FXL_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread()); + + task_runners_.GetGPUTaskRunner()->PostTask( + [rasterizer = rasterizer_->GetWeakPtr(), texture] { + if (rasterizer) { + if (auto registry = rasterizer->GetTextureRegistry()) { + registry->RegisterTexture(texture); + } + } + }); +} - std::string all_dart_flags; - if (command_line.GetOptionValue(FlagForSwitch(Switch::DartFlags), - &all_dart_flags)) { - std::stringstream stream(all_dart_flags); - std::istream_iterator end; - for (std::istream_iterator it(stream); it != end; ++it) - settings.dart_flags.push_back(*it); - } +// |shell::PlatformView::Delegate| +void Shell::OnPlatformViewUnregisterTexture(const PlatformView& view, + int64_t texture_id) { + FXL_DCHECK(is_setup_); + FXL_DCHECK(&view == platform_view_.get()); + FXL_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread()); + + task_runners_.GetGPUTaskRunner()->PostTask( + [rasterizer = rasterizer_->GetWeakPtr(), texture_id]() { + if (rasterizer) { + if (auto registry = rasterizer->GetTextureRegistry()) { + registry->UnregisterTexture(texture_id); + } + } + }); +} + +// |shell::PlatformView::Delegate| +void Shell::OnPlatformViewMarkTextureFrameAvailable(const PlatformView& view, + int64_t texture_id) { + FXL_DCHECK(is_setup_); + FXL_DCHECK(&view == platform_view_.get()); + FXL_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread()); - command_line.GetOptionValue(FlagForSwitch(Switch::LogTag), &settings.log_tag); + task_runners_.GetUITaskRunner()->PostTask([engine = engine_->GetWeakPtr()]() { + if (engine) { + engine->ScheduleFrame(false); + } + }); +} + +// |shell::PlatformView::Delegate| +void Shell::OnPlatformViewSetNextFrameCallback(const PlatformView& view, + fxl::Closure closure) { + FXL_DCHECK(is_setup_); + FXL_DCHECK(&view == platform_view_.get()); + FXL_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread()); + + task_runners_.GetGPUTaskRunner()->PostTask( + [rasterizer = rasterizer_->GetWeakPtr(), closure = std::move(closure)]() { + if (rasterizer) { + rasterizer->SetNextFrameCallback(std::move(closure)); + } + }); +} - blink::Settings::Set(settings); +// |shell::Animator::Delegate| +void Shell::OnAnimatorBeginFrame(const Animator& animator, + fxl::TimePoint frame_time) { + FXL_DCHECK(is_setup_); + FXL_DCHECK(task_runners_.GetUITaskRunner()->RunsTasksOnCurrentThread()); - Init(std::move(command_line), bundle_path); + if (engine_) { + engine_->BeginFrame(frame_time); + } } -void Shell::Init(fxl::CommandLine command_line, - const std::string& bundle_path) { -#if FLUTTER_RUNTIME_MODE != FLUTTER_RUNTIME_MODE_RELEASE - bool trace_skia = command_line.HasOption(FlagForSwitch(Switch::TraceSkia)); - InitSkiaEventTracer(trace_skia); -#endif +// |shell::Animator::Delegate| +void Shell::OnAnimatorNotifyIdle(const Animator& animator, int64_t deadline) { + FXL_DCHECK(is_setup_); + FXL_DCHECK(task_runners_.GetUITaskRunner()->RunsTasksOnCurrentThread()); - FXL_DCHECK(!g_shell); - g_shell = new Shell(std::move(command_line)); - blink::Threads::UI()->PostTask( - [bundle_path]() { Engine::Init(bundle_path); }); + if (engine_) { + engine_->NotifyIdle(deadline); + } } -Shell& Shell::Shared() { - FXL_DCHECK(g_shell); - return *g_shell; +// |shell::Animator::Delegate| +void Shell::OnAnimatorDraw( + const Animator& animator, + fxl::RefPtr> pipeline) { + FXL_DCHECK(is_setup_); + + task_runners_.GetGPUTaskRunner()->PostTask( + [rasterizer = rasterizer_->GetWeakPtr(), + pipeline = std::move(pipeline)]() { + if (rasterizer) { + rasterizer->Draw(pipeline); + } + }); } -const fxl::CommandLine& Shell::GetCommandLine() const { - return command_line_; +// |shell::Animator::Delegate| +void Shell::OnAnimatorDrawLastLayerTree(const Animator& animator) { + FXL_DCHECK(is_setup_); + + task_runners_.GetGPUTaskRunner()->PostTask( + [rasterizer = rasterizer_->GetWeakPtr()]() { + if (rasterizer) { + rasterizer->DrawLastLayerTree(); + } + }); } -void Shell::InitGpuThread() { - gpu_thread_checker_.reset(new fxl::ThreadChecker()); +// |shell::Engine::Delegate| +void Shell::OnEngineUpdateSemantics(const Engine& engine, + blink::SemanticsNodeUpdates update) { + FXL_DCHECK(is_setup_); + FXL_DCHECK(task_runners_.GetUITaskRunner()->RunsTasksOnCurrentThread()); + + task_runners_.GetPlatformTaskRunner()->PostTask( + [view = platform_view_->GetWeakPtr(), update = std::move(update)] { + if (view) { + view->UpdateSemantics(std::move(update)); + } + }); } -void Shell::InitUIThread() { - ui_thread_checker_.reset(new fxl::ThreadChecker()); +// |shell::Engine::Delegate| +void Shell::OnEngineHandlePlatformMessage( + const Engine& engine, + fxl::RefPtr message) { + FXL_DCHECK(is_setup_); + FXL_DCHECK(task_runners_.GetUITaskRunner()->RunsTasksOnCurrentThread()); + + task_runners_.GetPlatformTaskRunner()->PostTask( + [view = platform_view_->GetWeakPtr(), message = std::move(message)]() { + if (view) { + view->HandlePlatformMessage(std::move(message)); + } + }); } -void Shell::AddPlatformView(PlatformView* platform_view) { - if (platform_view == nullptr) { - return; +// |blink::ServiceProtocol::Handler| +fxl::RefPtr Shell::GetServiceProtocolHandlerTaskRunner( + fxl::StringView method) const { + FXL_DCHECK(is_setup_); + auto found = service_protocol_handlers_.find(method.ToString()); + if (found != service_protocol_handlers_.end()) { + return found->second.first; } - std::lock_guard lock(platform_views_mutex_); - platform_views_.insert(platform_view); + return task_runners_.GetUITaskRunner(); } -void Shell::RemovePlatformView(PlatformView* platform_view) { - if (platform_view == nullptr) { - return; +// |blink::ServiceProtocol::Handler| +bool Shell::HandleServiceProtocolMessage( + fxl::StringView method, // one if the extension names specified above. + const ServiceProtocolMap& params, + rapidjson::Document& response) { + auto found = service_protocol_handlers_.find(method.ToString()); + if (found != service_protocol_handlers_.end()) { + return found->second.second(params, response); } - std::lock_guard lock(platform_views_mutex_); - platform_views_.erase(platform_view); + return false; } -void Shell::IteratePlatformViews( - std::function iterator) const { - if (iterator == nullptr) { - return; +// |blink::ServiceProtocol::Handler| +blink::ServiceProtocol::Handler::Description +Shell::GetServiceProtocolDescription() const { + return { + engine_->GetUIIsolateMainPort(), + engine_->GetUIIsolateName(), + }; +} + +static void ServiceProtocolParameterError(rapidjson::Document& response, + std::string parameter_name) { + auto& allocator = response.GetAllocator(); + response.SetObject(); + const int64_t kInvalidParams = -32602; + response.AddMember("code", kInvalidParams, allocator); + response.AddMember("message", "Invalid params", allocator); + { + rapidjson::Value details(rapidjson::kObjectType); + details.AddMember("details", parameter_name, allocator); + response.AddMember("data", details, allocator); } - std::lock_guard lock(platform_views_mutex_); - for (PlatformView* view : platform_views_) { - if (!iterator(view)) { - return; - } +} + +// Service protocol handler +bool Shell::OnServiceProtocolScreenshot( + const blink::ServiceProtocol::Handler::ServiceProtocolMap& params, + rapidjson::Document& response) { + FXL_DCHECK(task_runners_.GetGPUTaskRunner()->RunsTasksOnCurrentThread()); + auto screenshot = rasterizer_->ScreenshotLastLayerTree( + Rasterizer::ScreenshotType::CompressedImage, true); + if (screenshot.data) { + response.SetObject(); + auto& allocator = response.GetAllocator(); + response.AddMember("type", "Screenshot", allocator); + rapidjson::Value image; + image.SetString(static_cast(screenshot.data->data()), + screenshot.data->size(), allocator); + response.AddMember("screenshot", image, allocator); + return true; } + ServiceProtocolParameterError(response, + "Could not capture image screenshot."); + return false; } -void Shell::RunInPlatformView(uintptr_t view_id, - const char* main_script, - const char* packages_file, - const char* asset_directory, - bool* view_existed, - int64_t* dart_isolate_id, - std::string* isolate_name) { - fxl::AutoResetWaitableEvent latch; - FXL_DCHECK(view_id != 0); - FXL_DCHECK(main_script); - FXL_DCHECK(packages_file); - FXL_DCHECK(asset_directory); - FXL_DCHECK(view_existed); - - blink::Threads::UI()->PostTask([this, view_id, main_script, packages_file, - asset_directory, view_existed, - dart_isolate_id, isolate_name, &latch]() { - RunInPlatformViewUIThread(view_id, main_script, packages_file, - asset_directory, view_existed, dart_isolate_id, - isolate_name, &latch); - }); - latch.Wait(); +// Service protocol handler +bool Shell::OnServiceProtocolScreenshotSKP( + const blink::ServiceProtocol::Handler::ServiceProtocolMap& params, + rapidjson::Document& response) { + FXL_DCHECK(task_runners_.GetGPUTaskRunner()->RunsTasksOnCurrentThread()); + auto screenshot = rasterizer_->ScreenshotLastLayerTree( + Rasterizer::ScreenshotType::SkiaPicture, true); + if (screenshot.data) { + response.SetObject(); + auto& allocator = response.GetAllocator(); + response.AddMember("type", "ScreenshotSkp", allocator); + rapidjson::Value skp; + skp.SetString(static_cast(screenshot.data->data()), + screenshot.data->size(), allocator); + response.AddMember("skp", skp, allocator); + return true; + } + ServiceProtocolParameterError(response, "Could not capture SKP screenshot."); + return false; } -void Shell::RunInPlatformViewUIThread(uintptr_t view_id, - const std::string& main, - const std::string& packages, - const std::string& assets_directory, - bool* view_existed, - int64_t* dart_isolate_id, - std::string* isolate_name, - fxl::AutoResetWaitableEvent* latch) { - FXL_DCHECK(ui_thread_checker_ && - ui_thread_checker_->IsCreationThreadCurrent()); - - *view_existed = false; - - IteratePlatformViews( - [view_id, // argument -#if !defined(OS_WIN) - // Using std::move on const references inside lambda capture is - // not supported on Windows for some reason. - assets_directory = std::move(assets_directory), // argument - main = std::move(main), // argument - packages = std::move(packages), // argument -#else - assets_directory, // argument - main, // argument - packages, // argument -#endif - &view_existed, // out - &dart_isolate_id, // out - &isolate_name // out - ](PlatformView* view) -> bool { - if (reinterpret_cast(view) != view_id) { - // Keep looking. - return true; - } - *view_existed = true; - view->RunFromSource(assets_directory, main, packages); - *dart_isolate_id = view->engine().GetUIIsolateMainPort(); - *isolate_name = view->engine().GetUIIsolateName(); - // We found the requested view. Stop iterating over platform views. - return false; - }); +// Service protocol handler +bool Shell::OnServiceProtocolRunInView( + const blink::ServiceProtocol::Handler::ServiceProtocolMap& params, + rapidjson::Document& response) { + FXL_DCHECK(task_runners_.GetUITaskRunner()->RunsTasksOnCurrentThread()); + + if (params.count("mainScript") == 0) { + ServiceProtocolParameterError(response, + "'mainScript' parameter is missing."); + return false; + } + + if (params.count("packagesFile") == 0) { + ServiceProtocolParameterError(response, + "'packagesFile' parameter is missing."); + return false; + } + + if (params.count("assetDirectory") == 0) { + ServiceProtocolParameterError(response, + "'assetDirectory' parameter is missing."); + return false; + } - latch->Signal(); + RunConfiguration configuration(IsolateConfiguration::CreateForSource( + params.at("mainScript").ToString(), + params.at("packagesFile").ToString())); + + configuration.AddAssetResolver(std::make_unique( + fml::OpenFile(params.at("assetDirectory").ToString().c_str(), + fml::OpenPermission::kRead, true))); + + auto& allocator = response.GetAllocator(); + response.SetObject(); + if (engine_->Restart(std::move(configuration))) { + response.AddMember("type", "Success", allocator); + auto new_description = GetServiceProtocolDescription(); + rapidjson::Value view(rapidjson::kObjectType); + new_description.Write(this, view, allocator); + response.AddMember("view", view, allocator); + return true; + } else { + FXL_DLOG(ERROR) << "Could not run configuration in engine."; + response.AddMember("type", "Failure", allocator); + return false; + } + + FXL_DCHECK(false); + return false; } -void Shell::SetAssetBundlePathInPlatformView(uintptr_t view_id, - const char* asset_directory, - bool* view_existed, - int64_t* dart_isolate_id, - std::string* isolate_name) { - fxl::AutoResetWaitableEvent latch; - FXL_DCHECK(view_id != 0); - FXL_DCHECK(asset_directory); - FXL_DCHECK(view_existed); - - blink::Threads::UI()->PostTask([this, view_id, asset_directory, view_existed, - dart_isolate_id, isolate_name, &latch]() { - SetAssetBundlePathInPlatformViewUIThread(view_id, asset_directory, - view_existed, dart_isolate_id, - isolate_name, &latch); - }); - latch.Wait(); +// Service protocol handler +bool Shell::OnServiceProtocolFlushUIThreadTasks( + const blink::ServiceProtocol::Handler::ServiceProtocolMap& params, + rapidjson::Document& response) { + FXL_DCHECK(task_runners_.GetUITaskRunner()->RunsTasksOnCurrentThread()); + // This API should not be invoked by production code. + // It can potentially starve the service isolate if the main isolate pauses + // at a breakpoint or is in an infinite loop. + // + // It should be invoked from the VM Service and and blocks it until UI thread + // tasks are processed. + response.SetObject(); + response.AddMember("type", "Success", response.GetAllocator()); + return true; +} + +// Service protocol handler +bool Shell::OnServiceProtocolSetAssetBundlePath( + const blink::ServiceProtocol::Handler::ServiceProtocolMap& params, + rapidjson::Document& response) { + FXL_DCHECK(task_runners_.GetUITaskRunner()->RunsTasksOnCurrentThread()); + + if (params.count("assetDirectory") == 0) { + ServiceProtocolParameterError(response, + "'assetDirectory' parameter is missing."); + return false; + } + + auto& allocator = response.GetAllocator(); + response.SetObject(); + + auto asset_manager = fxl::MakeRefCounted(); + + asset_manager->PushFront(std::make_unique( + fml::OpenFile(params.at("assetDirectory").ToString().c_str(), + fml::OpenPermission::kRead, true))); + + if (engine_->UpdateAssetManager(std::move(asset_manager))) { + response.AddMember("type", "Success", allocator); + auto new_description = GetServiceProtocolDescription(); + rapidjson::Value view(rapidjson::kObjectType); + new_description.Write(this, view, allocator); + response.AddMember("view", view, allocator); + return true; + } else { + FXL_DLOG(ERROR) << "Could not update asset directory."; + response.AddMember("type", "Failure", allocator); + return false; + } + + FXL_DCHECK(false); + return false; } -void Shell::SetAssetBundlePathInPlatformViewUIThread( - uintptr_t view_id, - const std::string& assets_directory, - bool* view_existed, - int64_t* dart_isolate_id, - std::string* isolate_name, - fxl::AutoResetWaitableEvent* latch) { - FXL_DCHECK(ui_thread_checker_ && - ui_thread_checker_->IsCreationThreadCurrent()); - - *view_existed = false; - - IteratePlatformViews( - [view_id, // argument -#if !defined(OS_WIN) - // Using std::move on const references inside lambda capture is - // not supported on Windows for some reason. - // TODO(https://github.com/flutter/flutter/issues/13908): - // Investigate the root cause of the difference. - assets_directory = std::move(assets_directory), // argument -#else - assets_directory, // argument -#endif - &view_existed, // out - &dart_isolate_id, // out - &isolate_name // out - ](PlatformView* view) -> bool { - if (reinterpret_cast(view) != view_id) { - // Keep looking. - return true; +Rasterizer::Screenshot Shell::Screenshot( + Rasterizer::ScreenshotType screenshot_type, + bool base64_encode) { + TRACE_EVENT0("flutter", "Shell::Screenshot"); + fxl::AutoResetWaitableEvent latch; + Rasterizer::Screenshot screenshot; + fml::TaskRunner::RunNowOrPostTask( + task_runners_.GetGPUTaskRunner(), [&latch, // + rasterizer = GetRasterizer(), // + &screenshot, // + screenshot_type, // + base64_encode // + ]() { + if (rasterizer) { + screenshot = rasterizer->ScreenshotLastLayerTree(screenshot_type, + base64_encode); } - *view_existed = true; - view->SetAssetBundlePath(assets_directory); - *dart_isolate_id = view->engine().GetUIIsolateMainPort(); - *isolate_name = view->engine().GetUIIsolateName(); - // We found the requested view. Stop iterating over - // platform views. - return false; + latch.Signal(); }); - - latch->Signal(); + latch.Wait(); + return screenshot; } } // namespace shell diff --git a/shell/common/shell.h b/shell/common/shell.h index 92c315dcf2aa7..02a642b0ba40f 100644 --- a/shell/common/shell.h +++ b/shell/common/shell.h @@ -5,96 +5,214 @@ #ifndef SHELL_COMMON_SHELL_H_ #define SHELL_COMMON_SHELL_H_ -#include -#include - +#include +#include + +#include "flutter/common/settings.h" +#include "flutter/common/task_runners.h" +#include "flutter/flow/texture.h" +#include "flutter/fml/memory/thread_checker.h" +#include "flutter/fml/memory/weak_ptr.h" #include "flutter/fml/thread.h" -#include "flutter/shell/common/tracing_controller.h" -#include "lib/fxl/command_line.h" +#include "flutter/lib/ui/semantics/semantics_node.h" +#include "flutter/lib/ui/window/platform_message.h" +#include "flutter/runtime/service_protocol.h" +#include "flutter/shell/common/animator.h" +#include "flutter/shell/common/engine.h" +#include "flutter/shell/common/io_manager.h" +#include "flutter/shell/common/platform_view.h" +#include "flutter/shell/common/rasterizer.h" +#include "flutter/shell/common/surface.h" #include "lib/fxl/functional/closure.h" #include "lib/fxl/macros.h" #include "lib/fxl/memory/ref_ptr.h" #include "lib/fxl/memory/weak_ptr.h" +#include "lib/fxl/strings/string_view.h" +#include "lib/fxl/synchronization/thread_annotations.h" #include "lib/fxl/synchronization/thread_checker.h" #include "lib/fxl/synchronization/waitable_event.h" -#include "lib/fxl/tasks/task_runner.h" namespace shell { -class PlatformView; - -class Shell { +class Shell final : public PlatformView::Delegate, + public Animator::Delegate, + public Engine::Delegate, + public blink::ServiceProtocol::Handler { public: + template + using CreateCallback = std::function(Shell&)>; + static std::unique_ptr Create( + blink::TaskRunners task_runners, + blink::Settings settings, + CreateCallback on_create_platform_view, + CreateCallback on_create_rasterizer); + ~Shell(); - static void InitStandalone(fxl::CommandLine command_line, - std::string icu_data_path = "", - std::string application_library_path = "", - std::string bundle_path = ""); + const blink::Settings& GetSettings() const; - static Shell& Shared(); + const blink::TaskRunners& GetTaskRunners() const; - const fxl::CommandLine& GetCommandLine() const; + fml::WeakPtr GetRasterizer(); - void AddPlatformView(PlatformView* platform_view); + fml::WeakPtr GetEngine(); - void RemovePlatformView(PlatformView* platform_view); + fml::WeakPtr GetPlatformView(); - void IteratePlatformViews( - std::function iterator) const; + const blink::DartVM& GetDartVM() const; - // Attempt to run a script inside a flutter view indicated by |view_id|. - // Will set |view_existed| to true if the view was found and false otherwise. - void RunInPlatformView(uintptr_t view_id, - const char* main_script, - const char* packages_file, - const char* asset_directory, - bool* view_existed, - int64_t* dart_isolate_id, - std::string* isolate_name); + bool IsSetup() const; - void SetAssetBundlePathInPlatformView(uintptr_t view_id, - const char* asset_directory, - bool* view_existed, - int64_t* dart_isolate_id, - std::string* isolate_name); + Rasterizer::Screenshot Screenshot(Rasterizer::ScreenshotType type, + bool base64_encode); private: - fxl::CommandLine command_line_; - std::unique_ptr gpu_thread_; - std::unique_ptr ui_thread_; - std::unique_ptr io_thread_; - std::unique_ptr gpu_thread_checker_; - std::unique_ptr ui_thread_checker_; - TracingController tracing_controller_; - mutable std::mutex platform_views_mutex_; - std::unordered_set platform_views_; - - static void Init(fxl::CommandLine command_line, - const std::string& bundle_path); - - Shell(fxl::CommandLine command_line); - - void InitGpuThread(); - - void InitUIThread(); - - void RunInPlatformViewUIThread(uintptr_t view_id, - const std::string& main, - const std::string& packages, - const std::string& assets_directory, - bool* view_existed, - int64_t* dart_isolate_id, - std::string* isolate_name, - fxl::AutoResetWaitableEvent* latch); - - void SetAssetBundlePathInPlatformViewUIThread( - uintptr_t view_id, - const std::string& main, - bool* view_existed, - int64_t* dart_isolate_id, - std::string* isolate_name, - fxl::AutoResetWaitableEvent* latch); + using ServiceProtocolHandler = std::function; + + const blink::TaskRunners task_runners_; + const blink::Settings settings_; + fxl::RefPtr vm_; + std::unique_ptr platform_view_; // on platform task runner + std::unique_ptr engine_; // on UI task runner + std::unique_ptr rasterizer_; // on GPU task runner + std::unique_ptr io_manager_; // on IO task runner + + std::unordered_map, + ServiceProtocolHandler> // task-runner/function + // pair + > + service_protocol_handlers_; + bool is_setup_ = false; + + Shell(blink::TaskRunners task_runners, blink::Settings settings); + + static std::unique_ptr CreateShellOnPlatformThread( + blink::TaskRunners task_runners, + blink::Settings settings, + Shell::CreateCallback on_create_platform_view, + Shell::CreateCallback on_create_rasterizer); + + bool Setup(std::unique_ptr platform_view, + std::unique_ptr engine, + std::unique_ptr rasterizer, + std::unique_ptr io_manager); + + // |shell::PlatformView::Delegate| + void OnPlatformViewCreated(const PlatformView& view, + std::unique_ptr surface) override; + + // |shell::PlatformView::Delegate| + void OnPlatformViewDestroyed(const PlatformView& view) override; + + // |shell::PlatformView::Delegate| + void OnPlatformViewSetViewportMetrics( + const PlatformView& view, + const blink::ViewportMetrics& metrics) override; + + // |shell::PlatformView::Delegate| + void OnPlatformViewDispatchPlatformMessage( + const PlatformView& view, + fxl::RefPtr message) override; + + // |shell::PlatformView::Delegate| + void OnPlatformViewDispatchPointerDataPacket( + const PlatformView& view, + std::unique_ptr packet) override; + + // |shell::PlatformView::Delegate| + void OnPlatformViewDispatchSemanticsAction( + const PlatformView& view, + int32_t id, + blink::SemanticsAction action, + std::vector args) override; + + // |shell::PlatformView::Delegate| + void OnPlatformViewSetSemanticsEnabled(const PlatformView& view, + bool enabled) override; + + // |shell::PlatformView::Delegate| + void OnPlatformViewRegisterTexture( + const PlatformView& view, + std::shared_ptr texture) override; + + // |shell::PlatformView::Delegate| + void OnPlatformViewUnregisterTexture(const PlatformView& view, + int64_t texture_id) override; + + // |shell::PlatformView::Delegate| + void OnPlatformViewMarkTextureFrameAvailable(const PlatformView& view, + int64_t texture_id) override; + + // |shell::PlatformView::Delegate| + void OnPlatformViewSetNextFrameCallback(const PlatformView& view, + fxl::Closure closure) override; + + // |shell::Animator::Delegate| + void OnAnimatorBeginFrame(const Animator& animator, + fxl::TimePoint frame_time) override; + + // |shell::Animator::Delegate| + void OnAnimatorNotifyIdle(const Animator& animator, + int64_t deadline) override; + + // |shell::Animator::Delegate| + void OnAnimatorDraw( + const Animator& animator, + fxl::RefPtr> pipeline) override; + + // |shell::Animator::Delegate| + void OnAnimatorDrawLastLayerTree(const Animator& animator) override; + + // |shell::Engine::Delegate| + void OnEngineUpdateSemantics(const Engine& engine, + blink::SemanticsNodeUpdates update) override; + + // |shell::Engine::Delegate| + void OnEngineHandlePlatformMessage( + const Engine& engine, + fxl::RefPtr message) override; + + // |blink::ServiceProtocol::Handler| + fxl::RefPtr GetServiceProtocolHandlerTaskRunner( + fxl::StringView method) const override; + + // |blink::ServiceProtocol::Handler| + bool HandleServiceProtocolMessage( + fxl::StringView method, // one if the extension names specified above. + const ServiceProtocolMap& params, + rapidjson::Document& response) override; + + // |blink::ServiceProtocol::Handler| + blink::ServiceProtocol::Handler::Description GetServiceProtocolDescription() + const override; + + // Service protocol handler + bool OnServiceProtocolScreenshot( + const blink::ServiceProtocol::Handler::ServiceProtocolMap& params, + rapidjson::Document& response); + + // Service protocol handler + bool OnServiceProtocolScreenshotSKP( + const blink::ServiceProtocol::Handler::ServiceProtocolMap& params, + rapidjson::Document& response); + + // Service protocol handler + bool OnServiceProtocolRunInView( + const blink::ServiceProtocol::Handler::ServiceProtocolMap& params, + rapidjson::Document& response); + + // Service protocol handler + bool OnServiceProtocolFlushUIThreadTasks( + const blink::ServiceProtocol::Handler::ServiceProtocolMap& params, + rapidjson::Document& response); + + // Service protocol handler + bool OnServiceProtocolSetAssetBundlePath( + const blink::ServiceProtocol::Handler::ServiceProtocolMap& params, + rapidjson::Document& response); FXL_DISALLOW_COPY_AND_ASSIGN(Shell); }; diff --git a/shell/common/shell_unittests.cc b/shell/common/shell_unittests.cc new file mode 100644 index 0000000000000..67689341a1586 --- /dev/null +++ b/shell/common/shell_unittests.cc @@ -0,0 +1,133 @@ +// Copyright 2017 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. + +#define FML_USED_ON_EMBEDDER + +#include +#include +#include + +#include "flutter/fml/message_loop.h" +#include "flutter/shell/common/platform_view.h" +#include "flutter/shell/common/rasterizer.h" +#include "flutter/shell/common/shell.h" +#include "flutter/shell/common/thread_host.h" +#include "gtest/gtest.h" +#include "lib/fxl/synchronization/waitable_event.h" + +#define CURRENT_TEST_NAME \ + std::string { \ + ::testing::UnitTest::GetInstance()->current_test_info()->name() \ + } + +namespace shell { + +TEST(ShellTest, InitializeWithInvalidThreads) { + blink::Settings settings = {}; + settings.task_observer_add = [](intptr_t, fxl::Closure) {}; + settings.task_observer_remove = [](intptr_t) {}; + settings.using_blink = false; + blink::TaskRunners task_runners("test", nullptr, nullptr, nullptr, nullptr); + auto shell = Shell::Create( + std::move(task_runners), settings, + [](Shell& shell) { + return std::make_unique(shell, shell.GetTaskRunners()); + }, + [](Shell& shell) { + return std::make_unique(shell.GetTaskRunners()); + }); + ASSERT_FALSE(shell); +} + +TEST(ShellTest, InitializeWithDifferentThreads) { + blink::Settings settings = {}; + settings.task_observer_add = [](intptr_t, fxl::Closure) {}; + settings.task_observer_remove = [](intptr_t) {}; + settings.using_blink = false; + ThreadHost thread_host("io.flutter.test." + CURRENT_TEST_NAME + ".", + ThreadHost::Type::Platform | ThreadHost::Type::GPU | + ThreadHost::Type::IO | ThreadHost::Type::UI); + blink::TaskRunners task_runners("test", + thread_host.platform_thread->GetTaskRunner(), + thread_host.gpu_thread->GetTaskRunner(), + thread_host.ui_thread->GetTaskRunner(), + thread_host.io_thread->GetTaskRunner()); + auto shell = Shell::Create( + std::move(task_runners), settings, + [](Shell& shell) { + return std::make_unique(shell, shell.GetTaskRunners()); + }, + [](Shell& shell) { + return std::make_unique(shell.GetTaskRunners()); + }); + ASSERT_TRUE(shell); +} + +TEST(ShellTest, InitializeWithSingleThread) { + blink::Settings settings = {}; + settings.task_observer_add = [](intptr_t, fxl::Closure) {}; + settings.task_observer_remove = [](intptr_t) {}; + settings.using_blink = false; + ThreadHost thread_host("io.flutter.test." + CURRENT_TEST_NAME + ".", + ThreadHost::Type::Platform); + auto task_runner = thread_host.platform_thread->GetTaskRunner(); + blink::TaskRunners task_runners("test", task_runner, task_runner, task_runner, + task_runner); + auto shell = Shell::Create( + std::move(task_runners), settings, + [](Shell& shell) { + return std::make_unique(shell, shell.GetTaskRunners()); + }, + [](Shell& shell) { + return std::make_unique(shell.GetTaskRunners()); + }); + ASSERT_TRUE(shell); +} + +TEST(ShellTest, InitializeWithSingleThreadWhichIsTheCallingThread) { + blink::Settings settings = {}; + settings.task_observer_add = [](intptr_t, fxl::Closure) {}; + settings.task_observer_remove = [](intptr_t) {}; + settings.using_blink = false; + fml::MessageLoop::EnsureInitializedForCurrentThread(); + auto task_runner = fml::MessageLoop::GetCurrent().GetTaskRunner(); + blink::TaskRunners task_runners("test", task_runner, task_runner, task_runner, + task_runner); + auto shell = Shell::Create( + std::move(task_runners), settings, + [](Shell& shell) { + return std::make_unique(shell, shell.GetTaskRunners()); + }, + [](Shell& shell) { + return std::make_unique(shell.GetTaskRunners()); + }); + ASSERT_TRUE(shell); +} + +TEST(ShellTest, InitializeWithMultipleThreadButCallingThreadAsPlatformThread) { + blink::Settings settings = {}; + settings.task_observer_add = [](intptr_t, fxl::Closure) {}; + settings.task_observer_remove = [](intptr_t) {}; + settings.using_blink = false; + ThreadHost thread_host( + "io.flutter.test." + CURRENT_TEST_NAME + ".", + ThreadHost::Type::GPU | ThreadHost::Type::IO | ThreadHost::Type::UI); + fml::MessageLoop::EnsureInitializedForCurrentThread(); + blink::TaskRunners task_runners( + "test", fml::MessageLoop::GetCurrent().GetTaskRunner(), + thread_host.gpu_thread->GetTaskRunner(), + thread_host.ui_thread->GetTaskRunner(), + thread_host.io_thread->GetTaskRunner()); + auto shell = Shell::Create( + std::move(task_runners), settings, + [](Shell& shell) { + return std::make_unique(shell, shell.GetTaskRunners()); + }, + [](Shell& shell) { + return std::make_unique(shell.GetTaskRunners()); + }); + ASSERT_TRUE(shell); +} + +} // namespace shell diff --git a/shell/common/surface.cc b/shell/common/surface.cc index 01d288a5e5085..228647e8b3512 100644 --- a/shell/common/surface.cc +++ b/shell/common/surface.cc @@ -3,6 +3,7 @@ // found in the LICENSE file. #include "flutter/shell/common/surface.h" + #include "lib/fxl/logging.h" #include "third_party/skia/include/core/SkColorSpaceXformCanvas.h" #include "third_party/skia/include/core/SkSurface.h" @@ -59,27 +60,22 @@ bool SurfaceFrame::PerformSubmit() { return false; } -Surface::Surface() : scale_(1.0) {} - -Surface::~Surface() = default; +Surface::Surface() : Surface(std::make_unique()) {} -bool Surface::SupportsScaling() const { - return false; +Surface::Surface(std::unique_ptr compositor_context) + : compositor_context_(std::move(compositor_context)) { + FXL_DCHECK(compositor_context_); + // TODO: Get rid of these explicit calls and move the logic to the c/dtors of + // the compositor context. + compositor_context_->OnGrContextCreated(); } -double Surface::GetScale() const { - return scale_; +Surface::~Surface() { + compositor_context_->OnGrContextDestroyed(); } -void Surface::SetScale(double scale) { - static constexpr double kMaxScale = 1.0; - static constexpr double kMinScale = 0.25; - if (scale > kMaxScale) { - scale = kMaxScale; - } else if (scale < kMinScale) { - scale = kMinScale; - } - scale_ = scale; +flow::CompositorContext& Surface::GetCompositorContext() { + return *compositor_context_; } } // namespace shell diff --git a/shell/common/surface.h b/shell/common/surface.h index 906480237f416..6133a7d519fff 100644 --- a/shell/common/surface.h +++ b/shell/common/surface.h @@ -7,7 +7,7 @@ #include -#include "lib/fxl/compiler_specific.h" +#include "flutter/flow/compositor_context.h" #include "lib/fxl/macros.h" #include "third_party/skia/include/core/SkCanvas.h" @@ -45,6 +45,8 @@ class Surface { public: Surface(); + Surface(std::unique_ptr compositor_context); + virtual ~Surface(); virtual bool IsValid() = 0; @@ -53,14 +55,12 @@ class Surface { virtual GrContext* GetContext() = 0; - virtual bool SupportsScaling() const; - - double GetScale() const; - - void SetScale(double scale); + flow::CompositorContext& GetCompositorContext(); private: - double scale_; + std::unique_ptr compositor_context_; + + FXL_DISALLOW_COPY_AND_ASSIGN(Surface); }; } // namespace shell diff --git a/shell/common/switches.cc b/shell/common/switches.cc index c1c82ff166ac1..b49d65f5d73d5 100644 --- a/shell/common/switches.cc +++ b/shell/common/switches.cc @@ -90,4 +90,125 @@ const fxl::StringView FlagForSwitch(Switch swtch) { return fxl::StringView(); } +template +static bool GetSwitchValue(const fxl::CommandLine& command_line, + shell::Switch sw, + T* result) { + std::string switch_string; + + if (!command_line.GetOptionValue(shell::FlagForSwitch(sw), &switch_string)) { + return false; + } + + std::stringstream stream(switch_string); + T value = 0; + if (stream >> value) { + *result = value; + return true; + } + + return false; +} + +blink::Settings SettingsFromCommandLine(const fxl::CommandLine& command_line) { + blink::Settings settings = {}; + + // Enable Observatory + settings.enable_observatory = + !command_line.HasOption(FlagForSwitch(Switch::DisableObservatory)); + + // Set Observatory Port + if (command_line.HasOption(FlagForSwitch(Switch::DeviceObservatoryPort))) { + if (!GetSwitchValue(command_line, Switch::DeviceObservatoryPort, + &settings.observatory_port)) { + FXL_LOG(INFO) + << "Observatory port specified was malformed. Will default to " + << settings.observatory_port; + } + } + + // Checked mode overrides. + settings.dart_non_checked_mode = + command_line.HasOption(FlagForSwitch(Switch::DartNonCheckedMode)); + + settings.ipv6 = command_line.HasOption(FlagForSwitch(Switch::IPv6)); + + settings.start_paused = + command_line.HasOption(FlagForSwitch(Switch::StartPaused)); + + settings.enable_dart_profiling = + command_line.HasOption(FlagForSwitch(Switch::EnableDartProfiling)); + + settings.enable_software_rendering = + command_line.HasOption(FlagForSwitch(Switch::EnableSoftwareRendering)); + + settings.using_blink = + command_line.HasOption(FlagForSwitch(Switch::EnableBlink)); + + settings.endless_trace_buffer = + command_line.HasOption(FlagForSwitch(Switch::EndlessTraceBuffer)); + + settings.trace_startup = + command_line.HasOption(FlagForSwitch(Switch::TraceStartup)); + + settings.skia_deterministic_rendering_on_cpu = + command_line.HasOption(FlagForSwitch(Switch::SkiaDeterministicRendering)); + + command_line.GetOptionValue(FlagForSwitch(Switch::FLX), &settings.flx_path); + + command_line.GetOptionValue(FlagForSwitch(Switch::FlutterAssetsDir), + &settings.assets_path); + + command_line.GetOptionValue(FlagForSwitch(Switch::Snapshot), + &settings.script_snapshot_path); + + command_line.GetOptionValue(FlagForSwitch(Switch::MainDartFile), + &settings.main_dart_file_path); + + command_line.GetOptionValue(FlagForSwitch(Switch::Packages), + &settings.packages_file_path); + + command_line.GetOptionValue(FlagForSwitch(Switch::AotSnapshotPath), + &settings.aot_snapshot_path); + + command_line.GetOptionValue(FlagForSwitch(Switch::AotVmSnapshotData), + &settings.aot_vm_snapshot_data_filename); + + command_line.GetOptionValue(FlagForSwitch(Switch::AotVmSnapshotInstructions), + &settings.aot_vm_snapshot_instr_filename); + + command_line.GetOptionValue(FlagForSwitch(Switch::AotIsolateSnapshotData), + &settings.aot_isolate_snapshot_data_filename); + + command_line.GetOptionValue( + FlagForSwitch(Switch::AotIsolateSnapshotInstructions), + &settings.aot_isolate_snapshot_instr_filename); + + command_line.GetOptionValue(FlagForSwitch(Switch::CacheDirPath), + &settings.temp_directory_path); + + command_line.GetOptionValue(FlagForSwitch(Switch::ICUDataFilePath), + &settings.icu_data_path); + + settings.use_test_fonts = + command_line.HasOption(FlagForSwitch(Switch::UseTestFonts)); + + command_line.GetOptionValue(FlagForSwitch(Switch::LogTag), &settings.log_tag); + std::string all_dart_flags; + if (command_line.GetOptionValue(FlagForSwitch(Switch::DartFlags), + &all_dart_flags)) { + std::stringstream stream(all_dart_flags); + std::istream_iterator end; + for (std::istream_iterator it(stream); it != end; ++it) + settings.dart_flags.push_back(*it); + } + +#if FLUTTER_RUNTIME_MODE != FLUTTER_RUNTIME_MODE_RELEASE + settings.trace_skia = + command_line.HasOption(FlagForSwitch(Switch::TraceSkia)); +#endif + + return settings; +} + } // namespace shell diff --git a/shell/common/switches.h b/shell/common/switches.h index 91778a78e92cc..6b67b1f6a1567 100644 --- a/shell/common/switches.h +++ b/shell/common/switches.h @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "flutter/common/settings.h" +#include "lib/fxl/command_line.h" #include "lib/fxl/strings/string_view.h" #ifndef SHELL_COMMON_SWITCHES_H_ @@ -23,12 +25,29 @@ namespace shell { DEF_SWITCHES_START DEF_SWITCH(AotSharedLibraryPath, "aot-shared-library-path", "Path to the *.so.") -DEF_SWITCH(AotSnapshotPath, "aot-snapshot-path", "Path to the AOT snapshot.") -DEF_SWITCH(AotVmSnapshotData, "vm-snapshot-data", "") -DEF_SWITCH(AotVmSnapshotInstructions, "vm-snapshot-instr", "") -DEF_SWITCH(AotIsolateSnapshotData, "isolate-snapshot-data", "") -DEF_SWITCH(AotIsolateSnapshotInstructions, "isolate-snapshot-instr", "") +DEF_SWITCH(AotSnapshotPath, + "aot-snapshot-path", + "Path to the directory containing the four files specified by " + "AotVmSnapshotData, AotVmSnapshotInstructions, " + "AotVmSnapshotInstructions and AotIsolateSnapshotInstructions.") +DEF_SWITCH(AotVmSnapshotData, + "vm-snapshot-data", + "The VM snapshot data that will be memory mapped as read-only. " + "AotSnapshotPath must be present.") +DEF_SWITCH(AotVmSnapshotInstructions, + "vm-snapshot-instr", + "The VM instructions snapshot that will be memory mapped as read " + "and executable. AotSnapshotPath must be present.") +DEF_SWITCH(AotIsolateSnapshotData, + "isolate-snapshot-data", + "The isolate snapshot data that will be memory mapped as read-only. " + "AotSnapshotPath must be present.") +DEF_SWITCH(AotIsolateSnapshotInstructions, + "isolate-snapshot-instr", + "The isolate instructions snapshot that will be memory mapped as " + "read and executable. AotSnapshotPath must be present.") DEF_SWITCH(CacheDirPath, "cache-dir-path", "Path to the cache directory.") +DEF_SWITCH(ICUDataFilePath, "icu-data-file-path", "Path to the ICU data file.") DEF_SWITCH(DartFlags, "dart-flags", "Flags passed directly to the Dart VM without being interpreted " @@ -73,10 +92,6 @@ DEF_SWITCH(FlutterAssetsDir, DEF_SWITCH(Help, "help", "Display this help text.") DEF_SWITCH(LogTag, "log-tag", "Tag associated with log messages.") DEF_SWITCH(MainDartFile, "dart-main", "The path to the main Dart file.") -DEF_SWITCH(NonInteractive, - "non-interactive", - "Make the shell non-interactive. By default, the shell attempts " - "to setup a window and create an OpenGL context.") DEF_SWITCH(Packages, "packages", "Specify the path to the packages.") DEF_SWITCH(Snapshot, "snapshot-blob", "Specify the path to the snapshot blob") DEF_SWITCH(StartPaused, @@ -113,7 +128,9 @@ DEF_SWITCHES_END void PrintUsage(const std::string& executable_name); -const fxl::StringView FlagForSwitch(Switch sw); +const fxl::StringView FlagForSwitch(Switch swtch); + +blink::Settings SettingsFromCommandLine(const fxl::CommandLine& command_line); } // namespace shell diff --git a/shell/common/thread_host.cc b/shell/common/thread_host.cc new file mode 100644 index 0000000000000..f35594829d5d9 --- /dev/null +++ b/shell/common/thread_host.cc @@ -0,0 +1,38 @@ +// Copyright 2017 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. + +#include "flutter/shell/common/thread_host.h" + +namespace shell { + +ThreadHost::ThreadHost() = default; + +ThreadHost::ThreadHost(std::string name_prefix, uint64_t mask) { + if (mask & ThreadHost::Type::Platform) { + platform_thread = std::make_unique(name_prefix + ".platform"); + } + + if (mask & ThreadHost::Type::UI) { + ui_thread = std::make_unique(name_prefix + ".ui"); + } + + if (mask & ThreadHost::Type::GPU) { + gpu_thread = std::make_unique(name_prefix + ".gpu"); + } + + if (mask & ThreadHost::Type::IO) { + io_thread = std::make_unique(name_prefix + ".io"); + } +} + +ThreadHost::~ThreadHost() = default; + +void ThreadHost::Reset() { + platform_thread.reset(); + ui_thread.reset(); + gpu_thread.reset(); + io_thread.reset(); +} + +} // namespace shell diff --git a/shell/common/thread_host.h b/shell/common/thread_host.h new file mode 100644 index 0000000000000..a688aa6a06a85 --- /dev/null +++ b/shell/common/thread_host.h @@ -0,0 +1,43 @@ +// Copyright 2017 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. + +#ifndef FLUTTER_SHELL_COMMON_THREAD_HOST_H_ +#define FLUTTER_SHELL_COMMON_THREAD_HOST_H_ + +#include + +#include "flutter/fml/thread.h" +#include "lib/fxl/macros.h" + +namespace shell { + +struct ThreadHost { + enum Type { + Platform = 1 << 0, + UI = 1 << 1, + GPU = 1 << 2, + IO = 1 << 3, + }; + + std::unique_ptr platform_thread; + std::unique_ptr ui_thread; + std::unique_ptr gpu_thread; + std::unique_ptr io_thread; + + ThreadHost(); + + ThreadHost(ThreadHost&&) = default; + + ThreadHost& operator=(ThreadHost&&) = default; + + ThreadHost(std::string name_prefix, uint64_t type_mask); + + ~ThreadHost(); + + void Reset(); +}; + +} // namespace shell + +#endif // FLUTTER_SHELL_COMMON_THREAD_HOST_H_ diff --git a/shell/common/tracing_controller.cc b/shell/common/tracing_controller.cc deleted file mode 100644 index 6fa8d9d5ee87f..0000000000000 --- a/shell/common/tracing_controller.cc +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "flutter/shell/common/tracing_controller.h" - -#include - -#include "flutter/common/threads.h" -#include "flutter/fml/trace_event.h" -#include "flutter/runtime/dart_init.h" -#include "flutter/shell/common/shell.h" -#include "lib/fxl/logging.h" -#include "third_party/dart/runtime/include/dart_tools_api.h" - -namespace shell { - -TracingController::TracingController() : tracing_active_(false) { - blink::SetEmbedderTracingCallbacks( - std::unique_ptr( - new blink::EmbedderTracingCallbacks([this]() { StartTracing(); }, - [this]() { StopTracing(); }))); -} - -TracingController::~TracingController() { - blink::SetEmbedderTracingCallbacks(nullptr); -} - -static void AddTraceMetadata() { - blink::Threads::Gpu()->PostTask([]() { Dart_SetThreadName("gpu_thread"); }); - blink::Threads::UI()->PostTask([]() { Dart_SetThreadName("ui_thread"); }); - blink::Threads::IO()->PostTask([]() { Dart_SetThreadName("io_thread"); }); - blink::Threads::Platform()->PostTask( - []() { Dart_SetThreadName("platform_thread"); }); -} - -void TracingController::StartTracing() { - if (tracing_active_) - return; - tracing_active_ = true; - AddTraceMetadata(); -} - -void TracingController::StopTracing() { - if (!tracing_active_) { - return; - } - tracing_active_ = false; -} - -} // namespace shell diff --git a/shell/common/tracing_controller.h b/shell/common/tracing_controller.h deleted file mode 100644 index 3f9e6f03ab14c..0000000000000 --- a/shell/common/tracing_controller.h +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef SHELL_COMMON_TRACING_CONTROLLER_H_ -#define SHELL_COMMON_TRACING_CONTROLLER_H_ - -#include - -#include "lib/fxl/macros.h" - -namespace shell { - -class TracingController { - public: - TracingController(); - - ~TracingController(); - - void StartTracing(); - - void StopTracing(); - - bool tracing_active() const { return tracing_active_; } - - private: - bool tracing_active_; - - FXL_DISALLOW_COPY_AND_ASSIGN(TracingController); -}; - -} // namespace shell - -#endif // SHELL_COMMON_TRACING_CONTROLLER_H_ diff --git a/shell/common/vsync_waiter.cc b/shell/common/vsync_waiter.cc index 5acc235b1f68d..7233631b8d4f3 100644 --- a/shell/common/vsync_waiter.cc +++ b/shell/common/vsync_waiter.cc @@ -4,8 +4,40 @@ #include "flutter/shell/common/vsync_waiter.h" +#include "flutter/fml/task_runner.h" +#include "flutter/fml/trace_event.h" + namespace shell { +VsyncWaiter::VsyncWaiter(blink::TaskRunners task_runners) + : task_runners_(std::move(task_runners)) {} + VsyncWaiter::~VsyncWaiter() = default; +void VsyncWaiter::AsyncWaitForVsync(Callback callback) { + { + std::lock_guard lock(callback_mutex_); + callback_ = std::move(callback); + } + AwaitVSync(); +} + +void VsyncWaiter::FireCallback(fxl::TimePoint frame_start_time, + fxl::TimePoint frame_target_time) { + // Note: The tag name must be "VSYNC" (it is special) so that the "Highlight + // Vsync" checkbox in the timeline can be enabled. + TRACE_EVENT1("flutter", "VSYNC", "mode", "basic"); + Callback callback; + + { + std::lock_guard lock(callback_mutex_); + callback = std::move(callback_); + } + + if (callback) { + task_runners_.GetUITaskRunner()->PostTask( + std::bind(std::move(callback), frame_start_time, frame_target_time)); + } +} + } // namespace shell diff --git a/shell/common/vsync_waiter.h b/shell/common/vsync_waiter.h index 77319ed8b6966..82231fdf4f853 100644 --- a/shell/common/vsync_waiter.h +++ b/shell/common/vsync_waiter.h @@ -6,7 +6,10 @@ #define FLUTTER_SHELL_COMMON_VSYNC_WAITER_H_ #include +#include +#include +#include "flutter/common/task_runners.h" #include "lib/fxl/time/time_point.h" namespace shell { @@ -16,9 +19,23 @@ class VsyncWaiter { using Callback = std::function; - virtual void AsyncWaitForVsync(Callback callback) = 0; - virtual ~VsyncWaiter(); + + void AsyncWaitForVsync(Callback callback); + + protected: + const blink::TaskRunners task_runners_; + std::mutex callback_mutex_; + Callback callback_; + + VsyncWaiter(blink::TaskRunners task_runners); + + virtual void AwaitVSync() = 0; + + void FireCallback(fxl::TimePoint frame_start_time, + fxl::TimePoint frame_target_time); + + FXL_DISALLOW_COPY_AND_ASSIGN(VsyncWaiter); }; } // namespace shell diff --git a/shell/common/vsync_waiter_fallback.cc b/shell/common/vsync_waiter_fallback.cc index 01c86cdedc6aa..bcf061bb7615f 100644 --- a/shell/common/vsync_waiter_fallback.cc +++ b/shell/common/vsync_waiter_fallback.cc @@ -4,7 +4,6 @@ #include "flutter/shell/common/vsync_waiter_fallback.h" -#include "flutter/common/threads.h" #include "lib/fxl/logging.h" namespace shell { @@ -21,28 +20,25 @@ fxl::TimePoint SnapToNextTick(fxl::TimePoint value, } // namespace -VsyncWaiterFallback::VsyncWaiterFallback() - : phase_(fxl::TimePoint::Now()), weak_factory_(this) {} +VsyncWaiterFallback::VsyncWaiterFallback(blink::TaskRunners task_runners) + : VsyncWaiter(std::move(task_runners)), + phase_(fxl::TimePoint::Now()), + weak_factory_(this) {} VsyncWaiterFallback::~VsyncWaiterFallback() = default; constexpr fxl::TimeDelta interval = fxl::TimeDelta::FromSecondsF(1.0 / 60.0); -void VsyncWaiterFallback::AsyncWaitForVsync(Callback callback) { - FXL_DCHECK(!callback_); - callback_ = std::move(callback); - +void VsyncWaiterFallback::AwaitVSync() { fxl::TimePoint now = fxl::TimePoint::Now(); fxl::TimePoint next = SnapToNextTick(now, phase_, interval); - blink::Threads::UI()->PostDelayedTask( + task_runners_.GetUITaskRunner()->PostDelayedTask( [self = weak_factory_.GetWeakPtr()] { - if (!self) - return; - fxl::TimePoint frame_time = fxl::TimePoint::Now(); - Callback callback = std::move(self->callback_); - self->callback_ = Callback(); - callback(frame_time, frame_time + interval); + if (self) { + const auto frame_time = fxl::TimePoint::Now(); + self->FireCallback(frame_time, frame_time + interval); + } }, next - now); } diff --git a/shell/common/vsync_waiter_fallback.h b/shell/common/vsync_waiter_fallback.h index bfb7e118b1330..d3cc8faaf6ce7 100644 --- a/shell/common/vsync_waiter_fallback.h +++ b/shell/common/vsync_waiter_fallback.h @@ -5,25 +5,24 @@ #ifndef FLUTTER_SHELL_COMMON_VSYNC_WAITER_FALLBACK_H_ #define FLUTTER_SHELL_COMMON_VSYNC_WAITER_FALLBACK_H_ -#include "flutter/fml/memory/weak_ptr.h" #include "flutter/shell/common/vsync_waiter.h" #include "lib/fxl/macros.h" +#include "lib/fxl/memory/weak_ptr.h" #include "lib/fxl/time/time_point.h" namespace shell { -class VsyncWaiterFallback : public VsyncWaiter { +class VsyncWaiterFallback final : public VsyncWaiter { public: - VsyncWaiterFallback(); - ~VsyncWaiterFallback() override; + VsyncWaiterFallback(blink::TaskRunners task_runners); - void AsyncWaitForVsync(Callback callback) override; + ~VsyncWaiterFallback() override; private: fxl::TimePoint phase_; - Callback callback_; + fxl::WeakPtrFactory weak_factory_; - fml::WeakPtrFactory weak_factory_; + void AwaitVSync() override; FXL_DISALLOW_COPY_AND_ASSIGN(VsyncWaiterFallback); }; diff --git a/shell/gpu/BUILD.gn b/shell/gpu/BUILD.gn index c050e120b0a10..066ea4fab90da 100644 --- a/shell/gpu/BUILD.gn +++ b/shell/gpu/BUILD.gn @@ -6,8 +6,6 @@ import("$flutter_root/shell/config.gni") source_set("gpu") { sources = [ - "gpu_rasterizer.cc", - "gpu_rasterizer.h", "gpu_surface_gl.cc", "gpu_surface_gl.h", "gpu_surface_software.cc", diff --git a/shell/gpu/gpu_rasterizer.cc b/shell/gpu/gpu_rasterizer.cc deleted file mode 100644 index 3424f989b3205..0000000000000 --- a/shell/gpu/gpu_rasterizer.cc +++ /dev/null @@ -1,168 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "gpu_rasterizer.h" - -#include -#include - -#include "flutter/common/threads.h" -#include "flutter/glue/trace_event.h" -#include "flutter/shell/common/picture_serializer.h" -#include "flutter/shell/common/platform_view.h" -#include "flutter/shell/common/shell.h" -#include "third_party/skia/include/core/SkPicture.h" - -namespace shell { - -GPURasterizer::GPURasterizer(std::unique_ptr info) - : compositor_context_(std::move(info)), weak_factory_(this) {} - -GPURasterizer::~GPURasterizer() = default; - -fml::WeakPtr GPURasterizer::GetWeakRasterizerPtr() { - return weak_factory_.GetWeakPtr(); -} - -void GPURasterizer::Setup(std::unique_ptr surface, - fxl::Closure continuation, - fxl::AutoResetWaitableEvent* setup_completion_event) { - surface_ = std::move(surface); - compositor_context_.OnGrContextCreated(); - - continuation(); - - setup_completion_event->Signal(); -} - -void GPURasterizer::Clear(SkColor color, const SkISize& size) { - if (surface_ == nullptr) { - return; - } - - auto frame = surface_->AcquireFrame(size); - - if (frame == nullptr) { - return; - } - - SkCanvas* canvas = frame->SkiaCanvas(); - - if (canvas == nullptr) { - return; - } - - canvas->clear(color); - - frame->Submit(); -} - -void GPURasterizer::Teardown( - fxl::AutoResetWaitableEvent* teardown_completion_event) { - compositor_context_.OnGrContextDestroyed(); - if (surface_) { - surface_.reset(); - } - last_layer_tree_.reset(); - teardown_completion_event->Signal(); -} - -flow::LayerTree* GPURasterizer::GetLastLayerTree() { - return last_layer_tree_.get(); -} - -void GPURasterizer::DrawLastLayerTree() { - if (!last_layer_tree_ || !surface_) { - return; - } - DrawToSurface(*last_layer_tree_); -} - -flow::TextureRegistry& GPURasterizer::GetTextureRegistry() { - return compositor_context_.texture_registry(); -} - -void GPURasterizer::Draw( - fxl::RefPtr> pipeline) { - TRACE_EVENT0("flutter", "GPURasterizer::Draw"); - - flutter::Pipeline::Consumer consumer = - std::bind(&GPURasterizer::DoDraw, this, std::placeholders::_1); - - // Consume as many pipeline items as possible. But yield the event loop - // between successive tries. - switch (pipeline->Consume(consumer)) { - case flutter::PipelineConsumeResult::MoreAvailable: { - auto weak_this = weak_factory_.GetWeakPtr(); - blink::Threads::Gpu()->PostTask([weak_this, pipeline]() { - if (weak_this) { - weak_this->Draw(pipeline); - } - }); - break; - } - default: - break; - } -} - -void GPURasterizer::DoDraw(std::unique_ptr layer_tree) { - if (!layer_tree || !surface_) { - return; - } - - // There is no way for the compositor to know how long the layer tree - // construction took. Fortunately, the layer tree does. Grab that time - // for instrumentation. - compositor_context_.engine_time().SetLapTime(layer_tree->construction_time()); - - DrawToSurface(*layer_tree); - - NotifyNextFrameOnce(); - - last_layer_tree_ = std::move(layer_tree); -} - -void GPURasterizer::DrawToSurface(flow::LayerTree& layer_tree) { - auto frame = surface_->AcquireFrame(layer_tree.frame_size()); - - if (frame == nullptr) { - return; - } - - auto canvas = frame->SkiaCanvas(); - - if (canvas == nullptr) { - return; - } - - auto compositor_frame = - compositor_context_.AcquireFrame(surface_->GetContext(), canvas); - - canvas->clear(SK_ColorBLACK); - - layer_tree.Raster(compositor_frame); - - frame->Submit(); -} - -void GPURasterizer::AddNextFrameCallback(fxl::Closure nextFrameCallback) { - nextFrameCallback_ = nextFrameCallback; -} - -void GPURasterizer::NotifyNextFrameOnce() { - if (nextFrameCallback_) { - blink::Threads::Platform()->PostTask([callback = nextFrameCallback_] { - TRACE_EVENT0("flutter", "GPURasterizer::NotifyNextFrameOnce"); - callback(); - }); - nextFrameCallback_ = nullptr; - } -} - -void GPURasterizer::SetTextureRegistry(flow::TextureRegistry* textureRegistry) { - compositor_context_.SetTextureRegistry(textureRegistry); -} - -} // namespace shell diff --git a/shell/gpu/gpu_rasterizer.h b/shell/gpu/gpu_rasterizer.h deleted file mode 100644 index ad16ee2c47989..0000000000000 --- a/shell/gpu/gpu_rasterizer.h +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef SHELL_GPU_DIRECT_GPU_RASTERIZER_H_ -#define SHELL_GPU_DIRECT_GPU_RASTERIZER_H_ - -#include "flutter/flow/compositor_context.h" -#include "flutter/shell/common/rasterizer.h" -#include "lib/fxl/memory/weak_ptr.h" -#include "lib/fxl/synchronization/waitable_event.h" - -namespace shell { - -class Surface; - -class GPURasterizer : public Rasterizer { - public: - GPURasterizer(std::unique_ptr info); - - ~GPURasterizer() override; - - void Setup(std::unique_ptr surface, - fxl::Closure continuation, - fxl::AutoResetWaitableEvent* setup_completion_event) override; - - void Clear(SkColor color, const SkISize& size) override; - - void Teardown( - fxl::AutoResetWaitableEvent* teardown_completion_event) override; - - fml::WeakPtr GetWeakRasterizerPtr() override; - - flow::LayerTree* GetLastLayerTree() override; - - void DrawLastLayerTree() override; - - flow::TextureRegistry& GetTextureRegistry() override; - - void Draw(fxl::RefPtr> pipeline) override; - - // Set a callback to be called once when the next frame is drawn. - void AddNextFrameCallback(fxl::Closure nextFrameCallback) override; - - void SetTextureRegistry(flow::TextureRegistry* textureRegistry) override; - - private: - std::unique_ptr surface_; - flow::CompositorContext compositor_context_; - std::unique_ptr last_layer_tree_; - // A closure to be called when the underlaying surface presents a frame the - // next time. NULL if there is no callback or the callback was set back to - // NULL after being called. - fxl::Closure nextFrameCallback_; - fml::WeakPtrFactory weak_factory_; - - void DoDraw(std::unique_ptr layer_tree); - - void DrawToSurface(flow::LayerTree& layer_tree); - - void NotifyNextFrameOnce(); - - FXL_DISALLOW_COPY_AND_ASSIGN(GPURasterizer); -}; - -} // namespace shell - -#endif // SHELL_GPU_DIRECT_GPU_RASTERIZER_H_ diff --git a/shell/gpu/gpu_surface_gl.cc b/shell/gpu/gpu_surface_gl.cc index 36551f1c088eb..2e8b199409262 100644 --- a/shell/gpu/gpu_surface_gl.cc +++ b/shell/gpu/gpu_surface_gl.cc @@ -189,12 +189,11 @@ std::unique_ptr GPUSurfaceGL::AcquireFrame(const SkISize& size) { return nullptr; } - auto weak_this = weak_factory_.GetWeakPtr(); - - SurfaceFrame::SubmitCallback submit_callback = - [weak_this](const SurfaceFrame& surface_frame, SkCanvas* canvas) { - return weak_this ? weak_this->PresentSurface(canvas) : false; - }; + SurfaceFrame::SubmitCallback submit_callback = [weak = weak_factory_ + .GetWeakPtr()]( + const SurfaceFrame& surface_frame, SkCanvas* canvas) { + return weak ? weak->PresentSurface(canvas) : false; + }; return std::make_unique(surface, submit_callback); } diff --git a/shell/gpu/gpu_surface_gl.h b/shell/gpu/gpu_surface_gl.h index 3f951a4481b56..05ae1a21543cc 100644 --- a/shell/gpu/gpu_surface_gl.h +++ b/shell/gpu/gpu_surface_gl.h @@ -5,10 +5,9 @@ #ifndef SHELL_GPU_GPU_SURFACE_GL_H_ #define SHELL_GPU_GPU_SURFACE_GL_H_ -#include "flutter/fml/memory/weak_ptr.h" #include "flutter/shell/common/surface.h" -#include "flutter/synchronization/debug_thread_checker.h" #include "lib/fxl/macros.h" +#include "lib/fxl/memory/weak_ptr.h" #include "third_party/skia/include/gpu/GrContext.h" namespace shell { @@ -44,7 +43,7 @@ class GPUSurfaceGL : public Surface { sk_sp onscreen_surface_; sk_sp offscreen_surface_; bool valid_ = false; - fml::WeakPtrFactory weak_factory_; + fxl::WeakPtrFactory weak_factory_; bool CreateOrUpdateSurfaces(const SkISize& size); diff --git a/shell/gpu/gpu_surface_software.cc b/shell/gpu/gpu_surface_software.cc index e340e605652dd..ea8c827b22405 100644 --- a/shell/gpu/gpu_surface_software.cc +++ b/shell/gpu/gpu_surface_software.cc @@ -18,20 +18,13 @@ bool GPUSurfaceSoftware::IsValid() { return delegate_ != nullptr; } -bool GPUSurfaceSoftware::SupportsScaling() const { - return true; -} - std::unique_ptr GPUSurfaceSoftware::AcquireFrame( const SkISize& logical_size) { if (!IsValid()) { return nullptr; } - // Check if we need to support surface scaling. - const auto scale = SupportsScaling() ? GetScale() : 1.0; - const auto size = SkISize::Make(logical_size.width() * scale, - logical_size.height() * scale); + const auto size = SkISize::Make(logical_size.width(), logical_size.height()); sk_sp backing_store = delegate_->AcquireBackingStore(size); @@ -48,12 +41,10 @@ std::unique_ptr GPUSurfaceSoftware::AcquireFrame( // irrespective of surface scaling. SkCanvas* canvas = backing_store->getCanvas(); canvas->resetMatrix(); - canvas->scale(scale, scale); - SurfaceFrame::SubmitCallback - on_submit = [self = weak_factory_.GetWeakPtr()]( - const SurfaceFrame& surface_frame, SkCanvas* canvas) - ->bool { + SurfaceFrame::SubmitCallback on_submit = + [self = weak_factory_.GetWeakPtr()](const SurfaceFrame& surface_frame, + SkCanvas* canvas) -> bool { // If the surface itself went away, there is nothing more to do. if (!self || !self->IsValid() || canvas == nullptr) { return false; diff --git a/shell/gpu/gpu_surface_software.h b/shell/gpu/gpu_surface_software.h index f7312153db8bc..238754f312b80 100644 --- a/shell/gpu/gpu_surface_software.h +++ b/shell/gpu/gpu_surface_software.h @@ -5,9 +5,9 @@ #ifndef FLUTTER_SHELL_GPU_GPU_SURFACE_SOFTWARE_H_ #define FLUTTER_SHELL_GPU_GPU_SURFACE_SOFTWARE_H_ -#include "flutter/fml/memory/weak_ptr.h" #include "flutter/shell/common/surface.h" #include "lib/fxl/macros.h" +#include "lib/fxl/memory/weak_ptr.h" #include "third_party/skia/include/core/SkSurface.h" namespace shell { @@ -30,12 +30,9 @@ class GPUSurfaceSoftware : public Surface { GrContext* GetContext() override; - bool SupportsScaling() const override; - private: GPUSurfaceSoftwareDelegate* delegate_; - - fml::WeakPtrFactory weak_factory_; + fxl::WeakPtrFactory weak_factory_; FXL_DISALLOW_COPY_AND_ASSIGN(GPUSurfaceSoftware); }; diff --git a/shell/gpu/gpu_surface_vulkan.h b/shell/gpu/gpu_surface_vulkan.h index eafed43a6296d..f1e25fbe987b8 100644 --- a/shell/gpu/gpu_surface_vulkan.h +++ b/shell/gpu/gpu_surface_vulkan.h @@ -6,11 +6,12 @@ #define SHELL_GPU_GPU_SURFACE_VULKAN_H_ #include -#include "flutter/fml/memory/weak_ptr.h" + #include "flutter/shell/common/surface.h" #include "flutter/vulkan/vulkan_native_surface.h" #include "flutter/vulkan/vulkan_window.h" #include "lib/fxl/macros.h" +#include "lib/fxl/memory/weak_ptr.h" namespace shell { @@ -29,7 +30,7 @@ class GPUSurfaceVulkan : public Surface { private: vulkan::VulkanWindow window_; - fml::WeakPtrFactory weak_factory_; + fxl::WeakPtrFactory weak_factory_; FXL_DISALLOW_COPY_AND_ASSIGN(GPUSurfaceVulkan); }; diff --git a/shell/platform/BUILD.gn b/shell/platform/BUILD.gn index 4b1e1a9e0bfeb..d23ebba3c2a1f 100644 --- a/shell/platform/BUILD.gn +++ b/shell/platform/BUILD.gn @@ -13,12 +13,11 @@ group("platform") { ] } else if (is_linux) { deps = [ - "linux", "embedder", ] } else if (is_win) { deps = [ - "win" + "win", ] } else { assert(false, "Unknown/Unsupported platform.") diff --git a/shell/platform/android/BUILD.gn b/shell/platform/android/BUILD.gn index d9744ad890caa..129c877faed0f 100644 --- a/shell/platform/android/BUILD.gn +++ b/shell/platform/android/BUILD.gn @@ -17,21 +17,25 @@ shared_library("flutter_shell_native") { "android_context_gl.h", "android_environment_gl.cc", "android_environment_gl.h", - "android_external_texture_gl.h", "android_external_texture_gl.cc", + "android_external_texture_gl.h", "android_native_window.cc", "android_native_window.h", + "android_shell_holder.cc", + "android_shell_holder.h", "android_surface.cc", "android_surface.h", "android_surface_gl.cc", "android_surface_gl.h", - "android_surface_software.h", "android_surface_software.cc", - "apk_asset_provider.h", + "android_surface_software.h", "apk_asset_provider.cc", + "apk_asset_provider.h", "flutter_main.cc", "flutter_main.h", "library_loader.cc", + "platform_message_response_android.cc", + "platform_message_response_android.h", "platform_view_android.cc", "platform_view_android.h", "platform_view_android_jni.cc", @@ -41,10 +45,10 @@ shared_library("flutter_shell_native") { ] deps = [ + "$flutter_root/assets", "$flutter_root/common", "$flutter_root/flow", "$flutter_root/fml", - "$flutter_root/assets", "$flutter_root/lib/ui", "$flutter_root/runtime", "$flutter_root/shell/common", @@ -59,9 +63,7 @@ shared_library("flutter_shell_native") { deps += [ "//third_party/dart/runtime:libdart_precompiled_runtime" ] } - public_configs = [ - "$flutter_root:config", - ] + public_configs = [ "$flutter_root:config" ] defines = [] diff --git a/shell/platform/android/android_context_gl.cc b/shell/platform/android/android_context_gl.cc index f54629f8644b9..6338dc96908e1 100644 --- a/shell/platform/android/android_context_gl.cc +++ b/shell/platform/android/android_context_gl.cc @@ -3,9 +3,13 @@ // found in the LICENSE file. #include "flutter/shell/platform/android/android_context_gl.h" + #include + #include +#include "flutter/fml/trace_event.h" + namespace shell { template @@ -65,19 +69,17 @@ static EGLResult CreateContext(EGLDisplay display, return {context != EGL_NO_CONTEXT, context}; } -static EGLResult ChooseEGLConfiguration( - EGLDisplay display, - PlatformView::SurfaceConfig config) { +static EGLResult ChooseEGLConfiguration(EGLDisplay display) { EGLint attributes[] = { // clang-format off EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, EGL_SURFACE_TYPE, EGL_WINDOW_BIT, - EGL_RED_SIZE, config.red_bits, - EGL_GREEN_SIZE, config.green_bits, - EGL_BLUE_SIZE, config.blue_bits, - EGL_ALPHA_SIZE, config.alpha_bits, - EGL_DEPTH_SIZE, config.depth_bits, - EGL_STENCIL_SIZE, config.stencil_bits, + EGL_RED_SIZE, 8, + EGL_GREEN_SIZE, 8, + EGL_BLUE_SIZE, 8, + EGL_ALPHA_SIZE, 8, + EGL_DEPTH_SIZE, 0, + EGL_STENCIL_SIZE, 0, EGL_NONE, // termination sentinel // clang-format on }; @@ -142,7 +144,6 @@ bool AndroidContextGL::CreatePBufferSurface() { } AndroidContextGL::AndroidContextGL(fxl::RefPtr env, - PlatformView::SurfaceConfig config, const AndroidContextGL* share_context) : environment_(env), window_(nullptr), @@ -158,8 +159,7 @@ AndroidContextGL::AndroidContextGL(fxl::RefPtr env, // Choose a valid configuration. - std::tie(success, config_) = - ChooseEGLConfiguration(environment_->Display(), config); + std::tie(success, config_) = ChooseEGLConfiguration(environment_->Display()); if (!success) { FXL_LOG(ERROR) << "Could not choose an EGL configuration."; diff --git a/shell/platform/android/android_context_gl.h b/shell/platform/android/android_context_gl.h index 207f621c66bd1..b29851b34564d 100644 --- a/shell/platform/android/android_context_gl.h +++ b/shell/platform/android/android_context_gl.h @@ -44,7 +44,6 @@ class AndroidContextGL : public fxl::RefCountedThreadSafe { bool valid_; AndroidContextGL(fxl::RefPtr env, - PlatformView::SurfaceConfig config, const AndroidContextGL* share_context = nullptr); ~AndroidContextGL(); diff --git a/shell/platform/android/android_external_texture_gl.cc b/shell/platform/android/android_external_texture_gl.cc index 02a40c22474a1..7e504cab30dcc 100644 --- a/shell/platform/android/android_external_texture_gl.cc +++ b/shell/platform/android/android_external_texture_gl.cc @@ -5,7 +5,7 @@ #include "flutter/shell/platform/android/android_external_texture_gl.h" #include -#include "flutter/common/threads.h" + #include "flutter/shell/platform/android/platform_view_android_jni.h" #include "third_party/skia/include/gpu/GrTexture.h" @@ -19,17 +19,14 @@ AndroidExternalTextureGL::AndroidExternalTextureGL( AndroidExternalTextureGL::~AndroidExternalTextureGL() = default; void AndroidExternalTextureGL::OnGrContextCreated() { - ASSERT_IS_GPU_THREAD; state_ = AttachmentState::uninitialized; } void AndroidExternalTextureGL::MarkNewFrameAvailable() { - ASSERT_IS_GPU_THREAD; new_frame_ready_ = true; } void AndroidExternalTextureGL::Paint(SkCanvas& canvas, const SkRect& bounds) { - ASSERT_IS_GPU_THREAD; if (state_ == AttachmentState::detached) { return; } @@ -82,7 +79,6 @@ void AndroidExternalTextureGL::UpdateTransform() { } void AndroidExternalTextureGL::OnGrContextDestroyed() { - ASSERT_IS_GPU_THREAD; if (state_ == AttachmentState::attached) { Detach(); } diff --git a/shell/platform/android/android_shell_holder.cc b/shell/platform/android/android_shell_holder.cc new file mode 100644 index 0000000000000..01bb31eb9c13a --- /dev/null +++ b/shell/platform/android/android_shell_holder.cc @@ -0,0 +1,173 @@ +// Copyright 2018 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. + +#define FML_USED_ON_EMBEDDER + +#include "flutter/shell/platform/android/android_shell_holder.h" + +#include +#include + +#include +#include + +#include "flutter/fml/message_loop.h" +#include "flutter/shell/common/rasterizer.h" +#include "flutter/shell/platform/android/platform_view_android.h" +#include "lib/fxl/functional/make_copyable.h" + +namespace shell { + +static std::string CreateAndroidShellLabel(intptr_t handle) { + std::stringstream stream; + stream << "io.flutter.0x" << std::hex << handle; + return stream.str(); +} + +AndroidShellHolder::AndroidShellHolder( + blink::Settings settings, + fml::jni::JavaObjectWeakGlobalRef java_object) + : settings_(std::move(settings)), java_object_(java_object) { + auto thread_label = CreateAndroidShellLabel(reinterpret_cast(this)); + + thread_host_ = {thread_label, ThreadHost::Type::UI | ThreadHost::Type::GPU | + ThreadHost::Type::IO}; + + fml::WeakPtr weak_platform_view; + Shell::CreateCallback on_create_platform_view = + [java_object, &weak_platform_view](Shell& shell) { + auto platform_view_android = std::make_unique( + shell, // delegate + shell.GetTaskRunners(), // task runners + java_object, // java object handle for JNI interop + shell.GetSettings() + .enable_software_rendering // use software rendering + ); + weak_platform_view = platform_view_android->GetWeakPtr(); + return platform_view_android; + }; + + Shell::CreateCallback on_create_rasterizer = [](Shell& shell) { + return std::make_unique(shell.GetTaskRunners()); + }; + + // The current thread will be used as the platform thread. Ensure that the + // message loop is initialized. + fml::MessageLoop::EnsureInitializedForCurrentThread(); + + blink::TaskRunners task_runners( + thread_label, // label + fml::MessageLoop::GetCurrent().GetTaskRunner(), // platform + thread_host_.gpu_thread->GetTaskRunner(), // gpu + thread_host_.ui_thread->GetTaskRunner(), // ui + thread_host_.io_thread->GetTaskRunner() // io + ); + + shell_ = + Shell::Create(task_runners, // task runners + settings_, // settings + on_create_platform_view, // platform view create callback + on_create_rasterizer // rasterizer create callback + ); + + platform_view_ = weak_platform_view; + FXL_DCHECK(platform_view_); + + is_valid_ = shell_ != nullptr; + + if (is_valid_) { + task_runners.GetGPUTaskRunner()->PostTask( + []() { ::setpriority(PRIO_PROCESS, gettid(), -2); }); + task_runners.GetUITaskRunner()->PostTask( + []() { ::setpriority(PRIO_PROCESS, gettid(), -1); }); + } +} + +AndroidShellHolder::~AndroidShellHolder() = default; + +bool AndroidShellHolder::IsValid() const { + return is_valid_; +} + +const blink::Settings& AndroidShellHolder::GetSettings() const { + return settings_; +} + +void AndroidShellHolder::Launch(RunConfiguration config) { + if (!IsValid()) { + return; + } + + shell_->GetTaskRunners().GetUITaskRunner()->PostTask( + fxl::MakeCopyable([engine = shell_->GetEngine(), // + config = std::move(config) // + ]() mutable { + if (engine) { + if (!engine->Run(std::move(config))) { + FXL_LOG(ERROR) << "Could not launch engine in configuration."; + } + } + })); +} + +void AndroidShellHolder::SetViewportMetrics( + const blink::ViewportMetrics& metrics) { + if (!IsValid()) { + return; + } + + shell_->GetTaskRunners().GetUITaskRunner()->PostTask( + [engine = shell_->GetEngine(), metrics]() { + if (engine) { + engine->SetViewportMetrics(metrics); + } + }); +} + +void AndroidShellHolder::DispatchPointerDataPacket( + std::unique_ptr packet) { + if (!IsValid()) { + return; + } + + shell_->GetTaskRunners().GetUITaskRunner()->PostTask(fxl::MakeCopyable( + [engine = shell_->GetEngine(), packet = std::move(packet)] { + if (engine) { + engine->DispatchPointerDataPacket(*packet); + } + })); +} + +Rasterizer::Screenshot AndroidShellHolder::Screenshot( + Rasterizer::ScreenshotType type, + bool base64_encode) { + if (!IsValid()) { + return {nullptr, SkISize::MakeEmpty()}; + } + return shell_->Screenshot(type, base64_encode); +} + +fml::WeakPtr AndroidShellHolder::GetPlatformView() { + FXL_DCHECK(platform_view_); + return platform_view_; +} + +void AndroidShellHolder::UpdateAssetManager( + fxl::RefPtr asset_manager) { + if (!IsValid() || !asset_manager) { + return; + } + + shell_->GetTaskRunners().GetUITaskRunner()->PostTask( + [engine = shell_->GetEngine(), + asset_manager = std::move(asset_manager)]() { + if (engine) { + if (!engine->UpdateAssetManager(std::move(asset_manager))) { + FXL_DLOG(ERROR) << "Could not update asset asset manager."; + } + } + }); +} + +} // namespace shell diff --git a/shell/platform/android/android_shell_holder.h b/shell/platform/android/android_shell_holder.h new file mode 100644 index 0000000000000..e0905f0f9e42e --- /dev/null +++ b/shell/platform/android/android_shell_holder.h @@ -0,0 +1,59 @@ +// Copyright 2018 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. + +#ifndef FLUTTER_SHELL_PLATFORM_ANDROID_ANDROID_SHELL_HOLDER_H_ +#define FLUTTER_SHELL_PLATFORM_ANDROID_ANDROID_SHELL_HOLDER_H_ + +#include + +#include "flutter/fml/platform/android/jni_weak_ref.h" +#include "flutter/lib/ui/window/viewport_metrics.h" +#include "flutter/shell/common/run_configuration.h" +#include "flutter/shell/common/shell.h" +#include "flutter/shell/common/thread_host.h" +#include "flutter/shell/platform/android/platform_view_android.h" +#include "lib/fxl/files/unique_fd.h" +#include "lib/fxl/macros.h" + +namespace shell { + +class AndroidShellHolder { + public: + AndroidShellHolder(blink::Settings settings, + fml::jni::JavaObjectWeakGlobalRef java_object); + + ~AndroidShellHolder(); + + bool IsValid() const; + + void Launch(RunConfiguration configuration); + + void SetViewportMetrics(const blink::ViewportMetrics& metrics); + + void DispatchPointerDataPacket( + std::unique_ptr packet); + + const blink::Settings& GetSettings() const; + + fml::WeakPtr GetPlatformView(); + + Rasterizer::Screenshot Screenshot(Rasterizer::ScreenshotType type, + bool base64_encode); + + void UpdateAssetManager(fxl::RefPtr asset_manager); + + private: + const blink::Settings settings_; + const fml::jni::JavaObjectWeakGlobalRef java_object_; + fml::WeakPtr platform_view_; + ThreadHost thread_host_; + std::unique_ptr shell_; + bool is_valid_ = false; + + FXL_DISALLOW_COPY_AND_ASSIGN(AndroidShellHolder); +}; + +} // namespace shell + +#endif // FLUTTER_SHELL_PLATFORM_ANDROID_ANDROID_SHELL_HOLDER_H_ diff --git a/shell/platform/android/android_surface.cc b/shell/platform/android/android_surface.cc index 4dd08609da370..a8b41bacbf7c5 100644 --- a/shell/platform/android/android_surface.cc +++ b/shell/platform/android/android_surface.cc @@ -4,8 +4,32 @@ #include "flutter/shell/platform/android/android_surface.h" +#include + +#include "flutter/shell/platform/android/android_surface_gl.h" +#include "flutter/shell/platform/android/android_surface_software.h" +#if SHELL_ENABLE_VULKAN +#include "flutter/shell/platform/android/android_surface_vulkan.h" +#endif // SHELL_ENABLE_VULKAN + namespace shell { +std::unique_ptr AndroidSurface::Create( + bool use_software_rendering) { + if (use_software_rendering) { + auto software_surface = std::make_unique(); + return software_surface->IsValid() ? std::move(software_surface) : nullptr; + } +#if SHELL_ENABLE_VULKAN + auto vulkan_surface = std::make_unique(); + return vulkan_surface->IsValid() ? std::move(vulkan_surface) : nullptr; +#else // SHELL_ENABLE_VULKAN + auto gl_surface = std::make_unique(); + return gl_surface->IsOffscreenContextValid() ? std::move(gl_surface) + : nullptr; +#endif // SHELL_ENABLE_VULKAN +} + AndroidSurface::~AndroidSurface() = default; } // namespace shell diff --git a/shell/platform/android/android_surface.h b/shell/platform/android/android_surface.h index 2425a61e20707..858b07eb45d9f 100644 --- a/shell/platform/android/android_surface.h +++ b/shell/platform/android/android_surface.h @@ -19,6 +19,8 @@ namespace shell { class AndroidSurface { public: + static std::unique_ptr Create(bool use_software_rendering); + virtual ~AndroidSurface(); virtual bool IsValid() const = 0; @@ -27,14 +29,11 @@ class AndroidSurface { virtual std::unique_ptr CreateGPUSurface() = 0; - virtual SkISize OnScreenSurfaceSize() const = 0; - virtual bool OnScreenSurfaceResize(const SkISize& size) const = 0; virtual bool ResourceContextMakeCurrent() = 0; - virtual bool SetNativeWindow(fxl::RefPtr window, - PlatformView::SurfaceConfig config = {}) = 0; + virtual bool SetNativeWindow(fxl::RefPtr window) = 0; }; } // namespace shell diff --git a/shell/platform/android/android_surface_gl.cc b/shell/platform/android/android_surface_gl.cc index f30ac1272b043..274b652a97b74 100644 --- a/shell/platform/android/android_surface_gl.cc +++ b/shell/platform/android/android_surface_gl.cc @@ -6,14 +6,12 @@ #include -#include "flutter/common/threads.h" #include "lib/fxl/logging.h" #include "lib/fxl/memory/ref_ptr.h" namespace shell { -static fxl::RefPtr GlobalResourceLoadingContext( - PlatformView::SurfaceConfig offscreen_config) { +static fxl::RefPtr GlobalResourceLoadingContext() { // AndroidSurfaceGL instances are only ever created on the platform thread. So // there is no need to lock here. @@ -29,11 +27,7 @@ static fxl::RefPtr GlobalResourceLoadingContext( return nullptr; } - // TODO(chinmaygarde): We should check that the configurations are stable - // across multiple invocations. - - auto context = - fxl::MakeRefCounted(environment, offscreen_config); + auto context = fxl::MakeRefCounted(environment); if (!context->IsValid()) { return nullptr; @@ -43,10 +37,9 @@ static fxl::RefPtr GlobalResourceLoadingContext( return global_context; } -AndroidSurfaceGL::AndroidSurfaceGL( - PlatformView::SurfaceConfig offscreen_config) { +AndroidSurfaceGL::AndroidSurfaceGL() { // Acquire the offscreen context. - offscreen_context_ = GlobalResourceLoadingContext(offscreen_config); + offscreen_context_ = GlobalResourceLoadingContext(); if (!offscreen_context_ || !offscreen_context_->IsValid()) { offscreen_context_ = nullptr; @@ -60,14 +53,9 @@ bool AndroidSurfaceGL::IsOffscreenContextValid() const { } void AndroidSurfaceGL::TeardownOnScreenContext() { - fxl::AutoResetWaitableEvent latch; - blink::Threads::Gpu()->PostTask([this, &latch]() { - if (IsValid()) { - GLContextClearCurrent(); - } - latch.Signal(); - }); - latch.Wait(); + if (onscreen_context_) { + onscreen_context_->ClearCurrent(); + } onscreen_context_ = nullptr; } @@ -84,11 +72,6 @@ std::unique_ptr AndroidSurfaceGL::CreateGPUSurface() { return surface->IsValid() ? std::move(surface) : nullptr; } -SkISize AndroidSurfaceGL::OnScreenSurfaceSize() const { - FXL_DCHECK(onscreen_context_ && onscreen_context_->IsValid()); - return onscreen_context_->GetSize(); -} - bool AndroidSurfaceGL::OnScreenSurfaceResize(const SkISize& size) const { FXL_DCHECK(onscreen_context_ && onscreen_context_->IsValid()); return onscreen_context_->Resize(size); @@ -99,8 +82,8 @@ bool AndroidSurfaceGL::ResourceContextMakeCurrent() { return offscreen_context_->MakeCurrent(); } -bool AndroidSurfaceGL::SetNativeWindow(fxl::RefPtr window, - PlatformView::SurfaceConfig config) { +bool AndroidSurfaceGL::SetNativeWindow( + fxl::RefPtr window) { // In any case, we want to get rid of our current onscreen context. onscreen_context_ = nullptr; @@ -112,7 +95,7 @@ bool AndroidSurfaceGL::SetNativeWindow(fxl::RefPtr window, // Create the onscreen context. onscreen_context_ = fxl::MakeRefCounted( - offscreen_context_->Environment(), config, + offscreen_context_->Environment(), offscreen_context_.get() /* sharegroup */); if (!onscreen_context_->IsValid()) { diff --git a/shell/platform/android/android_surface_gl.h b/shell/platform/android/android_surface_gl.h index 08aee081c8266..e26f2bf50d3a9 100644 --- a/shell/platform/android/android_surface_gl.h +++ b/shell/platform/android/android_surface_gl.h @@ -16,35 +16,43 @@ namespace shell { -class AndroidSurfaceGL : public GPUSurfaceGLDelegate, public AndroidSurface { +class AndroidSurfaceGL final : public GPUSurfaceGLDelegate, + public AndroidSurface { public: - explicit AndroidSurfaceGL(PlatformView::SurfaceConfig offscreen_config); + AndroidSurfaceGL(); ~AndroidSurfaceGL() override; - bool IsValid() const override; - bool IsOffscreenContextValid() const; + // |shell::AndroidSurface| + bool IsValid() const override; + + // |shell::AndroidSurface| std::unique_ptr CreateGPUSurface() override; + // |shell::AndroidSurface| void TeardownOnScreenContext() override; - SkISize OnScreenSurfaceSize() const override; - + // |shell::AndroidSurface| bool OnScreenSurfaceResize(const SkISize& size) const override; + // |shell::AndroidSurface| bool ResourceContextMakeCurrent() override; - bool SetNativeWindow(fxl::RefPtr window, - PlatformView::SurfaceConfig config) override; + // |shell::AndroidSurface| + bool SetNativeWindow(fxl::RefPtr window) override; + // |shell::GPUSurfaceGLDelegate| bool GLContextMakeCurrent() override; + // |shell::GPUSurfaceGLDelegate| bool GLContextClearCurrent() override; + // |shell::GPUSurfaceGLDelegate| bool GLContextPresent() override; + // |shell::GPUSurfaceGLDelegate| intptr_t GLContextFBO() const override; private: diff --git a/shell/platform/android/android_surface_software.cc b/shell/platform/android/android_surface_software.cc index 68bf99b27ba56..4b1378802d9e4 100644 --- a/shell/platform/android/android_surface_software.cc +++ b/shell/platform/android/android_surface_software.cc @@ -3,15 +3,14 @@ // found in the LICENSE file. #include "flutter/shell/platform/android/android_surface_software.h" -#include "flutter/common/threads.h" -#include "flutter/fml/platform/android/jni_weak_ref.h" -#include "flutter/fml/platform/android/scoped_java_ref.h" -#include "flutter/shell/platform/android/platform_view_android_jni.h" #include #include +#include "flutter/fml/platform/android/jni_weak_ref.h" +#include "flutter/fml/platform/android/scoped_java_ref.h" #include "flutter/fml/trace_event.h" +#include "flutter/shell/platform/android/platform_view_android_jni.h" #include "lib/fxl/logging.h" namespace shell { @@ -132,17 +131,12 @@ bool AndroidSurfaceSoftware::PresentBackingStore( void AndroidSurfaceSoftware::TeardownOnScreenContext() {} -SkISize AndroidSurfaceSoftware::OnScreenSurfaceSize() const { - return SkISize(); -} - bool AndroidSurfaceSoftware::OnScreenSurfaceResize(const SkISize& size) const { return true; } bool AndroidSurfaceSoftware::SetNativeWindow( - fxl::RefPtr window, - PlatformView::SurfaceConfig config) { + fxl::RefPtr window) { native_window_ = std::move(window); if (!(native_window_ && native_window_->IsValid())) return false; diff --git a/shell/platform/android/android_surface_software.h b/shell/platform/android/android_surface_software.h index 76184b707cf74..0f82fc5dfe154 100644 --- a/shell/platform/android/android_surface_software.h +++ b/shell/platform/android/android_surface_software.h @@ -13,35 +13,39 @@ namespace shell { -class AndroidSurfaceSoftware : public AndroidSurface, - public GPUSurfaceSoftwareDelegate { +class AndroidSurfaceSoftware final : public AndroidSurface, + public GPUSurfaceSoftwareDelegate { public: AndroidSurfaceSoftware(); ~AndroidSurfaceSoftware() override; + // |shell::AndroidSurface| bool IsValid() const override; + // |shell::AndroidSurface| bool ResourceContextMakeCurrent() override; + // |shell::AndroidSurface| std::unique_ptr CreateGPUSurface() override; - sk_sp AcquireBackingStore(const SkISize& size) override; - - bool PresentBackingStore(sk_sp backing_store) override; - + // |shell::AndroidSurface| void TeardownOnScreenContext() override; - SkISize OnScreenSurfaceSize() const override; - + // |shell::AndroidSurface| bool OnScreenSurfaceResize(const SkISize& size) const override; - bool SetNativeWindow(fxl::RefPtr window, - PlatformView::SurfaceConfig config) override; + // |shell::AndroidSurface| + bool SetNativeWindow(fxl::RefPtr window) override; + + // |shell::GPUSurfaceSoftwareDelegate| + sk_sp AcquireBackingStore(const SkISize& size) override; + + // |shell::GPUSurfaceSoftwareDelegate| + bool PresentBackingStore(sk_sp backing_store) override; private: sk_sp sk_surface_; - fxl::RefPtr native_window_; SkColorType target_color_type_; SkAlphaType target_alpha_type_; diff --git a/shell/platform/android/android_surface_vulkan.cc b/shell/platform/android/android_surface_vulkan.cc index ccacf538b8781..e8817690188c2 100644 --- a/shell/platform/android/android_surface_vulkan.cc +++ b/shell/platform/android/android_surface_vulkan.cc @@ -21,10 +21,12 @@ bool AndroidSurfaceVulkan::IsValid() const { return proc_table_->HasAcquiredMandatoryProcAddresses(); } +// |shell::AndroidSurface| void AndroidSurfaceVulkan::TeardownOnScreenContext() { - // + // Nothing to do. } +// |shell::AndroidSurface| std::unique_ptr AndroidSurfaceVulkan::CreateGPUSurface() { if (!IsValid()) { return nullptr; @@ -52,21 +54,20 @@ std::unique_ptr AndroidSurfaceVulkan::CreateGPUSurface() { return gpu_surface; } -SkISize AndroidSurfaceVulkan::OnScreenSurfaceSize() const { - return native_window_ ? native_window_->GetSize() : SkISize::Make(0, 0); -} - +// |shell::AndroidSurface| bool AndroidSurfaceVulkan::OnScreenSurfaceResize(const SkISize& size) const { return true; } +// |shell::AndroidSurface| bool AndroidSurfaceVulkan::ResourceContextMakeCurrent() { + FXL_DLOG(ERROR) << "The vulkan backend does not support resource contexts."; return false; } +// |shell::AndroidSurface| bool AndroidSurfaceVulkan::SetNativeWindow( - fxl::RefPtr window, - PlatformView::SurfaceConfig config) { + fxl::RefPtr window) { native_window_ = std::move(window); return native_window_ && native_window_->IsValid(); } diff --git a/shell/platform/android/android_surface_vulkan.h b/shell/platform/android/android_surface_vulkan.h index f1ecf610c8584..fd3f493d49790 100644 --- a/shell/platform/android/android_surface_vulkan.h +++ b/shell/platform/android/android_surface_vulkan.h @@ -20,20 +20,23 @@ class AndroidSurfaceVulkan : public AndroidSurface { ~AndroidSurfaceVulkan() override; + // |shell::AndroidSurface| bool IsValid() const override; - void TeardownOnScreenContext() override; - + // |shell::AndroidSurface| std::unique_ptr CreateGPUSurface() override; - SkISize OnScreenSurfaceSize() const override; + // |shell::AndroidSurface| + void TeardownOnScreenContext() override; + // |shell::AndroidSurface| bool OnScreenSurfaceResize(const SkISize& size) const override; + // |shell::AndroidSurface| bool ResourceContextMakeCurrent() override; - bool SetNativeWindow(fxl::RefPtr window, - PlatformView::SurfaceConfig config) override; + // |shell::AndroidSurface| + bool SetNativeWindow(fxl::RefPtr window) override; private: fxl::RefPtr proc_table_; diff --git a/shell/platform/android/apk_asset_provider.cc b/shell/platform/android/apk_asset_provider.cc index fdd4910a12f4c..4cf6da16dc64f 100644 --- a/shell/platform/android/apk_asset_provider.cc +++ b/shell/platform/android/apk_asset_provider.cc @@ -7,32 +7,41 @@ namespace blink { +APKAssetProvider::APKAssetProvider(JNIEnv* env, + jobject jassetManager, + std::string directory) + : directory_(std::move(directory)) { + assetManager_ = AAssetManager_fromJava(env, jassetManager); +} + +APKAssetProvider::~APKAssetProvider() = default; + +bool APKAssetProvider::IsValid() const { + return true; +} + bool APKAssetProvider::GetAsBuffer(const std::string& asset_name, - std::vector* data) { + std::vector* data) const { std::stringstream ss; ss << directory_.c_str() << "/" << asset_name; - AAsset* asset = AAssetManager_open(assetManager_, ss.str().c_str(), AASSET_MODE_BUFFER); + AAsset* asset = + AAssetManager_open(assetManager_, ss.str().c_str(), AASSET_MODE_BUFFER); if (!asset) { - return false; + return false; } uint8_t* buffer = (uint8_t*)AAsset_getBuffer(asset); if (!buffer) { FXL_LOG(ERROR) << "Got null trying to acquire buffer for asset:" << asset; + AAsset_close(asset); return false; } data->resize(AAsset_getLength(asset)); std::copy(buffer, buffer + data->size(), data->begin()); + AAsset_close(asset); return true; } -APKAssetProvider::~APKAssetProvider() {} - -APKAssetProvider::APKAssetProvider(JNIEnv* env, jobject jassetManager, std::string directory) - : directory_(std::move(directory)) { - assetManager_ = AAssetManager_fromJava(env, jassetManager); -} - } // namespace blink diff --git a/shell/platform/android/apk_asset_provider.h b/shell/platform/android/apk_asset_provider.h index c40b1ed2a1866..70ddbe454bc61 100644 --- a/shell/platform/android/apk_asset_provider.h +++ b/shell/platform/android/apk_asset_provider.h @@ -5,28 +5,35 @@ #ifndef FLUTTER_ASSETS_APK_ASSET_PROVIDER_H_ #define FLUTTER_ASSETS_APK_ASSET_PROVIDER_H_ -#include #include +#include -#include "flutter/assets/asset_provider.h" +#include "flutter/assets/asset_resolver.h" #include "lib/fxl/memory/ref_counted.h" namespace blink { -class APKAssetProvider - : public AssetProvider { +class APKAssetProvider final : public AssetResolver { public: - explicit APKAssetProvider(JNIEnv* env, jobject assetManager, std::string directory); + explicit APKAssetProvider(JNIEnv* env, + jobject assetManager, + std::string directory); virtual ~APKAssetProvider(); + private: + AAssetManager* assetManager_; + const std::string directory_; + + // |blink::AssetResolver| + bool IsValid() const override; + + // |blink::AssetResolver| virtual bool GetAsBuffer(const std::string& asset_name, - std::vector* data); + std::vector* data) const override; - private: - AAssetManager* assetManager_; - const std::string directory_; + FXL_DISALLOW_COPY_AND_ASSIGN(APKAssetProvider); }; } // namespace blink -#endif // FLUTTER_ASSETS_APK_ASSET_PROVIDER_H \ No newline at end of file +#endif // FLUTTER_ASSETS_APK_ASSET_PROVIDER_H \ No newline at end of file diff --git a/shell/platform/android/flutter_main.cc b/shell/platform/android/flutter_main.cc index 6df346a128722..06fd8a82bbddf 100644 --- a/shell/platform/android/flutter_main.cc +++ b/shell/platform/android/flutter_main.cc @@ -2,38 +2,85 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#define FML_USED_ON_EMBEDDER + #include "flutter/shell/platform/android/flutter_main.h" #include +#include "flutter/fml/message_loop.h" +#include "flutter/fml/paths.h" #include "flutter/fml/platform/android/jni_util.h" +#include "flutter/runtime/dart_vm.h" #include "flutter/runtime/start_up.h" #include "flutter/shell/common/shell.h" +#include "flutter/shell/common/switches.h" #include "lib/fxl/arraysize.h" #include "lib/fxl/command_line.h" +#include "lib/fxl/files/file.h" #include "lib/fxl/macros.h" #include "third_party/dart/runtime/include/dart_tools_api.h" namespace shell { -static void Init(JNIEnv* env, - jclass clazz, - jobject context, - jobjectArray jargs, - jstring bundlePath) { - // Prepare command line arguments and initialize the shell. +FlutterMain::FlutterMain(blink::Settings settings) + : settings_(std::move(settings)) {} + +FlutterMain::~FlutterMain() = default; + +static std::unique_ptr g_flutter_main; + +FlutterMain& FlutterMain::Get() { + FXL_CHECK(g_flutter_main) << "ensureInitializationComplete must have already " + "been called."; + return *g_flutter_main; +} + +const blink::Settings& FlutterMain::GetSettings() const { + return settings_; +} + +void FlutterMain::Init(JNIEnv* env, + jclass clazz, + jobject context, + jobjectArray jargs, + jstring bundlePath) { std::vector args; - args.push_back("flutter_tester"); + args.push_back("flutter"); for (auto& arg : fml::jni::StringArrayToVector(env, jargs)) { args.push_back(std::move(arg)); } - auto command_line = fxl::CommandLineFromIterators(args.begin(), args.end()); - std::string icu_data_path = - command_line.GetOptionValueWithDefault("icu-data-file-path", ""); - Shell::InitStandalone(std::move(command_line), std::move(icu_data_path), - /* application_library_path= */ "", - fml::jni::JavaStringToString(env, bundlePath)); + + auto settings = SettingsFromCommandLine(command_line); + + settings.assets_path = fml::jni::JavaStringToString(env, bundlePath); + + if (!blink::DartVM::IsRunningPrecompiledCode()) { + // Check to see if the appropriate kernel files are present and configure + // settings accordingly. + auto platform_kernel_path = + fml::paths::JoinPaths({settings.assets_path, "platform.dill"}); + auto application_kernel_path = + fml::paths::JoinPaths({settings.assets_path, "kernel_blob.bin"}); + + if (files::IsFile(platform_kernel_path) && + files::IsFile(application_kernel_path)) { + settings.kernel_snapshot_path = platform_kernel_path; + settings.application_kernel_path = application_kernel_path; + } + } + + settings.task_observer_add = [](intptr_t key, fxl::Closure callback) { + fml::MessageLoop::GetCurrent().AddTaskObserver(key, std::move(callback)); + }; + + settings.task_observer_remove = [](intptr_t key) { + fml::MessageLoop::GetCurrent().RemoveTaskObserver(key); + }; + // Not thread safe. Will be removed when FlutterMain is refactored to no + // longer be a singleton. + g_flutter_main.reset(new FlutterMain(std::move(settings))); } static void RecordStartTimestamp(JNIEnv* env, @@ -44,7 +91,7 @@ static void RecordStartTimestamp(JNIEnv* env, blink::engine_main_enter_ts = Dart_TimelineGetMicros() - initTimeMicros; } -bool RegisterFlutterMain(JNIEnv* env) { +bool FlutterMain::Register(JNIEnv* env) { static const JNINativeMethod methods[] = { { .name = "nativeInit", diff --git a/shell/platform/android/flutter_main.h b/shell/platform/android/flutter_main.h index f4f65c499f966..6c8717e9cebdb 100644 --- a/shell/platform/android/flutter_main.h +++ b/shell/platform/android/flutter_main.h @@ -7,9 +7,34 @@ #include +#include "flutter/common/settings.h" +#include "lib/fxl/macros.h" + namespace shell { -bool RegisterFlutterMain(JNIEnv* env); +class FlutterMain { + public: + ~FlutterMain(); + + static bool Register(JNIEnv* env); + + static FlutterMain& Get(); + + const blink::Settings& GetSettings() const; + + private: + const blink::Settings settings_; + + FlutterMain(blink::Settings settings); + + static void Init(JNIEnv* env, + jclass clazz, + jobject context, + jobjectArray jargs, + jstring bundlePath); + + FXL_DISALLOW_COPY_AND_ASSIGN(FlutterMain); +}; } // namespace shell diff --git a/shell/platform/android/library_loader.cc b/shell/platform/android/library_loader.cc index c51d0114282f6..569825ce33e21 100644 --- a/shell/platform/android/library_loader.cc +++ b/shell/platform/android/library_loader.cc @@ -16,7 +16,7 @@ JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) { bool result = false; // Register FlutterMain. - result = shell::RegisterFlutterMain(env); + result = shell::FlutterMain::Register(env); FXL_CHECK(result); // Register PlatformView diff --git a/shell/platform/android/platform_message_response_android.cc b/shell/platform/android/platform_message_response_android.cc new file mode 100644 index 0000000000000..214080ec5bde3 --- /dev/null +++ b/shell/platform/android/platform_message_response_android.cc @@ -0,0 +1,62 @@ +// Copyright 2018 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. + +#include "flutter/shell/platform/android/platform_message_response_android.h" + +#include "flutter/shell/platform/android/platform_view_android_jni.h" +#include "lib/fxl/functional/make_copyable.h" + +namespace shell { + +PlatformMessageResponseAndroid::PlatformMessageResponseAndroid( + int response_id, + fml::jni::JavaObjectWeakGlobalRef weak_java_object, + fxl::RefPtr platform_task_runner) + : response_id_(response_id), + weak_java_object_(weak_java_object), + platform_task_runner_(std::move(platform_task_runner)) {} + +// |blink::PlatformMessageResponse| +void PlatformMessageResponseAndroid::Complete(std::vector data) { + platform_task_runner_->PostTask( + fxl::MakeCopyable([response = response_id_, // + weak_java_object = weak_java_object_, // + data = std::move(data) // + ]() { + // We are on the platform thread. Attempt to get the strong reference to + // the Java object. + auto env = fml::jni::AttachCurrentThread(); + auto java_object = weak_java_object.get(env); + + if (java_object.is_null()) { + // The Java object was collected before this message response got to + // it. Drop the response on the floor. + return; + } + + if (data.size() == 0) { + // If the data is empty, there is no reason to create a Java byte + // array. Make the response now with a nullptr now. + FlutterViewHandlePlatformMessageResponse(env, java_object.obj(), + response, nullptr); + } + + // Convert the vector to a Java byte array. + fml::jni::ScopedJavaLocalRef data_array( + env, env->NewByteArray(data.size())); + env->SetByteArrayRegion(data_array.obj(), 0, data.size(), + reinterpret_cast(data.data())); + + // Make the response call into Java. + FlutterViewHandlePlatformMessageResponse(env, java_object.obj(), + response, data_array.obj()); + })); +} + +// |blink::PlatformMessageResponse| +void PlatformMessageResponseAndroid::CompleteEmpty() { + Complete(std::vector{}); +} + +} // namespace shell diff --git a/shell/platform/android/platform_message_response_android.h b/shell/platform/android/platform_message_response_android.h new file mode 100644 index 0000000000000..2ee7f3336ac6d --- /dev/null +++ b/shell/platform/android/platform_message_response_android.h @@ -0,0 +1,39 @@ +// Copyright 2018 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. + +#ifndef FLUTTER_SHELL_PLATFORM_ANDROID_PLATFORM_MESSAGE_RESPONSE_ANDROID_H_ +#define FLUTTER_SHELL_PLATFORM_ANDROID_PLATFORM_MESSAGE_RESPONSE_ANDROID_H_ + +#include "flutter/fml/platform/android/jni_weak_ref.h" +#include "flutter/fml/task_runner.h" +#include "flutter/lib/ui/window/platform_message_response.h" +#include "lib/fxl/macros.h" + +namespace shell { + +class PlatformMessageResponseAndroid : public blink::PlatformMessageResponse { + public: + // |blink::PlatformMessageResponse| + void Complete(std::vector data) override; + + // |blink::PlatformMessageResponse| + void CompleteEmpty() override; + + private: + PlatformMessageResponseAndroid( + int response_id, + fml::jni::JavaObjectWeakGlobalRef weak_java_object, + fxl::RefPtr platform_task_runner); + + int response_id_; + fml::jni::JavaObjectWeakGlobalRef weak_java_object_; + fxl::RefPtr platform_task_runner_; + + FRIEND_MAKE_REF_COUNTED(PlatformMessageResponseAndroid); + FXL_DISALLOW_COPY_AND_ASSIGN(PlatformMessageResponseAndroid); +}; + +} // namespace shell + +#endif // FLUTTER_SHELL_PLATFORM_ANDROID_PLATFORM_MESSAGE_RESPONSE_ANDROID_H_ diff --git a/shell/platform/android/platform_view_android.cc b/shell/platform/android/platform_view_android.cc index 931fc28575ea3..fd68c91a2336b 100644 --- a/shell/platform/android/platform_view_android.cc +++ b/shell/platform/android/platform_view_android.cc @@ -4,288 +4,56 @@ #include "flutter/shell/platform/android/platform_view_android.h" -#include -#include -#include -#include - +#include #include -#include "flutter/common/settings.h" -#include "flutter/common/threads.h" -#include "flutter/fml/platform/android/jni_util.h" -#include "flutter/fml/platform/android/scoped_java_ref.h" -#include "flutter/runtime/dart_service_isolate.h" -#include "flutter/shell/common/null_rasterizer.h" -#include "flutter/shell/gpu/gpu_rasterizer.h" +#include "flutter/shell/common/io_manager.h" #include "flutter/shell/platform/android/android_external_texture_gl.h" #include "flutter/shell/platform/android/android_surface_gl.h" -#include "flutter/shell/platform/android/android_surface_software.h" -#include "flutter/shell/platform/android/apk_asset_provider.h" +#include "flutter/shell/platform/android/platform_message_response_android.h" #include "flutter/shell/platform/android/platform_view_android_jni.h" #include "flutter/shell/platform/android/vsync_waiter_android.h" -#include "lib/fxl/functional/make_copyable.h" - -#if SHELL_ENABLE_VULKAN -#include "flutter/shell/platform/android/android_surface_vulkan.h" -#endif // SHELL_ENABLE_VULKAN +#include "lib/fxl/synchronization/waitable_event.h" +#include "third_party/skia/include/gpu/gl/GrGLInterface.h" namespace shell { -class PlatformMessageResponseAndroid : public blink::PlatformMessageResponse { - FRIEND_MAKE_REF_COUNTED(PlatformMessageResponseAndroid); - - public: - void Complete(std::vector data) override { - fxl::RefPtr self(this); - blink::Threads::Platform()->PostTask( - fxl::MakeCopyable([ self, data = std::move(data) ]() mutable { - std::shared_ptr view = self->view_.lock(); - if (!view) - return; - static_cast(view.get()) - ->HandlePlatformMessageResponse(self->response_id_, - std::move(data)); - })); - } - - void CompleteEmpty() override { - fxl::RefPtr self(this); - blink::Threads::Platform()->PostTask(fxl::MakeCopyable([self]() mutable { - std::shared_ptr view = self->view_.lock(); - if (!view) - return; - static_cast(view.get()) - ->HandlePlatformMessageEmptyResponse(self->response_id_); - })); - } - - private: - PlatformMessageResponseAndroid(int response_id, - std::weak_ptr view) - : response_id_(response_id), view_(view) {} - - int response_id_; - std::weak_ptr view_; -}; - -static std::unique_ptr InitializePlatformSurfaceGL() { - const PlatformView::SurfaceConfig offscreen_config = { - .red_bits = 8, - .green_bits = 8, - .blue_bits = 8, - .alpha_bits = 8, - .depth_bits = 0, - .stencil_bits = 0, - }; - auto surface = std::make_unique(offscreen_config); - return surface->IsOffscreenContextValid() ? std::move(surface) : nullptr; -} - -static std::unique_ptr InitializePlatformSurfaceVulkan() { -#if SHELL_ENABLE_VULKAN - auto surface = std::make_unique(); - return surface->IsValid() ? std::move(surface) : nullptr; -#else // SHELL_ENABLE_VULKAN - return nullptr; -#endif // SHELL_ENABLE_VULKAN +PlatformViewAndroid::PlatformViewAndroid( + PlatformView::Delegate& delegate, + blink::TaskRunners task_runners, + fml::jni::JavaObjectWeakGlobalRef java_object, + bool use_software_rendering) + : PlatformView(delegate, std::move(task_runners)), + java_object_(java_object), + android_surface_(AndroidSurface::Create(use_software_rendering)) { + FXL_CHECK(android_surface_) + << "Could not create an OpenGL, Vulkan or Software surface to setup " + "rendering."; } -static std::unique_ptr InitializePlatformSurfaceSoftware() { - auto surface = std::make_unique(); - return surface->IsValid() ? std::move(surface) : nullptr; -} - -static std::unique_ptr InitializePlatformSurface() { - if (blink::Settings::Get().enable_software_rendering) { - if (auto surface = InitializePlatformSurfaceSoftware()) { - FXL_DLOG(INFO) << "Software surface initialized."; - return surface; - } - } - - if (auto surface = InitializePlatformSurfaceVulkan()) { - FXL_DLOG(INFO) << "Vulkan surface initialized."; - return surface; - } - - FXL_DLOG(INFO) - << "Could not initialize Vulkan surface. Falling back to OpenGL."; - - if (auto surface = InitializePlatformSurfaceGL()) { - FXL_DLOG(INFO) << "GL surface initialized."; - return surface; - } - - if (auto surface = InitializePlatformSurfaceSoftware()) { - FXL_DLOG(INFO) << "Software surface initialized."; - return surface; - } - - FXL_CHECK(false) - << "Could not initialize either the Vulkan, OpenGL, or Software" - "surface backends. Flutter requires a GPU to render."; - return nullptr; -} - -PlatformViewAndroid::PlatformViewAndroid() - : PlatformView(std::make_unique()), - android_surface_(InitializePlatformSurface()) {} - PlatformViewAndroid::~PlatformViewAndroid() = default; -void PlatformViewAndroid::Attach() { - CreateEngine(); - - // Eagerly setup the IO thread context. We have already setup the surface. - SetupResourceContextOnIOThread(); - - UpdateThreadPriorities(); +void PlatformViewAndroid::NotifyCreated( + fxl::RefPtr native_window) { + InstallFirstFrameCallback(); + android_surface_->SetNativeWindow(native_window); + PlatformView::NotifyCreated(); } -void PlatformViewAndroid::Detach() { - ReleaseSurface(); -} - -void PlatformViewAndroid::SurfaceCreated(JNIEnv* env, - jobject jsurface, - jint backgroundColor) { - // Note: This frame ensures that any local references used by - // ANativeWindow_fromSurface are released immediately. This is needed as a - // workaround for https://code.google.com/p/android/issues/detail?id=68174 - fml::jni::ScopedJavaLocalFrame scoped_local_reference_frame(env); - - // We have a drawing surface, so swap in a non-Null rasterizer. - SetRasterizer(std::make_unique(nullptr)); - - rasterizer_->AddNextFrameCallback([this]() { - JNIEnv* env = fml::jni::AttachCurrentThread(); - fml::jni::ScopedJavaLocalRef view = flutter_view_.get(env); - if (!view.is_null()) { - FlutterViewOnFirstFrame(env, view.obj()); - } - }); - - auto native_window = fxl::MakeRefCounted( - ANativeWindow_fromSurface(env, jsurface)); - - if (!native_window->IsValid()) { - return; - } - - if (!android_surface_->SetNativeWindow(native_window)) { - return; - } - - std::unique_ptr gpu_surface = android_surface_->CreateGPUSurface(); - - if (gpu_surface == nullptr || !gpu_surface->IsValid()) { - return; - } - - NotifyCreated(std::move(gpu_surface), [ - this, backgroundColor, native_window_size = native_window->GetSize() - ] { rasterizer().Clear(backgroundColor, native_window_size); }); -} - -void PlatformViewAndroid::SurfaceChanged(jint width, jint height) { - blink::Threads::Gpu()->PostTask([this, width, height]() { - if (android_surface_) { - android_surface_->OnScreenSurfaceResize(SkISize::Make(width, height)); - } - }); -} - -void PlatformViewAndroid::UpdateThreadPriorities() { - blink::Threads::Gpu()->PostTask( - []() { ::setpriority(PRIO_PROCESS, gettid(), -2); }); - - blink::Threads::UI()->PostTask( - []() { ::setpriority(PRIO_PROCESS, gettid(), -1); }); -} - -void PlatformViewAndroid::SurfaceDestroyed() { - ReleaseSurface(); -} - -void PlatformViewAndroid::RunBundleAndSnapshot(JNIEnv* env, std::string bundle_path, - std::string snapshot_override, - std::string entrypoint, - bool reuse_runtime_controller, - jobject assetManager) { - // TODO(jsimmons): remove snapshot_override from the public FlutterView API - FXL_CHECK(snapshot_override.empty()) << "snapshot_override is obsolete"; - - // The flutter assets directory name is the last directory of the bundle_path - // and the path into the APK - size_t last_slash_idx = bundle_path.rfind("/", bundle_path.size()); - std::string flutter_assets_dir = bundle_path.substr( - last_slash_idx + 1, bundle_path.size() - last_slash_idx); - - fxl::RefPtr asset_provider = - fxl::MakeRefCounted(env, assetManager, - flutter_assets_dir); - blink::Threads::UI()->PostTask( - [engine = engine_->GetWeakPtr(), - asset_provider = std::move(asset_provider), - bundle_path = std::move(bundle_path), entrypoint = std::move(entrypoint), - reuse_runtime_controller = reuse_runtime_controller] { - if (engine) - engine->RunBundleWithAssets( - std::move(asset_provider), std::move(bundle_path), - std::move(entrypoint), reuse_runtime_controller); - }); -} - -void PlatformViewAndroid::RunBundleAndSource(std::string bundle_path, - std::string main, - std::string packages) { - blink::Threads::UI()->PostTask([ - engine = engine_->GetWeakPtr(), bundle_path = std::move(bundle_path), - main = std::move(main), packages = std::move(packages) - ] { - if (engine) - engine->RunBundleAndSource(std::move(bundle_path), std::move(main), - std::move(packages)); - }); +void PlatformViewAndroid::NotifyDestroyed() { + PlatformView::NotifyDestroyed(); + android_surface_->TeardownOnScreenContext(); } -void PlatformViewAndroid::SetAssetBundlePathOnUI(std::string bundle_path) { - blink::Threads::UI()->PostTask( - [ engine = engine_->GetWeakPtr(), bundle_path = std::move(bundle_path) ] { - if (engine) - engine->SetAssetBundlePath(std::move(bundle_path)); +void PlatformViewAndroid::NotifyChanged(const SkISize& size) { + fxl::AutoResetWaitableEvent latch; + fml::TaskRunner::RunNowOrPostTask( + task_runners_.GetGPUTaskRunner(), // + [&latch, surface = android_surface_.get(), size]() { + surface->OnScreenSurfaceResize(size); + latch.Signal(); }); -} - -void PlatformViewAndroid::SetViewportMetrics(jfloat device_pixel_ratio, - jint physical_width, - jint physical_height, - jint physical_padding_top, - jint physical_padding_right, - jint physical_padding_bottom, - jint physical_padding_left, - jint physical_view_inset_top, - jint physical_view_inset_right, - jint physical_view_inset_bottom, - jint physical_view_inset_left) { - blink::ViewportMetrics metrics; - metrics.device_pixel_ratio = device_pixel_ratio; - metrics.physical_width = physical_width; - metrics.physical_height = physical_height; - metrics.physical_padding_top = physical_padding_top; - metrics.physical_padding_right = physical_padding_right; - metrics.physical_padding_bottom = physical_padding_bottom; - metrics.physical_padding_left = physical_padding_left; - metrics.physical_view_inset_top = physical_view_inset_top; - metrics.physical_view_inset_right = physical_view_inset_right; - metrics.physical_view_inset_bottom = physical_view_inset_bottom; - metrics.physical_view_inset_left = physical_view_inset_left; - - blink::Threads::UI()->PostTask([ engine = engine_->GetWeakPtr(), metrics ] { - if (engine) - engine->SetViewportMetrics(metrics); - }); + latch.Wait(); } void PlatformViewAndroid::DispatchPlatformMessage(JNIEnv* env, @@ -301,7 +69,7 @@ void PlatformViewAndroid::DispatchPlatformMessage(JNIEnv* env, fxl::RefPtr response; if (response_id) { response = fxl::MakeRefCounted( - response_id, GetWeakPtr()); + response_id, java_object_, task_runners_.GetPlatformTaskRunner()); } PlatformView::DispatchPlatformMessage( @@ -315,7 +83,7 @@ void PlatformViewAndroid::DispatchEmptyPlatformMessage(JNIEnv* env, fxl::RefPtr response; if (response_id) { response = fxl::MakeRefCounted( - response_id, GetWeakPtr()); + response_id, java_object_, task_runners_.GetPlatformTaskRunner()); } PlatformView::DispatchPlatformMessage( @@ -323,20 +91,6 @@ void PlatformViewAndroid::DispatchEmptyPlatformMessage(JNIEnv* env, std::move(response))); } -void PlatformViewAndroid::DispatchPointerDataPacket(JNIEnv* env, - jobject buffer, - jint position) { - uint8_t* data = static_cast(env->GetDirectBufferAddress(buffer)); - - blink::Threads::UI()->PostTask(fxl::MakeCopyable([ - engine = engine_->GetWeakPtr(), - packet = std::make_unique(data, position) - ] { - if (engine.get()) - engine->DispatchPointerDataPacket(*packet); - })); -} - void PlatformViewAndroid::InvokePlatformMessageResponseCallback( JNIEnv* env, jint response_id, @@ -369,10 +123,11 @@ void PlatformViewAndroid::InvokePlatformMessageEmptyResponseCallback( message_response->CompleteEmpty(); } +// |shell::PlatformView| void PlatformViewAndroid::HandlePlatformMessage( fxl::RefPtr message) { JNIEnv* env = fml::jni::AttachCurrentThread(); - fml::jni::ScopedJavaLocalRef view = flutter_view_.get(env); + fml::jni::ScopedJavaLocalRef view = java_object_.get(env); if (view.is_null()) return; @@ -402,35 +157,6 @@ void PlatformViewAndroid::HandlePlatformMessage( } } -void PlatformViewAndroid::HandlePlatformMessageResponse( - int response_id, - std::vector data) { - JNIEnv* env = fml::jni::AttachCurrentThread(); - - fml::jni::ScopedJavaLocalRef view = flutter_view_.get(env); - - if (view.is_null()) - return; - fml::jni::ScopedJavaLocalRef data_array( - env, env->NewByteArray(data.size())); - env->SetByteArrayRegion(data_array.obj(), 0, data.size(), - reinterpret_cast(data.data())); - - FlutterViewHandlePlatformMessageResponse(env, view.obj(), response_id, - data_array.obj()); -} - -void PlatformViewAndroid::HandlePlatformMessageEmptyResponse(int response_id) { - JNIEnv* env = fml::jni::AttachCurrentThread(); - - fml::jni::ScopedJavaLocalRef view = flutter_view_.get(env); - - if (view.is_null()) - return; - FlutterViewHandlePlatformMessageResponse(env, view.obj(), response_id, - nullptr); -} - void PlatformViewAndroid::DispatchSemanticsAction(JNIEnv* env, jint id, jint action, @@ -451,35 +177,14 @@ void PlatformViewAndroid::DispatchSemanticsAction(JNIEnv* env, id, static_cast(action), std::move(args_vector)); } -void PlatformViewAndroid::SetSemanticsEnabled(jboolean enabled) { - PlatformView::SetSemanticsEnabled(enabled); -} - -void PlatformViewAndroid::ReleaseSurface() { - NotifyDestroyed(); - android_surface_->TeardownOnScreenContext(); - SetRasterizer(std::make_unique()); -} - -VsyncWaiter* PlatformViewAndroid::GetVsyncWaiter() { - if (!vsync_waiter_) - vsync_waiter_ = std::make_unique(); - return vsync_waiter_.get(); -} - -bool PlatformViewAndroid::ResourceContextMakeCurrent() { - FXL_CHECK(android_surface_); - return android_surface_->ResourceContextMakeCurrent(); -} - -void PlatformViewAndroid::UpdateSemantics( - blink::SemanticsNodeUpdates update) { +// |shell::PlatformView| +void PlatformViewAndroid::UpdateSemantics(blink::SemanticsNodeUpdates update) { constexpr size_t kBytesPerNode = 36 * sizeof(int32_t); constexpr size_t kBytesPerChild = sizeof(int32_t); JNIEnv* env = fml::jni::AttachCurrentThread(); { - fml::jni::ScopedJavaLocalRef view = flutter_view_.get(env); + fml::jni::ScopedJavaLocalRef view = java_object_.get(env); if (view.is_null()) return; @@ -560,79 +265,6 @@ void PlatformViewAndroid::UpdateSemantics( } } -void PlatformViewAndroid::RunFromSource(const std::string& assets_directory, - const std::string& main, - const std::string& packages) { - JNIEnv* env = fml::jni::AttachCurrentThread(); - FXL_CHECK(env); - - { - fml::jni::ScopedJavaLocalRef local_flutter_view = - flutter_view_.get(env); - if (local_flutter_view.is_null()) { - // Collected. - return; - } - - // Grab the class of the flutter view. - jclass flutter_view_class = env->GetObjectClass(local_flutter_view.obj()); - FXL_CHECK(flutter_view_class); - - // Grab the runFromSource method id. - jmethodID run_from_source_method_id = env->GetMethodID( - flutter_view_class, "runFromSource", - "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V"); - FXL_CHECK(run_from_source_method_id); - - // Invoke runFromSource on the Android UI thread. - jstring java_assets_directory = env->NewStringUTF(assets_directory.c_str()); - FXL_CHECK(java_assets_directory); - jstring java_main = env->NewStringUTF(main.c_str()); - FXL_CHECK(java_main); - jstring java_packages = env->NewStringUTF(packages.c_str()); - FXL_CHECK(java_packages); - env->CallVoidMethod(local_flutter_view.obj(), run_from_source_method_id, - java_assets_directory, java_main, java_packages); - } - - // Detaching from the VM deletes any stray local references. - fml::jni::DetachFromVM(); -} - -void PlatformViewAndroid::SetAssetBundlePath( - const std::string& assets_directory) { - JNIEnv* env = fml::jni::AttachCurrentThread(); - FXL_CHECK(env); - - { - fml::jni::ScopedJavaLocalRef local_flutter_view = - flutter_view_.get(env); - if (local_flutter_view.is_null()) { - // Collected. - return; - } - - // Grab the class of the flutter view. - jclass flutter_view_class = env->GetObjectClass(local_flutter_view.obj()); - FXL_CHECK(flutter_view_class); - - // Grab the setAssetBundlePath method id. - jmethodID method_id = env->GetMethodID( - flutter_view_class, "setAssetBundlePathOnUI", "(Ljava/lang/String;)V"); - FXL_CHECK(method_id); - - // Invoke setAssetBundlePath on the Android UI thread. - jstring java_assets_directory = env->NewStringUTF(assets_directory.c_str()); - FXL_CHECK(java_assets_directory); - - env->CallVoidMethod(local_flutter_view.obj(), method_id, - java_assets_directory); - } - - // Detaching from the VM deletes any stray local references. - fml::jni::DetachFromVM(); -} - void PlatformViewAndroid::RegisterExternalTexture( int64_t texture_id, const fml::jni::JavaObjectWeakGlobalRef& surface_texture) { @@ -640,115 +272,57 @@ void PlatformViewAndroid::RegisterExternalTexture( std::make_shared(texture_id, surface_texture)); } -void PlatformViewAndroid::MarkTextureFrameAvailable(int64_t texture_id) { - blink::Threads::Gpu()->PostTask([this, texture_id]() { - std::shared_ptr texture = - static_pointer_cast( - rasterizer_->GetTextureRegistry().GetTexture(texture_id)); - if (texture) { - texture->MarkNewFrameAvailable(); - } - }); - PlatformView::MarkTextureFrameAvailable(texture_id); +// |shell::PlatformView| +std::unique_ptr PlatformViewAndroid::CreateVSyncWaiter() { + return std::make_unique(task_runners_); } -fml::jni::ScopedJavaLocalRef PlatformViewAndroid::GetBitmap( - JNIEnv* env) { - // Render the last frame to an array of pixels on the GPU thread. - // The pixels will be returned as a global JNI reference to an int array. - fxl::AutoResetWaitableEvent latch; - jobject pixels_ref = nullptr; - SkISize frame_size; - blink::Threads::Gpu()->PostTask([this, &latch, &pixels_ref, &frame_size]() { - GetBitmapGpuTask(&pixels_ref, &frame_size); - latch.Signal(); - }); - - latch.Wait(); - - // Convert the pixel array to an Android bitmap. - if (pixels_ref == nullptr) - return fml::jni::ScopedJavaLocalRef(); - - fml::jni::ScopedJavaGlobalRef pixels(env, pixels_ref); - - jclass bitmap_class = env->FindClass("android/graphics/Bitmap"); - FXL_CHECK(bitmap_class); - - jmethodID create_bitmap = env->GetStaticMethodID( - bitmap_class, "createBitmap", - "([IIILandroid/graphics/Bitmap$Config;)Landroid/graphics/Bitmap;"); - FXL_CHECK(create_bitmap); - - jclass bitmap_config_class = env->FindClass("android/graphics/Bitmap$Config"); - FXL_CHECK(bitmap_config_class); - - jmethodID bitmap_config_value_of = env->GetStaticMethodID( - bitmap_config_class, "valueOf", - "(Ljava/lang/String;)Landroid/graphics/Bitmap$Config;"); - FXL_CHECK(bitmap_config_value_of); - - jstring argb = env->NewStringUTF("ARGB_8888"); - FXL_CHECK(argb); - - jobject bitmap_config = env->CallStaticObjectMethod( - bitmap_config_class, bitmap_config_value_of, argb); - FXL_CHECK(bitmap_config); +// |shell::PlatformView| +std::unique_ptr PlatformViewAndroid::CreateRenderingSurface() { + return android_surface_->CreateGPUSurface(); +} - jobject bitmap = env->CallStaticObjectMethod( - bitmap_class, create_bitmap, pixels.obj(), frame_size.width(), - frame_size.height(), bitmap_config); +// |shell::PlatformView| +sk_sp PlatformViewAndroid::CreateResourceContext() const { + sk_sp resource_context; + if (android_surface_->ResourceContextMakeCurrent()) { + // TODO(chinmaygarde): Currently, this code depends on the fact that only + // the OpenGL surface will be able to make a resource context current. If + // this changes, this assumption breaks. Handle the same. + resource_context = IOManager::CreateCompatibleResourceLoadingContext( + GrBackend::kOpenGL_GrBackend, + reinterpret_cast(GrGLCreateNativeInterface())); + } else { + FXL_DLOG(ERROR) << "Could not make the resource context current."; + } - return fml::jni::ScopedJavaLocalRef(env, bitmap); + return resource_context; +} + +void PlatformViewAndroid::InstallFirstFrameCallback() { + // On Platform Task Runner. + SetNextFrameCallback( + [platform_view = GetWeakPtr(), + platform_task_runner = task_runners_.GetPlatformTaskRunner()]() { + // On GPU Task Runner. + platform_task_runner->PostTask([platform_view]() { + // Back on Platform Task Runner. + if (platform_view) { + reinterpret_cast(platform_view.get()) + ->FireFirstFrameCallback(); + } + }); + }); } -void PlatformViewAndroid::GetBitmapGpuTask(jobject* pixels_out, - SkISize* size_out) { - flow::LayerTree* layer_tree = rasterizer_->GetLastLayerTree(); - if (layer_tree == nullptr) - return; - +void PlatformViewAndroid::FireFirstFrameCallback() { JNIEnv* env = fml::jni::AttachCurrentThread(); - FXL_CHECK(env); - - const SkISize& frame_size = layer_tree->frame_size(); - jsize pixels_size = frame_size.width() * frame_size.height(); - jintArray pixels_array = env->NewIntArray(pixels_size); - FXL_CHECK(pixels_array); - - jint* pixels = env->GetIntArrayElements(pixels_array, nullptr); - FXL_CHECK(pixels); - - SkImageInfo image_info = - SkImageInfo::Make(frame_size.width(), frame_size.height(), - kRGBA_8888_SkColorType, kPremul_SkAlphaType); - - sk_sp surface = SkSurface::MakeRasterDirect( - image_info, pixels, frame_size.width() * sizeof(jint)); - - flow::CompositorContext compositor_context(nullptr); - SkCanvas* canvas = surface->getCanvas(); - flow::CompositorContext::ScopedFrame frame = - compositor_context.AcquireFrame(nullptr, canvas, false); - - canvas->clear(SK_ColorBLACK); - layer_tree->Raster(frame); - canvas->flush(); - - // Our configuration of Skia does not support rendering to the - // BitmapConfig.ARGB_8888 format expected by android.graphics.Bitmap. - // Convert from kRGBA_8888 to kBGRA_8888 (equivalent to ARGB_8888). - for (int i = 0; i < pixels_size; i++) { - uint8_t* bytes = reinterpret_cast(pixels + i); - std::swap(bytes[0], bytes[2]); + fml::jni::ScopedJavaLocalRef view = java_object_.get(env); + if (view.is_null()) { + // The Java object died. + return; } - - env->ReleaseIntArrayElements(pixels_array, pixels, 0); - - *pixels_out = env->NewGlobalRef(pixels_array); - *size_out = frame_size; - - fml::jni::DetachFromVM(); + FlutterViewOnFirstFrame(fml::jni::AttachCurrentThread(), view.obj()); } } // namespace shell diff --git a/shell/platform/android/platform_view_android.h b/shell/platform/android/platform_view_android.h index 4779ea16ab4a7..9976c443f4248 100644 --- a/shell/platform/android/platform_view_android.h +++ b/shell/platform/android/platform_view_android.h @@ -20,45 +20,23 @@ namespace shell { -class PlatformViewAndroid : public PlatformView { +class PlatformViewAndroid final : public PlatformView { public: static bool Register(JNIEnv* env); - PlatformViewAndroid(); + PlatformViewAndroid(PlatformView::Delegate& delegate, + blink::TaskRunners task_runners, + fml::jni::JavaObjectWeakGlobalRef java_object, + bool use_software_rendering); ~PlatformViewAndroid() override; - virtual void Attach() override; + void NotifyCreated(fxl::RefPtr native_window); - void Detach(); + void NotifyChanged(const SkISize& size); - void SurfaceCreated(JNIEnv* env, jobject jsurface, jint backgroundColor); - - void SurfaceChanged(jint width, jint height); - - void SurfaceDestroyed(); - - void RunBundleAndSnapshot(JNIEnv* env, std::string bundle_path, - std::string snapshot_override, - std::string entrypoint, - bool reuse_isolate, - jobject assetManager); - - void RunBundleAndSource(std::string bundle_path, - std::string main, - std::string packages); - - void SetViewportMetrics(jfloat device_pixel_ratio, - jint physical_width, - jint physical_height, - jint physical_padding_top, - jint physical_padding_right, - jint physical_padding_bottom, - jint physical_padding_left, - jint physical_view_inset_top, - jint physical_view_inset_right, - jint physical_view_inset_bottom, - jint physical_view_inset_left); + // |shell::PlatformView| + void NotifyDestroyed() override; void DispatchPlatformMessage(JNIEnv* env, std::string name, @@ -70,8 +48,6 @@ class PlatformViewAndroid : public PlatformView { std::string name, jint response_id); - void DispatchPointerDataPacket(JNIEnv* env, jobject buffer, jint position); - void InvokePlatformMessageResponseCallback(JNIEnv* env, jint response_id, jobject java_response_data, @@ -86,55 +62,37 @@ class PlatformViewAndroid : public PlatformView { jobject args, jint args_position); - void SetSemanticsEnabled(jboolean enabled); - - fml::jni::ScopedJavaLocalRef GetBitmap(JNIEnv* env); - - VsyncWaiter* GetVsyncWaiter() override; - - bool ResourceContextMakeCurrent() override; - - void UpdateSemantics(blink::SemanticsNodeUpdates update) override; - - void HandlePlatformMessage( - fxl::RefPtr message) override; - - void HandlePlatformMessageResponse(int response_id, - std::vector data); - - void HandlePlatformMessageEmptyResponse(int response_id); - - void RunFromSource(const std::string& assets_directory, - const std::string& main, - const std::string& packages) override; - - void SetAssetBundlePathOnUI(std::string bundle_path); - - void SetAssetBundlePath(const std::string& assets_directory) override; - void RegisterExternalTexture( int64_t texture_id, const fml::jni::JavaObjectWeakGlobalRef& surface_texture); - void MarkTextureFrameAvailable(int64_t texture_id) override; - - void set_flutter_view(const fml::jni::JavaObjectWeakGlobalRef& flutter_view) { - flutter_view_ = flutter_view; - } - private: - std::unique_ptr android_surface_; - fml::jni::JavaObjectWeakGlobalRef flutter_view_; + const fml::jni::JavaObjectWeakGlobalRef java_object_; + const std::unique_ptr android_surface_; // We use id 0 to mean that no response is expected. int next_response_id_ = 1; std::unordered_map> pending_responses_; - void UpdateThreadPriorities(); + // |shell::PlatformView| + void UpdateSemantics(blink::SemanticsNodeUpdates update) override; + + // |shell::PlatformView| + void HandlePlatformMessage( + fxl::RefPtr message) override; + + // |shell::PlatformView| + std::unique_ptr CreateVSyncWaiter() override; + + // |shell::PlatformView| + std::unique_ptr CreateRenderingSurface() override; + + // |shell::PlatformView| + sk_sp CreateResourceContext() const override; - void ReleaseSurface(); + void InstallFirstFrameCallback(); - void GetBitmapGpuTask(jobject* pixels_out, SkISize* size_out); + void FireFirstFrameCallback(); FXL_DISALLOW_COPY_AND_ASSIGN(PlatformViewAndroid); }; diff --git a/shell/platform/android/platform_view_android_jni.cc b/shell/platform/android/platform_view_android_jni.cc index c819f3bfb5e41..f75f54a896850 100644 --- a/shell/platform/android/platform_view_android_jni.cc +++ b/shell/platform/android/platform_view_android_jni.cc @@ -4,17 +4,26 @@ #include "flutter/shell/platform/android/platform_view_android_jni.h" +#include + +#include + +#include "flutter/assets/directory_asset_bundle.h" #include "flutter/common/settings.h" +#include "flutter/fml/file.h" #include "flutter/fml/platform/android/jni_util.h" #include "flutter/fml/platform/android/jni_weak_ref.h" #include "flutter/fml/platform/android/scoped_java_ref.h" #include "flutter/runtime/dart_service_isolate.h" +#include "flutter/shell/common/run_configuration.h" #include "flutter/shell/platform/android/android_external_texture_gl.h" +#include "flutter/shell/platform/android/android_shell_holder.h" +#include "flutter/shell/platform/android/apk_asset_provider.h" +#include "flutter/shell/platform/android/flutter_main.h" #include "lib/fxl/arraysize.h" -#include "lib/fxl/logging.h" -#define PLATFORM_VIEW \ - (*reinterpret_cast*>(platform_view)) +#define ANDROID_SHELL_HOLDER \ + (reinterpret_cast(shell_holder)) namespace shell { @@ -78,14 +87,12 @@ void FlutterViewOnFirstFrame(JNIEnv* env, jobject obj) { static jmethodID g_attach_to_gl_context_method = nullptr; void SurfaceTextureAttachToGLContext(JNIEnv* env, jobject obj, jint textureId) { - ASSERT_IS_GPU_THREAD; env->CallVoidMethod(obj, g_attach_to_gl_context_method, textureId); FXL_CHECK(CheckException(env)); } static jmethodID g_update_tex_image_method = nullptr; void SurfaceTextureUpdateTexImage(JNIEnv* env, jobject obj) { - ASSERT_IS_GPU_THREAD; env->CallVoidMethod(obj, g_update_tex_image_method); FXL_CHECK(CheckException(env)); } @@ -94,14 +101,12 @@ static jmethodID g_get_transform_matrix_method = nullptr; void SurfaceTextureGetTransformMatrix(JNIEnv* env, jobject obj, jfloatArray result) { - ASSERT_IS_GPU_THREAD; env->CallVoidMethod(obj, g_get_transform_matrix_method, result); FXL_CHECK(CheckException(env)); } static jmethodID g_detach_from_gl_context_method = nullptr; void SurfaceTextureDetachFromGLContext(JNIEnv* env, jobject obj) { - ASSERT_IS_GPU_THREAD; env->CallVoidMethod(obj, g_detach_from_gl_context_method); FXL_CHECK(CheckException(env)); } @@ -109,22 +114,22 @@ void SurfaceTextureDetachFromGLContext(JNIEnv* env, jobject obj) { // Called By Java static jlong Attach(JNIEnv* env, jclass clazz, jobject flutterView) { - auto view = new PlatformViewAndroid(); - auto storage = new std::shared_ptr(view); - // Create a weak reference to the flutterView Java object so that we can make - // calls into it later. - view->Attach(); - view->set_flutter_view(fml::jni::JavaObjectWeakGlobalRef(env, flutterView)); - return reinterpret_cast(storage); + fml::jni::JavaObjectWeakGlobalRef java_object(env, flutterView); + auto shell_holder = std::make_unique( + FlutterMain::Get().GetSettings(), java_object); + if (shell_holder->IsValid()) { + return reinterpret_cast(shell_holder.release()); + } else { + return 0; + } } -static void Detach(JNIEnv* env, jobject jcaller, jlong platform_view) { - PLATFORM_VIEW->Detach(); +static void Detach(JNIEnv* env, jobject jcaller, jlong shell_holder) { + // Nothing to do. } -static void Destroy(JNIEnv* env, jobject jcaller, jlong platform_view) { - PLATFORM_VIEW->Detach(); - delete &PLATFORM_VIEW; +static void Destroy(JNIEnv* env, jobject jcaller, jlong shell_holder) { + delete ANDROID_SHELL_HOLDER; } static jstring GetObservatoryUri(JNIEnv* env, jclass clazz) { @@ -134,67 +139,180 @@ static jstring GetObservatoryUri(JNIEnv* env, jclass clazz) { static void SurfaceCreated(JNIEnv* env, jobject jcaller, - jlong platform_view, - jobject surface, + jlong shell_holder, + jobject jsurface, jint backgroundColor) { - return PLATFORM_VIEW->SurfaceCreated(env, surface, backgroundColor); + // Note: This frame ensures that any local references used by + // ANativeWindow_fromSurface are released immediately. This is needed as a + // workaround for https://code.google.com/p/android/issues/detail?id=68174 + fml::jni::ScopedJavaLocalFrame scoped_local_reference_frame(env); + auto window = fxl::MakeRefCounted( + ANativeWindow_fromSurface(env, jsurface)); + ANDROID_SHELL_HOLDER->GetPlatformView()->NotifyCreated(std::move(window)); } static void SurfaceChanged(JNIEnv* env, jobject jcaller, - jlong platform_view, + jlong shell_holder, jint width, jint height) { - return PLATFORM_VIEW->SurfaceChanged(width, height); -} - -static void SurfaceDestroyed(JNIEnv* env, - jobject jcaller, - jlong platform_view) { - return PLATFORM_VIEW->SurfaceDestroyed(); -} - -static void RunBundleAndSnapshot(JNIEnv* env, - jobject jcaller, - jlong platform_view, - jstring bundlePath, - jstring snapshotOverride, - jstring entrypoint, - jboolean reuse_runtime_controller, - jobject assetManager) { - return PLATFORM_VIEW->RunBundleAndSnapshot( - env, - fml::jni::JavaStringToString(env, bundlePath), // - fml::jni::JavaStringToString(env, snapshotOverride), // - fml::jni::JavaStringToString(env, entrypoint), // - reuse_runtime_controller, // - assetManager - ); + ANDROID_SHELL_HOLDER->GetPlatformView()->NotifyChanged( + SkISize::Make(width, height)); } -void RunBundleAndSource(JNIEnv* env, - jobject jcaller, - jlong platform_view, - jstring bundlePath, - jstring main, - jstring packages) { - return PLATFORM_VIEW->RunBundleAndSource( - fml::jni::JavaStringToString(env, bundlePath), - fml::jni::JavaStringToString(env, main), - fml::jni::JavaStringToString(env, packages)); +static void SurfaceDestroyed(JNIEnv* env, jobject jcaller, jlong shell_holder) { + ANDROID_SHELL_HOLDER->GetPlatformView()->NotifyDestroyed(); +} + +std::unique_ptr CreateIsolateConfiguration( + const blink::AssetManager& asset_manager) { + if (blink::DartVM::IsRunningPrecompiledCode()) { + return IsolateConfiguration::CreateForPrecompiledCode(); + } + + const auto configuration_from_blob = + [&asset_manager](const std::string& snapshot_name) + -> std::unique_ptr { + std::vector blob; + if (asset_manager.GetAsBuffer(snapshot_name, &blob)) { + return IsolateConfiguration::CreateForSnapshot( + std::make_unique(std::move(blob))); + } + return nullptr; + }; + + if (auto kernel = configuration_from_blob("kernel_blob.bin")) { + return kernel; + } + + if (auto script = configuration_from_blob("snapshot_blob.bin")) { + return script; + } + + return nullptr; +} + +static void RunBundleAndSnapshot( + JNIEnv* env, + jobject jcaller, + jlong shell_holder, + jstring jbundlepath, + jstring /* snapshot override (unused) */, + jstring jEntrypoint, + jboolean /* reuse runtime controller (unused) */, + jobject jAssetManager) { + auto asset_manager = fxl::MakeRefCounted(); + + const auto bundlepath = fml::jni::JavaStringToString(env, jbundlepath); + + if (bundlepath.size() > 0) { + // If we got a bundle path, attempt to use that as a directory asset + // bundle. + asset_manager->PushBack(std::make_unique( + fml::OpenFile(bundlepath.c_str(), fml::OpenPermission::kRead, true))); + + // Use the last path component of the bundle path to determine the + // directory in the APK assets. + const auto last_slash_index = bundlepath.rfind("/", bundlepath.size()); + if (last_slash_index != std::string::npos) { + auto apk_asset_dir = bundlepath.substr( + last_slash_index + 1, bundlepath.size() - last_slash_index); + + asset_manager->PushBack(std::make_unique( + env, // jni environment + jAssetManager, // asset manager + std::move(apk_asset_dir)) // apk asset dir + ); + } + } + + auto isolate_configuration = CreateIsolateConfiguration(*asset_manager); + + if (!isolate_configuration) { + FXL_DLOG(ERROR) + << "Isolate configuration could not be determined for engine launch."; + return; + } + + RunConfiguration config(std::move(isolate_configuration), + std::move(asset_manager)); + + { + auto entrypoint = fml::jni::JavaStringToString(env, jEntrypoint); + if (entrypoint.size() > 0) { + config.SetEntrypoint(std::move(entrypoint)); + } + } + + ANDROID_SHELL_HOLDER->Launch(std::move(config)); +} + +static void RunBundleAndSource(JNIEnv* env, + jobject jcaller, + jlong shell_holder, + jstring jBundlePath, + jstring main, + jstring packages) { + auto asset_manager = fxl::MakeRefCounted(); + + const auto bundlepath = fml::jni::JavaStringToString(env, jBundlePath); + + if (bundlepath.size() > 0) { + auto directory = + fml::OpenFile(bundlepath.c_str(), fml::OpenPermission::kRead, true); + asset_manager->PushBack( + std::make_unique(std::move(directory))); + } + + auto main_file_path = fml::jni::JavaStringToString(env, main); + auto packages_file_path = fml::jni::JavaStringToString(env, packages); + + auto config = + IsolateConfiguration::CreateForSource(main_file_path, packages_file_path); + + if (!config) { + return; + } + + RunConfiguration run_configuration(std::move(config), + std::move(asset_manager)); + + ANDROID_SHELL_HOLDER->Launch(std::move(run_configuration)); } void SetAssetBundlePathOnUI(JNIEnv* env, jobject jcaller, - jlong platform_view, - jstring bundlePath) { - return PLATFORM_VIEW->SetAssetBundlePathOnUI( - fml::jni::JavaStringToString(env, bundlePath)); + jlong shell_holder, + jstring jBundlePath) { + const auto bundlepath = fml::jni::JavaStringToString(env, jBundlePath); + + if (bundlepath.size() == 0) { + return; + } + + auto directory = + fml::OpenFile(bundlepath.c_str(), fml::OpenPermission::kRead, true); + + if (!directory.is_valid()) { + return; + } + + std::unique_ptr directory_asset_bundle = + std::make_unique(std::move(directory)); + + if (!directory_asset_bundle->IsValid()) { + return; + } + + auto asset_manager = fxl::MakeRefCounted(); + asset_manager->PushBack(std::move(directory_asset_bundle)); + + ANDROID_SHELL_HOLDER->UpdateAssetManager(std::move(asset_manager)); } static void SetViewportMetrics(JNIEnv* env, jobject jcaller, - jlong platform_view, + jlong shell_holder, jfloat devicePixelRatio, jint physicalWidth, jint physicalHeight, @@ -206,115 +324,194 @@ static void SetViewportMetrics(JNIEnv* env, jint physicalViewInsetRight, jint physicalViewInsetBottom, jint physicalViewInsetLeft) { - return PLATFORM_VIEW->SetViewportMetrics(devicePixelRatio, // - physicalWidth, // - physicalHeight, // - physicalPaddingTop, // - physicalPaddingRight, // - physicalPaddingBottom, // - physicalPaddingLeft, // - physicalViewInsetTop, // - physicalViewInsetRight, // - physicalViewInsetBottom, // - physicalViewInsetLeft); + const blink::ViewportMetrics metrics = { + .device_pixel_ratio = devicePixelRatio, + .physical_width = physicalWidth, + .physical_height = physicalHeight, + .physical_padding_top = physicalPaddingTop, + .physical_padding_right = physicalPaddingRight, + .physical_padding_bottom = physicalPaddingBottom, + .physical_padding_left = physicalPaddingLeft, + .physical_view_inset_top = physicalViewInsetTop, + .physical_view_inset_right = physicalViewInsetRight, + .physical_view_inset_bottom = physicalViewInsetBottom, + .physical_view_inset_left = physicalViewInsetLeft, + }; + + ANDROID_SHELL_HOLDER->SetViewportMetrics(metrics); } -static jobject GetBitmap(JNIEnv* env, jobject jcaller, jlong platform_view) { - return PLATFORM_VIEW->GetBitmap(env).Release(); +static jobject GetBitmap(JNIEnv* env, jobject jcaller, jlong shell_holder) { + auto screenshot = ANDROID_SHELL_HOLDER->Screenshot( + Rasterizer::ScreenshotType::UncompressedImage, false); + if (screenshot.data == nullptr) { + return nullptr; + } + + const SkISize& frame_size = screenshot.frame_size; + jsize pixels_size = frame_size.width() * frame_size.height(); + jintArray pixels_array = env->NewIntArray(pixels_size); + FXL_CHECK(pixels_array); + + jint* pixels = env->GetIntArrayElements(pixels_array, nullptr); + FXL_CHECK(pixels); + + auto pixels_src = static_cast(screenshot.data->data()); + + // Our configuration of Skia does not support rendering to the + // BitmapConfig.ARGB_8888 format expected by android.graphics.Bitmap. + // Convert from kRGBA_8888 to kBGRA_8888 (equivalent to ARGB_8888). + for (int i = 0; i < pixels_size; i++) { + int32_t src_pixel = pixels_src[i]; + uint8_t* src_bytes = reinterpret_cast(&src_pixel); + std::swap(src_bytes[0], src_bytes[2]); + pixels[i] = src_pixel; + } + + env->ReleaseIntArrayElements(pixels_array, pixels, 0); + + jclass bitmap_class = env->FindClass("android/graphics/Bitmap"); + FXL_CHECK(bitmap_class); + + jmethodID create_bitmap = env->GetStaticMethodID( + bitmap_class, "createBitmap", + "([IIILandroid/graphics/Bitmap$Config;)Landroid/graphics/Bitmap;"); + FXL_CHECK(create_bitmap); + + jclass bitmap_config_class = env->FindClass("android/graphics/Bitmap$Config"); + FXL_CHECK(bitmap_config_class); + + jmethodID bitmap_config_value_of = env->GetStaticMethodID( + bitmap_config_class, "valueOf", + "(Ljava/lang/String;)Landroid/graphics/Bitmap$Config;"); + FXL_CHECK(bitmap_config_value_of); + + jstring argb = env->NewStringUTF("ARGB_8888"); + FXL_CHECK(argb); + + jobject bitmap_config = env->CallStaticObjectMethod( + bitmap_config_class, bitmap_config_value_of, argb); + FXL_CHECK(bitmap_config); + + return env->CallStaticObjectMethod(bitmap_class, create_bitmap, pixels_array, + frame_size.width(), frame_size.height(), + bitmap_config); } static void DispatchPlatformMessage(JNIEnv* env, jobject jcaller, - jlong platform_view, + jlong shell_holder, jstring channel, jobject message, jint position, jint responseId) { - return PLATFORM_VIEW->DispatchPlatformMessage( - env, fml::jni::JavaStringToString(env, channel), message, position, - responseId); + ANDROID_SHELL_HOLDER->GetPlatformView()->DispatchPlatformMessage( + env, // + fml::jni::JavaStringToString(env, channel), // + message, // + position, // + responseId // + ); } static void DispatchEmptyPlatformMessage(JNIEnv* env, jobject jcaller, - jlong platform_view, + jlong shell_holder, jstring channel, jint responseId) { - return PLATFORM_VIEW->DispatchEmptyPlatformMessage( - env, fml::jni::JavaStringToString(env, channel), responseId); + ANDROID_SHELL_HOLDER->GetPlatformView()->DispatchEmptyPlatformMessage( + env, // + fml::jni::JavaStringToString(env, channel), // + responseId // + ); } static void DispatchPointerDataPacket(JNIEnv* env, jobject jcaller, - jlong platform_view, + jlong shell_holder, jobject buffer, jint position) { - return PLATFORM_VIEW->DispatchPointerDataPacket(env, buffer, position); + uint8_t* data = static_cast(env->GetDirectBufferAddress(buffer)); + auto packet = std::make_unique(data, position); + ANDROID_SHELL_HOLDER->DispatchPointerDataPacket(std::move(packet)); } static void DispatchSemanticsAction(JNIEnv* env, jobject jcaller, - jlong platform_view, + jlong shell_holder, jint id, jint action, jobject args, jint args_position) { - return PLATFORM_VIEW->DispatchSemanticsAction(env, id, action, args, - args_position); + ANDROID_SHELL_HOLDER->GetPlatformView()->DispatchSemanticsAction( + env, // + id, // + action, // + args, // + args_position // + ); } static void SetSemanticsEnabled(JNIEnv* env, jobject jcaller, - jlong platform_view, + jlong shell_holder, jboolean enabled) { - return PLATFORM_VIEW->SetSemanticsEnabled(enabled); + ANDROID_SHELL_HOLDER->GetPlatformView()->SetSemanticsEnabled(enabled); } static jboolean GetIsSoftwareRendering(JNIEnv* env, jobject jcaller) { - return blink::Settings::Get().enable_software_rendering; + return FlutterMain::Get().GetSettings().enable_software_rendering; } static void RegisterTexture(JNIEnv* env, jobject jcaller, - jlong platform_view, + jlong shell_holder, jlong texture_id, jobject surface_texture) { - PLATFORM_VIEW->RegisterExternalTexture( - static_cast(texture_id), - fml::jni::JavaObjectWeakGlobalRef(env, surface_texture)); + ANDROID_SHELL_HOLDER->GetPlatformView()->RegisterExternalTexture( + static_cast(texture_id), // + fml::jni::JavaObjectWeakGlobalRef(env, surface_texture) // + ); } static void MarkTextureFrameAvailable(JNIEnv* env, jobject jcaller, - jlong platform_view, + jlong shell_holder, jlong texture_id) { - return PLATFORM_VIEW->MarkTextureFrameAvailable( + ANDROID_SHELL_HOLDER->GetPlatformView()->MarkTextureFrameAvailable( static_cast(texture_id)); } static void UnregisterTexture(JNIEnv* env, jobject jcaller, - jlong platform_view, + jlong shell_holder, jlong texture_id) { - PLATFORM_VIEW->UnregisterTexture(static_cast(texture_id)); + ANDROID_SHELL_HOLDER->GetPlatformView()->UnregisterTexture( + static_cast(texture_id)); } static void InvokePlatformMessageResponseCallback(JNIEnv* env, jobject jcaller, - jlong platform_view, + jlong shell_holder, jint responseId, jobject message, jint position) { - return PLATFORM_VIEW->InvokePlatformMessageResponseCallback( - env, responseId, message, position); + ANDROID_SHELL_HOLDER->GetPlatformView() + ->InvokePlatformMessageResponseCallback(env, // + responseId, // + message, // + position // + ); } static void InvokePlatformMessageEmptyResponseCallback(JNIEnv* env, jobject jcaller, - jlong platform_view, + jlong shell_holder, jint responseId) { - return PLATFORM_VIEW->InvokePlatformMessageEmptyResponseCallback(env, - responseId); + ANDROID_SHELL_HOLDER->GetPlatformView() + ->InvokePlatformMessageEmptyResponseCallback(env, // + responseId // + ); } bool PlatformViewAndroid::Register(JNIEnv* env) { @@ -353,8 +550,8 @@ bool PlatformViewAndroid::Register(JNIEnv* env) { }, { .name = "nativeRunBundleAndSnapshot", - .signature = - "(JLjava/lang/String;Ljava/lang/String;Ljava/lang/String;ZLandroid/content/res/AssetManager;)V", + .signature = "(JLjava/lang/String;Ljava/lang/String;Ljava/lang/" + "String;ZLandroid/content/res/AssetManager;)V", .fnPtr = reinterpret_cast(&shell::RunBundleAndSnapshot), }, { @@ -373,11 +570,6 @@ bool PlatformViewAndroid::Register(JNIEnv* env) { .signature = "(J)V", .fnPtr = reinterpret_cast(&shell::Detach), }, - { - .name = "nativeDestroy", - .signature = "(J)V", - .fnPtr = reinterpret_cast(&shell::Destroy), - }, { .name = "nativeGetObservatoryUri", .signature = "()Ljava/lang/String;", diff --git a/shell/platform/android/vsync_waiter_android.cc b/shell/platform/android/vsync_waiter_android.cc index 29e1958dcc011..052de023b9a12 100644 --- a/shell/platform/android/vsync_waiter_android.cc +++ b/shell/platform/android/vsync_waiter_android.cc @@ -7,7 +7,7 @@ #include #include -#include "flutter/common/threads.h" +#include "flutter/common/task_runners.h" #include "flutter/fml/platform/android/jni_util.h" #include "flutter/fml/platform/android/scoped_java_ref.h" #include "flutter/fml/trace_event.h" @@ -16,68 +16,49 @@ namespace shell { +static jlong CreatePendingCallback(VsyncWaiter::Callback callback); + +static void ConsumePendingCallback(jlong java_baton, + fxl::TimePoint frame_start_time, + fxl::TimePoint frame_target_time); + static fml::jni::ScopedJavaGlobalRef* g_vsync_waiter_class = nullptr; static jmethodID g_async_wait_for_vsync_method_ = nullptr; -VsyncWaiterAndroid::VsyncWaiterAndroid() : weak_factory_(this) {} +VsyncWaiterAndroid::VsyncWaiterAndroid(blink::TaskRunners task_runners) + : VsyncWaiter(std::move(task_runners)) {} VsyncWaiterAndroid::~VsyncWaiterAndroid() = default; -void VsyncWaiterAndroid::AsyncWaitForVsync(Callback callback) { - FXL_DCHECK(!callback_); - callback_ = std::move(callback); - fml::WeakPtr* weak = - new fml::WeakPtr(); - *weak = weak_factory_.GetWeakPtr(); +// |shell::VsyncWaiter| +void VsyncWaiterAndroid::AwaitVSync() { + auto java_baton = + CreatePendingCallback(std::bind(&VsyncWaiterAndroid::FireCallback, // + this, // + std::placeholders::_1, // + std::placeholders::_2 // + )); - blink::Threads::Platform()->PostTask([weak] { + task_runners_.GetPlatformTaskRunner()->PostTask([java_baton]() { JNIEnv* env = fml::jni::AttachCurrentThread(); - env->CallStaticVoidMethod(g_vsync_waiter_class->obj(), - g_async_wait_for_vsync_method_, - reinterpret_cast(weak)); + env->CallStaticVoidMethod(g_vsync_waiter_class->obj(), // + g_async_wait_for_vsync_method_, // + java_baton // + ); }); } -void VsyncWaiterAndroid::OnVsync(int64_t frameTimeNanos, - int64_t frameTargetTimeNanos) { - Callback callback = std::move(callback_); - callback_ = Callback(); - blink::Threads::UI()->PostTask( - [callback, frameTimeNanos, frameTargetTimeNanos] { - callback(fxl::TimePoint::FromEpochDelta( - fxl::TimeDelta::FromNanoseconds(frameTimeNanos)), - fxl::TimePoint::FromEpochDelta( - fxl::TimeDelta::FromNanoseconds(frameTargetTimeNanos))); - }); -} - static void OnNativeVsync(JNIEnv* env, jclass jcaller, jlong frameTimeNanos, jlong frameTargetTimeNanos, - jlong cookie) { - // Note: The tag name must be "VSYNC" (it is special) so that the "Highlight - // Vsync" checkbox in the timeline can be enabled. - // See: https://github.com/catapult-project/catapult/blob/2091404475cbba9b786 - // 442979b6ec631305275a6/tracing/tracing/extras/vsync/vsync_auditor.html#L26 -#if FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_RELEASE - TRACE_EVENT1("flutter", "VSYNC", "mode", "basic"); -#else - { - constexpr size_t num_chars = sizeof(jlong) * CHAR_BIT * 3.4 + 2; - char deadline[num_chars]; - sprintf(deadline, "%lld", frameTargetTimeNanos / 1000); // microseconds - TRACE_EVENT2("flutter", "VSYNC", "mode", "basic", "deadline", deadline); - } -#endif - fml::WeakPtr* weak = - reinterpret_cast*>(cookie); - VsyncWaiterAndroid* waiter = weak->get(); - delete weak; - if (waiter) { - waiter->OnVsync(static_cast(frameTimeNanos), - static_cast(frameTargetTimeNanos)); - } + jlong java_baton) { + auto frame_time = fxl::TimePoint::FromEpochDelta( + fxl::TimeDelta::FromNanoseconds(frameTimeNanos)); + auto target_time = fxl::TimePoint::FromEpochDelta( + fxl::TimeDelta::FromNanoseconds(frameTargetTimeNanos)); + + ConsumePendingCallback(java_baton, frame_time, target_time); } bool VsyncWaiterAndroid::Register(JNIEnv* env) { @@ -105,4 +86,27 @@ bool VsyncWaiterAndroid::Register(JNIEnv* env) { return env->RegisterNatives(clazz, methods, arraysize(methods)) == 0; } +struct PendingCallbackData { + VsyncWaiter::Callback callback; + + PendingCallbackData(VsyncWaiter::Callback p_callback) + : callback(std::move(p_callback)) { + FXL_DCHECK(callback); + } +}; + +static jlong CreatePendingCallback(VsyncWaiter::Callback callback) { + // This delete for this new is balanced in the consume call. + auto data = new PendingCallbackData(std::move(callback)); + return reinterpret_cast(data); +} + +static void ConsumePendingCallback(jlong java_baton, + fxl::TimePoint frame_start_time, + fxl::TimePoint frame_target_time) { + auto data = reinterpret_cast(java_baton); + data->callback(frame_start_time, frame_target_time); + delete data; +} + } // namespace shell diff --git a/shell/platform/android/vsync_waiter_android.h b/shell/platform/android/vsync_waiter_android.h index c73af4bfca6f0..fd72a0a21f563 100644 --- a/shell/platform/android/vsync_waiter_android.h +++ b/shell/platform/android/vsync_waiter_android.h @@ -12,22 +12,17 @@ namespace shell { -class VsyncWaiterAndroid : public VsyncWaiter { +class VsyncWaiterAndroid final : public VsyncWaiter { public: - VsyncWaiterAndroid(); - - ~VsyncWaiterAndroid() override; - static bool Register(JNIEnv* env); - void AsyncWaitForVsync(Callback callback) override; + VsyncWaiterAndroid(blink::TaskRunners task_runners); - void OnVsync(int64_t frameTimeNanos, int64_t frameTargetTimeNanos); + ~VsyncWaiterAndroid() override; private: - Callback callback_; - fml::WeakPtr self_; - fml::WeakPtrFactory weak_factory_; + // |shell::VsyncWaiter| + void AwaitVSync() override; FXL_DISALLOW_COPY_AND_ASSIGN(VsyncWaiterAndroid); }; diff --git a/shell/platform/darwin/BUILD.gn b/shell/platform/darwin/BUILD.gn index c971c443bcc30..c6f7acd6be76a 100644 --- a/shell/platform/darwin/BUILD.gn +++ b/shell/platform/darwin/BUILD.gn @@ -6,11 +6,8 @@ assert(is_mac || is_ios) group("darwin") { if (is_mac) { - deps = [ - "desktop:shell_standalone", - ] if (!is_fuchsia_host) { - deps += [ + deps = [ "desktop:shell_application_bundle", ] } @@ -45,15 +42,12 @@ source_set("flutter_channels") { "$flutter_root/runtime", "$flutter_root/shell/common", "$flutter_root/shell/gpu", - "$flutter_root/shell/testing", "$flutter_root/sky/engine/wtf", "//garnet/public/lib/fxl", "//third_party/skia", ] - public_configs = [ - "$flutter_root:config", - ] + public_configs = [ "$flutter_root:config" ] } executable("flutter_channels_unittests") { @@ -68,7 +62,7 @@ executable("flutter_channels_unittests") { deps = [ ":flutter_channels", - "//third_party/dart/runtime:libdart_jit", "$flutter_root/testing", + "//third_party/dart/runtime:libdart_jit", ] } diff --git a/shell/platform/darwin/common/BUILD.gn b/shell/platform/darwin/common/BUILD.gn index e6fa86366b080..a1023737973a7 100644 --- a/shell/platform/darwin/common/BUILD.gn +++ b/shell/platform/darwin/common/BUILD.gn @@ -9,28 +9,23 @@ source_set("common") { sources = [ "buffer_conversions.h", "buffer_conversions.mm", - "platform_mac.h", - "platform_mac.mm", - "process_info_mac.cc", - "process_info_mac.h", + "command_line.h", + "command_line.mm", ] set_sources_assignment_filter(sources_assignment_filter) deps = [ - "//third_party/dart/runtime:dart_api", "$flutter_root/common", "$flutter_root/flow", "$flutter_root/fml", "$flutter_root/runtime", "$flutter_root/shell/common", "$flutter_root/shell/gpu", - "$flutter_root/shell/testing", "$flutter_root/sky/engine/wtf", "//garnet/public/lib/fxl", + "//third_party/dart/runtime:dart_api", "//third_party/skia", ] - public_configs = [ - "$flutter_root:config", - ] + public_configs = [ "$flutter_root:config" ] } diff --git a/shell/platform/darwin/common/command_line.h b/shell/platform/darwin/common/command_line.h new file mode 100644 index 0000000000000..dfc995b90f378 --- /dev/null +++ b/shell/platform/darwin/common/command_line.h @@ -0,0 +1,17 @@ +// Copyright 2017 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. + +#ifndef FLUTTER_SHELL_PLATFORM_DARWIN_COMMON_COMMAND_LINE_H_ +#define FLUTTER_SHELL_PLATFORM_DARWIN_COMMON_COMMAND_LINE_H_ + +#include "lib/fxl/command_line.h" +#include "lib/fxl/macros.h" + +namespace shell { + +fxl::CommandLine CommandLineFromNSProcessInfo(); + +} // namespace shell + +#endif // FLUTTER_SHELL_PLATFORM_DARWIN_COMMON_COMMAND_LINE_H_ diff --git a/shell/platform/darwin/common/command_line.mm b/shell/platform/darwin/common/command_line.mm new file mode 100644 index 0000000000000..bf8d4cc9d40f4 --- /dev/null +++ b/shell/platform/darwin/common/command_line.mm @@ -0,0 +1,21 @@ +// Copyright 2017 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. + +#include "flutter/shell/platform/darwin/common/command_line.h" + +#import + +namespace shell { + +fxl::CommandLine CommandLineFromNSProcessInfo() { + std::vector args_vector; + + for (NSString* arg in [NSProcessInfo processInfo].arguments) { + args_vector.emplace_back(arg.UTF8String); + } + + return fxl::CommandLineFromIterators(args_vector.begin(), args_vector.end()); +} + +} // namespace shell diff --git a/shell/platform/darwin/common/platform_mac.h b/shell/platform/darwin/common/platform_mac.h deleted file mode 100644 index 1989b25f11c43..0000000000000 --- a/shell/platform/darwin/common/platform_mac.h +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef SHELL_PLATFORM_MAC_PLATFORM_MAC_H_ -#define SHELL_PLATFORM_MAC_PLATFORM_MAC_H_ - -#include "flutter/shell/common/engine.h" - -namespace shell { - -void PlatformMacMain(std::string icu_data_path, - std::string application_library_path, - std::string bundle_path); - -bool AttemptLaunchFromCommandLineSwitches(Engine* engine); - -} // namespace shell - -#endif // SHELL_PLATFORM_MAC_PLATFORM_MAC_H_ diff --git a/shell/platform/darwin/common/platform_mac.mm b/shell/platform/darwin/common/platform_mac.mm deleted file mode 100644 index 5af35a430beef..0000000000000 --- a/shell/platform/darwin/common/platform_mac.mm +++ /dev/null @@ -1,160 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "flutter/shell/platform/darwin/common/platform_mac.h" - -#include - -#include "flutter/common/threads.h" -#include "flutter/fml/trace_event.h" -#include "flutter/runtime/start_up.h" -#include "flutter/shell/common/shell.h" -#include "flutter/shell/common/switches.h" -#include "flutter/shell/common/tracing_controller.h" -#include "flutter/sky/engine/wtf/MakeUnique.h" -#include "lib/fxl/command_line.h" -#include "lib/fxl/strings/string_view.h" -#include "third_party/dart/runtime/include/dart_tools_api.h" - -namespace shell { - -static fxl::CommandLine InitializedCommandLine() { - std::vector args_vector; - - for (NSString* arg in [NSProcessInfo processInfo].arguments) { - args_vector.emplace_back(arg.UTF8String); - } - - return fxl::CommandLineFromIterators(args_vector.begin(), args_vector.end()); -} - -class EmbedderState { - public: - EmbedderState(std::string icu_data_path, - std::string application_library_path, - std::string bundle_path) { - blink::engine_main_enter_ts = Dart_TimelineGetMicros(); - FXL_DCHECK([NSThread isMainThread]) - << "Embedder initialization must occur on the main platform thread"; - - auto command_line = InitializedCommandLine(); - - // This is about as early as tracing of any kind can start. Add an instant - // marker that can be used as a reference for startup. - TRACE_EVENT_INSTANT0("flutter", "main"); - - shell::Shell::InitStandalone(std::move(command_line), icu_data_path, application_library_path, - bundle_path); - } - - ~EmbedderState() {} - - private: - FXL_DISALLOW_COPY_AND_ASSIGN(EmbedderState); -}; - -void PlatformMacMain(std::string icu_data_path, - std::string application_library_path, - std::string bundle_path) { - static std::unique_ptr g_embedder; - static std::once_flag once_main; - - std::call_once(once_main, [&]() { - g_embedder = - WTF::MakeUnique(icu_data_path, application_library_path, bundle_path); - }); -} - -static bool FlagsValidForCommandLineLaunch(const std::string& bundle_path, - const std::string& main, - const std::string& packages) { - if (main.empty() || packages.empty() || bundle_path.empty()) { - return false; - } - - // Ensure that the paths exists. This catches cases where the user has - // successfully launched the application from the tooling but has since moved - // the source files on disk and is launching again directly. - - NSFileManager* manager = [NSFileManager defaultManager]; - - if (![manager fileExistsAtPath:@(main.c_str())]) { - return false; - } - - if (![manager fileExistsAtPath:@(packages.c_str())]) { - return false; - } - - if (![manager fileExistsAtPath:@(bundle_path.c_str())]) { - return false; - } - - return true; -} - -static std::string ResolveCommandLineLaunchFlag(const fxl::StringView name) { - const auto& command_line = shell::Shell::Shared().GetCommandLine(); - - std::string command_line_option; - if (command_line.GetOptionValue(name, &command_line_option)) { - return command_line_option; - } - - const char* saved_default = - [[NSUserDefaults standardUserDefaults] stringForKey:@(name.data())].UTF8String; - - if (saved_default != NULL) { - return saved_default; - } - - return ""; -} - -bool AttemptLaunchFromCommandLineSwitches(Engine* engine) { - NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults]; - - const auto& command_line = shell::Shell::Shared().GetCommandLine(); - - if (command_line.HasOption(FlagForSwitch(Switch::FlutterAssetsDir)) || - command_line.HasOption(FlagForSwitch(Switch::MainDartFile)) || - command_line.HasOption(FlagForSwitch(Switch::Packages))) { - // The main dart file, Flutter assets directory and the package root must be - // specified in one go. We dont want to end up in a situation where we take - // one value from the command line and the others from user defaults. In - // case, any new flags are specified, forget about all the old ones. - [defaults removeObjectForKey:@(FlagForSwitch(Switch::FlutterAssetsDir).data())]; - [defaults removeObjectForKey:@(FlagForSwitch(Switch::MainDartFile).data())]; - [defaults removeObjectForKey:@(FlagForSwitch(Switch::Packages).data())]; - - [defaults synchronize]; - } - - std::string bundle_path = ResolveCommandLineLaunchFlag(FlagForSwitch(Switch::FlutterAssetsDir)); - std::string main = ResolveCommandLineLaunchFlag(FlagForSwitch(Switch::MainDartFile)); - std::string packages = ResolveCommandLineLaunchFlag(FlagForSwitch(Switch::Packages)); - - if (!FlagsValidForCommandLineLaunch(bundle_path, main, packages)) { - return false; - } - - // Save the newly resolved dart main file and the package root to user - // defaults so that the next time the user launches the application in the - // simulator without the tooling, the application boots up. - [defaults setObject:@(bundle_path.c_str()) - forKey:@(FlagForSwitch(Switch::FlutterAssetsDir).data())]; - [defaults setObject:@(main.c_str()) forKey:@(FlagForSwitch(Switch::MainDartFile).data())]; - [defaults setObject:@(packages.c_str()) forKey:@(FlagForSwitch(Switch::Packages).data())]; - - [defaults synchronize]; - - blink::Threads::UI()->PostTask([ engine = engine->GetWeakPtr(), bundle_path, main, packages ] { - if (engine) - engine->RunBundleAndSource(bundle_path, main, packages); - }); - - return true; -} - -} // namespace shell diff --git a/shell/platform/darwin/common/process_info_mac.cc b/shell/platform/darwin/common/process_info_mac.cc deleted file mode 100644 index 11f70f305a3e9..0000000000000 --- a/shell/platform/darwin/common/process_info_mac.cc +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "flutter/shell/platform/darwin/common/process_info_mac.h" - -namespace shell { - -ProcessInfoMac::ProcessInfoMac() = default; - -ProcessInfoMac::~ProcessInfoMac() = default; - -bool ProcessInfoMac::SampleNow() { - mach_msg_type_number_t size = MACH_TASK_BASIC_INFO_COUNT; - kern_return_t result = - task_info(mach_task_self(), // - MACH_TASK_BASIC_INFO, // - reinterpret_cast(&last_sample_), // - &size); - if (result == KERN_SUCCESS) { - return true; - } - - last_sample_ = {}; - return false; -} - -size_t ProcessInfoMac::GetVirtualMemorySize() { - return last_sample_.virtual_size; -} - -size_t ProcessInfoMac::GetResidentMemorySize() { - return last_sample_.resident_size; -} - -} // namespace shell diff --git a/shell/platform/darwin/common/process_info_mac.h b/shell/platform/darwin/common/process_info_mac.h deleted file mode 100644 index 7edc8034173e0..0000000000000 --- a/shell/platform/darwin/common/process_info_mac.h +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef FLUTTER_SHELL_PLATFORM_DARWIN_COMMON_PROCESS_INFO_MAC_H_ -#define FLUTTER_SHELL_PLATFORM_DARWIN_COMMON_PROCESS_INFO_MAC_H_ - -#include -#include -#include -#include "flutter/flow/process_info.h" -#include "lib/fxl/macros.h" - -namespace shell { - -class ProcessInfoMac : public flow::ProcessInfo { - public: - ProcessInfoMac(); - - ~ProcessInfoMac(); - - bool SampleNow() override; - - size_t GetVirtualMemorySize() override; - - size_t GetResidentMemorySize() override; - - private: - struct mach_task_basic_info last_sample_; - - FXL_DISALLOW_COPY_AND_ASSIGN(ProcessInfoMac); -}; - -} // namespace shell - -#endif // FLUTTER_SHELL_PLATFORM_DARWIN_COMMON_PROCESS_INFO_MAC_H_ diff --git a/shell/platform/darwin/desktop/BUILD.gn b/shell/platform/darwin/desktop/BUILD.gn index a9c05f85ef284..5ec3298be4aa4 100644 --- a/shell/platform/darwin/desktop/BUILD.gn +++ b/shell/platform/darwin/desktop/BUILD.gn @@ -8,10 +8,8 @@ source_set("mac_desktop_platform") { visibility = [ ":*" ] sources = [ - "flutter_app_delegate.h", - "flutter_app_delegate.m", - "flutter_application.h", - "flutter_application.mm", + "flutter_application_delegate.h", + "flutter_application_delegate.mm", "flutter_window.h", "flutter_window.mm", "main_mac.mm", @@ -22,32 +20,22 @@ source_set("mac_desktop_platform") { ] deps = [ - "//third_party/dart/runtime:libdart_jit", "$flutter_root/common", "$flutter_root/fml", "$flutter_root/shell/common", "$flutter_root/shell/gpu", "$flutter_root/shell/platform/darwin/common", - "$flutter_root/shell/testing", "$flutter_root/synchronization", "//garnet/public/lib/fxl", + "//third_party/dart/runtime:libdart_jit", "//third_party/skia", + "//third_party/skia:gpu", ] - public_configs = [ - "$flutter_root:config", - ] -} - -executable("shell_standalone") { - output_name = "flutter_tester" - deps = [ - ":mac_desktop_platform", - ] + public_configs = [ "$flutter_root:config" ] } if (!is_fuchsia_host) { - import("//build/config/mac/rules.gni") resource_copy_mac("mac_desktop_resources") { diff --git a/shell/platform/darwin/desktop/Info.plist b/shell/platform/darwin/desktop/Info.plist index 31d3f1240db6b..048afb6dadca2 100644 --- a/shell/platform/darwin/desktop/Info.plist +++ b/shell/platform/darwin/desktop/Info.plist @@ -9,11 +9,11 @@ CFBundleIconFile CFBundleIdentifier - io.flutter + io.flutter.engine CFBundleInfoDictionaryVersion 6.0 CFBundleName - Flutter + Flutter Engine CFBundlePackageType APPL CFBundleShortVersionString @@ -26,9 +26,7 @@ 10.6 NSHumanReadableCopyright Copyright 2015 The Flutter Authors. All rights reserved. - NSMainNibFile - flutter_mac NSPrincipalClass - FlutterApplication + NSApplication diff --git a/shell/platform/darwin/desktop/flutter_app_delegate.h b/shell/platform/darwin/desktop/flutter_app_delegate.h deleted file mode 100644 index d6addcb17d0a4..0000000000000 --- a/shell/platform/darwin/desktop/flutter_app_delegate.h +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef __SHELL_PLATFORM_DARWIN_DESKTOP_FLUTTER_APP_DELEGATE__ -#define __SHELL_PLATFORM_DARWIN_DESKTOP_FLUTTER_APP_DELEGATE__ - -#import - -@interface FlutterAppDelegate : NSObject - -@end - -#endif /* defined(__SHELL_PLATFORM_DARWIN_DESKTOP_FLUTTER_APP_DELEGATE__) */ diff --git a/shell/platform/darwin/desktop/flutter_app_delegate.m b/shell/platform/darwin/desktop/flutter_app_delegate.m deleted file mode 100644 index 7e2dfd68389f8..0000000000000 --- a/shell/platform/darwin/desktop/flutter_app_delegate.m +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright 2015 The Chromium 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 "flutter_app_delegate.h" - -@interface FlutterAppDelegate () - -@property(assign) IBOutlet NSWindow* window; - -@end - -@implementation FlutterAppDelegate - -@end diff --git a/shell/platform/darwin/desktop/flutter_application.mm b/shell/platform/darwin/desktop/flutter_application.mm deleted file mode 100644 index 57b1c83ba069a..0000000000000 --- a/shell/platform/darwin/desktop/flutter_application.mm +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "flutter/shell/platform/darwin/desktop/flutter_application.h" - -@implementation FlutterApplication -@end diff --git a/shell/platform/darwin/desktop/flutter_application.h b/shell/platform/darwin/desktop/flutter_application_delegate.h similarity index 65% rename from shell/platform/darwin/desktop/flutter_application.h rename to shell/platform/darwin/desktop/flutter_application_delegate.h index 6a4167b5f273a..3995557b25e79 100644 --- a/shell/platform/darwin/desktop/flutter_application.h +++ b/shell/platform/darwin/desktop/flutter_application_delegate.h @@ -1,13 +1,14 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. +// Copyright 2017 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. -#ifndef FLUTTER_SHELL_PLATFORM_DARWIN_DESKTOP_FLUTTER_APPLICATION_H_ -#define FLUTTER_SHELL_PLATFORM_DARWIN_DESKTOP_FLUTTER_APPLICATION_H_ +#ifndef FLUTTER_SHELL_PLATFORM_DARWIN_DESKTOP_FLUTTER_APPLICATION_DELEGATE_H_ +#define FLUTTER_SHELL_PLATFORM_DARWIN_DESKTOP_FLUTTER_APPLICATION_DELEGATE_H_ #import -@interface FlutterApplication : NSApplication +@interface FlutterApplicationDelegate : NSObject + @end -#endif // FLUTTER_SHELL_PLATFORM_DARWIN_DESKTOP_FLUTTER_APPLICATION_H_ +#endif // FLUTTER_SHELL_PLATFORM_DARWIN_DESKTOP_FLUTTER_APPLICATION_DELEGATE_H_ diff --git a/shell/platform/darwin/desktop/flutter_application_delegate.mm b/shell/platform/darwin/desktop/flutter_application_delegate.mm new file mode 100644 index 0000000000000..03076b848b25f --- /dev/null +++ b/shell/platform/darwin/desktop/flutter_application_delegate.mm @@ -0,0 +1,80 @@ +// Copyright 2017 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. + +#include "flutter/shell/platform/darwin/desktop/flutter_application_delegate.h" +#include "flutter/shell/platform/darwin/desktop/flutter_window.h" + +#include + +@implementation FlutterApplicationDelegate + +- (void)applicationWillFinishLaunching:(NSNotification*)notification { + [self configureMainMenuBar]; + [self onNewFlutterWindow:self]; +} + +- (void)configureMainMenuBar { + NSMenu* mainMenu = [[[NSMenu alloc] initWithTitle:@"MainMenu"] autorelease]; + + NSMenuItem* engineItem = + [[[NSMenuItem alloc] initWithTitle:@"Engine" action:NULL keyEquivalent:@""] autorelease]; + + NSMenu* engineMenu = [[[NSMenu alloc] initWithTitle:@"EngineMenu"] autorelease]; + + NSMenuItem* newEngineItem = [[[NSMenuItem alloc] initWithTitle:@"New Engine" + action:@selector(onNewFlutterWindow:) + keyEquivalent:@""] autorelease]; + newEngineItem.keyEquivalent = @"n"; + newEngineItem.keyEquivalentModifierMask = NSCommandKeyMask; + + NSMenuItem* shutdownEngineItem = + [[[NSMenuItem alloc] initWithTitle:@"Shutdown Engine" + action:@selector(onShutdownFlutterWindow:) + keyEquivalent:@""] autorelease]; + shutdownEngineItem.keyEquivalent = @"w"; + shutdownEngineItem.keyEquivalentModifierMask = NSCommandKeyMask; + + NSMenuItem* quitItem = [[[NSMenuItem alloc] initWithTitle:@"Quit" + action:@selector(onQuitFlutterApplication:) + keyEquivalent:@""] autorelease]; + quitItem.keyEquivalent = @"q"; + quitItem.keyEquivalentModifierMask = NSCommandKeyMask; + + [mainMenu addItem:engineItem]; + [engineItem setSubmenu:engineMenu]; + [engineMenu addItem:newEngineItem]; + [engineMenu addItem:shutdownEngineItem]; + [engineMenu addItem:quitItem]; + + [NSApplication sharedApplication].mainMenu = mainMenu; +} + +- (void)onNewFlutterWindow:(id)sender { + FlutterWindow* window = [[FlutterWindow alloc] init]; + [window setReleasedWhenClosed:YES]; + + NSWindow* currentKeyWindow = [NSApplication sharedApplication].keyWindow; + + if (currentKeyWindow == nil) { + [window center]; + } else { + [window center]; + NSPoint currentWindowFrameOrigin = window.frame.origin; + currentWindowFrameOrigin.x = currentKeyWindow.frame.origin.x + 20; + currentWindowFrameOrigin.y = currentKeyWindow.frame.origin.y - 20; + [window setFrameOrigin:currentWindowFrameOrigin]; + } + + [window makeKeyAndOrderFront:sender]; +} + +- (void)onShutdownFlutterWindow:(id)sender { + [[NSApplication sharedApplication].keyWindow close]; +} + +- (void)onQuitFlutterApplication:(id)sender { + exit(0); +} + +@end diff --git a/shell/platform/darwin/desktop/flutter_mac.xib b/shell/platform/darwin/desktop/flutter_mac.xib deleted file mode 100644 index c02ab7912da5a..0000000000000 --- a/shell/platform/darwin/desktop/flutter_mac.xib +++ /dev/null @@ -1,695 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Default - - - - - - - Left to Right - - - - - - - Right to Left - - - - - - - - - - - Default - - - - - - - Left to Right - - - - - - - Right to Left - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/shell/platform/darwin/desktop/flutter_window.h b/shell/platform/darwin/desktop/flutter_window.h index 851535ba38e9e..e07fe4eeb7520 100644 --- a/shell/platform/darwin/desktop/flutter_window.h +++ b/shell/platform/darwin/desktop/flutter_window.h @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef __SHELL_PLATFORM_DARWIN_DESKTOP_FLUTTER_WINDOW__ -#define __SHELL_PLATFORM_DARWIN_DESKTOP_FLUTTER_WINDOW__ +#ifndef SHELL_PLATFORM_DARWIN_DESKTOP_FLUTTER_WINDOW_H_ +#define SHELL_PLATFORM_DARWIN_DESKTOP_FLUTTER_WINDOW_H_ #import @@ -11,4 +11,4 @@ @end -#endif /* defined(__SHELL_PLATFORM_DARWIN_DESKTOP_FLUTTER_WINDOW__) */ +#endif // SHELL_PLATFORM_DARWIN_DESKTOP_FLUTTER_WINDOW_H_ diff --git a/shell/platform/darwin/desktop/flutter_window.mm b/shell/platform/darwin/desktop/flutter_window.mm index ca080e9275742..4c87200fb3856 100644 --- a/shell/platform/darwin/desktop/flutter_window.mm +++ b/shell/platform/darwin/desktop/flutter_window.mm @@ -2,16 +2,25 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#define FML_USED_ON_EMBEDDER + #import "flutter_window.h" -#include "flutter/common/threads.h" +#include + +#include "flutter/common/task_runners.h" +#include "flutter/fml/message_loop.h" +#include "flutter/shell/common/rasterizer.h" +#include "flutter/shell/common/switches.h" +#include "flutter/shell/common/thread_host.h" #include "flutter/shell/gpu/gpu_surface_gl.h" +#include "flutter/shell/platform/darwin/common/command_line.h" #include "flutter/shell/platform/darwin/desktop/platform_view_mac.h" +#include "lib/fxl/functional/make_copyable.h" -@interface FlutterWindow () +@interface FlutterWindow () -@property(assign) IBOutlet NSOpenGLView* renderSurface; -@property(getter=isSurfaceSetup) BOOL surfaceSetup; +@property(strong) NSOpenGLView* renderSurface; @end @@ -37,34 +46,130 @@ @interface FlutterWindow () } @implementation FlutterWindow { - std::shared_ptr _platformView; + shell::ThreadHost _thread_host; + std::unique_ptr _shell; bool _mouseIsDown; } -@synthesize renderSurface = _renderSurface; -@synthesize surfaceSetup = _surfaceSetup; +- (instancetype)init { + self = + [super initWithContentRect:NSMakeRect(10.0, 10.0, 800.0, 600.0) + styleMask:NSTitledWindowMask | NSClosableWindowMask | NSResizableWindowMask + backing:NSBackingStoreBuffered + defer:YES]; + if (self) { + self.delegate = self; + [self setupRenderSurface]; + [self setupShell]; + [self updateWindowSize]; + } + + return self; +} + +- (void)setupRenderSurface { + NSOpenGLView* renderSurface = [[[NSOpenGLView alloc] init] autorelease]; + const NSOpenGLPixelFormatAttribute attrs[] = { + NSOpenGLPFADoubleBuffer, // + NSOpenGLPFAAllowOfflineRenderers, // + 0 // + }; + renderSurface.pixelFormat = [[[NSOpenGLPixelFormat alloc] initWithAttributes:attrs] autorelease]; + renderSurface.autoresizingMask = NSViewWidthSizable | NSViewHeightSizable; + renderSurface.frame = + NSMakeRect(0.0, 0.0, self.contentView.bounds.size.width, self.contentView.bounds.size.height); + [self.contentView addSubview:renderSurface]; + self.renderSurface = renderSurface; +} -- (void)awakeFromNib { - [super awakeFromNib]; +static std::string CreateThreadLabel() { + std::stringstream stream; + static int index = 1; + stream << "io.flutter." << index++; + return stream.str(); +} - self.delegate = self; +- (void)setupShell { + FXL_DCHECK(!_shell) << "The shell must not already be set."; - [self updateWindowSize]; + auto thread_label = CreateThreadLabel(); + + // Create the threads on which to run the shell. + _thread_host = {thread_label, shell::ThreadHost::Type::GPU | shell::ThreadHost::Type::UI | + shell::ThreadHost::Type::IO}; + + // Grab the task runners for the newly created threads. + fml::MessageLoop::EnsureInitializedForCurrentThread(); + blink::TaskRunners task_runners(thread_label, // label + fml::MessageLoop::GetCurrent().GetTaskRunner(), // platform + _thread_host.gpu_thread->GetTaskRunner(), // GPU + _thread_host.ui_thread->GetTaskRunner(), // UI + _thread_host.io_thread->GetTaskRunner() // IO + ); + + // Figure out the settings from the command line arguments. + auto settings = shell::SettingsFromCommandLine(shell::CommandLineFromNSProcessInfo()); + + if (settings.icu_data_path.size() == 0) { + settings.icu_data_path = + [[NSBundle mainBundle] pathForResource:@"icudtl.dat" ofType:@""].UTF8String; + } + + settings.using_blink = false; + + settings.task_observer_add = [](intptr_t key, fxl::Closure callback) { + fml::MessageLoop::GetCurrent().AddTaskObserver(key, std::move(callback)); + }; + + settings.task_observer_remove = [](intptr_t key) { + fml::MessageLoop::GetCurrent().RemoveTaskObserver(key); + }; + + // Setup the callback that will be run on the appropriate threads. + shell::Shell::CreateCallback on_create_platform_view = + [render_surface = self.renderSurface](shell::Shell& shell) { + return std::make_unique(shell, render_surface); + }; + + shell::Shell::CreateCallback on_create_rasterizer = [](shell::Shell& shell) { + return std::make_unique(shell.GetTaskRunners()); + }; + + // Finally, create the shell. + _shell = shell::Shell::Create(std::move(task_runners), settings, on_create_platform_view, + on_create_rasterizer); + + // Launch the engine with the inferred run configuration. + _shell->GetTaskRunners().GetUITaskRunner()->PostTask(fxl::MakeCopyable( + [engine = _shell->GetEngine(), + config = shell::RunConfiguration::InferFromSettings(_shell->GetSettings())]() mutable { + if (engine) { + auto result = engine->Run(std::move(config)); + if (!result) { + FXL_LOG(ERROR) << "Could not launch the engine with configuration."; + } + } + })); + + [self notifySurfaceCreated]; } -- (void)setupPlatformView { - FXL_DCHECK(_platformView == nullptr) << "The platform view must not already be set."; +- (void)notifySurfaceCreated { + if (!_shell || !_shell->IsSetup()) { + return; + } - _platformView = std::make_shared(self.renderSurface); - _platformView->Attach(); - _platformView->SetupResourceContextOnIOThread(); - _platformView->NotifyCreated(std::make_unique(_platformView.get())); + // Tell the platform view that it has a GL surface. + _shell->GetPlatformView()->NotifyCreated(); } -// TODO(eseidel): This does not belong in flutter_window! -// Probably belongs in NSApplicationDelegate didFinishLaunching. -- (void)setupAndLoadDart { - _platformView->SetupAndLoadDart(); +- (void)notifySurfaceDestroyed { + if (!_shell || !_shell->IsSetup()) { + return; + } + + // Tell the platform view that its surface is about to be lost. + _shell->GetPlatformView()->NotifyDestroyed(); } - (void)windowDidResize:(NSNotification*)notification { @@ -72,34 +177,28 @@ - (void)windowDidResize:(NSNotification*)notification { } - (void)updateWindowSize { - [self setupSurfaceIfNecessary]; + if (!_shell) { + return; + } blink::ViewportMetrics metrics; auto size = self.renderSurface.frame.size; metrics.physical_width = size.width; metrics.physical_height = size.height; - - blink::Threads::UI()->PostTask([ engine = _platformView->engine().GetWeakPtr(), metrics ] { - if (engine.get()) { + _shell->GetTaskRunners().GetUITaskRunner()->PostTask([engine = _shell->GetEngine(), metrics]() { + if (engine) { engine->SetViewportMetrics(metrics); } }); } -- (void)setupSurfaceIfNecessary { - if (self.isSurfaceSetup) { - return; - } - - self.surfaceSetup = YES; - - [self setupPlatformView]; - [self setupAndLoadDart]; -} - #pragma mark - Responder overrides - (void)dispatchEvent:(NSEvent*)event phase:(NSEventPhase)phase { + if (!_shell) { + return; + } + NSPoint location = [_renderSurface convertPoint:event.locationInWindow fromView:nil]; location.y = _renderSurface.frame.size.height - location.y; @@ -134,13 +233,14 @@ - (void)dispatchEvent:(NSEvent*)event phase:(NSEventPhase)phase { break; } - blink::Threads::UI()->PostTask([ engine = _platformView->engine().GetWeakPtr(), pointer_data ] { - if (engine.get()) { - blink::PointerDataPacket packet(1); - packet.SetPointerData(0, pointer_data); - engine->DispatchPointerDataPacket(packet); - } - }); + _shell->GetTaskRunners().GetUITaskRunner()->PostTask( + [engine = _shell->GetEngine(), pointer_data] { + if (engine) { + blink::PointerDataPacket packet(1); + packet.SetPointerData(0, pointer_data); + engine->DispatchPointerDataPacket(packet); + } + }); } - (void)mouseDown:(NSEvent*)event { @@ -155,11 +255,18 @@ - (void)mouseUp:(NSEvent*)event { [self dispatchEvent:event phase:NSEventPhaseEnded]; } -- (void)dealloc { - if (_platformView) { - _platformView->NotifyDestroyed(); - } +- (void)reset { + [self notifySurfaceDestroyed]; + _shell.reset(); + _thread_host.Reset(); +} +- (void)windowWillClose:(NSNotification*)notification { + [self reset]; +} + +- (void)dealloc { + [self reset]; [super dealloc]; } diff --git a/shell/platform/darwin/desktop/main_mac.mm b/shell/platform/darwin/desktop/main_mac.mm index 8087d0e92cd6d..edd08d0c8b783 100644 --- a/shell/platform/darwin/desktop/main_mac.mm +++ b/shell/platform/darwin/desktop/main_mac.mm @@ -9,33 +9,18 @@ #include "flutter/fml/message_loop.h" #include "flutter/shell/common/shell.h" #include "flutter/shell/common/switches.h" -#include "flutter/shell/platform/darwin/common/platform_mac.h" -#include "flutter/shell/platform/darwin/desktop/flutter_application.h" -#include "flutter/shell/testing/testing.h" +#include "flutter/shell/platform/darwin/desktop/flutter_application_delegate.h" #include "lib/fxl/command_line.h" #include "lib/fxl/logging.h" -static fxl::CommandLine InitializedCommandLine() { +int main(int argc, const char* argv[]) { std::vector args_vector; for (NSString* arg in [NSProcessInfo processInfo].arguments) { args_vector.emplace_back(arg.UTF8String); } - return fxl::CommandLineFromIterators(args_vector.begin(), args_vector.end()); -} - -int main(int argc, const char* argv[]) { - [FlutterApplication sharedApplication]; - - // Can't use shell::Shell::Shared().GetCommandLine() because it is initialized only - // in shell::PlatformMacMain call below. - auto command_line = InitializedCommandLine(); - - std::string bundle_path = ""; - command_line.GetOptionValue(FlagForSwitch(shell::Switch::FlutterAssetsDir), &bundle_path); - - shell::PlatformMacMain("", "", bundle_path); + auto command_line = fxl::CommandLineFromIterators(args_vector.begin(), args_vector.end()); // Print help. if (command_line.HasOption(shell::FlagForSwitch(shell::Switch::Help))) { @@ -43,13 +28,7 @@ int main(int argc, const char* argv[]) { return EXIT_SUCCESS; } - // Decide between interactive and non-interactive modes. - if (command_line.HasOption(shell::FlagForSwitch(shell::Switch::NonInteractive))) { - if (!shell::InitForTesting(std::move(command_line))) - return 1; - fml::MessageLoop::GetCurrent().Run(); - return EXIT_SUCCESS; - } else { - return NSApplicationMain(argc, argv); - } + [NSApplication sharedApplication].delegate = + [[[FlutterApplicationDelegate alloc] init] autorelease]; + return NSApplicationMain(argc, argv); } diff --git a/shell/platform/darwin/desktop/platform_view_mac.h b/shell/platform/darwin/desktop/platform_view_mac.h index 501400b6803d4..d4b19b94d1d99 100644 --- a/shell/platform/darwin/desktop/platform_view_mac.h +++ b/shell/platform/darwin/desktop/platform_view_mac.h @@ -7,6 +7,7 @@ #include "flutter/fml/platform/darwin/scoped_nsobject.h" #include "flutter/shell/common/platform_view.h" +#include "flutter/shell/common/shell.h" #include "flutter/shell/gpu/gpu_surface_gl.h" #include "lib/fxl/memory/weak_ptr.h" @@ -15,15 +16,13 @@ namespace shell { -class PlatformViewMac : public PlatformView, public GPUSurfaceGLDelegate { +class PlatformViewMac final : public PlatformView, public GPUSurfaceGLDelegate { public: - PlatformViewMac(NSOpenGLView* gl_view); + PlatformViewMac(Shell& shell, NSOpenGLView* gl_view); ~PlatformViewMac() override; - virtual void Attach() override; - - void SetupAndLoadDart(); + std::unique_ptr CreateVSyncWaiter() override; bool GLContextMakeCurrent() override; @@ -33,27 +32,17 @@ class PlatformViewMac : public PlatformView, public GPUSurfaceGLDelegate { intptr_t GLContextFBO() const override; - VsyncWaiter* GetVsyncWaiter() override; - - bool ResourceContextMakeCurrent() override; - - void RunFromSource(const std::string& assets_directory, - const std::string& main, - const std::string& packages) override; - - void SetAssetBundlePath(const std::string& assets_directory) override; - private: fml::scoped_nsobject opengl_view_; fml::scoped_nsobject resource_loading_context_; bool IsValid() const; - void SetupAndLoadFromSource(const std::string& assets_directory, - const std::string& main, - const std::string& packages); + // |shell::PlatformView| + std::unique_ptr CreateRenderingSurface() override; - void SetAssetBundlePathOnUI(const std::string& assets_directory); + // |shell::PlatformView| + sk_sp CreateResourceContext() const override; FXL_DISALLOW_COPY_AND_ASSIGN(PlatformViewMac); }; diff --git a/shell/platform/darwin/desktop/platform_view_mac.mm b/shell/platform/darwin/desktop/platform_view_mac.mm index 42948386fbcd4..e112b15c0a62a 100644 --- a/shell/platform/darwin/desktop/platform_view_mac.mm +++ b/shell/platform/darwin/desktop/platform_view_mac.mm @@ -7,77 +7,27 @@ #include #include -#include "flutter/common/threads.h" #include "flutter/fml/trace_event.h" +#include "flutter/shell/common/io_manager.h" +#include "flutter/shell/common/rasterizer.h" #include "flutter/shell/common/switches.h" -#include "flutter/shell/gpu/gpu_rasterizer.h" -#include "flutter/shell/platform/darwin/common/platform_mac.h" -#include "flutter/shell/platform/darwin/common/process_info_mac.h" #include "flutter/shell/platform/darwin/desktop/vsync_waiter_mac.h" #include "lib/fxl/command_line.h" #include "lib/fxl/synchronization/waitable_event.h" +#include "third_party/skia/include/gpu/gl/GrGLInterface.h" namespace shell { -PlatformViewMac::PlatformViewMac(NSOpenGLView* gl_view) - : PlatformView(std::make_unique(std::make_unique())), +PlatformViewMac::PlatformViewMac(Shell& shell, NSOpenGLView* gl_view) + : PlatformView(shell, shell.GetTaskRunners()), opengl_view_([gl_view retain]), resource_loading_context_([[NSOpenGLContext alloc] initWithFormat:gl_view.pixelFormat shareContext:gl_view.openGLContext]) {} PlatformViewMac::~PlatformViewMac() = default; -void PlatformViewMac::Attach() { - CreateEngine(); -} - -void PlatformViewMac::SetupAndLoadDart() { - if (AttemptLaunchFromCommandLineSwitches(&engine())) { - // This attempts launching from a Flutter assets directory that does not - // contain a dart snapshot. - return; - } - - const auto& command_line = shell::Shell::Shared().GetCommandLine(); - - std::string bundle_path = - command_line.GetOptionValueWithDefault(FlagForSwitch(Switch::FlutterAssetsDir), ""); - if (!bundle_path.empty()) { - blink::Threads::UI()->PostTask([ engine = engine().GetWeakPtr(), bundle_path ] { - if (engine) - engine->RunBundle(bundle_path); - }); - return; - } - - auto args = command_line.positional_args(); - if (args.size() > 0) { - std::string main = args[0]; - std::string packages = - command_line.GetOptionValueWithDefault(FlagForSwitch(Switch::Packages), ""); - blink::Threads::UI()->PostTask([ engine = engine().GetWeakPtr(), main, packages ] { - if (engine) - engine->RunBundleAndSource(std::string(), main, packages); - }); - return; - } -} - -void PlatformViewMac::SetupAndLoadFromSource(const std::string& assets_directory, - const std::string& main, - const std::string& packages) { - blink::Threads::UI()->PostTask( - [ engine = engine().GetWeakPtr(), assets_directory, main, packages ] { - if (engine) - engine->RunBundleAndSource(assets_directory, main, packages); - }); -} - -void PlatformViewMac::SetAssetBundlePathOnUI(const std::string& assets_directory) { - blink::Threads::UI()->PostTask([ engine = engine().GetWeakPtr(), assets_directory ] { - if (engine) - engine->SetAssetBundlePath(assets_directory); - }); +std::unique_ptr PlatformViewMac::CreateVSyncWaiter() { + return std::make_unique(task_runners_); } intptr_t PlatformViewMac::GLContextFBO() const { @@ -115,21 +65,11 @@ return true; } -VsyncWaiter* PlatformViewMac::GetVsyncWaiter() { - if (!vsync_waiter_) - vsync_waiter_ = std::make_unique(); - return vsync_waiter_.get(); -} - -bool PlatformViewMac::ResourceContextMakeCurrent() { - NSOpenGLContext* context = resource_loading_context_.get(); - - if (context == nullptr) { - return false; - } - - [context makeCurrentContext]; - return true; +sk_sp PlatformViewMac::CreateResourceContext() const { + [resource_loading_context_.get() makeCurrentContext]; + return IOManager::CreateCompatibleResourceLoadingContext( + GrBackend::kOpenGL_GrBackend, + reinterpret_cast(GrGLCreateNativeInterface())); } bool PlatformViewMac::IsValid() const { @@ -146,30 +86,8 @@ return true; } -void PlatformViewMac::RunFromSource(const std::string& assets_directory, - const std::string& main, - const std::string& packages) { - auto latch = new fxl::ManualResetWaitableEvent(); - - dispatch_async(dispatch_get_main_queue(), ^{ - SetupAndLoadFromSource(assets_directory, main, packages); - latch->Signal(); - }); - - latch->Wait(); - delete latch; -} - -void PlatformViewMac::SetAssetBundlePath(const std::string& assets_directory) { - auto latch = new fxl::ManualResetWaitableEvent(); - - dispatch_async(dispatch_get_main_queue(), ^{ - SetAssetBundlePathOnUI(assets_directory); - latch->Signal(); - }); - - latch->Wait(); - delete latch; +std::unique_ptr PlatformViewMac::CreateRenderingSurface() { + return std::make_unique(this); } } // namespace shell diff --git a/shell/platform/darwin/desktop/vsync_waiter_mac.cc b/shell/platform/darwin/desktop/vsync_waiter_mac.cc index a28ff62edb918..0ccadaf42754d 100644 --- a/shell/platform/darwin/desktop/vsync_waiter_mac.cc +++ b/shell/platform/darwin/desktop/vsync_waiter_mac.cc @@ -6,14 +6,14 @@ #include -#include "flutter/common/threads.h" #include "lib/fxl/logging.h" namespace shell { #define link_ (reinterpret_cast(opaque_)) -VsyncWaiterMac::VsyncWaiterMac() : opaque_(nullptr) { +VsyncWaiterMac::VsyncWaiterMac(blink::TaskRunners task_runners) + : VsyncWaiter(std::move(task_runners)), opaque_(nullptr) { // Create the link. CVDisplayLinkRef link = nullptr; CVDisplayLinkCreateWithActiveCGDisplays(&link); @@ -48,18 +48,10 @@ void VsyncWaiterMac::OnDisplayLink() { CVDisplayLinkStop(link_); - auto callback = std::move(callback_); - callback_ = Callback(); - - blink::Threads::UI()->PostTask( - [callback, frame_start_time, frame_target_time] { - callback(frame_start_time, frame_target_time); - }); + FireCallback(frame_start_time, frame_target_time); } -void VsyncWaiterMac::AsyncWaitForVsync(Callback callback) { - FXL_DCHECK(!callback_); - callback_ = std::move(callback); +void VsyncWaiterMac::AwaitVSync() { CVDisplayLinkStart(link_); } diff --git a/shell/platform/darwin/desktop/vsync_waiter_mac.h b/shell/platform/darwin/desktop/vsync_waiter_mac.h index 15f551f212901..0ad929a509ea3 100644 --- a/shell/platform/darwin/desktop/vsync_waiter_mac.h +++ b/shell/platform/darwin/desktop/vsync_waiter_mac.h @@ -10,19 +10,19 @@ namespace shell { -class VsyncWaiterMac : public VsyncWaiter { +class VsyncWaiterMac final : public VsyncWaiter { public: - VsyncWaiterMac(); + VsyncWaiterMac(blink::TaskRunners task_runners); ~VsyncWaiterMac() override; - void AsyncWaitForVsync(Callback callback) override; - private: void* opaque_; - Callback callback_; + + void AwaitVSync() override; static void OnDisplayLink(void* context); + void OnDisplayLink(); FXL_DISALLOW_COPY_AND_ASSIGN(VsyncWaiterMac); diff --git a/shell/platform/darwin/ios/BUILD.gn b/shell/platform/darwin/ios/BUILD.gn index 310af79c2ed8e..8946ee954c23a 100644 --- a/shell/platform/darwin/ios/BUILD.gn +++ b/shell/platform/darwin/ios/BUILD.gn @@ -40,8 +40,6 @@ shared_library("create_flutter_framework_dylib") { "framework/Source/FlutterDartProject.mm", "framework/Source/FlutterDartProject_Internal.h", "framework/Source/FlutterHeadlessDartRunner.mm", - "framework/Source/FlutterDartSource.h", - "framework/Source/FlutterDartSource.mm", "framework/Source/FlutterNavigationController.mm", "framework/Source/FlutterPlatformPlugin.h", "framework/Source/FlutterPlatformPlugin.mm", @@ -53,14 +51,15 @@ shared_library("create_flutter_framework_dylib") { "framework/Source/FlutterView.h", "framework/Source/FlutterView.mm", "framework/Source/FlutterViewController.mm", + "framework/Source/FlutterViewController_Internal.h", "framework/Source/accessibility_bridge.h", "framework/Source/accessibility_bridge.mm", "framework/Source/accessibility_text_entry.h", "framework/Source/accessibility_text_entry.mm", - "framework/Source/flutter_main_ios.h", - "framework/Source/flutter_main_ios.mm", "framework/Source/flutter_touch_mapper.h", "framework/Source/flutter_touch_mapper.mm", + "framework/Source/platform_message_response_darwin.h", + "framework/Source/platform_message_response_darwin.mm", "framework/Source/platform_message_router.h", "framework/Source/platform_message_router.mm", "framework/Source/vsync_waiter_ios.h", @@ -86,6 +85,7 @@ shared_library("create_flutter_framework_dylib") { "$flutter_root/fml", "$flutter_root/glue", "$flutter_root/lib/ui", + "$flutter_root/runtime", "$flutter_root/shell/common", "$flutter_root/shell/gpu", "$flutter_root/shell/platform/darwin/common", @@ -95,7 +95,10 @@ shared_library("create_flutter_framework_dylib") { "//third_party/skia", ] if (flutter_runtime_mode == "debug") { - deps += [ "//third_party/dart/runtime:libdart_jit" ] + deps += [ + "$flutter_root/lib/snapshot", + "//third_party/dart/runtime:libdart_jit", + ] } else { deps += [ "//third_party/dart/runtime:libdart_precompiled_runtime" ] } diff --git a/shell/platform/darwin/ios/framework/Source/FlutterDartProject.mm b/shell/platform/darwin/ios/framework/Source/FlutterDartProject.mm index 468cef1667c29..6a96081f43694 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterDartProject.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterDartProject.mm @@ -2,42 +2,121 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#define FML_USED_ON_EMBEDDER + #include "flutter/shell/platform/darwin/ios/framework/Source/FlutterDartProject_Internal.h" -#include "flutter/common/threads.h" +#include "flutter/common/task_runners.h" +#include "flutter/fml/message_loop.h" +#include "flutter/fml/platform/darwin/scoped_nsobject.h" +#include "flutter/runtime/dart_vm.h" #include "flutter/shell/common/shell.h" #include "flutter/shell/common/switches.h" -#include "flutter/shell/platform/darwin/ios/framework/Source/FlutterDartSource.h" -#include "flutter/shell/platform/darwin/ios/framework/Source/flutter_main_ios.h" -#include "lib/fxl/strings/string_view.h" -#include "third_party/dart/runtime/include/dart_api.h" - -static NSURL* URLForSwitch(const fxl::StringView name) { - const auto& cmd = shell::Shell::Shared().GetCommandLine(); - NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults]; - - std::string switch_value; - if (cmd.GetOptionValue(name, &switch_value)) { - auto url = [NSURL fileURLWithPath:@(switch_value.c_str())]; - [defaults setURL:url forKey:@(name.data())]; - [defaults synchronize]; - return url; +#include "flutter/shell/platform/darwin/common/command_line.h" +#include "flutter/shell/platform/darwin/ios/framework/Headers/FlutterViewController.h" + +static const char* kScriptSnapshotFileName = "snapshot_blob.bin"; +static const char* kVMKernelSnapshotFileName = "platform.dill"; +static const char* kApplicationKernelSnapshotFileName = "kernel_blob.bin"; + +static blink::Settings DefaultSettingsForProcess() { + auto command_line = shell::CommandLineFromNSProcessInfo(); + + // Settings passed in explicitly via command line arguments take priority. + auto settings = shell::SettingsFromCommandLine(command_line); + + settings.task_observer_add = [](intptr_t key, fxl::Closure callback) { + fml::MessageLoop::GetCurrent().AddTaskObserver(key, std::move(callback)); + }; + + settings.task_observer_remove = [](intptr_t key) { + fml::MessageLoop::GetCurrent().RemoveTaskObserver(key); + }; + + // The command line arguments may not always be complete. If they aren't, attempt to fill in + // defaults. + + // Flutter ships the ICU data file in the the bundle of the engine. Look for it there. + if (settings.icu_data_path.size() == 0) { + NSBundle* bundle = [NSBundle bundleForClass:[FlutterViewController class]]; + NSString* icuDataPath = [bundle pathForResource:@"icudtl" ofType:@"dat"]; + if (icuDataPath.length > 0) { + settings.icu_data_path = icuDataPath.UTF8String; + } } - return [defaults URLForKey:@(name.data())]; -} + if (blink::DartVM::IsRunningPrecompiledCode()) { + // The application bundle could be specified in the Info.plist. + if (settings.application_library_path.size() == 0) { + NSString* libraryName = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"FLTLibraryPath"]; + NSString* libraryPath = [[NSBundle mainBundle] pathForResource:libraryName ofType:nil]; + if (libraryPath.length > 0) { + settings.application_library_path = + [NSBundle bundleWithPath:libraryPath].executablePath.UTF8String; + } + } -@implementation FlutterDartProject { - NSBundle* _precompiledDartBundle; - FlutterDartSource* _dartSource; + // In case the application bundle is still not specified, look for the App.framework in the + // Frameworks directory. + if (settings.application_library_path.size() == 0) { + NSString* applicationFrameworkPath = + [[NSBundle mainBundle] pathForResource:@"Frameworks/App.framework" ofType:@""]; + if (applicationFrameworkPath.length > 0) { + settings.application_library_path = + [NSBundle bundleWithPath:applicationFrameworkPath].executablePath.UTF8String; + } + } + } - VMType _vmTypeRequirement; + // Checks to see if the flutter assets directory is already present. + if (settings.assets_path.size() == 0) { + NSString* assetsPath = [[NSBundle mainBundle] pathForResource:@"flutter_assets" ofType:@""]; + + if (assetsPath.length > 0) { + settings.assets_path = assetsPath.UTF8String; + + if (!blink::DartVM::IsRunningPrecompiledCode()) { + // Looking for the various script and kernel snapshot buffers only makes sense if we have a + // VM that can use these buffers. + { + // Check if there is a script snapshot in the assets directory we could potentially use. + NSURL* scriptSnapshotURL = [NSURL URLWithString:@(kScriptSnapshotFileName) + relativeToURL:[NSURL fileURLWithPath:assetsPath]]; + if ([[NSFileManager defaultManager] fileExistsAtPath:scriptSnapshotURL.path]) { + settings.script_snapshot_path = scriptSnapshotURL.path.UTF8String; + } + } + + { + // Check if there is a VM kernel snapshot in the assets directory we could potentially + // use. + NSURL* vmKernelSnapshotURL = [NSURL URLWithString:@(kVMKernelSnapshotFileName) + relativeToURL:[NSURL fileURLWithPath:assetsPath]]; + if ([[NSFileManager defaultManager] fileExistsAtPath:vmKernelSnapshotURL.path]) { + settings.kernel_snapshot_path = vmKernelSnapshotURL.path.UTF8String; + } + } + + { + // Check if there is an application kernel snapshot in the assets directory we could + // potentially use. + NSURL* applicationKernelSnapshotURL = + [NSURL URLWithString:@(kApplicationKernelSnapshotFileName) + relativeToURL:[NSURL fileURLWithPath:assetsPath]]; + if ([[NSFileManager defaultManager] fileExistsAtPath:applicationKernelSnapshotURL.path]) { + settings.application_kernel_path = applicationKernelSnapshotURL.path.UTF8String; + } + } + } + } + } + + return settings; } -+ (void)initialize { - if (self == [FlutterDartProject class]) { - shell::FlutterMain(); - } +@implementation FlutterDartProject { + fml::scoped_nsobject _precompiledDartBundle; + blink::Settings _settings; } #pragma mark - Override base class designated initializers @@ -52,9 +131,16 @@ - (instancetype)initWithPrecompiledDartBundle:(NSBundle*)bundle { self = [super init]; if (self) { - _precompiledDartBundle = [bundle retain]; + _precompiledDartBundle.reset([bundle retain]); + + _settings = DefaultSettingsForProcess(); - [self checkReadiness]; + if (bundle != nil) { + NSString* executablePath = _precompiledDartBundle.get().executablePath; + if ([[NSFileManager defaultManager] fileExistsAtPath:executablePath]) { + _settings.application_library_path = executablePath.UTF8String; + } + } } return self; @@ -66,11 +152,15 @@ - (instancetype)initWithFlutterAssets:(NSURL*)flutterAssetsURL self = [super init]; if (self) { - _dartSource = [[FlutterDartSource alloc] initWithDartMain:dartMainURL - packages:dartPackages - flutterAssets:flutterAssetsURL]; + _settings = DefaultSettingsForProcess(); + + if ([[NSFileManager defaultManager] fileExistsAtPath:dartMainURL.path]) { + _settings.main_dart_file_path = dartMainURL.path.UTF8String; + } - [self checkReadiness]; + if ([[NSFileManager defaultManager] fileExistsAtPath:dartPackages.path]) { + _settings.packages_file_path = dartPackages.path.UTF8String; + } } return self; @@ -80,10 +170,17 @@ - (instancetype)initWithFlutterAssetsWithScriptSnapshot:(NSURL*)flutterAssetsURL self = [super init]; if (self) { - _dartSource = - [[FlutterDartSource alloc] initWithFlutterAssetsWithScriptSnapshot:flutterAssetsURL]; + _settings = DefaultSettingsForProcess(); + + if ([[NSFileManager defaultManager] fileExistsAtPath:flutterAssetsURL.path]) { + _settings.assets_path = flutterAssetsURL.path.UTF8String; - [self checkReadiness]; + NSURL* scriptSnapshotPath = + [NSURL URLWithString:@(kScriptSnapshotFileName) relativeToURL:flutterAssetsURL]; + if ([[NSFileManager defaultManager] fileExistsAtPath:scriptSnapshotPath.path]) { + _settings.script_snapshot_path = scriptSnapshotPath.path.UTF8String; + } + } } return self; @@ -92,243 +189,19 @@ - (instancetype)initWithFlutterAssetsWithScriptSnapshot:(NSURL*)flutterAssetsURL #pragma mark - Convenience initializers - (instancetype)initFromDefaultSourceForConfiguration { - NSBundle* bundle = [NSBundle mainBundle]; - - if (Dart_IsPrecompiledRuntime()) { - // Load from an AOTC snapshot. - return [self initWithPrecompiledDartBundle:bundle]; + if (blink::DartVM::IsRunningPrecompiledCode()) { + return [self initWithPrecompiledDartBundle:nil]; } else { - // Load directly from sources if the appropriate command line flags are - // specified. If not, try loading from a script snapshot in the framework - // bundle. - NSURL* flutterAssetsURL = URLForSwitch(shell::FlagForSwitch(shell::Switch::FlutterAssetsDir)); - - if (flutterAssetsURL == nil) { - // If the URL was not specified on the command line, look inside the - // FlutterApplication bundle. - NSString* flutterAssetsPath = [FlutterDartProject pathForFlutterAssetsFromBundle:bundle]; - if (flutterAssetsPath != nil) { - flutterAssetsURL = [NSURL fileURLWithPath:flutterAssetsPath isDirectory:NO]; - } - } - - if (flutterAssetsURL == nil) { - NSLog(@"Error: flutterAssets directory not present in bundle; unable to start app."); - [self release]; - return nil; - } - - NSURL* dartMainURL = URLForSwitch(shell::FlagForSwitch(shell::Switch::MainDartFile)); - NSURL* dartPackagesURL = URLForSwitch(shell::FlagForSwitch(shell::Switch::Packages)); - - return - [self initWithFlutterAssets:flutterAssetsURL dartMain:dartMainURL packages:dartPackagesURL]; + return [self initWithFlutterAssets:nil dartMain:nil packages:nil]; } - - NSAssert(NO, @"Unreachable"); - [self release]; - return nil; } -#pragma mark - Common initialization tasks - -- (void)checkReadiness { - if (_precompiledDartBundle != nil) { - _vmTypeRequirement = VMTypePrecompilation; - return; - } - - if (_dartSource != nil) { - _vmTypeRequirement = VMTypeInterpreter; - return; - } +- (const blink::Settings&)settings { + return _settings; } -#pragma mark - Assets-related utilities - -+ (NSString*)pathForFlutterAssetsFromBundle:(NSBundle*)bundle { - NSString* flutterAssetsName = [bundle objectForInfoDictionaryKey:@"FLTAssetsPath"]; - if (flutterAssetsName == nil) { - // Default to "flutter_assets" - flutterAssetsName = @"flutter_assets"; - } - - return [bundle pathForResource:flutterAssetsName ofType:nil]; -} - -#pragma mark - Launching the project in a preconfigured engine. - -static NSString* NSStringFromVMType(VMType type) { - switch (type) { - case VMTypeInvalid: - return @"Invalid"; - case VMTypeInterpreter: - return @"Interpreter"; - case VMTypePrecompilation: - return @"Precompilation"; - } - - return @"Unknown"; -} - -- (void)launchInEngine:(shell::Engine*)engine - withEntrypoint:(NSString*)entrypoint - embedderVMType:(VMType)embedderVMType - result:(LaunchResult)result { - if (_vmTypeRequirement == VMTypeInvalid) { - result(NO, @"The Dart project is invalid and cannot be loaded by any VM."); - return; - } - - if (embedderVMType == VMTypeInvalid) { - result(NO, @"The embedder is invalid."); - return; - } - - if (_vmTypeRequirement != embedderVMType) { - NSString* message = - [NSString stringWithFormat: - @"Could not load the project because of differing project type. " - @"The project can run in '%@' but the embedder is configured as " - @"'%@'", - NSStringFromVMType(_vmTypeRequirement), NSStringFromVMType(embedderVMType)]; - result(NO, message); - return; - } - - switch (_vmTypeRequirement) { - case VMTypeInterpreter: - [self runFromSourceInEngine:engine withEntrypoint:entrypoint result:result]; - return; - case VMTypePrecompilation: - [self runFromPrecompiledSourceInEngine:engine withEntrypoint:entrypoint result:result]; - return; - case VMTypeInvalid: - break; - } - - return result(NO, @"Internal error"); -} - -- (void)launchInEngine:(shell::Engine*)engine - embedderVMType:(VMType)embedderVMType - result:(LaunchResult)result { - if (_vmTypeRequirement == VMTypeInvalid) { - result(NO, @"The Dart project is invalid and cannot be loaded by any VM."); - return; - } - - if (embedderVMType == VMTypeInvalid) { - result(NO, @"The embedder is invalid."); - return; - } - - if (_vmTypeRequirement != embedderVMType) { - NSString* message = - [NSString stringWithFormat: - @"Could not load the project because of differing project type. " - @"The project can run in '%@' but the embedder is configured as " - @"'%@'", - NSStringFromVMType(_vmTypeRequirement), NSStringFromVMType(embedderVMType)]; - result(NO, message); - return; - } - - switch (_vmTypeRequirement) { - case VMTypeInterpreter: - [self runFromSourceInEngine:engine withEntrypoint:@"main" result:result]; - return; - case VMTypePrecompilation: - [self runFromPrecompiledSourceInEngine:engine withEntrypoint:@"main" result:result]; - return; - case VMTypeInvalid: - break; - } - - return result(NO, @"Internal error"); -} - -#pragma mark - Running from precompiled application bundles - -- (void)runFromPrecompiledSourceInEngine:(shell::Engine*)engine - withEntrypoint:(NSString*)entrypoint - result:(LaunchResult)result { - if (![_precompiledDartBundle load]) { - NSString* message = [NSString - stringWithFormat:@"Could not load the framework ('%@') containing precompiled code.", - _precompiledDartBundle.bundleIdentifier]; - result(NO, message); - return; - } - - NSString* path = [FlutterDartProject pathForFlutterAssetsFromBundle:_precompiledDartBundle]; - if (path.length == 0) { - NSString* message = [NSString stringWithFormat: - @"Could not find the 'flutter_assets' dir in " - @"the precompiled Dart bundle with ID '%@'", - _precompiledDartBundle.bundleIdentifier]; - result(NO, message); - return; - } - - std::string bundle_path = path.UTF8String; - blink::Threads::UI()->PostTask([ - engine = engine->GetWeakPtr(), bundle_path, entrypoint = std::string([entrypoint UTF8String]) - ] { - if (engine) - engine->RunBundle(bundle_path, entrypoint); - }); - - result(YES, @"Success"); -} - -#pragma mark - Running from source - -- (void)runFromSourceInEngine:(shell::Engine*)engine - withEntrypoint:(NSString*)entrypoint - result:(LaunchResult)result { - if (_dartSource == nil) { - result(NO, @"Dart source not specified."); - return; - } - - [_dartSource validate:^(BOOL success, NSString* message) { - if (!success) { - return result(NO, message); - } - - std::string bundle_path = _dartSource.flutterAssets.absoluteURL.path.UTF8String; - - if (_dartSource.assetsDirContainsScriptSnapshot) { - blink::Threads::UI()->PostTask([ - engine = engine->GetWeakPtr(), bundle_path, - entrypoint = std::string([entrypoint UTF8String]) - ] { - if (engine) - engine->RunBundle(bundle_path, entrypoint); - }); - } else { - std::string main = _dartSource.dartMain.absoluteURL.path.UTF8String; - std::string packages = _dartSource.packages.absoluteURL.path.UTF8String; - blink::Threads::UI()->PostTask( - [ engine = engine->GetWeakPtr(), bundle_path, main, packages ] { - if (engine) - engine->RunBundleAndSource(bundle_path, main, packages); - }); - } - - result(YES, @"Success"); - }]; -} - -#pragma mark - Misc. - -- (void)dealloc { - [_precompiledDartBundle unload]; - [_precompiledDartBundle release]; - [_dartSource release]; - - [super dealloc]; +- (shell::RunConfiguration)runConfiguration { + return shell::RunConfiguration::InferFromSettings(_settings); } @end diff --git a/shell/platform/darwin/ios/framework/Source/FlutterDartProject_Internal.h b/shell/platform/darwin/ios/framework/Source/FlutterDartProject_Internal.h index 75db7c4049dd9..7fe1fc364f328 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterDartProject_Internal.h +++ b/shell/platform/darwin/ios/framework/Source/FlutterDartProject_Internal.h @@ -5,32 +5,15 @@ #ifndef SHELL_PLATFORM_IOS_FRAMEWORK_SOURCE_FLUTTERDARTPROJECT_INTERNAL_H_ #define SHELL_PLATFORM_IOS_FRAMEWORK_SOURCE_FLUTTERDARTPROJECT_INTERNAL_H_ +#include "flutter/common/settings.h" #include "flutter/shell/common/engine.h" #include "flutter/shell/platform/darwin/ios/framework/Headers/FlutterDartProject.h" -enum VMType { - // An invalid VM configuration. - VMTypeInvalid = 0, - // VM can execute Dart code as an interpreter. - VMTypeInterpreter, - // VM can execute precompiled Dart code. - VMTypePrecompilation, -}; - -typedef void (^LaunchResult)(BOOL success, NSString* message); - @interface FlutterDartProject () -- (void)launchInEngine:(shell::Engine*)engine - embedderVMType:(VMType)type - result:(LaunchResult)result; - -- (void)launchInEngine:(shell::Engine*)engine - withEntrypoint:(NSString*)entrypoint - embedderVMType:(VMType)type - result:(LaunchResult)result; +- (const blink::Settings&)settings; -+ (NSString*)pathForFlutterAssetsFromBundle:(NSBundle*)bundle; +- (shell::RunConfiguration)runConfiguration; @end diff --git a/shell/platform/darwin/ios/framework/Source/FlutterDartSource.h b/shell/platform/darwin/ios/framework/Source/FlutterDartSource.h deleted file mode 100644 index c3881ce065185..0000000000000 --- a/shell/platform/darwin/ios/framework/Source/FlutterDartSource.h +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef SHELL_PLATFORM_IOS_FRAMEWORK_SOURCE_FLUTTERDARTSOURCE_H_ -#define SHELL_PLATFORM_IOS_FRAMEWORK_SOURCE_FLUTTERDARTSOURCE_H_ - -#import - -typedef void (^ValidationResult)(BOOL result, NSString* message); - -@interface FlutterDartSource : NSObject - -@property(nonatomic, readonly) NSURL* dartMain; -@property(nonatomic, readonly) NSURL* packages; -@property(nonatomic, readonly) NSURL* flutterAssets; -@property(nonatomic, readonly) BOOL assetsDirContainsScriptSnapshot; - -- (instancetype)initWithDartMain:(NSURL*)dartMain - packages:(NSURL*)packages - flutterAssets:(NSURL*)flutterAssets NS_DESIGNATED_INITIALIZER; - -- (instancetype)initWithFlutterAssetsWithScriptSnapshot:(NSURL*)flutterAssets - NS_DESIGNATED_INITIALIZER; - -- (void)validate:(ValidationResult)result; - -@end - -#endif // SHELL_PLATFORM_IOS_FRAMEWORK_SOURCE_FLUTTERDARTSOURCE_H_ diff --git a/shell/platform/darwin/ios/framework/Source/FlutterDartSource.mm b/shell/platform/darwin/ios/framework/Source/FlutterDartSource.mm deleted file mode 100644 index aecb4e5806b3a..0000000000000 --- a/shell/platform/darwin/ios/framework/Source/FlutterDartSource.mm +++ /dev/null @@ -1,100 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "flutter/shell/platform/darwin/ios/framework/Source/FlutterDartSource.h" - -@implementation FlutterDartSource - -@synthesize dartMain = _dartMain; -@synthesize packages = _packages; -@synthesize flutterAssets = _flutterAssets; -@synthesize assetsDirContainsScriptSnapshot = _assetsDirContainsScriptSnapshot; - -#pragma mark - Convenience Initializers - -- (instancetype)init { - return [self initWithDartMain:nil packages:nil flutterAssets:nil]; -} - -#pragma mark - Designated Initializers - -- (instancetype)initWithDartMain:(NSURL*)dartMain - packages:(NSURL*)packages - flutterAssets:(NSURL*)flutterAssets { - self = [super init]; - - if (self) { - _dartMain = [dartMain copy]; - _packages = [packages copy]; - _flutterAssets = [flutterAssets copy]; - - NSFileManager* fileManager = [NSFileManager defaultManager]; - - const BOOL dartMainExists = [fileManager fileExistsAtPath:dartMain.absoluteURL.path]; - const BOOL packagesExists = [fileManager fileExistsAtPath:packages.absoluteURL.path]; - - if (!dartMainExists || !packagesExists) { - // We cannot actually verify this without opening up the directory. This is - // just an assumption. - _assetsDirContainsScriptSnapshot = YES; - } - } - - return self; -} - -- (instancetype)initWithFlutterAssetsWithScriptSnapshot:(NSURL*)flutterAssets { - self = [super init]; - - if (self) { - _flutterAssets = [flutterAssets copy]; - _assetsDirContainsScriptSnapshot = YES; - } - - return self; -} - -static BOOL CheckDartProjectURL(NSMutableString* log, NSURL* url, NSString* logLabel) { - if (url == nil) { - [log appendFormat:@"The %@ was not specified.\n", logLabel]; - return false; - } - - if (!url.isFileURL) { - [log appendFormat:@"The %@ must be a file URL.\n", logLabel]; - return false; - } - - if (![[NSFileManager defaultManager] fileExistsAtPath:url.absoluteURL.path]) { - [log appendFormat:@"No file found at '%@' when looking for the %@.\n", url, logLabel]; - return false; - } - - return true; -} - -- (void)validate:(ValidationResult)result { - NSMutableString* log = [[[NSMutableString alloc] init] autorelease]; - - BOOL isValid = YES; - - isValid &= CheckDartProjectURL(log, _flutterAssets, @"Flutter assets"); - - if (!_assetsDirContainsScriptSnapshot) { - isValid &= CheckDartProjectURL(log, _dartMain, @"Dart main"); - isValid &= CheckDartProjectURL(log, _packages, @"Dart packages"); - } - - result(isValid, log); -} - -- (void)dealloc { - [_dartMain release]; - [_packages release]; - [_flutterAssets release]; - - [super dealloc]; -} - -@end diff --git a/shell/platform/darwin/ios/framework/Source/FlutterHeadlessDartRunner.mm b/shell/platform/darwin/ios/framework/Source/FlutterHeadlessDartRunner.mm index 2143f362f7905..dee11d08934a5 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterHeadlessDartRunner.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterHeadlessDartRunner.mm @@ -2,38 +2,84 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#define FML_USED_ON_EMBEDDER + #import "flutter/shell/platform/darwin/ios/framework/Headers/FlutterHeadlessDartRunner.h" +#include #include -#include "flutter/fml/platform/darwin/scoped_nsobject.h" -#include "flutter/shell/common/null_platform_view.h" -#include "flutter/shell/platform/darwin/ios/framework/Source/FlutterDartProject_Internal.h" +#include "flutter/fml/message_loop.h" +#include "flutter/shell/common/engine.h" +#include "flutter/shell/common/rasterizer.h" +#include "flutter/shell/common/run_configuration.h" +#include "flutter/shell/common/shell.h" +#include "flutter/shell/common/switches.h" +#include "flutter/shell/common/thread_host.h" +#include "flutter/shell/platform/darwin/common/command_line.h" +#include "lib/fxl/functional/make_copyable.h" -@interface FlutterHeadlessDartRunner () -@end +static std::unique_ptr CreateHeadlessPlatformView(shell::Shell& shell) { + return std::make_unique(shell, shell.GetTaskRunners()); +} -@implementation FlutterHeadlessDartRunner { - fml::scoped_nsprotocol _dartProject; - std::shared_ptr _platformView; +static std::unique_ptr CreateHeadlessRasterizer(shell::Shell& shell) { + return std::make_unique(shell.GetTaskRunners()); } -- (instancetype)init { - _dartProject.reset([[FlutterDartProject alloc] initFromDefaultSourceForConfiguration]); - _platformView = std::make_shared(); - _platformView->Attach(); - return self; +@implementation FlutterHeadlessDartRunner { + shell::ThreadHost _threadHost; + std::unique_ptr _shell; } - (void)runWithEntrypoint:(NSString*)entrypoint { - const enum VMType type = Dart_IsPrecompiledRuntime() ? VMTypePrecompilation : VMTypeInterpreter; - [_dartProject launchInEngine:&_platformView->engine() - withEntrypoint:entrypoint - embedderVMType:type - result:^(BOOL success, NSString* message) { - if (!success) - NSLog(@"%@", message); - }]; + if (_shell != nullptr || entrypoint.length == 0) { + FXL_LOG(ERROR) << "This headless dart runner was already used to run some code."; + return; + } + + const auto label = "io.flutter.headless"; + + // Create the threads to run the shell on. + _threadHost = { + label, // native thread label + shell::ThreadHost::Type::UI // managed threads to create + }; + + // Configure shell task runners. + auto current_task_runner = fml::MessageLoop::GetCurrent().GetTaskRunner(); + auto single_task_runner = _threadHost.ui_thread->GetTaskRunner(); + blink::TaskRunners task_runners(label, // dart thread label + current_task_runner, // platform + single_task_runner, // gpu + single_task_runner, // ui + single_task_runner // io + ); + + auto settings = shell::SettingsFromCommandLine(shell::CommandLineFromNSProcessInfo()); + + // Create the shell. This is a blocking operation. + _shell = shell::Shell::Create( + std::move(task_runners), // task runners + std::move(settings), // settings + std::bind(&CreateHeadlessPlatformView, std::placeholders::_1), // platform view creation + std::bind(&CreateHeadlessRasterizer, std::placeholders::_1) // rasterzier creation + ); + + if (_shell == nullptr) { + FXL_LOG(ERROR) << "Could not start a shell for the headless dart runner with entrypoint: " + << entrypoint.UTF8String; + return; + } + + // Override the default run configuration with the specified entrypoint. + _shell->GetTaskRunners().GetUITaskRunner()->PostTask( + fxl::MakeCopyable([engine = _shell->GetEngine(), + config = shell::RunConfiguration::InferFromSettings(settings)]() mutable { + if (!engine || !engine->Run(std::move(config))) { + FXL_LOG(ERROR) << "Could not launch engine with configuration."; + } + })); } @end diff --git a/shell/platform/darwin/ios/framework/Source/FlutterView.h b/shell/platform/darwin/ios/framework/Source/FlutterView.h index 661940620c947..5e3d303401725 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterView.h +++ b/shell/platform/darwin/ios/framework/Source/FlutterView.h @@ -7,8 +7,14 @@ #include +#include + +#include "flutter/shell/platform/darwin/ios/ios_surface.h" + @interface FlutterView : UIView +- (std::unique_ptr)createSurface; + @end #endif // SHELL_PLATFORM_IOS_FRAMEWORK_SOURCE_FLUTTER_VIEW_H_ diff --git a/shell/platform/darwin/ios/framework/Source/FlutterView.mm b/shell/platform/darwin/ios/framework/Source/FlutterView.mm index 7c382838c6f5c..27e3d48c4cb50 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterView.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterView.mm @@ -5,20 +5,41 @@ #include "flutter/shell/platform/darwin/ios/framework/Source/FlutterView.h" #include "flutter/common/settings.h" -#include "flutter/common/threads.h" +#include "flutter/common/task_runners.h" #include "flutter/flow/layers/layer_tree.h" +#include "flutter/fml/trace_event.h" #include "flutter/shell/common/platform_view.h" #include "flutter/shell/common/rasterizer.h" #include "flutter/shell/common/shell.h" +#include "flutter/shell/platform/darwin/ios/framework/Source/FlutterViewController_Internal.h" +#include "flutter/shell/platform/darwin/ios/ios_surface_gl.h" +#include "flutter/shell/platform/darwin/ios/ios_surface_software.h" #include "lib/fxl/synchronization/waitable_event.h" #include "third_party/skia/include/utils/mac/SkCGUtils.h" -@interface FlutterView () +@interface FlutterView () @end @implementation FlutterView +- (FlutterViewController*)flutterViewController { + // Find the first view controller in the responder chain and see if it is a FlutterViewController. + for (UIResponder* responder = self.nextResponder; responder != nil; + responder = responder.nextResponder) { + if ([responder isKindOfClass:[UIViewController class]]) { + if ([responder isKindOfClass:[FlutterViewController class]]) { + return reinterpret_cast(responder); + } else { + // Should only happen if a non-FlutterViewController tries to somehow (via dynamic class + // resolution or reparenting) set a FlutterView as its view. + return nil; + } + } + } + return nil; +} + - (void)layoutSubviews { if ([self.layer isKindOfClass:[CAEAGLLayer class]]) { CAEAGLLayer* layer = reinterpret_cast(self.layer); @@ -40,13 +61,24 @@ + (Class)layerClass { #endif // TARGET_IPHONE_SIMULATOR } +- (std::unique_ptr)createSurface { + if ([self.layer isKindOfClass:[CAEAGLLayer class]]) { + fml::scoped_nsobject eagl_layer( + reinterpret_cast([self.layer retain])); + return std::make_unique(std::move(eagl_layer)); + } else { + fml::scoped_nsobject layer(reinterpret_cast([self.layer retain])); + return std::make_unique(std::move(layer)); + } +} + - (BOOL)enableInputClicksWhenVisible { return YES; } -void SnapshotRasterizer(fml::WeakPtr rasterizer, - CGContextRef context, - bool is_opaque) { +static void SnapshotRasterizer(fml::WeakPtr rasterizer, + CGContextRef context, + bool is_opaque) { if (!rasterizer) { return; } @@ -77,10 +109,11 @@ void SnapshotRasterizer(fml::WeakPtr rasterizer, SkCanvas canvas(bitmap); - { - flow::CompositorContext compositor_context(nullptr); - auto frame = compositor_context.AcquireFrame(nullptr, &canvas, false /* instrumentation */); - layer_tree->Raster(frame, false /* ignore raster cache. */); + flow::CompositorContext compositor_context; + + if (auto frame = compositor_context.AcquireFrame(nullptr, &canvas, false /* instrumentation */)) { + layer_tree->Preroll(*frame, true /* ignore raster cache */); + layer_tree->Paint(*frame); } canvas.flush(); @@ -89,41 +122,6 @@ void SnapshotRasterizer(fml::WeakPtr rasterizer, SkCGDrawBitmap(context, bitmap, 0, 0); } -static fml::WeakPtr GetRandomRasterizer() { - fml::WeakPtr rasterizer; - shell::Shell::Shared().IteratePlatformViews([&rasterizer](shell::PlatformView* view) -> bool { - rasterizer = view->rasterizer().GetWeakRasterizerPtr(); - // We just grab the first rasterizer so there is no need to iterate - // further. - return false; - }); - return rasterizer; -} - -void SnapshotContents(CGContextRef context, bool is_opaque) { - // TODO(chinmaygarde): Currently, there is no way to get the rasterizer for - // a particular platform view from the shell. But, for now, we only have one - // platform view. So use that. Once we support multiple platform views, the - // shell will need to provide a way to get the rasterizer for a specific - // platform view. - SnapshotRasterizer(GetRandomRasterizer(), context, is_opaque); -} - -void SnapshotContentsSync(CGContextRef context, UIView* view) { - auto gpu_thread = blink::Threads::Gpu(); - - if (!gpu_thread) { - return; - } - - fxl::AutoResetWaitableEvent latch; - gpu_thread->PostTask([&latch, context, view]() { - SnapshotContents(context, [view isOpaque]); - latch.Signal(); - }); - latch.Wait(); -} - // Override the default CALayerDelegate method so that APIs that attempt to // screenshot the view display contents correctly. We cannot depend on // reading @@ -132,7 +130,22 @@ void SnapshotContentsSync(CGContextRef context, UIView* view) { // 2: The call is made of the platform thread and not the GPU thread. // 3: There may be a software rasterizer. - (void)drawLayer:(CALayer*)layer inContext:(CGContextRef)context { - SnapshotContentsSync(context, self); + TRACE_EVENT0("flutter", "SnapshotFlutterView"); + FlutterViewController* controller = [self flutterViewController]; + + if (controller == nil) { + return; + } + + auto& shell = [controller shell]; + + fxl::AutoResetWaitableEvent latch; + shell.GetTaskRunners().GetGPUTaskRunner()->PostTask( + [&latch, rasterizer = shell.GetRasterizer(), context, opaque = layer.opaque]() { + SnapshotRasterizer(std::move(rasterizer), context, opaque); + latch.Signal(); + }); + latch.Wait(); } @end diff --git a/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm b/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm index 87682de7b3514..e7474167070c9 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm @@ -2,90 +2,54 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#import "flutter/shell/platform/darwin/ios/framework/Headers/FlutterViewController.h" +#define FML_USED_ON_EMBEDDER + +#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterViewController_Internal.h" #include -#include "flutter/common/threads.h" -#include "flutter/flow/texture.h" +#include "flutter/fml/message_loop.h" #include "flutter/fml/platform/darwin/platform_version.h" -#include "flutter/fml/platform/darwin/scoped_block.h" #include "flutter/fml/platform/darwin/scoped_nsobject.h" -#include "flutter/lib/ui/painting/resource_context.h" -#include "flutter/shell/platform/darwin/common/buffer_conversions.h" -#include "flutter/shell/platform/darwin/common/platform_mac.h" -#include "flutter/shell/platform/darwin/ios/framework/Headers/FlutterCodecs.h" +#include "flutter/shell/common/thread_host.h" #include "flutter/shell/platform/darwin/ios/framework/Source/FlutterDartProject_Internal.h" #include "flutter/shell/platform/darwin/ios/framework/Source/FlutterPlatformPlugin.h" #include "flutter/shell/platform/darwin/ios/framework/Source/FlutterTextInputDelegate.h" #include "flutter/shell/platform/darwin/ios/framework/Source/FlutterTextInputPlugin.h" -#include "flutter/shell/platform/darwin/ios/framework/Source/flutter_main_ios.h" +#include "flutter/shell/platform/darwin/ios/framework/Source/FlutterView.h" #include "flutter/shell/platform/darwin/ios/framework/Source/flutter_touch_mapper.h" -#include "flutter/shell/platform/darwin/ios/ios_external_texture_gl.h" +#include "flutter/shell/platform/darwin/ios/framework/Source/platform_message_response_darwin.h" #include "flutter/shell/platform/darwin/ios/platform_view_ios.h" -#include "lib/fxl/functional/make_copyable.h" -#include "lib/fxl/time/time_delta.h" - -namespace { - -typedef void (^PlatformMessageResponseCallback)(NSData*); - -class PlatformMessageResponseDarwin : public blink::PlatformMessageResponse { - FRIEND_MAKE_REF_COUNTED(PlatformMessageResponseDarwin); - - public: - void Complete(std::vector data) override { - fxl::RefPtr self(this); - blink::Threads::Platform()->PostTask( - fxl::MakeCopyable([ self, data = std::move(data) ]() mutable { - self->callback_.get()(shell::GetNSDataFromVector(data)); - })); - } - - void CompleteEmpty() override { - fxl::RefPtr self(this); - blink::Threads::Platform()->PostTask( - fxl::MakeCopyable([self]() mutable { self->callback_.get()(nil); })); - } - - private: - explicit PlatformMessageResponseDarwin(PlatformMessageResponseCallback callback) - : callback_(callback, fml::OwnershipPolicy::Retain) {} - fml::ScopedBlock callback_; -}; - -} // namespace - -@interface FlutterViewController () +@interface FlutterViewController () @end @implementation FlutterViewController { - fml::scoped_nsprotocol _dartProject; + fml::scoped_nsobject _dartProject; + shell::ThreadHost _threadHost; + std::unique_ptr _shell; + + // Channels + fml::scoped_nsobject _platformPlugin; + fml::scoped_nsobject _textInputPlugin; + fml::scoped_nsobject _localizationChannel; + fml::scoped_nsobject _navigationChannel; + fml::scoped_nsobject _platformChannel; + fml::scoped_nsobject _textInputChannel; + fml::scoped_nsobject _lifecycleChannel; + fml::scoped_nsobject _systemChannel; + fml::scoped_nsobject _settingsChannel; + + // We keep a separate reference to this and create it ahead of time because we want to be able to + // setup a shell along with its platform view before the view has to appear. + fml::scoped_nsobject _flutterView; + fml::scoped_nsobject _launchView; UIInterfaceOrientationMask _orientationPreferences; UIStatusBarStyle _statusBarStyle; blink::ViewportMetrics _viewportMetrics; shell::TouchMapper _touchMapper; - std::shared_ptr _platformView; - fml::scoped_nsprotocol _platformPlugin; - fml::scoped_nsprotocol _textInputPlugin; - fml::scoped_nsprotocol _localizationChannel; - fml::scoped_nsprotocol _navigationChannel; - fml::scoped_nsprotocol _platformChannel; - fml::scoped_nsprotocol _textInputChannel; - fml::scoped_nsprotocol _lifecycleChannel; - fml::scoped_nsprotocol _systemChannel; - fml::scoped_nsprotocol _settingsChannel; - fml::scoped_nsprotocol _launchView; int64_t _nextTextureId; BOOL _initialized; - BOOL _connected; -} - -+ (void)initialize { - if (self == [FlutterViewController class]) { - shell::FlutterMain(); - } } #pragma mark - Manage and override all designated initializers @@ -125,26 +89,90 @@ - (void)performCommonViewControllerInitialization { _orientationPreferences = UIInterfaceOrientationMaskAll; _statusBarStyle = UIStatusBarStyleDefault; - _platformView = std::make_shared( - reinterpret_cast(self.view.layer), self); - - _platformView->Attach( - // First frame callback. - [self]() { - TRACE_EVENT0("flutter", "First Frame"); - if (_launchView) { - [UIView animateWithDuration:0.2 - animations:^{ - _launchView.get().alpha = 0; - } - completion:^(BOOL finished) { - [_launchView.get() removeFromSuperview]; - _launchView.reset(); - }]; + + if ([self setupShell]) { + [self setupChannels]; + [self setupNotificationCenterObservers]; + } +} + +- (shell::Shell&)shell { + FXL_DCHECK(_shell); + return *_shell; +} + +- (fml::WeakPtr)iosPlatformView { + FXL_DCHECK(_shell); + return _shell->GetPlatformView(); +} + +- (BOOL)setupShell { + FXL_DCHECK(_shell == nullptr); + + auto threadLabel = [NSString stringWithFormat:@"io.flutter.%p", self]; + + _threadHost = { + threadLabel.UTF8String, // label + shell::ThreadHost::Type::UI | shell::ThreadHost::Type::GPU | shell::ThreadHost::Type::IO}; + + // The current thread will be used as the platform thread. Ensure that the message loop is + // initialized. + fml::MessageLoop::EnsureInitializedForCurrentThread(); + + blink::TaskRunners task_runners(threadLabel.UTF8String, // label + fml::MessageLoop::GetCurrent().GetTaskRunner(), // platform + _threadHost.gpu_thread->GetTaskRunner(), // gpu + _threadHost.ui_thread->GetTaskRunner(), // ui + _threadHost.io_thread->GetTaskRunner() // io + ); + + _flutterView.reset([[FlutterView alloc] init]); + + // Lambda captures by pointers to ObjC objects are fine here because the create call is + // synchronous. + shell::Shell::CreateCallback on_create_platform_view = + [flutter_view_controller = self, flutter_view = _flutterView.get()](shell::Shell& shell) { + auto platform_view_ios = std::make_unique( + shell, // delegate + shell.GetTaskRunners(), // task runners + flutter_view_controller, // flutter view controller owner + flutter_view // flutter view owner + ); + return platform_view_ios; + }; + + shell::Shell::CreateCallback on_create_rasterizer = [](shell::Shell& shell) { + return std::make_unique(shell.GetTaskRunners()); + }; + + // Create the shell. + _shell = shell::Shell::Create(std::move(task_runners), // + [_dartProject settings], // + on_create_platform_view, // + on_create_rasterizer // + ); + + if (!_shell) { + FXL_LOG(ERROR) << "Could not setup a shell to run the Dart application."; + return false; + } + + // Launch the Dart application with the inferred run configuration. + _shell->GetTaskRunners().GetUITaskRunner()->PostTask( + fxl::MakeCopyable([engine = _shell->GetEngine(), // + config = [_dartProject.get() runConfiguration] // + ]() mutable { + if (engine) { + auto result = engine->Run(std::move(config)); + if (!result) { + FXL_LOG(ERROR) << "Could not launch engine with configuration."; + } } - }); - _platformView->SetupResourceContextOnIOThread(); + })); + return true; +} +- (void)setupChannels { _localizationChannel.reset([[FlutterMethodChannel alloc] initWithName:@"flutter/localization" binaryMessenger:self @@ -190,9 +218,8 @@ - (void)performCommonViewControllerInitialization { [_textInputChannel.get() setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) { [_textInputPlugin.get() handleMethodCall:call result:result]; }]; - _platformView->SetTextInputPlugin(_textInputPlugin); - - [self setupNotificationCenterObservers]; + static_cast(_shell->GetPlatformView().get()) + ->SetTextInputPlugin(_textInputPlugin); } - (void)setupNotificationCenterObservers { @@ -261,50 +288,24 @@ - (void)setupNotificationCenterObservers { - (void)setInitialRoute:(NSString*)route { [_navigationChannel.get() invokeMethod:@"setInitialRoute" arguments:route]; } -#pragma mark - Initializing the engine - -- (void)alertView:(UIAlertView*)alertView clickedButtonAtIndex:(NSInteger)buttonIndex { - exit(0); -} - -- (void)connectToEngineAndLoad { - if (_connected) - return; - _connected = YES; - - TRACE_EVENT0("flutter", "connectToEngineAndLoad"); - - // We ask the VM to check what it supports. - const enum VMType type = Dart_IsPrecompiledRuntime() ? VMTypePrecompilation : VMTypeInterpreter; - - [_dartProject launchInEngine:&_platformView->engine() - embedderVMType:type - result:^(BOOL success, NSString* message) { - if (!success) { - UIAlertView* alert = [[UIAlertView alloc] initWithTitle:@"Launch Error" - message:message - delegate:self - cancelButtonTitle:@"OK" - otherButtonTitles:nil]; - [alert show]; - [alert release]; - } - }]; -} #pragma mark - Loading the view - (void)loadView { - FlutterView* view = [[FlutterView alloc] init]; - - self.view = view; + self.view = _flutterView.get(); self.view.multipleTouchEnabled = YES; self.view.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; - [view release]; + [self installLaunchViewIfNecessary]; +} + +#pragma mark - Managing launch views +- (void)installLaunchViewIfNecessary { // Show the launch screen view again on top of the FlutterView if available. // This launch screen view will be removed once the first Flutter frame is rendered. + [_launchView.get() removeFromSuperview]; + _launchView.reset(); NSString* launchStoryboardName = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"UILaunchStoryboardName"]; if (launchStoryboardName && !self.isBeingPresented && !self.isMovingToParentViewController) { @@ -319,16 +320,57 @@ - (void)loadView { } } +- (void)removeLaunchViewIfPresent { + if (!_launchView) { + return; + } + + [UIView animateWithDuration:0.2 + animations:^{ + _launchView.get().alpha = 0; + } + completion:^(BOOL finished) { + [_launchView.get() removeFromSuperview]; + _launchView.reset(); + }]; +} + +- (void)installLaunchViewCallback { + if (!_shell || !_launchView) { + return; + } + auto weak_platform_view = _shell->GetPlatformView(); + if (!weak_platform_view) { + return; + } + __unsafe_unretained auto weak_flutter_view_controller = self; + // This is on the platform thread. + weak_platform_view->SetNextFrameCallback( + [weak_platform_view, weak_flutter_view_controller, + task_runner = _shell->GetTaskRunners().GetPlatformTaskRunner()]() { + // This is on the GPU thread. + task_runner->PostTask([weak_platform_view, weak_flutter_view_controller]() { + // We check if the weak platform view is alive. If it is alive, then the view controller + // also has to be alive since the view controller owns the platform view via the shell + // association. Thus, we are not convinced that the unsafe unretained weak object is in + // fact alive. + if (weak_platform_view) { + [weak_flutter_view_controller removeLaunchViewIfPresent]; + } + }); + }); +} + #pragma mark - Surface creation and teardown updates - (void)surfaceUpdated:(BOOL)appeared { - FXL_CHECK(_platformView != nullptr); - // NotifyCreated/NotifyDestroyed are synchronous and require hops between the UI and GPU thread. if (appeared) { - _platformView->NotifyCreated(); + [self installLaunchViewCallback]; + _shell->GetPlatformView()->NotifyCreated(); + } else { - _platformView->NotifyDestroyed(); + _shell->GetPlatformView()->NotifyDestroyed(); } } @@ -336,7 +378,6 @@ - (void)surfaceUpdated:(BOOL)appeared { - (void)viewWillAppear:(BOOL)animated { TRACE_EVENT0("flutter", "viewWillAppear"); - [self connectToEngineAndLoad]; // Only recreate surface on subsequent appearances when viewport metrics are known. // First time surface creation is done on viewDidLayoutSubviews. if (_viewportMetrics.physical_width) @@ -391,8 +432,6 @@ - (void)applicationWillResignActive:(NSNotification*)notification { - (void)applicationDidEnterBackground:(NSNotification*)notification { TRACE_EVENT0("flutter", "applicationDidEnterBackground"); [self surfaceUpdated:NO]; - // GrContext operations are blocked when the app is in the background. - blink::ResourceContext::Freeze(); [_lifecycleChannel.get() sendMessage:@"AppLifecycleState.paused"]; } @@ -400,7 +439,6 @@ - (void)applicationWillEnterForeground:(NSNotification*)notification { TRACE_EVENT0("flutter", "applicationWillEnterForeground"); if (_viewportMetrics.physical_width) [self surfaceUpdated:YES]; - blink::ResourceContext::Unfreeze(); [_lifecycleChannel.get() sendMessage:@"AppLifecycleState.inactive"]; } @@ -547,10 +585,11 @@ - (void)dispatchTouches:(NSSet*)touches phase:(UITouchPhase)phase { packet->SetPointerData(i++, pointer_data); } - blink::Threads::UI()->PostTask(fxl::MakeCopyable( - [ engine = _platformView->engine().GetWeakPtr(), packet = std::move(packet) ] { - if (engine.get()) + _shell->GetTaskRunners().GetUITaskRunner()->PostTask( + fxl::MakeCopyable([engine = _shell->GetEngine(), packet = std::move(packet)] { + if (engine) { engine->DispatchPointerDataPacket(*packet); + } })); } @@ -573,13 +612,11 @@ - (void)touchesCancelled:(NSSet*)touches withEvent:(UIEvent*)event { #pragma mark - Handle view resizing - (void)updateViewportMetrics { - blink::Threads::UI()->PostTask( - [ weak_platform_view = _platformView->GetWeakPtr(), metrics = _viewportMetrics ] { - if (!weak_platform_view) { - return; + _shell->GetTaskRunners().GetUITaskRunner()->PostTask( + [engine = _shell->GetEngine(), metrics = _viewportMetrics]() { + if (engine) { + engine->SetViewportMetrics(std::move(metrics)); } - weak_platform_view->UpdateSurfaceSize(); - weak_platform_view->engine().SetViewportMetrics(metrics); }); } @@ -709,7 +746,7 @@ - (void)onVoiceOverChanged:(NSNotification*)notification { #else bool enabled = UIAccessibilityIsVoiceOverRunning(); #endif - _platformView->ToggleAccessibility(self.view, enabled); + _shell->GetPlatformView()->SetSemanticsEnabled(enabled); } #pragma mark - Memory Notifications @@ -875,37 +912,41 @@ - (void)sendOnChannel:(NSString*)channel message:(NSData*)message binaryReply:(FlutterBinaryReply)callback { NSAssert(channel, @"The channel must not be null"); - fxl::RefPtr response = + fxl::RefPtr response = (callback == nil) ? nullptr - : fxl::MakeRefCounted(^(NSData* reply) { - callback(reply); - }); + : fxl::MakeRefCounted( + ^(NSData* reply) { + callback(reply); + }, + _shell->GetTaskRunners().GetPlatformTaskRunner()); fxl::RefPtr platformMessage = (message == nil) ? fxl::MakeRefCounted(channel.UTF8String, response) : fxl::MakeRefCounted( channel.UTF8String, shell::GetVectorFromNSData(message), response); - _platformView->DispatchPlatformMessage(platformMessage); + + _shell->GetPlatformView()->DispatchPlatformMessage(platformMessage); } - (void)setMessageHandlerOnChannel:(NSString*)channel binaryMessageHandler:(FlutterBinaryMessageHandler)handler { NSAssert(channel, @"The channel must not be null"); - _platformView->platform_message_router().SetMessageHandler(channel.UTF8String, handler); + [self iosPlatformView] -> GetPlatformMessageRouter().SetMessageHandler(channel.UTF8String, + handler); } #pragma mark - FlutterTextureRegistry - (int64_t)registerTexture:(NSObject*)texture { int64_t textureId = _nextTextureId++; - _platformView->RegisterExternalTexture(textureId, texture); + [self iosPlatformView] -> RegisterExternalTexture(textureId, texture); return textureId; } - (void)unregisterTexture:(int64_t)textureId { - _platformView->UnregisterTexture(textureId); + _shell->GetPlatformView()->UnregisterTexture(textureId); } - (void)textureFrameAvailable:(int64_t)textureId { - _platformView->MarkTextureFrameAvailable(textureId); + _shell->GetPlatformView()->MarkTextureFrameAvailable(textureId); } @end diff --git a/shell/platform/darwin/ios/framework/Source/FlutterViewController_Internal.h b/shell/platform/darwin/ios/framework/Source/FlutterViewController_Internal.h new file mode 100644 index 0000000000000..482379c8f17eb --- /dev/null +++ b/shell/platform/darwin/ios/framework/Source/FlutterViewController_Internal.h @@ -0,0 +1,17 @@ +// Copyright 2017 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. + +#ifndef FLUTTER_SHELL_PLATFORM_DARWIN_IOS_FRAMEWORK_SOURCE_FLUTTERVIEWCONTROLLER_INTERNAL_H_ +#define FLUTTER_SHELL_PLATFORM_DARWIN_IOS_FRAMEWORK_SOURCE_FLUTTERVIEWCONTROLLER_INTERNAL_H_ + +#include "flutter/shell/common/shell.h" +#include "flutter/shell/platform/darwin/ios/framework/Headers/FlutterViewController.h" + +@interface FlutterViewController () + +- (shell::Shell&)shell; + +@end + +#endif // FLUTTER_SHELL_PLATFORM_DARWIN_IOS_FRAMEWORK_SOURCE_FLUTTERVIEWCONTROLLER_INTERNAL_H_ diff --git a/shell/platform/darwin/ios/framework/Source/accessibility_bridge.mm b/shell/platform/darwin/ios/framework/Source/accessibility_bridge.mm index 3f96f37a9c2db..6a545b95e4276 100644 --- a/shell/platform/darwin/ios/framework/Source/accessibility_bridge.mm +++ b/shell/platform/darwin/ios/framework/Source/accessibility_bridge.mm @@ -427,7 +427,7 @@ - (BOOL)accessibilityScroll:(UIAccessibilityScrollDirection)direction { weak_factory_(this) { accessibility_channel_.reset([[FlutterBasicMessageChannel alloc] initWithName:@"flutter/accessibility" - binaryMessenger:platform_view->binary_messenger() + binaryMessenger:platform_view->GetOwnerViewController() codec:[FlutterStandardMessageCodec sharedInstance]]); [accessibility_channel_.get() setMessageHandler:^(id message, FlutterReply reply) { HandleEvent((NSDictionary*)message); @@ -440,7 +440,7 @@ - (BOOL)accessibilityScroll:(UIAccessibilityScrollDirection)direction { } UIView* AccessibilityBridge::textInputView() { - return [platform_view_->text_input_plugin() textInputView]; + return [platform_view_->GetTextInputPlugin() textInputView]; } void AccessibilityBridge::UpdateSemantics(blink::SemanticsNodeUpdates nodes) { diff --git a/shell/platform/darwin/ios/framework/Source/flutter_main_ios.h b/shell/platform/darwin/ios/framework/Source/flutter_main_ios.h deleted file mode 100644 index f2e54dd2c2806..0000000000000 --- a/shell/platform/darwin/ios/framework/Source/flutter_main_ios.h +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef FLUTTER_SHELL_PLATFORM_DARWIN_IOS_FRAMEWORK_SOURCE_FLUTTER_MAIN_IOS_H_ -#define FLUTTER_SHELL_PLATFORM_DARWIN_IOS_FRAMEWORK_SOURCE_FLUTTER_MAIN_IOS_H_ - -#include "lib/fxl/macros.h" - -namespace shell { - -/// Initializes the Flutter shell. This must be called before interacting with -/// the engine in any way. It is safe to call this method multiple times. -void FlutterMain(); - -} // namespace shell - -#endif // FLUTTER_SHELL_PLATFORM_DARWIN_IOS_FRAMEWORK_SOURCE_FLUTTER_MAIN_IOS_H_ diff --git a/shell/platform/darwin/ios/framework/Source/flutter_main_ios.mm b/shell/platform/darwin/ios/framework/Source/flutter_main_ios.mm deleted file mode 100644 index aa7ad5fd85727..0000000000000 --- a/shell/platform/darwin/ios/framework/Source/flutter_main_ios.mm +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "flutter/shell/platform/darwin/ios/framework/Source/flutter_main_ios.h" - -#include "flutter/shell/platform/darwin/common/platform_mac.h" -#include "flutter/shell/platform/darwin/ios/framework/Headers/FlutterViewController.h" -#include "flutter/shell/platform/darwin/ios/framework/Source/FlutterDartProject_Internal.h" - -namespace shell { - -void FlutterMain() { - NSBundle* bundle = [NSBundle bundleForClass:[FlutterViewController class]]; - NSString* icuDataPath = [bundle pathForResource:@"icudtl" ofType:@"dat"]; - NSString* libraryName = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"FLTLibraryPath"]; - - NSBundle* mainBundle = [NSBundle mainBundle]; - NSString* flutterAssetsPath = [FlutterDartProject pathForFlutterAssetsFromBundle:mainBundle]; - - shell::PlatformMacMain(icuDataPath.UTF8String, libraryName != nil ? libraryName.UTF8String : "", - flutterAssetsPath.UTF8String); -} - -} // namespace shell diff --git a/shell/platform/darwin/ios/framework/Source/platform_message_response_darwin.h b/shell/platform/darwin/ios/framework/Source/platform_message_response_darwin.h new file mode 100644 index 0000000000000..b861c5036e97d --- /dev/null +++ b/shell/platform/darwin/ios/framework/Source/platform_message_response_darwin.h @@ -0,0 +1,50 @@ +// Copyright 2018 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. + +#ifndef FLUTTER_SHELL_PLATFORM_DARWIN_IOS_FRAMEWORK_SOURCE_PLATFORM_MESSAGE_RESPONSE_DARWIN_H_ +#define FLUTTER_SHELL_PLATFORM_DARWIN_IOS_FRAMEWORK_SOURCE_PLATFORM_MESSAGE_RESPONSE_DARWIN_H_ + +#include + +#include "flutter/fml/platform/darwin/scoped_block.h" +#include "flutter/fml/task_runner.h" +#include "flutter/lib/ui/window/platform_message_response.h" +#include "flutter/shell/platform/darwin/common/buffer_conversions.h" +#include "lib/fxl/functional/make_copyable.h" +#include "lib/fxl/macros.h" + +typedef void (^PlatformMessageResponseCallback)(NSData*); + +namespace shell { + +class PlatformMessageResponseDarwin : public blink::PlatformMessageResponse { + public: + void Complete(std::vector data) override { + fxl::RefPtr self(this); + platform_task_runner_->PostTask(fxl::MakeCopyable([self, data = std::move(data)]() mutable { + self->callback_.get()(shell::GetNSDataFromVector(data)); + })); + } + + void CompleteEmpty() override { + fxl::RefPtr self(this); + platform_task_runner_->PostTask( + fxl::MakeCopyable([self]() mutable { self->callback_.get()(nil); })); + } + + private: + explicit PlatformMessageResponseDarwin(PlatformMessageResponseCallback callback, + fxl::RefPtr platform_task_runner) + : callback_(callback, fml::OwnershipPolicy::Retain), + platform_task_runner_(std::move(platform_task_runner)) {} + + fml::ScopedBlock callback_; + fxl::RefPtr platform_task_runner_; + + FRIEND_MAKE_REF_COUNTED(PlatformMessageResponseDarwin); +}; + +} // namespace shell + +#endif // FLUTTER_SHELL_PLATFORM_DARWIN_IOS_FRAMEWORK_SOURCE_PLATFORM_MESSAGE_RESPONSE_DARWIN_H_ diff --git a/shell/platform/darwin/ios/framework/Source/platform_message_response_darwin.mm b/shell/platform/darwin/ios/framework/Source/platform_message_response_darwin.mm new file mode 100644 index 0000000000000..8590b4e36942b --- /dev/null +++ b/shell/platform/darwin/ios/framework/Source/platform_message_response_darwin.mm @@ -0,0 +1,11 @@ +// Copyright 2018 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. + +#include "flutter/shell/platform/darwin/ios/framework/Source/platform_message_response_darwin.h" + +namespace shell { + +// + +} // namespace shell diff --git a/shell/platform/darwin/ios/framework/Source/platform_message_router.h b/shell/platform/darwin/ios/framework/Source/platform_message_router.h index cfa91c04771c3..d2488f82fe47c 100644 --- a/shell/platform/darwin/ios/framework/Source/platform_message_router.h +++ b/shell/platform/darwin/ios/framework/Source/platform_message_router.h @@ -7,6 +7,7 @@ #include +#include "flutter/fml/platform/darwin/scoped_block.h" #include "flutter/lib/ui/window/platform_message.h" #include "flutter/shell/platform/darwin/ios/framework/Headers/FlutterBinaryMessenger.h" #include "lib/fxl/memory/weak_ptr.h" @@ -18,13 +19,13 @@ class PlatformMessageRouter { PlatformMessageRouter(); ~PlatformMessageRouter(); - void HandlePlatformMessage(fxl::RefPtr message); + void HandlePlatformMessage(fxl::RefPtr message) const; void SetMessageHandler(const std::string& channel, FlutterBinaryMessageHandler handler); private: - std::unordered_map + std::unordered_map> message_handlers_; FXL_DISALLOW_COPY_AND_ASSIGN(PlatformMessageRouter); diff --git a/shell/platform/darwin/ios/framework/Source/platform_message_router.mm b/shell/platform/darwin/ios/framework/Source/platform_message_router.mm index 70625143b623e..3ab75bff522f8 100644 --- a/shell/platform/darwin/ios/framework/Source/platform_message_router.mm +++ b/shell/platform/darwin/ios/framework/Source/platform_message_router.mm @@ -14,7 +14,8 @@ PlatformMessageRouter::~PlatformMessageRouter() = default; -void PlatformMessageRouter::HandlePlatformMessage(fxl::RefPtr message) { +void PlatformMessageRouter::HandlePlatformMessage( + fxl::RefPtr message) const { fxl::RefPtr completer = message->response(); auto it = message_handlers_.find(message->channel()); if (it != message_handlers_.end()) { @@ -41,14 +42,10 @@ void PlatformMessageRouter::SetMessageHandler(const std::string& channel, FlutterBinaryMessageHandler handler) { - if (handler) - message_handlers_[channel] = [handler copy]; - else { - auto it = message_handlers_.find(channel); - if (it != message_handlers_.end()) { - [it->second release]; - message_handlers_.erase(it); - } + message_handlers_.erase(channel); + if (handler) { + message_handlers_[channel] = + fml::ScopedBlock{handler, fml::OwnershipPolicy::Retain}; } } diff --git a/shell/platform/darwin/ios/framework/Source/vsync_waiter_ios.h b/shell/platform/darwin/ios/framework/Source/vsync_waiter_ios.h index 6a3362b215667..23aaf02510d6d 100644 --- a/shell/platform/darwin/ios/framework/Source/vsync_waiter_ios.h +++ b/shell/platform/darwin/ios/framework/Source/vsync_waiter_ios.h @@ -5,27 +5,26 @@ #ifndef FLUTTER_SHELL_PLATFORM_DARWIN_IOS_FRAMEWORK_SOURCE_VSYNC_WAITER_IOS_H_ #define FLUTTER_SHELL_PLATFORM_DARWIN_IOS_FRAMEWORK_SOURCE_VSYNC_WAITER_IOS_H_ +#include "flutter/fml/memory/weak_ptr.h" +#include "flutter/fml/platform/darwin/scoped_nsobject.h" #include "flutter/shell/common/vsync_waiter.h" #include "lib/fxl/macros.h" -#if __OBJC__ @class VSyncClient; -#else // __OBJC__ -class VSyncClient; -#endif // __OBJC__ namespace shell { -class VsyncWaiterIOS : public VsyncWaiter { +class VsyncWaiterIOS final : public VsyncWaiter { public: - VsyncWaiterIOS(); - ~VsyncWaiterIOS() override; + VsyncWaiterIOS(blink::TaskRunners task_runners); - void AsyncWaitForVsync(Callback callback) override; + ~VsyncWaiterIOS() override; private: - Callback callback_; - VSyncClient* client_; + fml::scoped_nsobject client_; + + // |shell::VsyncWaiter| + void AwaitVSync() override; FXL_DISALLOW_COPY_AND_ASSIGN(VsyncWaiterIOS); }; diff --git a/shell/platform/darwin/ios/framework/Source/vsync_waiter_ios.mm b/shell/platform/darwin/ios/framework/Source/vsync_waiter_ios.mm index 78d6678189123..4589368acd3fb 100644 --- a/shell/platform/darwin/ios/framework/Source/vsync_waiter_ios.mm +++ b/shell/platform/darwin/ios/framework/Source/vsync_waiter_ios.mm @@ -10,29 +10,62 @@ #include #include -#include "flutter/common/threads.h" +#include "flutter/common/task_runners.h" #include "flutter/glue/trace_event.h" #include "lib/fxl/logging.h" @interface VSyncClient : NSObject +- (instancetype)initWithTaskRunner:(fxl::RefPtr)task_runner + callback:(shell::VsyncWaiter::Callback)callback; + +- (void)await; + +- (void)invalidate; + @end +namespace shell { + +VsyncWaiterIOS::VsyncWaiterIOS(blink::TaskRunners task_runners) + : VsyncWaiter(std::move(task_runners)), + client_([[VSyncClient alloc] initWithTaskRunner:task_runners_.GetUITaskRunner() + callback:std::bind(&VsyncWaiterIOS::FireCallback, + this, + std::placeholders::_1, + std::placeholders::_2)]) {} + +VsyncWaiterIOS::~VsyncWaiterIOS() { + // This way, we will get no more callbacks from the display link that holds a weak (non-nilling) + // reference to this C++ object. + [client_.get() invalidate]; +} + +void VsyncWaiterIOS::AwaitVSync() { + [client_.get() await]; +} + +} // namespace shell + @implementation VSyncClient { - CADisplayLink* _displayLink; - shell::VsyncWaiter::Callback _pendingCallback; + shell::VsyncWaiter::Callback callback_; + fml::scoped_nsobject display_link_; } -- (instancetype)init { +- (instancetype)initWithTaskRunner:(fxl::RefPtr)task_runner + callback:(shell::VsyncWaiter::Callback)callback { self = [super init]; if (self) { - _displayLink = - [[CADisplayLink displayLinkWithTarget:self selector:@selector(onDisplayLink:)] retain]; - _displayLink.paused = YES; - - blink::Threads::UI()->PostTask([client = [self retain]]() { - [client->_displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes]; + callback_ = std::move(callback); + display_link_ = fml::scoped_nsobject { + [[CADisplayLink displayLinkWithTarget:self selector:@selector(onDisplayLink:)] retain] + }; + display_link_.get().paused = YES; + + task_runner->PostTask([client = [self retain]]() { + [client->display_link_.get() addToRunLoop:[NSRunLoop currentRunLoop] + forMode:NSRunLoopCommonModes]; [client release]; }); } @@ -40,68 +73,28 @@ - (instancetype)init { return self; } -- (void)await:(shell::VsyncWaiter::Callback)callback { - FXL_DCHECK(!_pendingCallback); - _pendingCallback = std::move(callback); - _displayLink.paused = NO; +- (void)await { + display_link_.get().paused = NO; } - (void)onDisplayLink:(CADisplayLink*)link { fxl::TimePoint frame_start_time = fxl::TimePoint::Now(); fxl::TimePoint frame_target_time = frame_start_time + fxl::TimeDelta::FromSecondsF(link.duration); - _displayLink.paused = YES; - - // Note: The tag name must be "VSYNC" (it is special) so that the "Highlight - // Vsync" checkbox in the timeline can be enabled. - // See: https://github.com/catapult-project/catapult/blob/2091404475cbba9b786 - // 442979b6ec631305275a6/tracing/tracing/extras/vsync/vsync_auditor.html#L26 -#if FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_RELEASE - TRACE_EVENT1("flutter", "VSYNC", "mode", "basic"); -#else - { - fxl::TimeDelta delta = frame_target_time.ToEpochDelta(); - constexpr size_t num_chars = sizeof(int64_t) * CHAR_BIT * 3.4 + 2; - char deadline[num_chars]; - sprintf(deadline, "%lld", delta.ToMicroseconds()); - TRACE_EVENT2("flutter", "VSYNC", "mode", "basic", "deadline", deadline); - } -#endif - - // Note: Even though we know we are on the UI thread already (since the - // display link was scheduled on the UI thread in the contructor), we use - // the PostTask mechanism because the callback may have side-effects that need - // to be addressed via a task observer. Invoking the callback by itself - // bypasses such task observers. - // - // We are not using the PostTask for thread switching, but to make task - // observers work. - blink::Threads::UI()->PostTask([ - callback = _pendingCallback, frame_start_time, frame_target_time - ]() { callback(frame_start_time, frame_target_time); }); - - _pendingCallback = nullptr; + display_link_.get().paused = YES; + + callback_(frame_start_time, frame_target_time); +} + +- (void)invalidate { + // [CADisplayLink invalidate] is thread-safe. + [display_link_.get() invalidate]; } - (void)dealloc { - [_displayLink invalidate]; - [_displayLink release]; + [self invalidate]; [super dealloc]; } @end - -namespace shell { - -VsyncWaiterIOS::VsyncWaiterIOS() : client_([[VSyncClient alloc] init]) {} - -VsyncWaiterIOS::~VsyncWaiterIOS() { - [client_ release]; -} - -void VsyncWaiterIOS::AsyncWaitForVsync(Callback callback) { - [client_ await:callback]; -} - -} // namespace shell diff --git a/shell/platform/darwin/ios/ios_external_texture_gl.mm b/shell/platform/darwin/ios/ios_external_texture_gl.mm index 2cc9721e7f09a..f507cdce0c815 100644 --- a/shell/platform/darwin/ios/ios_external_texture_gl.mm +++ b/shell/platform/darwin/ios/ios_external_texture_gl.mm @@ -4,11 +4,10 @@ #include "flutter/shell/platform/darwin/ios/ios_external_texture_gl.h" -#include +#import #import #import -#include "flutter/common/threads.h" -#include "flutter/lib/ui/painting/resource_context.h" + #include "flutter/shell/platform/darwin/ios/framework/Source/vsync_waiter_ios.h" #include "third_party/skia/include/core/SkSurface.h" #include "third_party/skia/include/gpu/GrBackendSurface.h" @@ -26,7 +25,6 @@ IOSExternalTextureGL::~IOSExternalTextureGL() = default; void IOSExternalTextureGL::Paint(SkCanvas& canvas, const SkRect& bounds) { - ASSERT_IS_GPU_THREAD; if (!cache_ref_) { CVOpenGLESTextureCacheRef cache; CVReturn err = CVOpenGLESTextureCacheCreate(kCFAllocatorDefault, NULL, @@ -69,12 +67,9 @@ GrBackendTexture backendTexture(bounds.width(), bounds.height(), GrMipMapped::kN } } -void IOSExternalTextureGL::OnGrContextCreated() { - ASSERT_IS_GPU_THREAD -} +void IOSExternalTextureGL::OnGrContextCreated() {} void IOSExternalTextureGL::OnGrContextDestroyed() { - ASSERT_IS_GPU_THREAD texture_ref_.Reset(nullptr); cache_ref_.Reset(nullptr); } diff --git a/shell/platform/darwin/ios/ios_gl_context.h b/shell/platform/darwin/ios/ios_gl_context.h index 89ad7e0347eac..f42c1436461b1 100644 --- a/shell/platform/darwin/ios/ios_gl_context.h +++ b/shell/platform/darwin/ios/ios_gl_context.h @@ -18,7 +18,7 @@ namespace shell { class IOSGLContext { public: - IOSGLContext(PlatformView::SurfaceConfig config, CAEAGLLayer* layer); + IOSGLContext(fml::scoped_nsobject layer); ~IOSGLContext(); diff --git a/shell/platform/darwin/ios/ios_gl_context.mm b/shell/platform/darwin/ios/ios_gl_context.mm index de94018d0689e..77a124e64a291 100644 --- a/shell/platform/darwin/ios/ios_gl_context.mm +++ b/shell/platform/darwin/ios/ios_gl_context.mm @@ -3,21 +3,17 @@ // found in the LICENSE file. #include "flutter/shell/platform/darwin/ios/ios_gl_context.h" -#include "third_party/skia/include/gpu/GrContextOptions.h" -#include "third_party/skia/include/gpu/gl/GrGLInterface.h" #include -namespace shell { +#include "flutter/fml/trace_event.h" +#include "third_party/skia/include/gpu/GrContextOptions.h" +#include "third_party/skia/include/gpu/gl/GrGLInterface.h" -#define VERIFY(x) \ - if (!(x)) { \ - FXL_DLOG(ERROR) << "Failed: " #x; \ - return; \ - }; +namespace shell { -IOSGLContext::IOSGLContext(PlatformView::SurfaceConfig config, CAEAGLLayer* layer) - : layer_([layer retain]), +IOSGLContext::IOSGLContext(fml::scoped_nsobject layer) + : layer_(std::move(layer)), context_([[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2]), resource_context_([[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2 sharegroup:context_.get().sharegroup]), @@ -26,34 +22,34 @@ storage_size_width_(0), storage_size_height_(0), valid_(false) { - VERIFY(layer_ != nullptr); - VERIFY(context_ != nullptr); - VERIFY(resource_context_ != nullptr); + FXL_DCHECK(layer_ != nullptr); + FXL_DCHECK(context_ != nullptr); + FXL_DCHECK(resource_context_ != nullptr); bool context_current = [EAGLContext setCurrentContext:context_]; - VERIFY(context_current); - VERIFY(glGetError() == GL_NO_ERROR); + FXL_DCHECK(context_current); + FXL_DCHECK(glGetError() == GL_NO_ERROR); // Generate the framebuffer glGenFramebuffers(1, &framebuffer_); - VERIFY(glGetError() == GL_NO_ERROR); - VERIFY(framebuffer_ != GL_NONE); + FXL_DCHECK(glGetError() == GL_NO_ERROR); + FXL_DCHECK(framebuffer_ != GL_NONE); glBindFramebuffer(GL_FRAMEBUFFER, framebuffer_); - VERIFY(glGetError() == GL_NO_ERROR); + FXL_DCHECK(glGetError() == GL_NO_ERROR); // Setup color attachment glGenRenderbuffers(1, &colorbuffer_); - VERIFY(colorbuffer_ != GL_NONE); + FXL_DCHECK(colorbuffer_ != GL_NONE); glBindRenderbuffer(GL_RENDERBUFFER, colorbuffer_); - VERIFY(glGetError() == GL_NO_ERROR); + FXL_DCHECK(glGetError() == GL_NO_ERROR); glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, colorbuffer_); - VERIFY(glGetError() == GL_NO_ERROR); + FXL_DCHECK(glGetError() == GL_NO_ERROR); // TODO: // iOS displays are more variable than just P3 or sRGB. Reading the display @@ -139,24 +135,12 @@ return false; } - GLint width = 0; - GLint height = 0; - - if (colorbuffer_ != GL_NONE) { - // Fetch the dimensions of the color buffer whose backing was just updated - // so that backing of the attachments can be updated - glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, &width); - FXL_DCHECK(glGetError() == GL_NO_ERROR); - - glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &height); - FXL_DCHECK(glGetError() == GL_NO_ERROR); - - glBindRenderbuffer(GL_RENDERBUFFER, colorbuffer_); - FXL_DCHECK(glGetError() == GL_NO_ERROR); - } + // Fetch the dimensions of the color buffer whose backing was just updated. + glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, &storage_size_width_); + FXL_DCHECK(glGetError() == GL_NO_ERROR); - storage_size_width_ = width; - storage_size_height_ = height; + glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &storage_size_height_); + FXL_DCHECK(glGetError() == GL_NO_ERROR); FXL_DCHECK(glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE); diff --git a/shell/platform/darwin/ios/ios_surface.h b/shell/platform/darwin/ios/ios_surface.h index c164fab15500e..b629c2709e3d1 100644 --- a/shell/platform/darwin/ios/ios_surface.h +++ b/shell/platform/darwin/ios/ios_surface.h @@ -5,25 +5,17 @@ #ifndef FLUTTER_SHELL_PLATFORM_DARWIN_IOS_IOS_SURFACE_H_ #define FLUTTER_SHELL_PLATFORM_DARWIN_IOS_IOS_SURFACE_H_ +#include + #include "flutter/fml/platform/darwin/scoped_nsobject.h" -#include "flutter/shell/common/platform_view.h" +#include "flutter/shell/common/surface.h" #include "lib/fxl/macros.h" -@class CALayer; - namespace shell { class IOSSurface { public: - static std::unique_ptr Create( - PlatformView::SurfaceConfig surface_config, - CALayer* layer); - - IOSSurface(PlatformView::SurfaceConfig surface_config, CALayer* layer); - - CALayer* GetLayer() const; - - PlatformView::SurfaceConfig GetSurfaceConfig() const; + IOSSurface(); virtual ~IOSSurface(); @@ -36,9 +28,6 @@ class IOSSurface { virtual std::unique_ptr CreateGPUSurface() = 0; public: - PlatformView::SurfaceConfig surface_config_; - fml::scoped_nsobject layer_; - FXL_DISALLOW_COPY_AND_ASSIGN(IOSSurface); }; diff --git a/shell/platform/darwin/ios/ios_surface.mm b/shell/platform/darwin/ios/ios_surface.mm index 91067838330f6..b0b9cc3245183 100644 --- a/shell/platform/darwin/ios/ios_surface.mm +++ b/shell/platform/darwin/ios/ios_surface.mm @@ -4,40 +4,15 @@ #include "flutter/shell/platform/darwin/ios/ios_surface.h" -#include -#include #include -@class CALayer; -@class CAEAGLLayer; +#include +#include namespace shell { -std::unique_ptr IOSSurface::Create(PlatformView::SurfaceConfig surface_config, - CALayer* layer) { - // Check if we can use OpenGL. - if ([layer isKindOfClass:[CAEAGLLayer class]]) { - return std::make_unique(surface_config, reinterpret_cast(layer)); - } - - // If we ever support the metal rendering API, a check for CAMetalLayer would - // go here. - - // Finally, fallback to software rendering. - return std::make_unique(surface_config, layer); -} - -IOSSurface::IOSSurface(PlatformView::SurfaceConfig surface_config, CALayer* layer) - : surface_config_(surface_config), layer_([layer retain]) {} +IOSSurface::IOSSurface() = default; IOSSurface::~IOSSurface() = default; -CALayer* IOSSurface::GetLayer() const { - return layer_; -} - -PlatformView::SurfaceConfig IOSSurface::GetSurfaceConfig() const { - return surface_config_; -} - } // namespace shell diff --git a/shell/platform/darwin/ios/ios_surface_gl.h b/shell/platform/darwin/ios/ios_surface_gl.h index f7e43e2588984..7486d343eb0fd 100644 --- a/shell/platform/darwin/ios/ios_surface_gl.h +++ b/shell/platform/darwin/ios/ios_surface_gl.h @@ -5,6 +5,7 @@ #ifndef FLUTTER_SHELL_PLATFORM_DARWIN_IOS_IOS_SURFACE_GL_H_ #define FLUTTER_SHELL_PLATFORM_DARWIN_IOS_IOS_SURFACE_GL_H_ +#include "flutter/fml/platform/darwin/scoped_nsobject.h" #include "flutter/shell/gpu/gpu_surface_gl.h" #include "flutter/shell/platform/darwin/ios/ios_gl_context.h" #include "flutter/shell/platform/darwin/ios/ios_surface.h" @@ -16,7 +17,7 @@ namespace shell { class IOSSurfaceGL : public IOSSurface, public GPUSurfaceGLDelegate { public: - IOSSurfaceGL(PlatformView::SurfaceConfig surface_config, CAEAGLLayer* layer); + IOSSurfaceGL(fml::scoped_nsobject layer); ~IOSSurfaceGL() override; diff --git a/shell/platform/darwin/ios/ios_surface_gl.mm b/shell/platform/darwin/ios/ios_surface_gl.mm index 60756d6094fc9..253531c4800aa 100644 --- a/shell/platform/darwin/ios/ios_surface_gl.mm +++ b/shell/platform/darwin/ios/ios_surface_gl.mm @@ -4,13 +4,12 @@ #include "flutter/shell/platform/darwin/ios/ios_surface_gl.h" +#include "flutter/fml/trace_event.h" #include "flutter/shell/gpu/gpu_surface_gl.h" namespace shell { -IOSSurfaceGL::IOSSurfaceGL(PlatformView::SurfaceConfig surface_config, CAEAGLLayer* layer) - : IOSSurface(surface_config, reinterpret_cast(layer)), - context_(surface_config, layer) {} +IOSSurfaceGL::IOSSurfaceGL(fml::scoped_nsobject layer) : context_(std::move(layer)) {} IOSSurfaceGL::~IOSSurfaceGL() = default; diff --git a/shell/platform/darwin/ios/ios_surface_software.h b/shell/platform/darwin/ios/ios_surface_software.h index 7e3f264b28ce6..e8fc332f06c8c 100644 --- a/shell/platform/darwin/ios/ios_surface_software.h +++ b/shell/platform/darwin/ios/ios_surface_software.h @@ -5,17 +5,19 @@ #ifndef FLUTTER_SHELL_PLATFORM_DARWIN_IOS_IOS_SURFACE_SOFTWARE_H_ #define FLUTTER_SHELL_PLATFORM_DARWIN_IOS_IOS_SURFACE_SOFTWARE_H_ +#include "flutter/fml/platform/darwin/scoped_nsobject.h" #include "flutter/shell/gpu/gpu_surface_software.h" #include "flutter/shell/platform/darwin/ios/ios_surface.h" #include "lib/fxl/macros.h" +@class CALayer; + namespace shell { -class IOSSurfaceSoftware : public IOSSurface, - public GPUSurfaceSoftwareDelegate { +class IOSSurfaceSoftware final : public IOSSurface, + public GPUSurfaceSoftwareDelegate { public: - IOSSurfaceSoftware(PlatformView::SurfaceConfig surface_config, - CALayer* layer); + IOSSurfaceSoftware(fml::scoped_nsobject layer); ~IOSSurfaceSoftware() override; @@ -32,6 +34,7 @@ class IOSSurfaceSoftware : public IOSSurface, bool PresentBackingStore(sk_sp backing_store) override; private: + fml::scoped_nsobject layer_; sk_sp sk_surface_; FXL_DISALLOW_COPY_AND_ASSIGN(IOSSurfaceSoftware); diff --git a/shell/platform/darwin/ios/ios_surface_software.mm b/shell/platform/darwin/ios/ios_surface_software.mm index 9a4e90e45dfba..b09a5d9a2d8a3 100644 --- a/shell/platform/darwin/ios/ios_surface_software.mm +++ b/shell/platform/darwin/ios/ios_surface_software.mm @@ -15,15 +15,15 @@ namespace shell { -IOSSurfaceSoftware::IOSSurfaceSoftware(PlatformView::SurfaceConfig surface_config, CALayer* layer) - : IOSSurface(surface_config, layer) { +IOSSurfaceSoftware::IOSSurfaceSoftware(fml::scoped_nsobject layer) + : layer_(std::move(layer)) { UpdateStorageSizeIfNecessary(); } IOSSurfaceSoftware::~IOSSurfaceSoftware() = default; bool IOSSurfaceSoftware::IsValid() const { - return GetLayer() != nullptr; + return layer_; } bool IOSSurfaceSoftware::ResourceContextMakeCurrent() { @@ -120,8 +120,7 @@ return false; } - CALayer* layer = GetLayer(); - layer.contents = reinterpret_cast(static_cast(pixmap_image)); + layer_.get().contents = reinterpret_cast(static_cast(pixmap_image)); return true; } diff --git a/shell/platform/darwin/ios/platform_view_ios.h b/shell/platform/darwin/ios/platform_view_ios.h index dab19a0f9ce1a..e7849dda44641 100644 --- a/shell/platform/darwin/ios/platform_view_ios.h +++ b/shell/platform/darwin/ios/platform_view_ios.h @@ -8,95 +8,66 @@ #include #include "flutter/fml/memory/weak_ptr.h" +#include "flutter/fml/platform/darwin/scoped_nsobject.h" #include "flutter/shell/common/platform_view.h" #include "flutter/shell/platform/darwin/ios/framework/Headers/FlutterTexture.h" -#include "flutter/shell/platform/darwin/ios/framework/Source/FlutterTextInputPlugin.h" +#include "flutter/shell/platform/darwin/ios/framework/Headers/FlutterViewController.h" +#include "flutter/shell/platform/darwin/ios/framework/Source/FlutterView.h" #include "flutter/shell/platform/darwin/ios/framework/Source/accessibility_bridge.h" #include "flutter/shell/platform/darwin/ios/framework/Source/platform_message_router.h" #include "flutter/shell/platform/darwin/ios/ios_surface.h" #include "lib/fxl/functional/closure.h" #include "lib/fxl/macros.h" -@class CALayer; -@class UIView; - namespace shell { -class PlatformViewIOS : public PlatformView { +class PlatformViewIOS final : public PlatformView { public: - explicit PlatformViewIOS(CALayer* layer, - NSObject* binaryMessenger); + explicit PlatformViewIOS(PlatformView::Delegate& delegate, + blink::TaskRunners task_runners, + FlutterViewController* owner_controller_, + FlutterView* owner_view_); ~PlatformViewIOS() override; - void Attach() override; - - void Attach(fxl::Closure firstFrameCallback); - - void NotifyCreated(); - - void ToggleAccessibility(UIView* view, bool enabled); - - PlatformMessageRouter& platform_message_router() { - return platform_message_router_; - } - - fml::WeakPtr GetWeakPtr(); - - void UpdateSurfaceSize(); + PlatformMessageRouter& GetPlatformMessageRouter(); - VsyncWaiter* GetVsyncWaiter() override; - - bool ResourceContextMakeCurrent() override; - - void HandlePlatformMessage( - fxl::RefPtr message) override; + FlutterViewController* GetOwnerViewController() const; void RegisterExternalTexture(int64_t id, NSObject* texture); - void UpdateSemantics(blink::SemanticsNodeUpdates update) override; - - void RunFromSource(const std::string& assets_directory, - const std::string& main, - const std::string& packages) override; + fml::scoped_nsprotocol GetTextInputPlugin() const; - void SetAssetBundlePath(const std::string& assets_directory) override; - - /** - * Exposes the `FlutterTextInputPlugin` singleton for the - * `AccessibilityBridge` to be able to interact with the text entry system. - */ - fml::scoped_nsprotocol text_input_plugin() { - return text_input_plugin_; - } - - /** - * Sets the `FlutterTextInputPlugin` singleton returned by - * `text_input_plugin`. - */ void SetTextInputPlugin( - fml::scoped_nsprotocol textInputPlugin) { - text_input_plugin_ = textInputPlugin; - } - - NSObject* binary_messenger() const { - return binary_messenger_; - } + fml::scoped_nsprotocol plugin); private: + FlutterViewController* owner_controller_; // weak reference. + FlutterView* owner_view_; // weak reference. std::unique_ptr ios_surface_; PlatformMessageRouter platform_message_router_; std::unique_ptr accessibility_bridge_; - fxl::Closure firstFrameCallback_; - fml::WeakPtrFactory weak_factory_; - NSObject* binary_messenger_; fml::scoped_nsprotocol text_input_plugin_; + fxl::Closure firstFrameCallback_; + + // |shell::PlatformView| + std::unique_ptr CreateRenderingSurface() override; - void SetupAndLoadFromSource(const std::string& assets_directory, - const std::string& main, - const std::string& packages); + // |shell::PlatformView| + sk_sp CreateResourceContext() const override; + + // |shell::PlatformView| + void SetSemanticsEnabled(bool enabled) override; + + // |shell::PlatformView| + void HandlePlatformMessage( + fxl::RefPtr message) override; + + // |shell::PlatformView| + void UpdateSemantics(blink::SemanticsNodeUpdates update) override; - void SetAssetBundlePathOnUI(const std::string& assets_directory); + // |shell::PlatformView| + std::unique_ptr CreateVSyncWaiter() override; FXL_DISALLOW_COPY_AND_ASSIGN(PlatformViewIOS); }; diff --git a/shell/platform/darwin/ios/platform_view_ios.mm b/shell/platform/darwin/ios/platform_view_ios.mm index 19c5dd4e663a1..d6ac49b1923f8 100644 --- a/shell/platform/darwin/ios/platform_view_ios.mm +++ b/shell/platform/darwin/ios/platform_view_ios.mm @@ -8,135 +8,95 @@ #include -#include "flutter/common/threads.h" +#include "flutter/common/task_runners.h" #include "flutter/fml/trace_event.h" -#include "flutter/shell/gpu/gpu_rasterizer.h" -#include "flutter/shell/platform/darwin/common/process_info_mac.h" +#include "flutter/shell/common/io_manager.h" #include "flutter/shell/platform/darwin/ios/framework/Source/vsync_waiter_ios.h" #include "flutter/shell/platform/darwin/ios/ios_external_texture_gl.h" #include "lib/fxl/synchronization/waitable_event.h" +#include "third_party/skia/include/gpu/gl/GrGLInterface.h" namespace shell { -PlatformViewIOS::PlatformViewIOS(CALayer* layer, NSObject* binaryMessenger) - : PlatformView(std::make_unique(std::make_unique())), - ios_surface_(IOSSurface::Create(surface_config_, layer)), - weak_factory_(this), - binary_messenger_(binaryMessenger) {} - -PlatformViewIOS::~PlatformViewIOS() = default; - -void PlatformViewIOS::Attach() { - Attach(NULL); +PlatformViewIOS::PlatformViewIOS(PlatformView::Delegate& delegate, + blink::TaskRunners task_runners, + FlutterViewController* owner_controller, + FlutterView* owner_view) + : PlatformView(delegate, std::move(task_runners)), + owner_controller_(owner_controller), + owner_view_(owner_view), + ios_surface_(owner_view_.createSurface) { + FXL_DCHECK(ios_surface_ != nullptr); + FXL_DCHECK(owner_controller_ != nullptr); + FXL_DCHECK(owner_view_ != nullptr); } -void PlatformViewIOS::Attach(fxl::Closure firstFrameCallback) { - CreateEngine(); - - if (firstFrameCallback) { - firstFrameCallback_ = firstFrameCallback; - rasterizer_->AddNextFrameCallback([weakSelf = GetWeakPtr()] { - if (weakSelf) { - weakSelf->firstFrameCallback_(); - weakSelf->firstFrameCallback_ = nullptr; - } - }); - } -} +PlatformViewIOS::~PlatformViewIOS() = default; -void PlatformViewIOS::NotifyCreated() { - PlatformView::NotifyCreated(ios_surface_->CreateGPUSurface()); +FlutterViewController* PlatformViewIOS::GetOwnerViewController() const { + return owner_controller_; } -void PlatformViewIOS::ToggleAccessibility(UIView* view, bool enabled) { - if (enabled) { - if (!accessibility_bridge_) { - accessibility_bridge_.reset(new shell::AccessibilityBridge(view, this)); - } - } else { - accessibility_bridge_ = nullptr; - } - SetSemanticsEnabled(enabled); +PlatformMessageRouter& PlatformViewIOS::GetPlatformMessageRouter() { + return platform_message_router_; } -void PlatformViewIOS::SetupAndLoadFromSource(const std::string& assets_directory, - const std::string& main, - const std::string& packages) { - blink::Threads::UI()->PostTask( - [ engine = engine().GetWeakPtr(), assets_directory, main, packages ] { - if (engine) - engine->RunBundleAndSource(assets_directory, main, packages); - }); +void PlatformViewIOS::RegisterExternalTexture(int64_t texture_id, + NSObject* texture) { + RegisterTexture(std::make_shared(texture_id, texture)); } -void PlatformViewIOS::SetAssetBundlePathOnUI(const std::string& assets_directory) { - blink::Threads::UI()->PostTask([ engine = engine().GetWeakPtr(), assets_directory ] { - if (engine) - engine->SetAssetBundlePath(assets_directory); - }); +// |shell::PlatformView| +std::unique_ptr PlatformViewIOS::CreateRenderingSurface() { + return ios_surface_->CreateGPUSurface(); } -fml::WeakPtr PlatformViewIOS::GetWeakPtr() { - return weak_factory_.GetWeakPtr(); -} +// |shell::PlatformView| +sk_sp PlatformViewIOS::CreateResourceContext() const { + if (!ios_surface_->ResourceContextMakeCurrent()) { + FXL_DLOG(INFO) << "Could not make resource context current on IO thread. Async texture uploads " + "will be disabled."; + return nullptr; + } -void PlatformViewIOS::UpdateSurfaceSize() { - blink::Threads::Gpu()->PostTask([self = GetWeakPtr()]() { - if (self && self->ios_surface_ != nullptr) { - self->ios_surface_->UpdateStorageSizeIfNecessary(); - } - }); + return IOManager::CreateCompatibleResourceLoadingContext( + GrBackend::kOpenGL_GrBackend, + reinterpret_cast(GrGLCreateNativeInterface())); } -VsyncWaiter* PlatformViewIOS::GetVsyncWaiter() { - if (!vsync_waiter_) { - vsync_waiter_ = std::make_unique(); +// |shell::PlatformView| +void PlatformViewIOS::SetSemanticsEnabled(bool enabled) { + if (enabled && !accessibility_bridge_) { + accessibility_bridge_ = std::make_unique(owner_view_, this); + } else { + accessibility_bridge_.reset(); } - return vsync_waiter_.get(); -} - -bool PlatformViewIOS::ResourceContextMakeCurrent() { - return ios_surface_ != nullptr ? ios_surface_->ResourceContextMakeCurrent() : false; + PlatformView::SetSemanticsEnabled(enabled); } +// |shell::PlatformView| void PlatformViewIOS::UpdateSemantics(blink::SemanticsNodeUpdates update) { - if (accessibility_bridge_) + if (accessibility_bridge_) { accessibility_bridge_->UpdateSemantics(std::move(update)); + } } +// |shell::PlatformView| void PlatformViewIOS::HandlePlatformMessage(fxl::RefPtr message) { platform_message_router_.HandlePlatformMessage(std::move(message)); } -void PlatformViewIOS::RegisterExternalTexture(int64_t texture_id, - NSObject* texture) { - RegisterTexture(std::make_shared(texture_id, texture)); +// |shell::PlatformView| +std::unique_ptr PlatformViewIOS::CreateVSyncWaiter() { + return std::make_unique(task_runners_); } -void PlatformViewIOS::RunFromSource(const std::string& assets_directory, - const std::string& main, - const std::string& packages) { - auto latch = new fxl::ManualResetWaitableEvent(); - - dispatch_async(dispatch_get_main_queue(), ^{ - SetupAndLoadFromSource(assets_directory, main, packages); - latch->Signal(); - }); - - latch->Wait(); - delete latch; +fml::scoped_nsprotocol PlatformViewIOS::GetTextInputPlugin() const { + return text_input_plugin_; } -void PlatformViewIOS::SetAssetBundlePath(const std::string& assets_directory) { - auto latch = new fxl::ManualResetWaitableEvent(); - - dispatch_async(dispatch_get_main_queue(), ^{ - SetAssetBundlePathOnUI(assets_directory); - latch->Signal(); - }); - - latch->Wait(); - delete latch; +void PlatformViewIOS::SetTextInputPlugin(fml::scoped_nsprotocol plugin) { + text_input_plugin_ = plugin; } } // namespace shell diff --git a/shell/platform/embedder/BUILD.gn b/shell/platform/embedder/BUILD.gn index 7ce6f7e8d5be1..b1e0b3e4f3b71 100644 --- a/shell/platform/embedder/BUILD.gn +++ b/shell/platform/embedder/BUILD.gn @@ -4,16 +4,21 @@ import("$flutter_root/testing/testing.gni") -source_set("embedder") { +static_library("embedder") { + complete_static_lib = true + sources = [ "embedder.cc", "embedder.h", + "embedder_engine.cc", + "embedder_engine.h", "embedder_include.c", "platform_view_embedder.cc", "platform_view_embedder.h", ] deps = [ + "$flutter_root/assets", "$flutter_root/common", "$flutter_root/fml", "$flutter_root/shell/common", @@ -22,6 +27,7 @@ source_set("embedder") { "//third_party/dart/runtime:libdart_jit", "//third_party/dart/runtime/bin:embedded_dart_io", "//third_party/skia", + "//third_party/skia:gpu", "//topaz/lib/tonic", ] diff --git a/shell/platform/embedder/embedder.cc b/shell/platform/embedder/embedder.cc index 2ac87f753357e..1e416b0271fc1 100644 --- a/shell/platform/embedder/embedder.cc +++ b/shell/platform/embedder/embedder.cc @@ -2,14 +2,24 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#define FML_USED_ON_EMBEDDER + #define FLUTTER_EXPORT __attribute__((visibility("default"))) #include "flutter/shell/platform/embedder/embedder.h" #include -#include "flutter/common/threads.h" + +#include "flutter/assets/directory_asset_bundle.h" +#include "flutter/common/task_runners.h" +#include "flutter/fml/file.h" #include "flutter/fml/message_loop.h" +#include "flutter/shell/common/rasterizer.h" +#include "flutter/shell/common/switches.h" +#include "flutter/shell/platform/embedder/embedder.h" +#include "flutter/shell/platform/embedder/embedder_engine.h" #include "flutter/shell/platform/embedder/platform_view_embedder.h" +#include "lib/fxl/command_line.h" #include "lib/fxl/functional/make_copyable.h" #define SAFE_ACCESS(pointer, member, default_value) \ @@ -41,21 +51,6 @@ bool IsRendererValid(const FlutterRendererConfig* config) { return true; } -class PlatformViewHolder { - public: - PlatformViewHolder(std::shared_ptr ptr) - : platform_view_(std::move(ptr)) {} - - std::shared_ptr view() const { - return platform_view_; - } - - private: - std::shared_ptr platform_view_; - - FXL_DISALLOW_COPY_AND_ASSIGN(PlatformViewHolder); -}; - struct _FlutterPlatformMessageResponseHandle { fxl::RefPtr message; }; @@ -65,6 +60,7 @@ FlutterResult FlutterEngineRun(size_t version, const FlutterProjectArgs* args, void* user_data, FlutterEngine* engine_out) { + // Step 0: Figure out arguments for shell creation. if (version != FLUTTER_ENGINE_VERSION) { return kInvalidLibraryVersion; } @@ -87,51 +83,44 @@ FlutterResult FlutterEngineRun(size_t version, return kInvalidArguments; } - auto make_current = - [ ptr = config->open_gl.make_current, user_data ]()->bool { - return ptr(user_data); - }; + auto make_current = [ptr = config->open_gl.make_current, + user_data]() -> bool { return ptr(user_data); }; - auto clear_current = - [ ptr = config->open_gl.clear_current, user_data ]()->bool { - return ptr(user_data); - }; + auto clear_current = [ptr = config->open_gl.clear_current, + user_data]() -> bool { return ptr(user_data); }; - auto present = [ ptr = config->open_gl.present, user_data ]()->bool { + auto present = [ptr = config->open_gl.present, user_data]() -> bool { return ptr(user_data); }; - auto fbo_callback = - [ ptr = config->open_gl.fbo_callback, user_data ]()->intptr_t { - return ptr(user_data); - }; + auto fbo_callback = [ptr = config->open_gl.fbo_callback, + user_data]() -> intptr_t { return ptr(user_data); }; shell::PlatformViewEmbedder::PlatformMessageResponseCallback platform_message_response_callback = nullptr; if (SAFE_ACCESS(args, platform_message_callback, nullptr) != nullptr) { platform_message_response_callback = - [ ptr = args->platform_message_callback, - user_data ](fxl::RefPtr message) { - auto handle = new FlutterPlatformMessageResponseHandle(); - const FlutterPlatformMessage incoming_message = { - .struct_size = sizeof(FlutterPlatformMessage), - .channel = message->channel().c_str(), - .message = message->data().data(), - .message_size = message->data().size(), - .response_handle = handle, - }; - handle->message = std::move(message); - return ptr(&incoming_message, user_data); - }; + [ptr = args->platform_message_callback, + user_data](fxl::RefPtr message) { + auto handle = new FlutterPlatformMessageResponseHandle(); + const FlutterPlatformMessage incoming_message = { + .struct_size = sizeof(FlutterPlatformMessage), + .channel = message->channel().c_str(), + .message = message->data().data(), + .message_size = message->data().size(), + .response_handle = handle, + }; + handle->message = std::move(message); + return ptr(&incoming_message, user_data); + }; } const FlutterOpenGLRendererConfig* open_gl_config = &config->open_gl; std::function make_resource_current_callback = nullptr; if (SAFE_ACCESS(open_gl_config, make_resource_current, nullptr) != nullptr) { - make_resource_current_callback = - [ ptr = config->open_gl.make_resource_current, user_data ]() { - return ptr(user_data); - }; + make_resource_current_callback = [ptr = + config->open_gl.make_resource_current, + user_data]() { return ptr(user_data); }; } std::string icu_data_path; @@ -147,17 +136,33 @@ FlutterResult FlutterEngineRun(size_t version, SAFE_ACCESS(args, command_line_argv, nullptr)); } - static std::once_flag once_shell_initialization; - std::call_once(once_shell_initialization, [&]() { - fxl::CommandLine null_command_line; - shell::Shell::InitStandalone( - std::move(command_line), - icu_data_path, // icu data path default lookup. - "" // application library not supported in JIT mode. - ); - }); - - shell::PlatformViewEmbedder::DispatchTable table = { + blink::Settings settings = shell::SettingsFromCommandLine(command_line); + settings.icu_data_path = icu_data_path; + settings.main_dart_file_path = args->main_path; + settings.packages_file_path = args->packages_path; + settings.assets_path = args->assets_path; + settings.task_observer_add = [](intptr_t key, fxl::Closure callback) { + fml::MessageLoop::GetCurrent().AddTaskObserver(key, std::move(callback)); + }; + settings.task_observer_remove = [](intptr_t key) { + fml::MessageLoop::GetCurrent().RemoveTaskObserver(key); + }; + + // Create a thread host with the current thread as the platform thread and all + // other threads managed. + shell::ThreadHost thread_host("io.flutter", shell::ThreadHost::Type::GPU | + shell::ThreadHost::Type::IO | + shell::ThreadHost::Type::UI); + fml::MessageLoop::EnsureInitializedForCurrentThread(); + blink::TaskRunners task_runners( + "io.flutter", + fml::MessageLoop::GetCurrent().GetTaskRunner(), // platform + thread_host.gpu_thread->GetTaskRunner(), // gpu + thread_host.ui_thread->GetTaskRunner(), // ui + thread_host.io_thread->GetTaskRunner() // io + ); + + shell::PlatformViewEmbedder::DispatchTable dispatch_table = { .gl_make_current_callback = make_current, .gl_clear_current_callback = clear_current, .gl_present_callback = present, @@ -166,31 +171,57 @@ FlutterResult FlutterEngineRun(size_t version, .gl_make_resource_current_callback = make_resource_current_callback, }; - auto platform_view = std::make_shared(table); - platform_view->Attach(); - - std::string assets(args->assets_path); - std::string main(args->main_path); - std::string packages(args->packages_path); - - blink::Threads::UI()->PostTask([ - weak_engine = platform_view->engine().GetWeakPtr(), // - assets = std::move(assets), // - main = std::move(main), // - packages = std::move(packages) // - ] { - if (auto engine = weak_engine) { - if (main.empty()) { - engine->RunBundle(assets); - } else { - engine->RunBundleAndSource(assets, main, packages); - } - } - }); - - *engine_out = reinterpret_cast( - new PlatformViewHolder(std::move(platform_view))); + shell::Shell::CreateCallback on_create_platform_view = + [dispatch_table](shell::Shell& shell) { + return std::make_unique( + shell, // delegate + shell.GetTaskRunners(), // task runners + dispatch_table // embedder dispatch table + ); + }; + shell::Shell::CreateCallback on_create_rasterizer = + [](shell::Shell& shell) { + return std::make_unique(shell.GetTaskRunners()); + }; + + // Step 1: Create the engine. + auto embedder_engine = + std::make_unique(std::move(thread_host), // + std::move(task_runners), // + settings, // + on_create_platform_view, // + on_create_rasterizer // + ); + + if (!embedder_engine->IsValid()) { + return kInvalidArguments; + } + + // Step 2: Setup the rendering surface. + if (!embedder_engine->NotifyCreated()) { + return kInvalidArguments; + } + + // Step 3: Run the engine. + shell::RunConfiguration run_configuration( + shell::IsolateConfiguration::CreateForSource( + settings.main_dart_file_path, settings.packages_file_path)); + + run_configuration.AddAssetResolver( + std::make_unique( + fml::Duplicate(settings.assets_dir))); + + run_configuration.AddAssetResolver( + std::make_unique(fml::OpenFile( + settings.assets_path.c_str(), fml::OpenPermission::kRead, true))); + + if (!embedder_engine->Run(std::move(run_configuration))) { + return kInvalidArguments; + } + + // Finally! Release the ownership of the embedder engine to the caller. + *engine_out = reinterpret_cast(embedder_engine.release()); return kSuccess; } @@ -198,7 +229,9 @@ FlutterResult FlutterEngineShutdown(FlutterEngine engine) { if (engine == nullptr) { return kInvalidArguments; } - delete reinterpret_cast(engine); + auto embedder_engine = reinterpret_cast(engine); + embedder_engine->NotifyDestroyed(); + delete embedder_engine; return kSuccess; } @@ -209,21 +242,16 @@ FlutterResult FlutterEngineSendWindowMetricsEvent( return kInvalidArguments; } - auto holder = reinterpret_cast(engine); - blink::ViewportMetrics metrics; metrics.physical_width = SAFE_ACCESS(flutter_metrics, width, 0.0); metrics.physical_height = SAFE_ACCESS(flutter_metrics, height, 0.0); metrics.device_pixel_ratio = SAFE_ACCESS(flutter_metrics, pixel_ratio, 1.0); - blink::Threads::UI()->PostTask( - [ weak_engine = holder->view()->engine().GetWeakPtr(), metrics ] { - if (auto engine = weak_engine) { - engine->SetViewportMetrics(metrics); - } - }); - return kSuccess; + return reinterpret_cast(engine)->SetViewportMetrics( + std::move(metrics)) + ? kSuccess + : kInvalidArguments; } inline blink::PointerData::Change ToPointerDataChange( @@ -266,19 +294,10 @@ FlutterResult FlutterEngineSendPointerEvent(FlutterEngine engine, reinterpret_cast(current) + current->struct_size); } - blink::Threads::UI()->PostTask(fxl::MakeCopyable([ - weak_engine = reinterpret_cast(engine) - ->view() - ->engine() - .GetWeakPtr(), - packet = std::move(packet) - ] { - if (auto engine = weak_engine) { - engine->DispatchPointerDataPacket(*packet); - } - })); - - return kSuccess; + return reinterpret_cast(engine) + ->DispatchPointerDataPacket(std::move(packet)) + ? kSuccess + : kInvalidArguments; } FlutterResult FlutterEngineSendPlatformMessage( @@ -293,8 +312,6 @@ FlutterResult FlutterEngineSendPlatformMessage( return kInvalidArguments; } - auto holder = reinterpret_cast(engine); - auto message = fxl::MakeRefCounted( flutter_message->channel, std::vector( @@ -302,13 +319,10 @@ FlutterResult FlutterEngineSendPlatformMessage( flutter_message->message + flutter_message->message_size), nullptr); - blink::Threads::UI()->PostTask( - [ weak_engine = holder->view()->engine().GetWeakPtr(), message ] { - if (auto engine = weak_engine) { - engine->DispatchPlatformMessage(message); - } - }); - return kSuccess; + return reinterpret_cast(engine)->SendPlatformMessage( + std::move(message)) + ? kSuccess + : kInvalidArguments; } FlutterResult FlutterEngineSendPlatformMessageResponse( diff --git a/shell/platform/embedder/embedder_engine.cc b/shell/platform/embedder/embedder_engine.cc new file mode 100644 index 0000000000000..bfa6af9a63fdd --- /dev/null +++ b/shell/platform/embedder/embedder_engine.cc @@ -0,0 +1,115 @@ +// Copyright 2017 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. + +#include "flutter/shell/platform/embedder/embedder_engine.h" + +#include "lib/fxl/functional/make_copyable.h" + +namespace shell { + +EmbedderEngine::EmbedderEngine( + ThreadHost thread_host, + blink::TaskRunners task_runners, + blink::Settings settings, + Shell::CreateCallback on_create_platform_view, + Shell::CreateCallback on_create_rasterizer) + : thread_host_(std::move(thread_host)), + shell_(Shell::Create(std::move(task_runners), + std::move(settings), + on_create_platform_view, + on_create_rasterizer)) { + is_valid_ = shell_ != nullptr; +} + +EmbedderEngine::~EmbedderEngine() = default; + +bool EmbedderEngine::IsValid() const { + return is_valid_; +} + +bool EmbedderEngine::NotifyCreated() { + if (!IsValid()) { + return false; + } + + shell_->GetPlatformView()->NotifyCreated(); + return true; +} + +bool EmbedderEngine::NotifyDestroyed() { + if (!IsValid()) { + return false; + } + + shell_->GetPlatformView()->NotifyDestroyed(); + return true; +} + +bool EmbedderEngine::Run(RunConfiguration run_configuration) { + if (!IsValid()) { + return false; + } + + shell_->GetTaskRunners().GetUITaskRunner()->PostTask( + fxl::MakeCopyable([engine = shell_->GetEngine(), // engine + config = std::move(run_configuration) // config + ]() mutable { + if (engine) { + auto result = engine->Run(std::move(config)); + if (!result) { + FXL_LOG(ERROR) << "Could not launch the engine with configuration."; + } + } + })); + + return true; +} + +bool EmbedderEngine::SetViewportMetrics(blink::ViewportMetrics metrics) { + if (!IsValid()) { + return false; + } + + shell_->GetTaskRunners().GetUITaskRunner()->PostTask( + [engine = shell_->GetEngine(), metrics = std::move(metrics)]() { + if (engine) { + engine->SetViewportMetrics(std::move(metrics)); + } + }); + return true; +} + +bool EmbedderEngine::DispatchPointerDataPacket( + std::unique_ptr packet) { + if (!IsValid() || !packet) { + return false; + } + + shell_->GetTaskRunners().GetUITaskRunner()->PostTask(fxl::MakeCopyable( + [engine = shell_->GetEngine(), packet = std::move(packet)] { + if (engine) { + engine->DispatchPointerDataPacket(*packet); + } + })); + + return true; +} + +bool EmbedderEngine::SendPlatformMessage( + fxl::RefPtr message) { + if (!IsValid() || !message) { + return false; + } + + shell_->GetTaskRunners().GetUITaskRunner()->PostTask( + [engine = shell_->GetEngine(), message] { + if (engine) { + engine->DispatchPlatformMessage(message); + } + }); + + return true; +} + +} // namespace shell diff --git a/shell/platform/embedder/embedder_engine.h b/shell/platform/embedder/embedder_engine.h new file mode 100644 index 0000000000000..c01b86c2aac47 --- /dev/null +++ b/shell/platform/embedder/embedder_engine.h @@ -0,0 +1,53 @@ +// Copyright 2017 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. + +#ifndef FLUTTER_SHELL_PLATFORM_EMBEDDER_EMBEDDER_ENGINE_H_ +#define FLUTTER_SHELL_PLATFORM_EMBEDDER_EMBEDDER_ENGINE_H_ + +#include + +#include "flutter/shell/common/shell.h" +#include "flutter/shell/common/thread_host.h" +#include "flutter/shell/platform/embedder/embedder.h" +#include "lib/fxl/macros.h" + +namespace shell { + +// The object that is returned to the embedder as an opaque pointer to the +// instance of the Flutter engine. +class EmbedderEngine { + public: + EmbedderEngine(ThreadHost thread_host, blink::TaskRunners task_runners, + blink::Settings settings, + Shell::CreateCallback on_create_platform_view, + Shell::CreateCallback on_create_rasterizer); + + ~EmbedderEngine(); + + bool NotifyCreated(); + + bool NotifyDestroyed(); + + bool Run(RunConfiguration run_configuration); + + bool IsValid() const; + + bool SetViewportMetrics(blink::ViewportMetrics metrics); + + bool DispatchPointerDataPacket( + std::unique_ptr packet); + + bool SendPlatformMessage(fxl::RefPtr message); + + private: + const ThreadHost thread_host_; + std::unique_ptr shell_; + bool is_valid_ = false; + + FXL_DISALLOW_COPY_AND_ASSIGN(EmbedderEngine); +}; + +} // namespace shell + +#endif // FLUTTER_SHELL_PLATFORM_EMBEDDER_EMBEDDER_ENGINE_H_ diff --git a/shell/platform/embedder/platform_view_embedder.cc b/shell/platform/embedder/platform_view_embedder.cc index 46e0390770da4..5090584594322 100644 --- a/shell/platform/embedder/platform_view_embedder.cc +++ b/shell/platform/embedder/platform_view_embedder.cc @@ -3,17 +3,19 @@ // found in the LICENSE file. #include "flutter/shell/platform/embedder/platform_view_embedder.h" -#include "flutter/shell/gpu/gpu_rasterizer.h" + +#include "flutter/shell/common/io_manager.h" +#include "third_party/skia/include/gpu/gl/GrGLInterface.h" namespace shell { -PlatformViewEmbedder::PlatformViewEmbedder(DispatchTable dispatch_table) - : PlatformView(std::make_unique(nullptr)), +PlatformViewEmbedder::PlatformViewEmbedder(PlatformView::Delegate& delegate, + blink::TaskRunners task_runners, + DispatchTable dispatch_table) + : PlatformView(delegate, std::move(task_runners)), dispatch_table_(dispatch_table) {} -PlatformViewEmbedder::~PlatformViewEmbedder() { - NotifyDestroyed(); -} +PlatformViewEmbedder::~PlatformViewEmbedder() = default; bool PlatformViewEmbedder::GLContextMakeCurrent() { return dispatch_table_.gl_make_current_callback(); @@ -31,33 +33,6 @@ intptr_t PlatformViewEmbedder::GLContextFBO() const { return dispatch_table_.gl_fbo_callback(); } -void PlatformViewEmbedder::Attach() { - CreateEngine(); - NotifyCreated(std::make_unique(this)); - - if (dispatch_table_.gl_make_resource_current_callback != nullptr) { - SetupResourceContextOnIOThread(); - } -} - -bool PlatformViewEmbedder::ResourceContextMakeCurrent() { - if (dispatch_table_.gl_make_resource_current_callback == nullptr) { - return false; - } - return dispatch_table_.gl_make_resource_current_callback(); -} - -void PlatformViewEmbedder::RunFromSource(const std::string& assets_directory, - const std::string& main, - const std::string& packages) { - FXL_LOG(INFO) << "Hot reloading is unsupported on this platform."; -} - -void PlatformViewEmbedder::SetAssetBundlePath( - const std::string& assets_directory) { - FXL_LOG(INFO) << "Set asset bundle path is unsupported on this platform."; -} - void PlatformViewEmbedder::HandlePlatformMessage( fxl::RefPtr message) { if (!message) { @@ -76,4 +51,18 @@ void PlatformViewEmbedder::HandlePlatformMessage( dispatch_table_.platform_message_response_callback(std::move(message)); } +std::unique_ptr PlatformViewEmbedder::CreateRenderingSurface() { + return std::make_unique(this); +} + +sk_sp PlatformViewEmbedder::CreateResourceContext() const { + auto callback = dispatch_table_.gl_make_resource_current_callback; + if (callback && callback()) { + return IOManager::CreateCompatibleResourceLoadingContext( + GrBackend::kOpenGL_GrBackend, + reinterpret_cast(GrGLCreateNativeInterface())); + } + return nullptr; +} + } // namespace shell diff --git a/shell/platform/embedder/platform_view_embedder.h b/shell/platform/embedder/platform_view_embedder.h index c287fc5ac86f1..a5b76dfaa07ba 100644 --- a/shell/platform/embedder/platform_view_embedder.h +++ b/shell/platform/embedder/platform_view_embedder.h @@ -12,7 +12,8 @@ namespace shell { -class PlatformViewEmbedder : public PlatformView, public GPUSurfaceGLDelegate { +class PlatformViewEmbedder final : public PlatformView, + public GPUSurfaceGLDelegate { public: using PlatformMessageResponseCallback = std::function)>; @@ -26,9 +27,11 @@ class PlatformViewEmbedder : public PlatformView, public GPUSurfaceGLDelegate { std::function gl_make_resource_current_callback; // optional }; - PlatformViewEmbedder(DispatchTable dispatch_table); + PlatformViewEmbedder(PlatformView::Delegate& delegate, + blink::TaskRunners task_runners, + DispatchTable dispatch_table); - ~PlatformViewEmbedder(); + ~PlatformViewEmbedder() override; // |shell::GPUSurfaceGLDelegate| bool GLContextMakeCurrent() override; @@ -42,20 +45,6 @@ class PlatformViewEmbedder : public PlatformView, public GPUSurfaceGLDelegate { // |shell::GPUSurfaceGLDelegate| intptr_t GLContextFBO() const override; - // |shell::PlatformView| - void Attach() override; - - // |shell::PlatformView| - bool ResourceContextMakeCurrent() override; - - // |shell::PlatformView| - void RunFromSource(const std::string& assets_directory, - const std::string& main, - const std::string& packages) override; - - // |shell::PlatformView| - void SetAssetBundlePath(const std::string& assets_directory) override; - // |shell::PlatformView| void HandlePlatformMessage( fxl::RefPtr message) override; @@ -63,6 +52,12 @@ class PlatformViewEmbedder : public PlatformView, public GPUSurfaceGLDelegate { private: DispatchTable dispatch_table_; + // |shell::PlatformView| + std::unique_ptr CreateRenderingSurface() override; + + // |shell::PlatformView| + sk_sp CreateResourceContext() const override; + FXL_DISALLOW_COPY_AND_ASSIGN(PlatformViewEmbedder); }; diff --git a/shell/platform/linux/BUILD.gn b/shell/platform/linux/BUILD.gn deleted file mode 100644 index 6e9f1f596b09d..0000000000000 --- a/shell/platform/linux/BUILD.gn +++ /dev/null @@ -1,23 +0,0 @@ -# Copyright 2016 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -executable("linux") { - output_name = "flutter_tester" - - sources = [ - "main_linux.cc", - ] - - deps = [ - "//third_party/dart/runtime:libdart_jit", - "//third_party/dart/runtime/bin:embedded_dart_io", - "$flutter_root/common", - "$flutter_root/fml", - "$flutter_root/shell/common", - "$flutter_root/shell/testing", - "//garnet/public/lib/fxl", - "//third_party/skia", - "//topaz/lib/tonic", - ] -} diff --git a/shell/platform/linux/main_linux.cc b/shell/platform/linux/main_linux.cc deleted file mode 100644 index a20d425ed1eed..0000000000000 --- a/shell/platform/linux/main_linux.cc +++ /dev/null @@ -1,142 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "flutter/common/threads.h" -#include "flutter/fml/message_loop.h" -#include "flutter/shell/common/platform_view.h" -#include "flutter/shell/common/shell.h" -#include "flutter/shell/common/switches.h" -#include "flutter/shell/testing/test_runner.h" -#include "flutter/shell/testing/testing.h" -#include "flutter/sky/engine/public/web/Sky.h" -#include "lib/fxl/command_line.h" -#include "lib/tonic/dart_microtask_queue.h" -#include "third_party/dart/runtime/bin/embedded_dart_io.h" - -namespace { - -// Exit codes used by the Dart command line tool. -const int kApiErrorExitCode = 253; -const int kCompilationErrorExitCode = 254; -const int kErrorExitCode = 255; - -// Checks whether the engine's main Dart isolate has no pending work. If so, -// then exit the given message loop. -class ScriptCompletionTaskObserver : public fml::TaskObserver { - public: - ScriptCompletionTaskObserver(fxl::RefPtr task_runner) - : main_task_runner_(std::move(task_runner)), - prev_live_(false), - last_error_(tonic::kNoError) {} - - void DidProcessTask() override { - shell::TestRunner& test_runner = shell::TestRunner::Shared(); - shell::Engine& engine = test_runner.platform_view().engine(); - - if (engine.GetLoadScriptError() != tonic::kNoError) { - last_error_ = engine.GetLoadScriptError(); - main_task_runner_->PostTask( - []() { fml::MessageLoop::GetCurrent().Terminate(); }); - return; - } - - bool live = engine.UIIsolateHasLivePorts(); - if (prev_live_ && !live) { - last_error_ = engine.GetUIIsolateLastError(); - main_task_runner_->PostTask( - []() { fml::MessageLoop::GetCurrent().Terminate(); }); - } - prev_live_ = live; - } - - tonic::DartErrorHandleType last_error() { return last_error_; } - - private: - fxl::RefPtr main_task_runner_; - bool prev_live_; - tonic::DartErrorHandleType last_error_; -}; - -int ConvertErrorTypeToExitCode(tonic::DartErrorHandleType error) { - switch (error) { - case tonic::kCompilationErrorType: - return kCompilationErrorExitCode; - case tonic::kApiErrorType: - return kApiErrorExitCode; - case tonic::kUnknownErrorType: - return kErrorExitCode; - default: - return 0; - } -} - -void RunNonInteractive(fxl::CommandLine initial_command_line, - bool run_forever) { - // This is a platform thread (i.e not one created by fml::Thread), so perform - // one time initialization. - fml::MessageLoop::EnsureInitializedForCurrentThread(); - - std::string bundle_path = ""; - initial_command_line.GetOptionValue( - FlagForSwitch(shell::Switch::FlutterAssetsDir), &bundle_path); - - shell::Shell::InitStandalone(initial_command_line, - /* icu_data_path= */ "", - /* application_library_path= */ "", bundle_path); - - // Note that this task observer must be added after the observer that drains - // the microtask queue. - ScriptCompletionTaskObserver task_observer( - fml::MessageLoop::GetCurrent().GetTaskRunner()); - if (!run_forever) { - blink::Threads::UI()->PostTask([&task_observer] { - fml::MessageLoop::GetCurrent().AddTaskObserver(&task_observer); - }); - } - - if (!shell::InitForTesting(initial_command_line)) { - shell::PrintUsage("flutter_tester"); - exit(EXIT_FAILURE); - return; - } - - fml::MessageLoop::GetCurrent().Run(); - - shell::TestRunner& test_runner = shell::TestRunner::Shared(); - tonic::DartErrorHandleType error = - test_runner.platform_view().engine().GetLoadScriptError(); - if (error == tonic::kNoError) - error = task_observer.last_error(); - if (error == tonic::kNoError) { - fxl::AutoResetWaitableEvent latch; - blink::Threads::UI()->PostTask([&error, &latch] { - error = tonic::DartMicrotaskQueue::GetForCurrentThread()->GetLastError(); - latch.Signal(); - }); - latch.Wait(); - } - - // The script has completed and the engine may not be in a clean state, - // so just stop the process. - exit(ConvertErrorTypeToExitCode(error)); -} - -} // namespace - -int main(int argc, char* argv[]) { - dart::bin::SetExecutableName(argv[0]); - dart::bin::SetExecutableArguments(argc - 1, argv); - - auto command_line = fxl::CommandLineFromArgcArgv(argc, argv); - - if (command_line.HasOption(shell::FlagForSwitch(shell::Switch::Help))) { - shell::PrintUsage("flutter_tester"); - return EXIT_SUCCESS; - } - - bool run_forever = - command_line.HasOption(shell::FlagForSwitch(shell::Switch::RunForever)); - RunNonInteractive(std::move(command_line), run_forever); - return EXIT_SUCCESS; -} diff --git a/shell/testing/BUILD.gn b/shell/testing/BUILD.gn index 659c241cc49b8..6554599bf7a85 100644 --- a/shell/testing/BUILD.gn +++ b/shell/testing/BUILD.gn @@ -2,24 +2,26 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. -source_set("testing") { +executable("testing") { + testonly = true + + output_name = "flutter_tester" + + public_configs = [ "$flutter_root:config" ] + sources = [ - "platform_view_test.cc", - "platform_view_test.h", - "test_runner.cc", - "test_runner.h", - "testing.cc", - "testing.h", + "tester_main.cc", ] deps = [ + "$flutter_root/assets", "$flutter_root/common", + "$flutter_root/fml", "$flutter_root/shell/common", "//garnet/public/lib/fxl", + "//third_party/dart/runtime:libdart_jit", + "//third_party/dart/runtime/bin:embedded_dart_io", "//third_party/skia", - ] - - public_configs = [ - "$flutter_root:config", + "//topaz/lib/tonic", ] } diff --git a/shell/testing/platform_view_test.cc b/shell/testing/platform_view_test.cc deleted file mode 100644 index a59684d31bc94..0000000000000 --- a/shell/testing/platform_view_test.cc +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "flutter/shell/testing/platform_view_test.h" - -#include "flutter/shell/common/null_rasterizer.h" -#include "flutter/shell/common/shell.h" - -namespace shell { - -PlatformViewTest::PlatformViewTest() - : PlatformView(std::unique_ptr(new NullRasterizer())) {} - -void PlatformViewTest::Attach() { - CreateEngine(); -} - -PlatformViewTest::~PlatformViewTest() = default; - -bool PlatformViewTest::ResourceContextMakeCurrent() { - return false; -} - -void PlatformViewTest::RunFromSource(const std::string& assets_directory, - const std::string& main, - const std::string& packages) {} - -void PlatformViewTest::SetAssetBundlePath(const std::string& assets_directory) { -} - -} // namespace shell diff --git a/shell/testing/platform_view_test.h b/shell/testing/platform_view_test.h deleted file mode 100644 index 3612d0363ab58..0000000000000 --- a/shell/testing/platform_view_test.h +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef SHELL_TESTING_PLATFORM_VIEW_TEST_H_ -#define SHELL_TESTING_PLATFORM_VIEW_TEST_H_ - -#include "flutter/shell/common/platform_view.h" -#include "lib/fxl/macros.h" -#include "lib/fxl/memory/weak_ptr.h" - -namespace shell { - -class Shell; - -class PlatformViewTest : public PlatformView { - public: - PlatformViewTest(); - - ~PlatformViewTest(); - - virtual void Attach() override; - - bool ResourceContextMakeCurrent() override; - - void RunFromSource(const std::string& assets_directory, - const std::string& main, - const std::string& packages) override; - - void SetAssetBundlePath(const std::string& assets_directory) override; - - private: - FXL_DISALLOW_COPY_AND_ASSIGN(PlatformViewTest); -}; - -} // namespace shell - -#endif // SHELL_TESTING_PLATFORM_VIEW_TEST_H_ diff --git a/shell/testing/test_runner.cc b/shell/testing/test_runner.cc deleted file mode 100644 index 838b04120c773..0000000000000 --- a/shell/testing/test_runner.cc +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "flutter/shell/testing/test_runner.h" - -#include - -#include "flutter/common/threads.h" -#include "flutter/shell/common/platform_view.h" -#include "flutter/shell/common/shell.h" -#include "flutter/shell/testing/platform_view_test.h" - -namespace shell { - -TestRunner::TestRunner() - : platform_view_(std::make_shared()) { - platform_view_->Attach(); - blink::ViewportMetrics metrics; - metrics.device_pixel_ratio = 3.0; - metrics.physical_width = 2400; // 800 at 3x resolution - metrics.physical_height = 1800; // 600 at 3x resolution - - blink::Threads::UI()->PostTask( - [ engine = platform_view_->engine().GetWeakPtr(), metrics ] { - if (engine) - engine->SetViewportMetrics(metrics); - }); -} - -TestRunner::~TestRunner() = default; - -TestRunner& TestRunner::Shared() { - static TestRunner* g_test_runner = nullptr; - if (!g_test_runner) - g_test_runner = new TestRunner(); - return *g_test_runner; -} - -void TestRunner::Run(const TestDescriptor& test) { - blink::Threads::UI()->PostTask( - [ engine = platform_view_->engine().GetWeakPtr(), test ] { - if (engine) - engine->RunBundleAndSource(std::string(), test.path, test.packages); - }); -} - -} // namespace shell diff --git a/shell/testing/test_runner.h b/shell/testing/test_runner.h deleted file mode 100644 index 8f303d7eb7683..0000000000000 --- a/shell/testing/test_runner.h +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef SHELL_TESTING_TEST_RUNNER_H_ -#define SHELL_TESTING_TEST_RUNNER_H_ - -#include -#include - -#include "lib/fxl/macros.h" -#include "lib/fxl/memory/weak_ptr.h" - -namespace shell { - -class PlatformView; - -class TestRunner { - public: - static TestRunner& Shared(); - - struct TestDescriptor { - std::string path; - std::string packages; - }; - - void Run(const TestDescriptor& test); - - PlatformView& platform_view() { return *platform_view_; } - - private: - TestRunner(); - ~TestRunner(); - - std::shared_ptr platform_view_; - - FXL_DISALLOW_COPY_AND_ASSIGN(TestRunner); -}; - -} // namespace shell - -#endif // SHELL_TESTING_TEST_RUNNER_H_ diff --git a/shell/testing/tester_main.cc b/shell/testing/tester_main.cc new file mode 100644 index 0000000000000..b4d0bba705c9d --- /dev/null +++ b/shell/testing/tester_main.cc @@ -0,0 +1,235 @@ +// Copyright 2018 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. + +#define FML_USED_ON_EMBEDDER + +#include + +#include "flutter/assets/asset_manager.h" +#include "flutter/assets/directory_asset_bundle.h" +#include "flutter/fml/file.h" +#include "flutter/fml/message_loop.h" +#include "flutter/shell/common/platform_view.h" +#include "flutter/shell/common/rasterizer.h" +#include "flutter/shell/common/shell.h" +#include "flutter/shell/common/switches.h" +#include "flutter/shell/common/thread_host.h" +#include "lib/fxl/functional/make_copyable.h" +#include "lib/fxl/synchronization/waitable_event.h" +#include "third_party/dart/runtime/bin/embedded_dart_io.h" + +namespace shell { + +// Checks whether the engine's main Dart isolate has no pending work. If so, +// then exit the given message loop. +class ScriptCompletionTaskObserver { + public: + ScriptCompletionTaskObserver(Shell& shell, + fxl::RefPtr main_task_runner, + bool run_forever) + : engine_(shell.GetEngine()), + main_task_runner_(std::move(main_task_runner)), + run_forever_(run_forever) {} + + int GetExitCodeForLastError() const { + // Exit codes used by the Dart command line tool. + const int kApiErrorExitCode = 253; + const int kCompilationErrorExitCode = 254; + const int kErrorExitCode = 255; + switch (last_error_) { + case tonic::kCompilationErrorType: + return kCompilationErrorExitCode; + case tonic::kApiErrorType: + return kApiErrorExitCode; + case tonic::kUnknownErrorType: + return kErrorExitCode; + default: + return 0; + } + } + + void DidProcessTask() { + if (engine_) { + last_error_ = engine_->GetUIIsolateLastError(); + if (engine_->UIIsolateHasLivePorts()) { + // The UI isolate still has live ports and is running. Nothing to do + // just yet. + return; + } + } + + if (run_forever_) { + // We need this script to run forever. We have already recorded the last + // error. Keep going. + return; + } + + if (!has_terminated) { + // Only try to terminate the loop once. + has_terminated = true; + main_task_runner_->PostTask( + []() { fml::MessageLoop::GetCurrent().Terminate(); }); + } + } + + private: + fml::WeakPtr engine_; + fxl::RefPtr main_task_runner_; + bool run_forever_ = false; + tonic::DartErrorHandleType last_error_ = tonic::kUnknownErrorType; + bool has_terminated = false; + + FXL_DISALLOW_COPY_AND_ASSIGN(ScriptCompletionTaskObserver); +}; + +int RunTester(const blink::Settings& settings, bool run_forever) { + const auto thread_label = "io.flutter.test"; + + fml::MessageLoop::EnsureInitializedForCurrentThread(); + + auto current_task_runner = fml::MessageLoop::GetCurrent().GetTaskRunner(); + + // Setup a single threaded test runner configuration. + const blink::TaskRunners task_runners(thread_label, // dart thread label + current_task_runner, // platform + current_task_runner, // gpu + current_task_runner, // ui + current_task_runner // io + ); + + Shell::CreateCallback on_create_platform_view = + [](Shell& shell) { + return std::make_unique(shell, shell.GetTaskRunners()); + }; + + Shell::CreateCallback on_create_rasterizer = [](Shell& shell) { + return std::make_unique(shell.GetTaskRunners()); + }; + + auto shell = Shell::Create(task_runners, // + settings, // + on_create_platform_view, // + on_create_rasterizer // + ); + + if (!shell || !shell->IsSetup()) { + FXL_LOG(ERROR) << "Could not setup the shell."; + return EXIT_FAILURE; + } + + auto isolate_configuration = IsolateConfiguration::CreateForSource( + settings.main_dart_file_path, settings.packages_file_path); + + if (!isolate_configuration) { + FXL_LOG(ERROR) << "Could create isolate configuration."; + return EXIT_FAILURE; + } + + auto asset_manager = fxl::MakeRefCounted(); + asset_manager->PushBack(std::make_unique( + fml::Duplicate(settings.assets_dir))); + asset_manager->PushBack( + std::make_unique(fml::OpenFile( + settings.assets_path.c_str(), fml::OpenPermission::kRead, true))); + + RunConfiguration run_configuration(std::move(isolate_configuration), + std::move(asset_manager)); + + // The script completion task observer that will be installed on the UI thread + // that watched if the engine has any live ports. + ScriptCompletionTaskObserver completion_observer( + *shell, // a valid shell + fml::MessageLoop::GetCurrent() + .GetTaskRunner(), // the message loop to terminate + run_forever // should the exit be ignored + ); + + bool engine_did_run = false; + + shell->GetTaskRunners().GetUITaskRunner()->PostTask(fxl::MakeCopyable( + [&completion_observer, engine = shell->GetEngine(), + config = std::move(run_configuration), &engine_did_run]() mutable { + fml::MessageLoop::GetCurrent().AddTaskObserver( + reinterpret_cast(&completion_observer), + [&completion_observer]() { completion_observer.DidProcessTask(); }); + if (engine->Run(std::move(config))) { + engine_did_run = true; + + blink::ViewportMetrics metrics; + metrics.device_pixel_ratio = 3.0; + metrics.physical_width = 2400; // 800 at 3x resolution + metrics.physical_height = 1800; // 600 at 3x resolution + engine->SetViewportMetrics(metrics); + + } else { + FXL_DLOG(ERROR) << "Could not launch the engine with configuration."; + } + })); + + // Run the message loop and wait for the script to do its thing. + fml::MessageLoop::GetCurrent().Run(); + + // Cleanup the completion observer synchronously as it is living on the + // stack. + fxl::AutoResetWaitableEvent latch; + fml::TaskRunner::RunNowOrPostTask( + shell->GetTaskRunners().GetUITaskRunner(), + [&latch, &completion_observer] { + fml::MessageLoop::GetCurrent().RemoveTaskObserver( + reinterpret_cast(&completion_observer)); + latch.Signal(); + }); + latch.Wait(); + + if (!engine_did_run) { + // If the engine itself didn't have a chance to run, there is no point in + // asking it if there was an error. Signal a failure unconditionally. + return EXIT_FAILURE; + } + + return completion_observer.GetExitCodeForLastError(); +} + +} // namespace shell + +int main(int argc, char* argv[]) { + dart::bin::SetExecutableName(argv[0]); + dart::bin::SetExecutableArguments(argc - 1, argv); + + auto command_line = fxl::CommandLineFromArgcArgv(argc, argv); + + if (command_line.HasOption(shell::FlagForSwitch(shell::Switch::Help))) { + shell::PrintUsage("flutter_tester"); + return EXIT_SUCCESS; + } + + auto settings = shell::SettingsFromCommandLine(command_line); + if (command_line.positional_args().size() > 0) { + // The tester may not use the switch for the main dart file path. Specifying + // it as a positional argument instead. + settings.main_dart_file_path = command_line.positional_args()[0]; + } + + if (settings.main_dart_file_path.size() == 0) { + FXL_LOG(ERROR) << "Main dart file path not specified."; + return EXIT_FAILURE; + } + + settings.icu_data_path = "icudtl.dat"; + + // The tools that read logs get confused if there is a log tag specified. + settings.log_tag = ""; + + settings.task_observer_add = [](intptr_t key, fxl::Closure callback) { + fml::MessageLoop::GetCurrent().AddTaskObserver(key, std::move(callback)); + }; + + settings.task_observer_remove = [](intptr_t key) { + fml::MessageLoop::GetCurrent().RemoveTaskObserver(key); + }; + + return shell::RunTester( + settings, + command_line.HasOption(shell::FlagForSwitch(shell::Switch::RunForever))); +} diff --git a/shell/testing/testing.cc b/shell/testing/testing.cc deleted file mode 100644 index 13a3ab5be3d93..0000000000000 --- a/shell/testing/testing.cc +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "flutter/shell/testing/testing.h" - -#include "flutter/shell/common/switches.h" -#include "flutter/shell/testing/test_runner.h" - -namespace shell { - -bool InitForTesting(const fxl::CommandLine& command_line) { - TestRunner::TestDescriptor test; - test.packages = command_line.GetOptionValueWithDefault( - FlagForSwitch(Switch::Packages), ""); - auto args = command_line.positional_args(); - if (args.empty()) - return false; - test.path = args[0]; - TestRunner::Shared().Run(test); - return true; -} - -} // namespace shell diff --git a/shell/testing/testing.h b/shell/testing/testing.h deleted file mode 100644 index 99f8b4fe983de..0000000000000 --- a/shell/testing/testing.h +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef SHELL_TESTING_TESTING_H_ -#define SHELL_TESTING_TESTING_H_ - -#include "lib/fxl/command_line.h" - -namespace shell { - -bool InitForTesting(const fxl::CommandLine& command_line); - -} // namespace shell - -#endif // SHELL_TESTING_TESTING_H_ From 0ceae628bb10134ea2373ef1840696920313f7d7 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Tue, 27 Mar 2018 11:37:33 -0700 Subject: [PATCH 2/2] Address PR comments. --- shell/common/engine.cc | 4 +-- shell/common/isolate_configuration.cc | 4 +-- shell/common/platform_view.h | 2 +- shell/common/rasterizer.cc | 2 +- shell/common/run_configuration.cc | 6 ++--- shell/common/run_configuration.h | 2 +- shell/common/shell.cc | 19 +++++++++++++ shell/common/vsync_waiter.cc | 17 +++++++----- shell/gpu/gpu_surface_gl.cc | 2 ++ .../android/android_external_texture_gl.cc | 3 ++- .../android/android_external_texture_gl.h | 3 +-- .../platform/android/android_shell_holder.cc | 10 +++---- shell/platform/android/flutter_main.cc | 2 +- .../framework/Source/FlutterDartProject.mm | 27 ++++++++++++++++++- .../framework/Source/FlutterViewController.mm | 13 ++++++++- .../darwin/ios/ios_external_texture_gl.h | 2 ++ .../darwin/ios/ios_external_texture_gl.mm | 8 +++--- shell/platform/embedder/embedder.cc | 4 +-- shell/testing/BUILD.gn | 4 +++ shell/testing/tester_main.cc | 4 +++ 20 files changed, 101 insertions(+), 37 deletions(-) diff --git a/shell/common/engine.cc b/shell/common/engine.cc index ecfeb649c9652..ca43389143a3c 100644 --- a/shell/common/engine.cc +++ b/shell/common/engine.cc @@ -57,9 +57,7 @@ Engine::Engine(Delegate& delegate, if (legacy_sky_platform_) { // TODO: Remove this legacy call along with the platform. This is what makes // the engine unable to run from multiple threads in the legacy - // configuration. The microtask handler are still in this initializer still. - // So care must be taken to remove them from the legacy platform - // initializer. + // configuration. blink::InitEngine(legacy_sky_platform_.get()); } diff --git a/shell/common/isolate_configuration.cc b/shell/common/isolate_configuration.cc index fd330e544ceb5..0706ee5ab58df 100644 --- a/shell/common/isolate_configuration.cc +++ b/shell/common/isolate_configuration.cc @@ -104,8 +104,8 @@ std::unique_ptr IsolateConfiguration::InferFromSettings( // Running from kernel snapshot. { std::vector kernel; - if (asset_manager && - asset_manager->GetAsBuffer(settings.application_kernel_path, &kernel)) { + if (asset_manager && asset_manager->GetAsBuffer( + settings.application_kernel_asset, &kernel)) { return CreateForSnapshot( std::make_unique(std::move(kernel))); } diff --git a/shell/common/platform_view.h b/shell/common/platform_view.h index eda08714f5a8a..56179a6114777 100644 --- a/shell/common/platform_view.h +++ b/shell/common/platform_view.h @@ -112,7 +112,7 @@ class PlatformView { void UnregisterTexture(int64_t texture_id); // Called once per texture update (e.g. video frame), on the platform thread. - virtual void MarkTextureFrameAvailable(int64_t texture_id); + void MarkTextureFrameAvailable(int64_t texture_id); protected: PlatformView::Delegate& delegate_; diff --git a/shell/common/rasterizer.cc b/shell/common/rasterizer.cc index b116f31b5ba00..56bb9e832e4f8 100644 --- a/shell/common/rasterizer.cc +++ b/shell/common/rasterizer.cc @@ -177,7 +177,7 @@ Rasterizer::Screenshot Rasterizer::ScreenshotLastLayerTree( } if (data == nullptr) { - FXL_DLOG(INFO) << "Sceenshot data will null."; + FXL_DLOG(INFO) << "Sceenshot data was null."; return {nullptr, SkISize::MakeEmpty()}; } diff --git a/shell/common/run_configuration.cc b/shell/common/run_configuration.cc index 8bb69e8436f0f..7fb385fb4d9dc 100644 --- a/shell/common/run_configuration.cc +++ b/shell/common/run_configuration.cc @@ -39,7 +39,7 @@ RunConfiguration::RunConfiguration( RunConfiguration::RunConfiguration( std::unique_ptr configuration, fxl::RefPtr asset_manager) - : run_configuration_(std::move(configuration)), + : isolate_configuration_(std::move(configuration)), asset_manager_(std::move(asset_manager)) {} RunConfiguration::RunConfiguration(RunConfiguration&&) = default; @@ -47,7 +47,7 @@ RunConfiguration::RunConfiguration(RunConfiguration&&) = default; RunConfiguration::~RunConfiguration() = default; bool RunConfiguration::IsValid() const { - return asset_manager_ && run_configuration_; + return asset_manager_ && isolate_configuration_; } bool RunConfiguration::AddAssetResolver( @@ -74,7 +74,7 @@ const std::string& RunConfiguration::GetEntrypoint() const { std::unique_ptr RunConfiguration::TakeIsolateConfiguration() { - return std::move(run_configuration_); + return std::move(isolate_configuration_); } } // namespace shell diff --git a/shell/common/run_configuration.h b/shell/common/run_configuration.h index f3995622cb204..59aa07566685c 100644 --- a/shell/common/run_configuration.h +++ b/shell/common/run_configuration.h @@ -44,7 +44,7 @@ class RunConfiguration { std::unique_ptr TakeIsolateConfiguration(); private: - std::unique_ptr run_configuration_; + std::unique_ptr isolate_configuration_; fxl::RefPtr asset_manager_; std::string entrypoint_ = "main"; diff --git a/shell/common/shell.cc b/shell/common/shell.cc index 4bbc795f33f06..53527a996bd76 100644 --- a/shell/common/shell.cc +++ b/shell/common/shell.cc @@ -522,6 +522,25 @@ void Shell::OnPlatformViewMarkTextureFrameAvailable(const PlatformView& view, FXL_DCHECK(&view == platform_view_.get()); FXL_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread()); + // Tell the rasterizer that one of its textures has a new frame available. + task_runners_.GetGPUTaskRunner()->PostTask( + [rasterizer = rasterizer_->GetWeakPtr(), texture_id]() { + auto registry = rasterizer->GetTextureRegistry(); + + if (!registry) { + return; + } + + auto texture = registry->GetTexture(texture_id); + + if (!texture) { + return; + } + + texture->MarkNewFrameAvailable(); + }); + + // Schedule a new frame without having to rebuild the layer tree. task_runners_.GetUITaskRunner()->PostTask([engine = engine_->GetWeakPtr()]() { if (engine) { engine->ScheduleFrame(false); diff --git a/shell/common/vsync_waiter.cc b/shell/common/vsync_waiter.cc index 7233631b8d4f3..ba2f21c389c11 100644 --- a/shell/common/vsync_waiter.cc +++ b/shell/common/vsync_waiter.cc @@ -24,9 +24,6 @@ void VsyncWaiter::AsyncWaitForVsync(Callback callback) { void VsyncWaiter::FireCallback(fxl::TimePoint frame_start_time, fxl::TimePoint frame_target_time) { - // Note: The tag name must be "VSYNC" (it is special) so that the "Highlight - // Vsync" checkbox in the timeline can be enabled. - TRACE_EVENT1("flutter", "VSYNC", "mode", "basic"); Callback callback; { @@ -34,10 +31,18 @@ void VsyncWaiter::FireCallback(fxl::TimePoint frame_start_time, callback = std::move(callback_); } - if (callback) { - task_runners_.GetUITaskRunner()->PostTask( - std::bind(std::move(callback), frame_start_time, frame_target_time)); + if (!callback) { + return; } + + task_runners_.GetUITaskRunner()->PostTask( + [callback, frame_start_time, frame_target_time]() { + // Note: The tag name must be "VSYNC" (it is special) so that the + // "Highlight + // Vsync" checkbox in the timeline can be enabled. + TRACE_EVENT0("flutter", "VSYNC"); + callback(frame_start_time, frame_target_time); + }); } } // namespace shell diff --git a/shell/gpu/gpu_surface_gl.cc b/shell/gpu/gpu_surface_gl.cc index 2e8b199409262..0b2bc9b95de62 100644 --- a/shell/gpu/gpu_surface_gl.cc +++ b/shell/gpu/gpu_surface_gl.cc @@ -60,6 +60,8 @@ GPUSurfaceGL::~GPUSurfaceGL() { return; } + GetCompositorContext().OnGrContextDestroyed(); + onscreen_surface_ = nullptr; context_->releaseResourcesAndAbandonContext(); context_ = nullptr; diff --git a/shell/platform/android/android_external_texture_gl.cc b/shell/platform/android/android_external_texture_gl.cc index 7e504cab30dcc..35db9cbbfdb95 100644 --- a/shell/platform/android/android_external_texture_gl.cc +++ b/shell/platform/android/android_external_texture_gl.cc @@ -39,7 +39,8 @@ void AndroidExternalTextureGL::Paint(SkCanvas& canvas, const SkRect& bounds) { Update(); new_frame_ready_ = false; } - GrGLTextureInfo textureInfo = {GL_TEXTURE_EXTERNAL_OES, texture_name_, GL_RGBA8_OES}; + GrGLTextureInfo textureInfo = {GL_TEXTURE_EXTERNAL_OES, texture_name_, + GL_RGBA8_OES}; GrBackendTexture backendTexture(1, 1, GrMipMapped::kNo, textureInfo); sk_sp image = SkImage::MakeFromTexture( canvas.getGrContext(), backendTexture, kTopLeft_GrSurfaceOrigin, diff --git a/shell/platform/android/android_external_texture_gl.h b/shell/platform/android/android_external_texture_gl.h index df890b91c827f..67d6541d964c2 100644 --- a/shell/platform/android/android_external_texture_gl.h +++ b/shell/platform/android/android_external_texture_gl.h @@ -25,8 +25,7 @@ class AndroidExternalTextureGL : public flow::Texture { virtual void OnGrContextDestroyed() override; - // Called on GPU thread. - void MarkNewFrameAvailable(); + void MarkNewFrameAvailable() override; private: void Attach(jint textureName); diff --git a/shell/platform/android/android_shell_holder.cc b/shell/platform/android/android_shell_holder.cc index 01bb31eb9c13a..7dc454f1fb74a 100644 --- a/shell/platform/android/android_shell_holder.cc +++ b/shell/platform/android/android_shell_holder.cc @@ -10,6 +10,7 @@ #include #include +#include #include #include "flutter/fml/message_loop.h" @@ -19,17 +20,12 @@ namespace shell { -static std::string CreateAndroidShellLabel(intptr_t handle) { - std::stringstream stream; - stream << "io.flutter.0x" << std::hex << handle; - return stream.str(); -} - AndroidShellHolder::AndroidShellHolder( blink::Settings settings, fml::jni::JavaObjectWeakGlobalRef java_object) : settings_(std::move(settings)), java_object_(java_object) { - auto thread_label = CreateAndroidShellLabel(reinterpret_cast(this)); + static size_t shell_count = 1; + auto thread_label = std::to_string(shell_count++); thread_host_ = {thread_label, ThreadHost::Type::UI | ThreadHost::Type::GPU | ThreadHost::Type::IO}; diff --git a/shell/platform/android/flutter_main.cc b/shell/platform/android/flutter_main.cc index 06fd8a82bbddf..08d97add93b87 100644 --- a/shell/platform/android/flutter_main.cc +++ b/shell/platform/android/flutter_main.cc @@ -67,7 +67,7 @@ void FlutterMain::Init(JNIEnv* env, if (files::IsFile(platform_kernel_path) && files::IsFile(application_kernel_path)) { settings.kernel_snapshot_path = platform_kernel_path; - settings.application_kernel_path = application_kernel_path; + settings.application_kernel_asset = application_kernel_path; } } diff --git a/shell/platform/darwin/ios/framework/Source/FlutterDartProject.mm b/shell/platform/darwin/ios/framework/Source/FlutterDartProject.mm index 6a96081f43694..3c613c9fbad8f 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterDartProject.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterDartProject.mm @@ -104,7 +104,7 @@ [NSURL URLWithString:@(kApplicationKernelSnapshotFileName) relativeToURL:[NSURL fileURLWithPath:assetsPath]]; if ([[NSFileManager defaultManager] fileExistsAtPath:applicationKernelSnapshotURL.path]) { - settings.application_kernel_path = applicationKernelSnapshotURL.path.UTF8String; + settings.application_kernel_asset = applicationKernelSnapshotURL.path.UTF8String; } } } @@ -204,4 +204,29 @@ - (instancetype)initFromDefaultSourceForConfiguration { return shell::RunConfiguration::InferFromSettings(_settings); } +#pragma mark - Assets-related utilities + ++ (NSString*)flutterAssetsName:(NSBundle*)bundle { + NSString* flutterAssetsName = [bundle objectForInfoDictionaryKey:@"FLTAssetsPath"]; + if (flutterAssetsName == nil) { + // Default to "flutter_assets" + flutterAssetsName = @"flutter_assets"; + } + return flutterAssetsName; +} + ++ (NSString*)pathForFlutterAssetsFromBundle:(NSBundle*)bundle { + NSString* flutterAssetsName = [FlutterDartProject flutterAssetsName:bundle]; + return [bundle pathForResource:flutterAssetsName ofType:nil]; +} + ++ (NSString*)lookupKeyForAsset:(NSString*)asset { + NSString* flutterAssetsName = [FlutterDartProject flutterAssetsName:[NSBundle mainBundle]]; + return [NSString stringWithFormat:@"%@/%@", flutterAssetsName, asset]; +} + ++ (NSString*)lookupKeyForAsset:(NSString*)asset fromPackage:(NSString*)package { + return [self lookupKeyForAsset:[NSString stringWithFormat:@"packages/%@/%@", package, asset]]; +} + @end diff --git a/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm b/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm index e7474167070c9..17e9a4eff48f6 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm @@ -109,7 +109,9 @@ - (void)performCommonViewControllerInitialization { - (BOOL)setupShell { FXL_DCHECK(_shell == nullptr); - auto threadLabel = [NSString stringWithFormat:@"io.flutter.%p", self]; + static size_t shell_count = 1; + + auto threadLabel = [NSString stringWithFormat:@"io.flutter.%zu", shell_count++]; _threadHost = { threadLabel.UTF8String, // label @@ -949,4 +951,13 @@ - (void)unregisterTexture:(int64_t)textureId { - (void)textureFrameAvailable:(int64_t)textureId { _shell->GetPlatformView()->MarkTextureFrameAvailable(textureId); } + +- (NSString*)lookupKeyForAsset:(NSString*)asset { + return [FlutterDartProject lookupKeyForAsset:asset]; +} + +- (NSString*)lookupKeyForAsset:(NSString*)asset fromPackage:(NSString*)package { + return [FlutterDartProject lookupKeyForAsset:asset fromPackage:package]; +} + @end diff --git a/shell/platform/darwin/ios/ios_external_texture_gl.h b/shell/platform/darwin/ios/ios_external_texture_gl.h index a658fdf31628a..baedf298bd61d 100644 --- a/shell/platform/darwin/ios/ios_external_texture_gl.h +++ b/shell/platform/darwin/ios/ios_external_texture_gl.h @@ -25,6 +25,8 @@ class IOSExternalTextureGL : public flow::Texture { virtual void OnGrContextDestroyed() override; + virtual void MarkNewFrameAvailable() override; + private: NSObject* external_texture_; fml::CFRef cache_ref_; diff --git a/shell/platform/darwin/ios/ios_external_texture_gl.mm b/shell/platform/darwin/ios/ios_external_texture_gl.mm index f507cdce0c815..0bd9a8ca3c7cb 100644 --- a/shell/platform/darwin/ios/ios_external_texture_gl.mm +++ b/shell/platform/darwin/ios/ios_external_texture_gl.mm @@ -55,10 +55,8 @@ return; } GrGLTextureInfo textureInfo = {CVOpenGLESTextureGetTarget(texture_ref_), - CVOpenGLESTextureGetName(texture_ref_), - GL_RGBA8_OES}; - GrBackendTexture backendTexture(bounds.width(), bounds.height(), GrMipMapped::kNo, - textureInfo); + CVOpenGLESTextureGetName(texture_ref_), GL_RGBA8_OES}; + GrBackendTexture backendTexture(bounds.width(), bounds.height(), GrMipMapped::kNo, textureInfo); sk_sp image = SkImage::MakeFromTexture(canvas.getGrContext(), backendTexture, kTopLeft_GrSurfaceOrigin, kRGBA_8888_SkColorType, kPremul_SkAlphaType, nullptr); @@ -74,4 +72,6 @@ GrBackendTexture backendTexture(bounds.width(), bounds.height(), GrMipMapped::kN cache_ref_.Reset(nullptr); } +void IOSExternalTextureGL::MarkNewFrameAvailable() {} + } // namespace shell diff --git a/shell/platform/embedder/embedder.cc b/shell/platform/embedder/embedder.cc index 1e416b0271fc1..859004f3d9e69 100644 --- a/shell/platform/embedder/embedder.cc +++ b/shell/platform/embedder/embedder.cc @@ -204,9 +204,7 @@ FlutterResult FlutterEngineRun(size_t version, } // Step 3: Run the engine. - shell::RunConfiguration run_configuration( - shell::IsolateConfiguration::CreateForSource( - settings.main_dart_file_path, settings.packages_file_path)); + auto run_configuration = shell::RunConfiguration::InferFromSettings(settings); run_configuration.AddAssetResolver( std::make_unique( diff --git a/shell/testing/BUILD.gn b/shell/testing/BUILD.gn index 6554599bf7a85..f11a3a2d07501 100644 --- a/shell/testing/BUILD.gn +++ b/shell/testing/BUILD.gn @@ -24,4 +24,8 @@ executable("testing") { "//third_party/skia", "//topaz/lib/tonic", ] + + if (is_linux) { + ldflags = [ "-rdynamic" ] + } } diff --git a/shell/testing/tester_main.cc b/shell/testing/tester_main.cc index b4d0bba705c9d..61d1c13ba122d 100644 --- a/shell/testing/tester_main.cc +++ b/shell/testing/tester_main.cc @@ -10,6 +10,7 @@ #include "flutter/assets/directory_asset_bundle.h" #include "flutter/fml/file.h" #include "flutter/fml/message_loop.h" +#include "flutter/fml/paths.h" #include "flutter/shell/common/platform_view.h" #include "flutter/shell/common/rasterizer.h" #include "flutter/shell/common/shell.h" @@ -218,6 +219,9 @@ int main(int argc, char* argv[]) { settings.icu_data_path = "icudtl.dat"; + settings.kernel_snapshot_path = + fml::paths::JoinPaths({settings.assets_path, "platform.dill"}); + // The tools that read logs get confused if there is a log tag specified. settings.log_tag = "";