mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
This makes sure the frame timings recorder and vsync waiter implementations get the correct refresh rate if or when it adjusts at runtime. This should be OK because we'll only query the refresh rate when the display metrics actually change, which is much less frequent than every frame. I experimented with an NDK implementation in the previous patch, but that vastly restricts the API levels we can support, and currently on API 30 and 31 it just calls Java methods through JNI anyway. I've refactored the way Display updates are reported so that AndroidDisplay can just dynamically get the refresh rate correctly. These values are used by a service protocol extension and by some Stopwatches on the CompositorContext. See also #29765 Fixes flutter/flutter#93688 Fixes flutter/flutter#93698
1884 lines
71 KiB
C++
1884 lines
71 KiB
C++
// Copyright 2013 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 RAPIDJSON_HAS_STDSTRING 1
|
|
#include "flutter/shell/common/shell.h"
|
|
|
|
#include <memory>
|
|
#include <sstream>
|
|
#include <vector>
|
|
|
|
#include "flutter/assets/directory_asset_bundle.h"
|
|
#include "flutter/common/graphics/persistent_cache.h"
|
|
#include "flutter/fml/base32.h"
|
|
#include "flutter/fml/file.h"
|
|
#include "flutter/fml/icu_util.h"
|
|
#include "flutter/fml/log_settings.h"
|
|
#include "flutter/fml/logging.h"
|
|
#include "flutter/fml/make_copyable.h"
|
|
#include "flutter/fml/message_loop.h"
|
|
#include "flutter/fml/paths.h"
|
|
#include "flutter/fml/trace_event.h"
|
|
#include "flutter/fml/unique_fd.h"
|
|
#include "flutter/runtime/dart_vm.h"
|
|
#include "flutter/shell/common/engine.h"
|
|
#include "flutter/shell/common/skia_event_tracer_impl.h"
|
|
#include "flutter/shell/common/switches.h"
|
|
#include "flutter/shell/common/vsync_waiter.h"
|
|
#include "rapidjson/stringbuffer.h"
|
|
#include "rapidjson/writer.h"
|
|
#include "third_party/dart/runtime/include/dart_tools_api.h"
|
|
#include "third_party/skia/include/core/SkGraphics.h"
|
|
#include "third_party/skia/include/utils/SkBase64.h"
|
|
#include "third_party/tonic/common/log.h"
|
|
|
|
namespace flutter {
|
|
|
|
constexpr char kSkiaChannel[] = "flutter/skia";
|
|
constexpr char kSystemChannel[] = "flutter/system";
|
|
constexpr char kTypeKey[] = "type";
|
|
constexpr char kFontChange[] = "fontsChange";
|
|
|
|
namespace {
|
|
|
|
std::unique_ptr<Engine> CreateEngine(
|
|
Engine::Delegate& delegate,
|
|
const PointerDataDispatcherMaker& dispatcher_maker,
|
|
DartVM& vm,
|
|
fml::RefPtr<const DartSnapshot> isolate_snapshot,
|
|
TaskRunners task_runners,
|
|
const PlatformData& platform_data,
|
|
Settings settings,
|
|
std::unique_ptr<Animator> animator,
|
|
fml::WeakPtr<IOManager> io_manager,
|
|
fml::RefPtr<SkiaUnrefQueue> unref_queue,
|
|
fml::WeakPtr<SnapshotDelegate> snapshot_delegate,
|
|
std::shared_ptr<VolatilePathTracker> volatile_path_tracker) {
|
|
return std::make_unique<Engine>(delegate, //
|
|
dispatcher_maker, //
|
|
vm, //
|
|
isolate_snapshot, //
|
|
task_runners, //
|
|
platform_data, //
|
|
settings, //
|
|
std::move(animator), //
|
|
io_manager, //
|
|
unref_queue, //
|
|
snapshot_delegate, //
|
|
volatile_path_tracker);
|
|
}
|
|
|
|
// Though there can be multiple shells, some settings apply to all components in
|
|
// the process. These have to be set up before the shell or any of its
|
|
// sub-components can be initialized. In a perfect world, this would be empty.
|
|
// TODO(chinmaygarde): The unfortunate side effect of this call is that settings
|
|
// that cause shell initialization failures will still lead to some of their
|
|
// settings being applied.
|
|
void PerformInitializationTasks(Settings& settings) {
|
|
{
|
|
fml::LogSettings log_settings;
|
|
log_settings.min_log_level =
|
|
settings.verbose_logging ? fml::LOG_INFO : fml::LOG_ERROR;
|
|
fml::SetLogSettings(log_settings);
|
|
}
|
|
|
|
static std::once_flag gShellSettingsInitialization = {};
|
|
std::call_once(gShellSettingsInitialization, [&settings] {
|
|
if (settings.engine_start_timestamp.count() == 0) {
|
|
settings.engine_start_timestamp =
|
|
std::chrono::microseconds(Dart_TimelineGetMicros());
|
|
}
|
|
|
|
tonic::SetLogHandler(
|
|
[](const char* message) { FML_LOG(ERROR) << message; });
|
|
|
|
if (settings.trace_skia) {
|
|
InitSkiaEventTracer(settings.trace_skia, settings.trace_skia_allowlist);
|
|
}
|
|
|
|
if (!settings.trace_allowlist.empty()) {
|
|
fml::tracing::TraceSetAllowlist(settings.trace_allowlist);
|
|
}
|
|
|
|
if (!settings.skia_deterministic_rendering_on_cpu) {
|
|
SkGraphics::Init();
|
|
} else {
|
|
FML_DLOG(INFO) << "Skia deterministic rendering is enabled.";
|
|
}
|
|
|
|
if (settings.icu_initialization_required) {
|
|
if (settings.icu_data_path.size() != 0) {
|
|
fml::icu::InitializeICU(settings.icu_data_path);
|
|
} else if (settings.icu_mapper) {
|
|
fml::icu::InitializeICUFromMapping(settings.icu_mapper());
|
|
} else {
|
|
FML_DLOG(WARNING) << "Skipping ICU initialization in the shell.";
|
|
}
|
|
}
|
|
});
|
|
|
|
PersistentCache::SetCacheSkSL(settings.cache_sksl);
|
|
}
|
|
|
|
} // namespace
|
|
|
|
std::unique_ptr<Shell> Shell::Create(
|
|
const PlatformData& platform_data,
|
|
TaskRunners task_runners,
|
|
Settings settings,
|
|
const Shell::CreateCallback<PlatformView>& on_create_platform_view,
|
|
const Shell::CreateCallback<Rasterizer>& on_create_rasterizer,
|
|
bool is_gpu_disabled) {
|
|
// This must come first as it initializes tracing.
|
|
PerformInitializationTasks(settings);
|
|
|
|
TRACE_EVENT0("flutter", "Shell::Create");
|
|
|
|
// Always use the `vm_snapshot` and `isolate_snapshot` provided by the
|
|
// settings to launch the VM. If the VM is already running, the snapshot
|
|
// arguments are ignored.
|
|
auto vm_snapshot = DartSnapshot::VMSnapshotFromSettings(settings);
|
|
auto isolate_snapshot = DartSnapshot::IsolateSnapshotFromSettings(settings);
|
|
auto vm = DartVMRef::Create(settings, vm_snapshot, isolate_snapshot);
|
|
FML_CHECK(vm) << "Must be able to initialize the VM.";
|
|
|
|
// If the settings did not specify an `isolate_snapshot`, fall back to the
|
|
// one the VM was launched with.
|
|
if (!isolate_snapshot) {
|
|
isolate_snapshot = vm->GetVMData()->GetIsolateSnapshot();
|
|
}
|
|
return CreateWithSnapshot(std::move(platform_data), //
|
|
std::move(task_runners), //
|
|
/*parent_merger=*/nullptr, //
|
|
std::move(settings), //
|
|
std::move(vm), //
|
|
std::move(isolate_snapshot), //
|
|
std::move(on_create_platform_view), //
|
|
std::move(on_create_rasterizer), //
|
|
CreateEngine, is_gpu_disabled);
|
|
}
|
|
|
|
std::unique_ptr<Shell> Shell::CreateShellOnPlatformThread(
|
|
DartVMRef vm,
|
|
fml::RefPtr<fml::RasterThreadMerger> parent_merger,
|
|
TaskRunners task_runners,
|
|
const PlatformData& platform_data,
|
|
Settings settings,
|
|
fml::RefPtr<const DartSnapshot> isolate_snapshot,
|
|
const Shell::CreateCallback<PlatformView>& on_create_platform_view,
|
|
const Shell::CreateCallback<Rasterizer>& on_create_rasterizer,
|
|
const Shell::EngineCreateCallback& on_create_engine,
|
|
bool is_gpu_disabled) {
|
|
if (!task_runners.IsValid()) {
|
|
FML_LOG(ERROR) << "Task runners to run the shell were invalid.";
|
|
return nullptr;
|
|
}
|
|
|
|
auto shell = std::unique_ptr<Shell>(
|
|
new Shell(std::move(vm), task_runners, parent_merger, settings,
|
|
std::make_shared<VolatilePathTracker>(
|
|
task_runners.GetUITaskRunner(),
|
|
!settings.skia_deterministic_rendering_on_cpu),
|
|
is_gpu_disabled));
|
|
|
|
// Create the rasterizer on the raster thread.
|
|
std::promise<std::unique_ptr<Rasterizer>> rasterizer_promise;
|
|
auto rasterizer_future = rasterizer_promise.get_future();
|
|
std::promise<fml::WeakPtr<SnapshotDelegate>> snapshot_delegate_promise;
|
|
auto snapshot_delegate_future = snapshot_delegate_promise.get_future();
|
|
fml::TaskRunner::RunNowOrPostTask(
|
|
task_runners.GetRasterTaskRunner(), [&rasterizer_promise, //
|
|
&snapshot_delegate_promise,
|
|
on_create_rasterizer, //
|
|
shell = shell.get() //
|
|
]() {
|
|
TRACE_EVENT0("flutter", "ShellSetupGPUSubsystem");
|
|
std::unique_ptr<Rasterizer> rasterizer(on_create_rasterizer(*shell));
|
|
snapshot_delegate_promise.set_value(rasterizer->GetSnapshotDelegate());
|
|
rasterizer_promise.set_value(std::move(rasterizer));
|
|
});
|
|
|
|
// 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;
|
|
}
|
|
|
|
// 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;
|
|
}
|
|
|
|
// 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.
|
|
std::promise<std::unique_ptr<ShellIOManager>> io_manager_promise;
|
|
auto io_manager_future = io_manager_promise.get_future();
|
|
std::promise<fml::WeakPtr<ShellIOManager>> weak_io_manager_promise;
|
|
auto weak_io_manager_future = weak_io_manager_promise.get_future();
|
|
std::promise<fml::RefPtr<SkiaUnrefQueue>> unref_queue_promise;
|
|
auto unref_queue_future = unref_queue_promise.get_future();
|
|
auto io_task_runner = shell->GetTaskRunners().GetIOTaskRunner();
|
|
|
|
// TODO(gw280): The WeakPtr here asserts that we are derefing it on the
|
|
// same thread as it was created on. We are currently on the IO thread
|
|
// inside this lambda but we need to deref the PlatformView, which was
|
|
// constructed on the platform thread.
|
|
//
|
|
// https://github.com/flutter/flutter/issues/42948
|
|
fml::TaskRunner::RunNowOrPostTask(
|
|
io_task_runner,
|
|
[&io_manager_promise, //
|
|
&weak_io_manager_promise, //
|
|
&unref_queue_promise, //
|
|
platform_view = platform_view->GetWeakPtr(), //
|
|
io_task_runner, //
|
|
is_backgrounded_sync_switch = shell->GetIsGpuDisabledSyncSwitch() //
|
|
]() {
|
|
TRACE_EVENT0("flutter", "ShellSetupIOSubsystem");
|
|
auto io_manager = std::make_unique<ShellIOManager>(
|
|
platform_view.getUnsafe()->CreateResourceContext(),
|
|
is_backgrounded_sync_switch, io_task_runner);
|
|
weak_io_manager_promise.set_value(io_manager->GetWeakPtr());
|
|
unref_queue_promise.set_value(io_manager->GetSkiaUnrefQueue());
|
|
io_manager_promise.set_value(std::move(io_manager));
|
|
});
|
|
|
|
// Send dispatcher_maker to the engine constructor because shell won't have
|
|
// platform_view set until Shell::Setup is called later.
|
|
auto dispatcher_maker = platform_view->GetDispatcherMaker();
|
|
|
|
// Create the engine on the UI thread.
|
|
std::promise<std::unique_ptr<Engine>> engine_promise;
|
|
auto engine_future = engine_promise.get_future();
|
|
fml::TaskRunner::RunNowOrPostTask(
|
|
shell->GetTaskRunners().GetUITaskRunner(),
|
|
fml::MakeCopyable([&engine_promise, //
|
|
shell = shell.get(), //
|
|
&dispatcher_maker, //
|
|
&platform_data, //
|
|
isolate_snapshot = std::move(isolate_snapshot), //
|
|
vsync_waiter = std::move(vsync_waiter), //
|
|
&weak_io_manager_future, //
|
|
&snapshot_delegate_future, //
|
|
&unref_queue_future, //
|
|
&on_create_engine]() mutable {
|
|
TRACE_EVENT0("flutter", "ShellSetupUISubsystem");
|
|
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<Animator>(*shell, task_runners,
|
|
std::move(vsync_waiter));
|
|
|
|
engine_promise.set_value(
|
|
on_create_engine(*shell, //
|
|
dispatcher_maker, //
|
|
*shell->GetDartVM(), //
|
|
std::move(isolate_snapshot), //
|
|
task_runners, //
|
|
platform_data, //
|
|
shell->GetSettings(), //
|
|
std::move(animator), //
|
|
weak_io_manager_future.get(), //
|
|
unref_queue_future.get(), //
|
|
snapshot_delegate_future.get(), //
|
|
shell->volatile_path_tracker_));
|
|
}));
|
|
|
|
if (!shell->Setup(std::move(platform_view), //
|
|
engine_future.get(), //
|
|
rasterizer_future.get(), //
|
|
io_manager_future.get()) //
|
|
) {
|
|
return nullptr;
|
|
}
|
|
|
|
return shell;
|
|
}
|
|
|
|
std::unique_ptr<Shell> Shell::CreateWithSnapshot(
|
|
const PlatformData& platform_data,
|
|
TaskRunners task_runners,
|
|
fml::RefPtr<fml::RasterThreadMerger> parent_thread_merger,
|
|
Settings settings,
|
|
DartVMRef vm,
|
|
fml::RefPtr<const DartSnapshot> isolate_snapshot,
|
|
const Shell::CreateCallback<PlatformView>& on_create_platform_view,
|
|
const Shell::CreateCallback<Rasterizer>& on_create_rasterizer,
|
|
const Shell::EngineCreateCallback& on_create_engine,
|
|
bool is_gpu_disabled) {
|
|
// This must come first as it initializes tracing.
|
|
PerformInitializationTasks(settings);
|
|
|
|
TRACE_EVENT0("flutter", "Shell::CreateWithSnapshot");
|
|
|
|
const bool callbacks_valid =
|
|
on_create_platform_view && on_create_rasterizer && on_create_engine;
|
|
if (!task_runners.IsValid() || !callbacks_valid) {
|
|
return nullptr;
|
|
}
|
|
|
|
fml::AutoResetWaitableEvent latch;
|
|
std::unique_ptr<Shell> shell;
|
|
fml::TaskRunner::RunNowOrPostTask(
|
|
task_runners.GetPlatformTaskRunner(),
|
|
fml::MakeCopyable(
|
|
[&latch, //
|
|
&shell, //
|
|
parent_thread_merger, //
|
|
task_runners = std::move(task_runners), //
|
|
platform_data = std::move(platform_data), //
|
|
settings = std::move(settings), //
|
|
vm = std::move(vm), //
|
|
isolate_snapshot = std::move(isolate_snapshot), //
|
|
on_create_platform_view = std::move(on_create_platform_view), //
|
|
on_create_rasterizer = std::move(on_create_rasterizer), //
|
|
on_create_engine = std::move(on_create_engine),
|
|
is_gpu_disabled]() mutable {
|
|
shell = CreateShellOnPlatformThread(
|
|
std::move(vm), //
|
|
parent_thread_merger, //
|
|
std::move(task_runners), //
|
|
std::move(platform_data), //
|
|
std::move(settings), //
|
|
std::move(isolate_snapshot), //
|
|
std::move(on_create_platform_view), //
|
|
std::move(on_create_rasterizer), //
|
|
std::move(on_create_engine), is_gpu_disabled);
|
|
latch.Signal();
|
|
}));
|
|
latch.Wait();
|
|
return shell;
|
|
}
|
|
|
|
Shell::Shell(DartVMRef vm,
|
|
TaskRunners task_runners,
|
|
fml::RefPtr<fml::RasterThreadMerger> parent_merger,
|
|
Settings settings,
|
|
std::shared_ptr<VolatilePathTracker> volatile_path_tracker,
|
|
bool is_gpu_disabled)
|
|
: task_runners_(std::move(task_runners)),
|
|
parent_raster_thread_merger_(parent_merger),
|
|
settings_(std::move(settings)),
|
|
vm_(std::move(vm)),
|
|
is_gpu_disabled_sync_switch_(new fml::SyncSwitch(is_gpu_disabled)),
|
|
volatile_path_tracker_(std::move(volatile_path_tracker)),
|
|
weak_factory_gpu_(nullptr),
|
|
weak_factory_(this) {
|
|
FML_CHECK(vm_) << "Must have access to VM to create a shell.";
|
|
FML_DCHECK(task_runners_.IsValid());
|
|
FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread());
|
|
|
|
display_manager_ = std::make_unique<DisplayManager>();
|
|
|
|
// Generate a WeakPtrFactory for use with the raster thread. This does not
|
|
// need to wait on a latch because it can only ever be used from the raster
|
|
// thread from this class, so we have ordering guarantees.
|
|
fml::TaskRunner::RunNowOrPostTask(
|
|
task_runners_.GetRasterTaskRunner(), fml::MakeCopyable([this]() mutable {
|
|
this->weak_factory_gpu_ =
|
|
std::make_unique<fml::TaskRunnerAffineWeakPtrFactory<Shell>>(this);
|
|
}));
|
|
|
|
// Install service protocol handlers.
|
|
|
|
service_protocol_handlers_[ServiceProtocol::kScreenshotExtensionName] = {
|
|
task_runners_.GetRasterTaskRunner(),
|
|
std::bind(&Shell::OnServiceProtocolScreenshot, this,
|
|
std::placeholders::_1, std::placeholders::_2)};
|
|
service_protocol_handlers_[ServiceProtocol::kScreenshotSkpExtensionName] = {
|
|
task_runners_.GetRasterTaskRunner(),
|
|
std::bind(&Shell::OnServiceProtocolScreenshotSKP, this,
|
|
std::placeholders::_1, std::placeholders::_2)};
|
|
service_protocol_handlers_[ServiceProtocol::kRunInViewExtensionName] = {
|
|
task_runners_.GetUITaskRunner(),
|
|
std::bind(&Shell::OnServiceProtocolRunInView, this, std::placeholders::_1,
|
|
std::placeholders::_2)};
|
|
service_protocol_handlers_
|
|
[ServiceProtocol::kFlushUIThreadTasksExtensionName] = {
|
|
task_runners_.GetUITaskRunner(),
|
|
std::bind(&Shell::OnServiceProtocolFlushUIThreadTasks, this,
|
|
std::placeholders::_1, std::placeholders::_2)};
|
|
service_protocol_handlers_
|
|
[ServiceProtocol::kSetAssetBundlePathExtensionName] = {
|
|
task_runners_.GetUITaskRunner(),
|
|
std::bind(&Shell::OnServiceProtocolSetAssetBundlePath, this,
|
|
std::placeholders::_1, std::placeholders::_2)};
|
|
service_protocol_handlers_
|
|
[ServiceProtocol::kGetDisplayRefreshRateExtensionName] = {
|
|
task_runners_.GetUITaskRunner(),
|
|
std::bind(&Shell::OnServiceProtocolGetDisplayRefreshRate, this,
|
|
std::placeholders::_1, std::placeholders::_2)};
|
|
service_protocol_handlers_[ServiceProtocol::kGetSkSLsExtensionName] = {
|
|
task_runners_.GetIOTaskRunner(),
|
|
std::bind(&Shell::OnServiceProtocolGetSkSLs, this, std::placeholders::_1,
|
|
std::placeholders::_2)};
|
|
service_protocol_handlers_
|
|
[ServiceProtocol::kEstimateRasterCacheMemoryExtensionName] = {
|
|
task_runners_.GetRasterTaskRunner(),
|
|
std::bind(&Shell::OnServiceProtocolEstimateRasterCacheMemory, this,
|
|
std::placeholders::_1, std::placeholders::_2)};
|
|
}
|
|
|
|
Shell::~Shell() {
|
|
PersistentCache::GetCacheForProcess()->RemoveWorkerTaskRunner(
|
|
task_runners_.GetIOTaskRunner());
|
|
|
|
vm_->GetServiceProtocol()->RemoveHandler(this);
|
|
|
|
fml::AutoResetWaitableEvent ui_latch, gpu_latch, platform_latch, io_latch;
|
|
|
|
fml::TaskRunner::RunNowOrPostTask(
|
|
task_runners_.GetUITaskRunner(),
|
|
fml::MakeCopyable([this, &ui_latch]() mutable {
|
|
engine_.reset();
|
|
ui_latch.Signal();
|
|
}));
|
|
ui_latch.Wait();
|
|
|
|
fml::TaskRunner::RunNowOrPostTask(
|
|
task_runners_.GetRasterTaskRunner(),
|
|
fml::MakeCopyable(
|
|
[this, rasterizer = std::move(rasterizer_), &gpu_latch]() mutable {
|
|
rasterizer.reset();
|
|
this->weak_factory_gpu_.reset();
|
|
gpu_latch.Signal();
|
|
}));
|
|
gpu_latch.Wait();
|
|
|
|
fml::TaskRunner::RunNowOrPostTask(
|
|
task_runners_.GetIOTaskRunner(),
|
|
fml::MakeCopyable([io_manager = std::move(io_manager_),
|
|
platform_view = platform_view_.get(),
|
|
&io_latch]() mutable {
|
|
io_manager.reset();
|
|
if (platform_view) {
|
|
platform_view->ReleaseResourceContext();
|
|
}
|
|
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(),
|
|
fml::MakeCopyable([platform_view = std::move(platform_view_),
|
|
&platform_latch]() mutable {
|
|
platform_view.reset();
|
|
platform_latch.Signal();
|
|
}));
|
|
platform_latch.Wait();
|
|
}
|
|
|
|
std::unique_ptr<Shell> Shell::Spawn(
|
|
RunConfiguration run_configuration,
|
|
const std::string& initial_route,
|
|
const CreateCallback<PlatformView>& on_create_platform_view,
|
|
const CreateCallback<Rasterizer>& on_create_rasterizer) const {
|
|
FML_DCHECK(task_runners_.IsValid());
|
|
// It's safe to store this value since it is set on the platform thread.
|
|
bool is_gpu_disabled = false;
|
|
GetIsGpuDisabledSyncSwitch()->Execute(
|
|
fml::SyncSwitch::Handlers()
|
|
.SetIfFalse([&is_gpu_disabled] { is_gpu_disabled = false; })
|
|
.SetIfTrue([&is_gpu_disabled] { is_gpu_disabled = true; }));
|
|
std::unique_ptr<Shell> result = (CreateWithSnapshot(
|
|
PlatformData{}, task_runners_, rasterizer_->GetRasterThreadMerger(),
|
|
GetSettings(), vm_, vm_->GetVMData()->GetIsolateSnapshot(),
|
|
on_create_platform_view, on_create_rasterizer,
|
|
[engine = this->engine_.get(), initial_route](
|
|
Engine::Delegate& delegate,
|
|
const PointerDataDispatcherMaker& dispatcher_maker, DartVM& vm,
|
|
fml::RefPtr<const DartSnapshot> isolate_snapshot,
|
|
TaskRunners task_runners, const PlatformData& platform_data,
|
|
Settings settings, std::unique_ptr<Animator> animator,
|
|
fml::WeakPtr<IOManager> io_manager,
|
|
fml::RefPtr<SkiaUnrefQueue> unref_queue,
|
|
fml::WeakPtr<SnapshotDelegate> snapshot_delegate,
|
|
std::shared_ptr<VolatilePathTracker> volatile_path_tracker) {
|
|
return engine->Spawn(/*delegate=*/delegate,
|
|
/*dispatcher_maker=*/dispatcher_maker,
|
|
/*settings=*/settings,
|
|
/*animator=*/std::move(animator),
|
|
/*initial_route=*/initial_route);
|
|
},
|
|
is_gpu_disabled));
|
|
result->shared_resource_context_ = io_manager_->GetSharedResourceContext();
|
|
result->RunEngine(std::move(run_configuration));
|
|
return result;
|
|
}
|
|
|
|
void Shell::NotifyLowMemoryWarning() const {
|
|
auto trace_id = fml::tracing::TraceNonce();
|
|
TRACE_EVENT_ASYNC_BEGIN0("flutter", "Shell::NotifyLowMemoryWarning",
|
|
trace_id);
|
|
// This does not require a current isolate but does require a running VM.
|
|
// Since a valid shell will not be returned to the embedder without a valid
|
|
// DartVMRef, we can be certain that this is a safe spot to assume a VM is
|
|
// running.
|
|
::Dart_NotifyLowMemory();
|
|
|
|
task_runners_.GetRasterTaskRunner()->PostTask(
|
|
[rasterizer = rasterizer_->GetWeakPtr(), trace_id = trace_id]() {
|
|
if (rasterizer) {
|
|
rasterizer->NotifyLowMemoryWarning();
|
|
}
|
|
TRACE_EVENT_ASYNC_END0("flutter", "Shell::NotifyLowMemoryWarning",
|
|
trace_id);
|
|
});
|
|
// The IO Manager uses resource cache limits of 0, so it is not necessary
|
|
// to purge them.
|
|
}
|
|
|
|
void Shell::RunEngine(RunConfiguration run_configuration) {
|
|
RunEngine(std::move(run_configuration), nullptr);
|
|
}
|
|
|
|
void Shell::RunEngine(
|
|
RunConfiguration run_configuration,
|
|
const std::function<void(Engine::RunStatus)>& result_callback) {
|
|
auto result = [platform_runner = task_runners_.GetPlatformTaskRunner(),
|
|
result_callback](Engine::RunStatus run_result) {
|
|
if (!result_callback) {
|
|
return;
|
|
}
|
|
platform_runner->PostTask(
|
|
[result_callback, run_result]() { result_callback(run_result); });
|
|
};
|
|
FML_DCHECK(is_setup_);
|
|
FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread());
|
|
|
|
fml::TaskRunner::RunNowOrPostTask(
|
|
task_runners_.GetUITaskRunner(),
|
|
fml::MakeCopyable(
|
|
[run_configuration = std::move(run_configuration),
|
|
weak_engine = weak_engine_, result]() mutable {
|
|
if (!weak_engine) {
|
|
FML_LOG(ERROR)
|
|
<< "Could not launch engine with configuration - no engine.";
|
|
result(Engine::RunStatus::Failure);
|
|
return;
|
|
}
|
|
auto run_result = weak_engine->Run(std::move(run_configuration));
|
|
if (run_result == flutter::Engine::RunStatus::Failure) {
|
|
FML_LOG(ERROR) << "Could not launch engine with configuration.";
|
|
}
|
|
result(run_result);
|
|
}));
|
|
}
|
|
|
|
std::optional<DartErrorCode> Shell::GetUIIsolateLastError() const {
|
|
FML_DCHECK(is_setup_);
|
|
FML_DCHECK(task_runners_.GetUITaskRunner()->RunsTasksOnCurrentThread());
|
|
|
|
if (!weak_engine_) {
|
|
return std::nullopt;
|
|
}
|
|
switch (weak_engine_->GetUIIsolateLastError()) {
|
|
case tonic::kCompilationErrorType:
|
|
return DartErrorCode::CompilationError;
|
|
case tonic::kApiErrorType:
|
|
return DartErrorCode::ApiError;
|
|
case tonic::kUnknownErrorType:
|
|
return DartErrorCode::UnknownError;
|
|
case tonic::kNoError:
|
|
return DartErrorCode::NoError;
|
|
}
|
|
return DartErrorCode::UnknownError;
|
|
}
|
|
|
|
bool Shell::EngineHasLivePorts() const {
|
|
FML_DCHECK(is_setup_);
|
|
FML_DCHECK(task_runners_.GetUITaskRunner()->RunsTasksOnCurrentThread());
|
|
|
|
if (!weak_engine_) {
|
|
return false;
|
|
}
|
|
|
|
return weak_engine_->UIIsolateHasLivePorts();
|
|
}
|
|
|
|
bool Shell::IsSetup() const {
|
|
return is_setup_;
|
|
}
|
|
|
|
bool Shell::Setup(std::unique_ptr<PlatformView> platform_view,
|
|
std::unique_ptr<Engine> engine,
|
|
std::unique_ptr<Rasterizer> rasterizer,
|
|
std::unique_ptr<ShellIOManager> io_manager) {
|
|
if (is_setup_) {
|
|
return false;
|
|
}
|
|
|
|
if (!platform_view || !engine || !rasterizer || !io_manager) {
|
|
return false;
|
|
}
|
|
|
|
platform_view_ = std::move(platform_view);
|
|
platform_message_handler_ = platform_view_->GetPlatformMessageHandler();
|
|
engine_ = std::move(engine);
|
|
rasterizer_ = std::move(rasterizer);
|
|
io_manager_ = std::move(io_manager);
|
|
|
|
// Set the external view embedder for the rasterizer.
|
|
auto view_embedder = platform_view_->CreateExternalViewEmbedder();
|
|
rasterizer_->SetExternalViewEmbedder(view_embedder);
|
|
rasterizer_->SetSnapshotSurfaceProducer(
|
|
platform_view_->CreateSnapshotSurfaceProducer());
|
|
|
|
// The weak ptr must be generated in the platform thread which owns the unique
|
|
// ptr.
|
|
weak_engine_ = engine_->GetWeakPtr();
|
|
weak_rasterizer_ = rasterizer_->GetWeakPtr();
|
|
weak_platform_view_ = platform_view_->GetWeakPtr();
|
|
|
|
// Setup the time-consuming default font manager right after engine created.
|
|
if (!settings_.prefetched_default_font_manager) {
|
|
fml::TaskRunner::RunNowOrPostTask(task_runners_.GetUITaskRunner(),
|
|
[engine = weak_engine_] {
|
|
if (engine) {
|
|
engine->SetupDefaultFontManager();
|
|
}
|
|
});
|
|
}
|
|
|
|
is_setup_ = true;
|
|
|
|
PersistentCache::GetCacheForProcess()->AddWorkerTaskRunner(
|
|
task_runners_.GetIOTaskRunner());
|
|
|
|
PersistentCache::GetCacheForProcess()->SetIsDumpingSkp(
|
|
settings_.dump_skp_on_shader_compilation);
|
|
|
|
if (settings_.purge_persistent_cache) {
|
|
PersistentCache::GetCacheForProcess()->Purge();
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
const Settings& Shell::GetSettings() const {
|
|
return settings_;
|
|
}
|
|
|
|
const TaskRunners& Shell::GetTaskRunners() const {
|
|
return task_runners_;
|
|
}
|
|
|
|
const fml::RefPtr<fml::RasterThreadMerger> Shell::GetParentRasterThreadMerger()
|
|
const {
|
|
return parent_raster_thread_merger_;
|
|
}
|
|
|
|
fml::TaskRunnerAffineWeakPtr<Rasterizer> Shell::GetRasterizer() const {
|
|
FML_DCHECK(is_setup_);
|
|
return weak_rasterizer_;
|
|
}
|
|
|
|
fml::WeakPtr<Engine> Shell::GetEngine() {
|
|
FML_DCHECK(is_setup_);
|
|
return weak_engine_;
|
|
}
|
|
|
|
fml::WeakPtr<PlatformView> Shell::GetPlatformView() {
|
|
FML_DCHECK(is_setup_);
|
|
return weak_platform_view_;
|
|
}
|
|
|
|
fml::WeakPtr<ShellIOManager> Shell::GetIOManager() {
|
|
FML_DCHECK(is_setup_);
|
|
return io_manager_->GetWeakPtr();
|
|
}
|
|
|
|
DartVM* Shell::GetDartVM() {
|
|
return &vm_;
|
|
}
|
|
|
|
// |PlatformView::Delegate|
|
|
void Shell::OnPlatformViewCreated(std::unique_ptr<Surface> surface) {
|
|
TRACE_EVENT0("flutter", "Shell::OnPlatformViewCreated");
|
|
FML_DCHECK(is_setup_);
|
|
FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread());
|
|
|
|
// Prevent any request to change the thread configuration for raster and
|
|
// platform queues while the platform view is being created.
|
|
//
|
|
// This prevents false positives such as this method starts assuming that the
|
|
// raster and platform queues have a given thread configuration, but then the
|
|
// configuration is changed by a task, and the assumption is no longer true.
|
|
//
|
|
// This incorrect assumption can lead to deadlock.
|
|
// See `should_post_raster_task` for more.
|
|
rasterizer_->DisableThreadMergerIfNeeded();
|
|
|
|
// The normal flow executed by this method is that the platform thread is
|
|
// starting the sequence and waiting on the latch. Later the UI thread posts
|
|
// raster_task to the raster thread which signals the latch. If the raster and
|
|
// the platform threads are the same this results in a deadlock as the
|
|
// raster_task will never be posted to the platform/raster thread that is
|
|
// blocked on a latch. To avoid the described deadlock, if the raster and the
|
|
// platform threads are the same, should_post_raster_task will be false, and
|
|
// then instead of posting a task to the raster thread, the ui thread just
|
|
// signals the latch and the platform/raster thread follows with executing
|
|
// raster_task.
|
|
const bool should_post_raster_task =
|
|
!task_runners_.GetRasterTaskRunner()->RunsTasksOnCurrentThread();
|
|
|
|
fml::AutoResetWaitableEvent latch;
|
|
auto raster_task =
|
|
fml::MakeCopyable([&waiting_for_first_frame = waiting_for_first_frame_,
|
|
rasterizer = rasterizer_->GetWeakPtr(), //
|
|
surface = std::move(surface)]() mutable {
|
|
if (rasterizer) {
|
|
// Enables the thread merger which may be used by the external view
|
|
// embedder.
|
|
rasterizer->EnableThreadMergerIfNeeded();
|
|
rasterizer->Setup(std::move(surface));
|
|
}
|
|
|
|
waiting_for_first_frame.store(true);
|
|
});
|
|
|
|
// TODO(91717): This probably isn't necessary. The engine should be able to
|
|
// handle things here via normal lifecycle messages.
|
|
// https://github.com/flutter/flutter/issues/91717
|
|
auto ui_task = [engine = engine_->GetWeakPtr()] {
|
|
if (engine) {
|
|
engine->OnOutputSurfaceCreated();
|
|
}
|
|
};
|
|
|
|
// Threading: Capture platform view by raw pointer and not the weak pointer.
|
|
// We are going to use the pointer on the IO thread which is not safe with a
|
|
// weak pointer. However, we are preventing the platform view from being
|
|
// collected by using a latch.
|
|
auto* platform_view = platform_view_.get();
|
|
|
|
FML_DCHECK(platform_view);
|
|
|
|
auto io_task = [io_manager = io_manager_->GetWeakPtr(), platform_view,
|
|
ui_task_runner = task_runners_.GetUITaskRunner(), ui_task,
|
|
shared_resource_context = shared_resource_context_,
|
|
raster_task_runner = task_runners_.GetRasterTaskRunner(),
|
|
raster_task, should_post_raster_task, &latch] {
|
|
if (io_manager && !io_manager->GetResourceContext()) {
|
|
sk_sp<GrDirectContext> resource_context;
|
|
if (shared_resource_context) {
|
|
resource_context = shared_resource_context;
|
|
} else {
|
|
resource_context = platform_view->CreateResourceContext();
|
|
}
|
|
io_manager->NotifyResourceContextAvailable(resource_context);
|
|
}
|
|
// Step 1: Post a task on the UI thread to tell the engine that it has
|
|
// an output surface.
|
|
fml::TaskRunner::RunNowOrPostTask(ui_task_runner, ui_task);
|
|
|
|
// Step 2: Tell the raster thread that it should create a surface for
|
|
// its rasterizer.
|
|
if (should_post_raster_task) {
|
|
fml::TaskRunner::RunNowOrPostTask(raster_task_runner, raster_task);
|
|
}
|
|
latch.Signal();
|
|
};
|
|
|
|
fml::TaskRunner::RunNowOrPostTask(task_runners_.GetIOTaskRunner(), io_task);
|
|
|
|
latch.Wait();
|
|
if (!should_post_raster_task) {
|
|
// See comment on should_post_raster_task, in this case the raster_task
|
|
// wasn't executed, and we just run it here as the platform thread
|
|
// is the raster thread.
|
|
raster_task();
|
|
}
|
|
}
|
|
|
|
// |PlatformView::Delegate|
|
|
void Shell::OnPlatformViewDestroyed() {
|
|
TRACE_EVENT0("flutter", "Shell::OnPlatformViewDestroyed");
|
|
FML_DCHECK(is_setup_);
|
|
FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread());
|
|
|
|
// Prevent any request to change the thread configuration for raster and
|
|
// platform queues while the platform view is being destroyed.
|
|
//
|
|
// This prevents false positives such as this method starts assuming that the
|
|
// raster and platform queues have a given thread configuration, but then the
|
|
// configuration is changed by a task, and the assumption is no longer true.
|
|
//
|
|
// This incorrect assumption can lead to deadlock.
|
|
rasterizer_->DisableThreadMergerIfNeeded();
|
|
|
|
// 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.
|
|
// The UI thread does not need to be serialized here - there is sufficient
|
|
// guardrailing in the rasterizer to allow the UI thread to post work to it
|
|
// even after the surface has been torn down.
|
|
|
|
fml::AutoResetWaitableEvent latch;
|
|
|
|
auto io_task = [io_manager = io_manager_.get(), &latch]() {
|
|
// Execute any pending Skia object deletions while GPU access is still
|
|
// allowed.
|
|
io_manager->GetIsGpuDisabledSyncSwitch()->Execute(
|
|
fml::SyncSwitch::Handlers().SetIfFalse(
|
|
[&] { io_manager->GetSkiaUnrefQueue()->Drain(); }));
|
|
// Step 4: All done. Signal the latch that the platform thread is waiting
|
|
// on.
|
|
latch.Signal();
|
|
};
|
|
|
|
auto raster_task = [rasterizer = rasterizer_->GetWeakPtr(),
|
|
io_task_runner = task_runners_.GetIOTaskRunner(),
|
|
io_task]() {
|
|
if (rasterizer) {
|
|
// Enables the thread merger which is required prior tearing down the
|
|
// rasterizer. If the raster and platform threads are merged, tearing down
|
|
// the rasterizer unmerges the threads.
|
|
rasterizer->EnableThreadMergerIfNeeded();
|
|
rasterizer->Teardown();
|
|
}
|
|
// Step 3: Tell the IO thread to complete its remaining work.
|
|
fml::TaskRunner::RunNowOrPostTask(io_task_runner, io_task);
|
|
};
|
|
|
|
// TODO(91717): This probably isn't necessary. The engine should be able to
|
|
// handle things here via normal lifecycle messages.
|
|
// https://github.com/flutter/flutter/issues/91717
|
|
auto ui_task = [engine = engine_->GetWeakPtr()]() {
|
|
if (engine) {
|
|
engine->OnOutputSurfaceDestroyed();
|
|
}
|
|
};
|
|
|
|
// Step 1: 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);
|
|
|
|
// Step 2: Post a task to the Raster thread (possibly this thread) to tell the
|
|
// rasterizer the output surface is going away.
|
|
fml::TaskRunner::RunNowOrPostTask(task_runners_.GetRasterTaskRunner(),
|
|
raster_task);
|
|
latch.Wait();
|
|
}
|
|
|
|
// |PlatformView::Delegate|
|
|
void Shell::OnPlatformViewSetViewportMetrics(const ViewportMetrics& metrics) {
|
|
FML_DCHECK(is_setup_);
|
|
FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread());
|
|
|
|
if (metrics.device_pixel_ratio <= 0 || metrics.physical_width <= 0 ||
|
|
metrics.physical_height <= 0) {
|
|
FML_DLOG(ERROR)
|
|
<< "Embedding reported invalid ViewportMetrics, ignoring update."
|
|
<< "\nphysical_width: " << metrics.physical_width
|
|
<< "\nphysical_height: " << metrics.physical_height
|
|
<< "\ndevice_pixel_ratio: " << metrics.device_pixel_ratio;
|
|
return;
|
|
}
|
|
|
|
// This is the formula Android uses.
|
|
// https://android.googlesource.com/platform/frameworks/base/+/master/libs/hwui/renderthread/CacheManager.cpp#41
|
|
size_t max_bytes = metrics.physical_width * metrics.physical_height * 12 * 4;
|
|
task_runners_.GetRasterTaskRunner()->PostTask(
|
|
[rasterizer = rasterizer_->GetWeakPtr(), max_bytes] {
|
|
if (rasterizer) {
|
|
rasterizer->SetResourceCacheMaxBytes(max_bytes, false);
|
|
}
|
|
});
|
|
|
|
task_runners_.GetUITaskRunner()->PostTask(
|
|
[engine = engine_->GetWeakPtr(), metrics]() {
|
|
if (engine) {
|
|
engine->SetViewportMetrics(metrics);
|
|
}
|
|
});
|
|
|
|
{
|
|
std::scoped_lock<std::mutex> lock(resize_mutex_);
|
|
expected_frame_size_ =
|
|
SkISize::Make(metrics.physical_width, metrics.physical_height);
|
|
}
|
|
}
|
|
|
|
// |PlatformView::Delegate|
|
|
void Shell::OnPlatformViewDispatchPlatformMessage(
|
|
std::unique_ptr<PlatformMessage> message) {
|
|
FML_DCHECK(is_setup_);
|
|
FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread());
|
|
|
|
task_runners_.GetUITaskRunner()->PostTask(fml::MakeCopyable(
|
|
[engine = engine_->GetWeakPtr(), message = std::move(message)]() mutable {
|
|
if (engine) {
|
|
engine->DispatchPlatformMessage(std::move(message));
|
|
}
|
|
}));
|
|
}
|
|
|
|
// |PlatformView::Delegate|
|
|
void Shell::OnPlatformViewDispatchPointerDataPacket(
|
|
std::unique_ptr<PointerDataPacket> packet) {
|
|
TRACE_EVENT0("flutter", "Shell::OnPlatformViewDispatchPointerDataPacket");
|
|
TRACE_FLOW_BEGIN("flutter", "PointerEvent", next_pointer_flow_id_);
|
|
FML_DCHECK(is_setup_);
|
|
FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread());
|
|
task_runners_.GetUITaskRunner()->PostTask(
|
|
fml::MakeCopyable([engine = weak_engine_, packet = std::move(packet),
|
|
flow_id = next_pointer_flow_id_]() mutable {
|
|
if (engine) {
|
|
engine->DispatchPointerDataPacket(std::move(packet), flow_id);
|
|
}
|
|
}));
|
|
next_pointer_flow_id_++;
|
|
}
|
|
|
|
// |PlatformView::Delegate|
|
|
void Shell::OnPlatformViewDispatchKeyDataPacket(
|
|
std::unique_ptr<KeyDataPacket> packet,
|
|
std::function<void(bool /* handled */)> callback) {
|
|
TRACE_EVENT0("flutter", "Shell::OnPlatformViewDispatchKeyDataPacket");
|
|
FML_DCHECK(is_setup_);
|
|
FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread());
|
|
|
|
task_runners_.GetUITaskRunner()->PostTask(
|
|
fml::MakeCopyable([engine = weak_engine_, packet = std::move(packet),
|
|
callback = std::move(callback)]() mutable {
|
|
if (engine) {
|
|
engine->DispatchKeyDataPacket(std::move(packet), std::move(callback));
|
|
}
|
|
}));
|
|
}
|
|
|
|
// |PlatformView::Delegate|
|
|
void Shell::OnPlatformViewDispatchSemanticsAction(int32_t id,
|
|
SemanticsAction action,
|
|
fml::MallocMapping args) {
|
|
FML_DCHECK(is_setup_);
|
|
FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread());
|
|
|
|
task_runners_.GetUITaskRunner()->PostTask(
|
|
fml::MakeCopyable([engine = engine_->GetWeakPtr(), id, action,
|
|
args = std::move(args)]() mutable {
|
|
if (engine) {
|
|
engine->DispatchSemanticsAction(id, action, std::move(args));
|
|
}
|
|
}));
|
|
}
|
|
|
|
// |PlatformView::Delegate|
|
|
void Shell::OnPlatformViewSetSemanticsEnabled(bool enabled) {
|
|
FML_DCHECK(is_setup_);
|
|
FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread());
|
|
|
|
task_runners_.GetUITaskRunner()->PostTask(
|
|
[engine = engine_->GetWeakPtr(), enabled] {
|
|
if (engine) {
|
|
engine->SetSemanticsEnabled(enabled);
|
|
}
|
|
});
|
|
}
|
|
|
|
// |PlatformView::Delegate|
|
|
void Shell::OnPlatformViewSetAccessibilityFeatures(int32_t flags) {
|
|
FML_DCHECK(is_setup_);
|
|
FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread());
|
|
|
|
task_runners_.GetUITaskRunner()->PostTask(
|
|
[engine = engine_->GetWeakPtr(), flags] {
|
|
if (engine) {
|
|
engine->SetAccessibilityFeatures(flags);
|
|
}
|
|
});
|
|
}
|
|
|
|
// |PlatformView::Delegate|
|
|
void Shell::OnPlatformViewRegisterTexture(
|
|
std::shared_ptr<flutter::Texture> texture) {
|
|
FML_DCHECK(is_setup_);
|
|
FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread());
|
|
|
|
task_runners_.GetRasterTaskRunner()->PostTask(
|
|
[rasterizer = rasterizer_->GetWeakPtr(), texture] {
|
|
if (rasterizer) {
|
|
if (auto* registry = rasterizer->GetTextureRegistry()) {
|
|
registry->RegisterTexture(texture);
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
// |PlatformView::Delegate|
|
|
void Shell::OnPlatformViewUnregisterTexture(int64_t texture_id) {
|
|
FML_DCHECK(is_setup_);
|
|
FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread());
|
|
|
|
task_runners_.GetRasterTaskRunner()->PostTask(
|
|
[rasterizer = rasterizer_->GetWeakPtr(), texture_id]() {
|
|
if (rasterizer) {
|
|
if (auto* registry = rasterizer->GetTextureRegistry()) {
|
|
registry->UnregisterTexture(texture_id);
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
// |PlatformView::Delegate|
|
|
void Shell::OnPlatformViewMarkTextureFrameAvailable(int64_t texture_id) {
|
|
FML_DCHECK(is_setup_);
|
|
FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread());
|
|
|
|
// Tell the rasterizer that one of its textures has a new frame available.
|
|
task_runners_.GetRasterTaskRunner()->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);
|
|
}
|
|
});
|
|
}
|
|
|
|
// |PlatformView::Delegate|
|
|
void Shell::OnPlatformViewSetNextFrameCallback(const fml::closure& closure) {
|
|
FML_DCHECK(is_setup_);
|
|
FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread());
|
|
|
|
task_runners_.GetRasterTaskRunner()->PostTask(
|
|
[rasterizer = rasterizer_->GetWeakPtr(), closure = closure]() {
|
|
if (rasterizer) {
|
|
rasterizer->SetNextFrameCallback(std::move(closure));
|
|
}
|
|
});
|
|
}
|
|
|
|
// |Animator::Delegate|
|
|
void Shell::OnAnimatorBeginFrame(fml::TimePoint frame_target_time,
|
|
uint64_t frame_number) {
|
|
FML_DCHECK(is_setup_);
|
|
FML_DCHECK(task_runners_.GetUITaskRunner()->RunsTasksOnCurrentThread());
|
|
|
|
// record the target time for use by rasterizer.
|
|
{
|
|
std::scoped_lock time_recorder_lock(time_recorder_mutex_);
|
|
latest_frame_target_time_.emplace(frame_target_time);
|
|
}
|
|
if (engine_) {
|
|
engine_->BeginFrame(frame_target_time, frame_number);
|
|
}
|
|
}
|
|
|
|
// |Animator::Delegate|
|
|
void Shell::OnAnimatorNotifyIdle(int64_t deadline) {
|
|
FML_DCHECK(is_setup_);
|
|
FML_DCHECK(task_runners_.GetUITaskRunner()->RunsTasksOnCurrentThread());
|
|
|
|
if (engine_) {
|
|
engine_->NotifyIdle(deadline);
|
|
volatile_path_tracker_->OnFrame();
|
|
}
|
|
}
|
|
|
|
// |Animator::Delegate|
|
|
void Shell::OnAnimatorDraw(
|
|
std::shared_ptr<Pipeline<flutter::LayerTree>> pipeline,
|
|
std::unique_ptr<FrameTimingsRecorder> frame_timings_recorder) {
|
|
FML_DCHECK(is_setup_);
|
|
|
|
// record the target time for use by rasterizer.
|
|
{
|
|
std::scoped_lock time_recorder_lock(time_recorder_mutex_);
|
|
const fml::TimePoint frame_target_time =
|
|
frame_timings_recorder->GetVsyncTargetTime();
|
|
if (!latest_frame_target_time_) {
|
|
latest_frame_target_time_ = frame_target_time;
|
|
} else if (latest_frame_target_time_ < frame_target_time) {
|
|
latest_frame_target_time_ = frame_target_time;
|
|
}
|
|
}
|
|
|
|
auto discard_callback = [this](flutter::LayerTree& tree) {
|
|
std::scoped_lock<std::mutex> lock(resize_mutex_);
|
|
return !expected_frame_size_.isEmpty() &&
|
|
tree.frame_size() != expected_frame_size_;
|
|
};
|
|
|
|
task_runners_.GetRasterTaskRunner()->PostTask(fml::MakeCopyable(
|
|
[&waiting_for_first_frame = waiting_for_first_frame_,
|
|
&waiting_for_first_frame_condition = waiting_for_first_frame_condition_,
|
|
rasterizer = rasterizer_->GetWeakPtr(),
|
|
weak_pipeline = std::weak_ptr<Pipeline<LayerTree>>(pipeline),
|
|
discard_callback = std::move(discard_callback),
|
|
frame_timings_recorder = std::move(frame_timings_recorder)]() mutable {
|
|
if (rasterizer) {
|
|
std::shared_ptr<Pipeline<LayerTree>> pipeline = weak_pipeline.lock();
|
|
if (pipeline) {
|
|
rasterizer->Draw(std::move(frame_timings_recorder),
|
|
std::move(pipeline), std::move(discard_callback));
|
|
}
|
|
|
|
if (waiting_for_first_frame.load()) {
|
|
waiting_for_first_frame.store(false);
|
|
waiting_for_first_frame_condition.notify_all();
|
|
}
|
|
}
|
|
}));
|
|
}
|
|
|
|
// |Animator::Delegate|
|
|
void Shell::OnAnimatorDrawLastLayerTree(
|
|
std::unique_ptr<FrameTimingsRecorder> frame_timings_recorder) {
|
|
FML_DCHECK(is_setup_);
|
|
|
|
auto task = fml::MakeCopyable(
|
|
[rasterizer = rasterizer_->GetWeakPtr(),
|
|
frame_timings_recorder = std::move(frame_timings_recorder)]() mutable {
|
|
if (rasterizer) {
|
|
rasterizer->DrawLastLayerTree(std::move(frame_timings_recorder));
|
|
}
|
|
});
|
|
|
|
task_runners_.GetRasterTaskRunner()->PostTask(task);
|
|
}
|
|
|
|
// |Engine::Delegate|
|
|
void Shell::OnEngineUpdateSemantics(SemanticsNodeUpdates update,
|
|
CustomAccessibilityActionUpdates actions) {
|
|
FML_DCHECK(is_setup_);
|
|
FML_DCHECK(task_runners_.GetUITaskRunner()->RunsTasksOnCurrentThread());
|
|
|
|
task_runners_.GetPlatformTaskRunner()->PostTask(
|
|
[view = platform_view_->GetWeakPtr(), update = std::move(update),
|
|
actions = std::move(actions)] {
|
|
if (view) {
|
|
view->UpdateSemantics(std::move(update), std::move(actions));
|
|
}
|
|
});
|
|
}
|
|
|
|
// |Engine::Delegate|
|
|
void Shell::OnEngineHandlePlatformMessage(
|
|
std::unique_ptr<PlatformMessage> message) {
|
|
FML_DCHECK(is_setup_);
|
|
FML_DCHECK(task_runners_.GetUITaskRunner()->RunsTasksOnCurrentThread());
|
|
|
|
if (message->channel() == kSkiaChannel) {
|
|
HandleEngineSkiaMessage(std::move(message));
|
|
return;
|
|
}
|
|
|
|
if (platform_message_handler_) {
|
|
platform_message_handler_->HandlePlatformMessage(std::move(message));
|
|
} else {
|
|
task_runners_.GetPlatformTaskRunner()->PostTask(
|
|
fml::MakeCopyable([view = platform_view_->GetWeakPtr(),
|
|
message = std::move(message)]() mutable {
|
|
if (view) {
|
|
view->HandlePlatformMessage(std::move(message));
|
|
}
|
|
}));
|
|
}
|
|
}
|
|
|
|
void Shell::HandleEngineSkiaMessage(std::unique_ptr<PlatformMessage> message) {
|
|
const auto& data = message->data();
|
|
|
|
rapidjson::Document document;
|
|
document.Parse(reinterpret_cast<const char*>(data.GetMapping()),
|
|
data.GetSize());
|
|
if (document.HasParseError() || !document.IsObject()) {
|
|
return;
|
|
}
|
|
auto root = document.GetObject();
|
|
auto method = root.FindMember("method");
|
|
if (method->value != "Skia.setResourceCacheMaxBytes") {
|
|
return;
|
|
}
|
|
auto args = root.FindMember("args");
|
|
if (args == root.MemberEnd() || !args->value.IsInt()) {
|
|
return;
|
|
}
|
|
|
|
task_runners_.GetRasterTaskRunner()->PostTask(
|
|
[rasterizer = rasterizer_->GetWeakPtr(), max_bytes = args->value.GetInt(),
|
|
response = std::move(message->response())] {
|
|
if (rasterizer) {
|
|
rasterizer->SetResourceCacheMaxBytes(static_cast<size_t>(max_bytes),
|
|
true);
|
|
}
|
|
if (response) {
|
|
// The framework side expects this to be valid json encoded as a list.
|
|
// Return `[true]` to signal success.
|
|
std::vector<uint8_t> data = {'[', 't', 'r', 'u', 'e', ']'};
|
|
response->Complete(
|
|
std::make_unique<fml::DataMapping>(std::move(data)));
|
|
}
|
|
});
|
|
}
|
|
|
|
// |Engine::Delegate|
|
|
void Shell::OnPreEngineRestart() {
|
|
FML_DCHECK(is_setup_);
|
|
FML_DCHECK(task_runners_.GetUITaskRunner()->RunsTasksOnCurrentThread());
|
|
|
|
fml::AutoResetWaitableEvent latch;
|
|
fml::TaskRunner::RunNowOrPostTask(
|
|
task_runners_.GetPlatformTaskRunner(),
|
|
[view = platform_view_->GetWeakPtr(), &latch]() {
|
|
if (view) {
|
|
view->OnPreEngineRestart();
|
|
}
|
|
latch.Signal();
|
|
});
|
|
// This is blocking as any embedded platform views has to be flushed before
|
|
// we re-run the Dart code.
|
|
latch.Wait();
|
|
}
|
|
|
|
// |Engine::Delegate|
|
|
void Shell::OnRootIsolateCreated() {
|
|
if (is_added_to_service_protocol_) {
|
|
return;
|
|
}
|
|
auto description = GetServiceProtocolDescription();
|
|
fml::TaskRunner::RunNowOrPostTask(
|
|
task_runners_.GetPlatformTaskRunner(),
|
|
[self = weak_factory_.GetWeakPtr(),
|
|
description = std::move(description)]() {
|
|
if (self) {
|
|
self->vm_->GetServiceProtocol()->AddHandler(self.get(), description);
|
|
}
|
|
});
|
|
is_added_to_service_protocol_ = true;
|
|
}
|
|
|
|
// |Engine::Delegate|
|
|
void Shell::UpdateIsolateDescription(const std::string isolate_name,
|
|
int64_t isolate_port) {
|
|
Handler::Description description(isolate_port, isolate_name);
|
|
vm_->GetServiceProtocol()->SetHandlerDescription(this, description);
|
|
}
|
|
|
|
void Shell::SetNeedsReportTimings(bool value) {
|
|
needs_report_timings_ = value;
|
|
}
|
|
|
|
// |Engine::Delegate|
|
|
std::unique_ptr<std::vector<std::string>> Shell::ComputePlatformResolvedLocale(
|
|
const std::vector<std::string>& supported_locale_data) {
|
|
return platform_view_->ComputePlatformResolvedLocales(supported_locale_data);
|
|
}
|
|
|
|
void Shell::LoadDartDeferredLibrary(
|
|
intptr_t loading_unit_id,
|
|
std::unique_ptr<const fml::Mapping> snapshot_data,
|
|
std::unique_ptr<const fml::Mapping> snapshot_instructions) {
|
|
task_runners_.GetUITaskRunner()->PostTask(fml::MakeCopyable(
|
|
[engine = engine_->GetWeakPtr(), loading_unit_id,
|
|
data = std::move(snapshot_data),
|
|
instructions = std::move(snapshot_instructions)]() mutable {
|
|
if (engine) {
|
|
engine->LoadDartDeferredLibrary(loading_unit_id, std::move(data),
|
|
std::move(instructions));
|
|
}
|
|
}));
|
|
}
|
|
|
|
void Shell::LoadDartDeferredLibraryError(intptr_t loading_unit_id,
|
|
const std::string error_message,
|
|
bool transient) {
|
|
engine_->LoadDartDeferredLibraryError(loading_unit_id, error_message,
|
|
transient);
|
|
}
|
|
|
|
void Shell::UpdateAssetResolverByType(
|
|
std::unique_ptr<AssetResolver> updated_asset_resolver,
|
|
AssetResolver::AssetResolverType type) {
|
|
engine_->GetAssetManager()->UpdateResolverByType(
|
|
std::move(updated_asset_resolver), type);
|
|
}
|
|
|
|
// |Engine::Delegate|
|
|
void Shell::RequestDartDeferredLibrary(intptr_t loading_unit_id) {
|
|
task_runners_.GetPlatformTaskRunner()->PostTask(
|
|
[view = platform_view_->GetWeakPtr(), loading_unit_id] {
|
|
if (view) {
|
|
view->RequestDartDeferredLibrary(loading_unit_id);
|
|
}
|
|
});
|
|
}
|
|
|
|
void Shell::ReportTimings() {
|
|
FML_DCHECK(is_setup_);
|
|
FML_DCHECK(task_runners_.GetRasterTaskRunner()->RunsTasksOnCurrentThread());
|
|
|
|
auto timings = std::move(unreported_timings_);
|
|
unreported_timings_ = {};
|
|
task_runners_.GetUITaskRunner()->PostTask([timings, engine = weak_engine_] {
|
|
if (engine) {
|
|
engine->ReportTimings(std::move(timings));
|
|
}
|
|
});
|
|
}
|
|
|
|
size_t Shell::UnreportedFramesCount() const {
|
|
// Check that this is running on the raster thread to avoid race conditions.
|
|
FML_DCHECK(task_runners_.GetRasterTaskRunner()->RunsTasksOnCurrentThread());
|
|
FML_DCHECK(unreported_timings_.size() % (FrameTiming::kStatisticsCount) == 0);
|
|
return unreported_timings_.size() / (FrameTiming::kStatisticsCount);
|
|
}
|
|
|
|
void Shell::OnFrameRasterized(const FrameTiming& timing) {
|
|
FML_DCHECK(is_setup_);
|
|
FML_DCHECK(task_runners_.GetRasterTaskRunner()->RunsTasksOnCurrentThread());
|
|
|
|
// The C++ callback defined in settings.h and set by Flutter runner. This is
|
|
// independent of the timings report to the Dart side.
|
|
if (settings_.frame_rasterized_callback) {
|
|
settings_.frame_rasterized_callback(timing);
|
|
}
|
|
|
|
if (!needs_report_timings_) {
|
|
return;
|
|
}
|
|
|
|
size_t old_count = unreported_timings_.size();
|
|
(void)old_count;
|
|
for (auto phase : FrameTiming::kPhases) {
|
|
unreported_timings_.push_back(
|
|
timing.Get(phase).ToEpochDelta().ToMicroseconds());
|
|
}
|
|
unreported_timings_.push_back(timing.GetLayerCacheCount());
|
|
unreported_timings_.push_back(timing.GetLayerCacheBytes());
|
|
unreported_timings_.push_back(timing.GetPictureCacheCount());
|
|
unreported_timings_.push_back(timing.GetPictureCacheBytes());
|
|
unreported_timings_.push_back(timing.GetFrameNumber());
|
|
FML_DCHECK(unreported_timings_.size() ==
|
|
old_count + FrameTiming::kStatisticsCount);
|
|
|
|
// In tests using iPhone 6S with profile mode, sending a batch of 1 frame or a
|
|
// batch of 100 frames have roughly the same cost of less than 0.1ms. Sending
|
|
// a batch of 500 frames costs about 0.2ms. The 1 second threshold usually
|
|
// kicks in before we reaching the following 100 frames threshold. The 100
|
|
// threshold here is mainly for unit tests (so we don't have to write a
|
|
// 1-second unit test), and make sure that our vector won't grow too big with
|
|
// future 120fps, 240fps, or 1000fps displays.
|
|
//
|
|
// In the profile/debug mode, the timings are used by development tools which
|
|
// require a latency of no more than 100ms. Hence we lower that 1-second
|
|
// threshold to 100ms because performance overhead isn't that critical in
|
|
// those cases.
|
|
if (!first_frame_rasterized_ || UnreportedFramesCount() >= 100) {
|
|
first_frame_rasterized_ = true;
|
|
ReportTimings();
|
|
} else if (!frame_timings_report_scheduled_) {
|
|
#if FLUTTER_RELEASE
|
|
constexpr int kBatchTimeInMilliseconds = 1000;
|
|
#else
|
|
constexpr int kBatchTimeInMilliseconds = 100;
|
|
#endif
|
|
|
|
// Also make sure that frame times get reported with a max latency of 1
|
|
// second. Otherwise, the timings of last few frames of an animation may
|
|
// never be reported until the next animation starts.
|
|
frame_timings_report_scheduled_ = true;
|
|
task_runners_.GetRasterTaskRunner()->PostDelayedTask(
|
|
[self = weak_factory_gpu_->GetWeakPtr()]() {
|
|
if (!self) {
|
|
return;
|
|
}
|
|
self->frame_timings_report_scheduled_ = false;
|
|
if (self->UnreportedFramesCount() > 0) {
|
|
self->ReportTimings();
|
|
}
|
|
},
|
|
fml::TimeDelta::FromMilliseconds(kBatchTimeInMilliseconds));
|
|
}
|
|
}
|
|
|
|
fml::Milliseconds Shell::GetFrameBudget() {
|
|
double display_refresh_rate = display_manager_->GetMainDisplayRefreshRate();
|
|
if (display_refresh_rate > 0) {
|
|
return fml::RefreshRateToFrameBudget(display_refresh_rate);
|
|
} else {
|
|
return fml::kDefaultFrameBudget;
|
|
}
|
|
}
|
|
|
|
fml::TimePoint Shell::GetLatestFrameTargetTime() const {
|
|
std::scoped_lock time_recorder_lock(time_recorder_mutex_);
|
|
FML_CHECK(latest_frame_target_time_.has_value())
|
|
<< "GetLatestFrameTargetTime called before OnAnimatorBeginFrame";
|
|
return latest_frame_target_time_.value();
|
|
}
|
|
|
|
// |ServiceProtocol::Handler|
|
|
fml::RefPtr<fml::TaskRunner> Shell::GetServiceProtocolHandlerTaskRunner(
|
|
std::string_view method) const {
|
|
FML_DCHECK(is_setup_);
|
|
auto found = service_protocol_handlers_.find(method);
|
|
if (found != service_protocol_handlers_.end()) {
|
|
return found->second.first;
|
|
}
|
|
return task_runners_.GetUITaskRunner();
|
|
}
|
|
|
|
// |ServiceProtocol::Handler|
|
|
bool Shell::HandleServiceProtocolMessage(
|
|
std::string_view method, // one if the extension names specified above.
|
|
const ServiceProtocolMap& params,
|
|
rapidjson::Document* response) {
|
|
auto found = service_protocol_handlers_.find(method);
|
|
if (found != service_protocol_handlers_.end()) {
|
|
return found->second.second(params, response);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// |ServiceProtocol::Handler|
|
|
ServiceProtocol::Handler::Description Shell::GetServiceProtocolDescription()
|
|
const {
|
|
FML_DCHECK(task_runners_.GetUITaskRunner()->RunsTasksOnCurrentThread());
|
|
|
|
if (!weak_engine_) {
|
|
return ServiceProtocol::Handler::Description();
|
|
}
|
|
|
|
return {
|
|
weak_engine_->GetUIIsolateMainPort(),
|
|
weak_engine_->GetUIIsolateName(),
|
|
};
|
|
}
|
|
|
|
static void ServiceProtocolParameterError(rapidjson::Document* response,
|
|
std::string error_details) {
|
|
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", error_details, allocator);
|
|
response->AddMember("data", details, allocator);
|
|
}
|
|
}
|
|
|
|
static void ServiceProtocolFailureError(rapidjson::Document* response,
|
|
std::string message) {
|
|
auto& allocator = response->GetAllocator();
|
|
response->SetObject();
|
|
const int64_t kJsonServerError = -32000;
|
|
response->AddMember("code", kJsonServerError, allocator);
|
|
response->AddMember("message", message, allocator);
|
|
}
|
|
|
|
// Service protocol handler
|
|
bool Shell::OnServiceProtocolScreenshot(
|
|
const ServiceProtocol::Handler::ServiceProtocolMap& params,
|
|
rapidjson::Document* response) {
|
|
FML_DCHECK(task_runners_.GetRasterTaskRunner()->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<const char*>(screenshot.data->data()),
|
|
screenshot.data->size(), allocator);
|
|
response->AddMember("screenshot", image, allocator);
|
|
return true;
|
|
}
|
|
ServiceProtocolFailureError(response, "Could not capture image screenshot.");
|
|
return false;
|
|
}
|
|
|
|
// Service protocol handler
|
|
bool Shell::OnServiceProtocolScreenshotSKP(
|
|
const ServiceProtocol::Handler::ServiceProtocolMap& params,
|
|
rapidjson::Document* response) {
|
|
FML_DCHECK(task_runners_.GetRasterTaskRunner()->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<const char*>(screenshot.data->data()),
|
|
screenshot.data->size(), allocator);
|
|
response->AddMember("skp", skp, allocator);
|
|
return true;
|
|
}
|
|
ServiceProtocolFailureError(response, "Could not capture SKP screenshot.");
|
|
return false;
|
|
}
|
|
|
|
// Service protocol handler
|
|
bool Shell::OnServiceProtocolRunInView(
|
|
const ServiceProtocol::Handler::ServiceProtocolMap& params,
|
|
rapidjson::Document* response) {
|
|
FML_DCHECK(task_runners_.GetUITaskRunner()->RunsTasksOnCurrentThread());
|
|
|
|
if (params.count("mainScript") == 0) {
|
|
ServiceProtocolParameterError(response,
|
|
"'mainScript' parameter is missing.");
|
|
return false;
|
|
}
|
|
|
|
if (params.count("assetDirectory") == 0) {
|
|
ServiceProtocolParameterError(response,
|
|
"'assetDirectory' parameter is missing.");
|
|
return false;
|
|
}
|
|
|
|
std::string main_script_path =
|
|
fml::paths::FromURI(params.at("mainScript").data());
|
|
std::string asset_directory_path =
|
|
fml::paths::FromURI(params.at("assetDirectory").data());
|
|
|
|
auto main_script_file_mapping =
|
|
std::make_unique<fml::FileMapping>(fml::OpenFile(
|
|
main_script_path.c_str(), false, fml::FilePermission::kRead));
|
|
|
|
auto isolate_configuration = IsolateConfiguration::CreateForKernel(
|
|
std::move(main_script_file_mapping));
|
|
|
|
RunConfiguration configuration(std::move(isolate_configuration));
|
|
|
|
configuration.SetEntrypointAndLibrary(engine_->GetLastEntrypoint(),
|
|
engine_->GetLastEntrypointLibrary());
|
|
|
|
configuration.AddAssetResolver(std::make_unique<DirectoryAssetBundle>(
|
|
fml::OpenDirectory(asset_directory_path.c_str(), false,
|
|
fml::FilePermission::kRead),
|
|
false));
|
|
|
|
// Preserve any original asset resolvers to avoid syncing unchanged assets
|
|
// over the DevFS connection.
|
|
auto old_asset_manager = engine_->GetAssetManager();
|
|
if (old_asset_manager != nullptr) {
|
|
for (auto& old_resolver : old_asset_manager->TakeResolvers()) {
|
|
if (old_resolver->IsValidAfterAssetManagerChange()) {
|
|
configuration.AddAssetResolver(std::move(old_resolver));
|
|
}
|
|
}
|
|
}
|
|
|
|
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 {
|
|
FML_DLOG(ERROR) << "Could not run configuration in engine.";
|
|
ServiceProtocolFailureError(response,
|
|
"Could not run configuration in engine.");
|
|
return false;
|
|
}
|
|
|
|
FML_DCHECK(false);
|
|
return false;
|
|
}
|
|
|
|
// Service protocol handler
|
|
bool Shell::OnServiceProtocolFlushUIThreadTasks(
|
|
const ServiceProtocol::Handler::ServiceProtocolMap& params,
|
|
rapidjson::Document* response) {
|
|
FML_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;
|
|
}
|
|
|
|
bool Shell::OnServiceProtocolGetDisplayRefreshRate(
|
|
const ServiceProtocol::Handler::ServiceProtocolMap& params,
|
|
rapidjson::Document* response) {
|
|
FML_DCHECK(task_runners_.GetUITaskRunner()->RunsTasksOnCurrentThread());
|
|
response->SetObject();
|
|
response->AddMember("type", "DisplayRefreshRate", response->GetAllocator());
|
|
response->AddMember("fps", display_manager_->GetMainDisplayRefreshRate(),
|
|
response->GetAllocator());
|
|
return true;
|
|
}
|
|
|
|
double Shell::GetMainDisplayRefreshRate() {
|
|
return display_manager_->GetMainDisplayRefreshRate();
|
|
}
|
|
|
|
void Shell::RegisterImageDecoder(ImageGeneratorFactory factory,
|
|
int32_t priority) {
|
|
FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread());
|
|
FML_DCHECK(is_setup_);
|
|
|
|
fml::TaskRunner::RunNowOrPostTask(
|
|
task_runners_.GetUITaskRunner(),
|
|
[engine = engine_->GetWeakPtr(), factory = std::move(factory),
|
|
priority]() {
|
|
if (engine) {
|
|
engine->GetImageGeneratorRegistry()->AddFactory(factory, priority);
|
|
}
|
|
});
|
|
}
|
|
|
|
bool Shell::OnServiceProtocolGetSkSLs(
|
|
const ServiceProtocol::Handler::ServiceProtocolMap& params,
|
|
rapidjson::Document* response) {
|
|
FML_DCHECK(task_runners_.GetIOTaskRunner()->RunsTasksOnCurrentThread());
|
|
response->SetObject();
|
|
response->AddMember("type", "GetSkSLs", response->GetAllocator());
|
|
|
|
rapidjson::Value shaders_json(rapidjson::kObjectType);
|
|
PersistentCache* persistent_cache = PersistentCache::GetCacheForProcess();
|
|
std::vector<PersistentCache::SkSLCache> sksls = persistent_cache->LoadSkSLs();
|
|
for (const auto& sksl : sksls) {
|
|
size_t b64_size =
|
|
SkBase64::Encode(sksl.value->data(), sksl.value->size(), nullptr);
|
|
sk_sp<SkData> b64_data = SkData::MakeUninitialized(b64_size + 1);
|
|
char* b64_char = static_cast<char*>(b64_data->writable_data());
|
|
SkBase64::Encode(sksl.value->data(), sksl.value->size(), b64_char);
|
|
b64_char[b64_size] = 0; // make it null terminated for printing
|
|
rapidjson::Value shader_value(b64_char, response->GetAllocator());
|
|
std::string_view key_view(reinterpret_cast<const char*>(sksl.key->data()),
|
|
sksl.key->size());
|
|
auto encode_result = fml::Base32Encode(key_view);
|
|
if (!encode_result.first) {
|
|
continue;
|
|
}
|
|
rapidjson::Value shader_key(encode_result.second, response->GetAllocator());
|
|
shaders_json.AddMember(shader_key, shader_value, response->GetAllocator());
|
|
}
|
|
response->AddMember("SkSLs", shaders_json, response->GetAllocator());
|
|
return true;
|
|
}
|
|
|
|
bool Shell::OnServiceProtocolEstimateRasterCacheMemory(
|
|
const ServiceProtocol::Handler::ServiceProtocolMap& params,
|
|
rapidjson::Document* response) {
|
|
FML_DCHECK(task_runners_.GetRasterTaskRunner()->RunsTasksOnCurrentThread());
|
|
const auto& raster_cache = rasterizer_->compositor_context()->raster_cache();
|
|
response->SetObject();
|
|
response->AddMember("type", "EstimateRasterCacheMemory",
|
|
response->GetAllocator());
|
|
response->AddMember<uint64_t>("layerBytes",
|
|
raster_cache.EstimateLayerCacheByteSize(),
|
|
response->GetAllocator());
|
|
response->AddMember<uint64_t>("pictureBytes",
|
|
raster_cache.EstimatePictureCacheByteSize(),
|
|
response->GetAllocator());
|
|
return true;
|
|
}
|
|
|
|
// Service protocol handler
|
|
bool Shell::OnServiceProtocolSetAssetBundlePath(
|
|
const ServiceProtocol::Handler::ServiceProtocolMap& params,
|
|
rapidjson::Document* response) {
|
|
FML_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 = std::make_shared<AssetManager>();
|
|
|
|
asset_manager->PushFront(std::make_unique<DirectoryAssetBundle>(
|
|
fml::OpenDirectory(params.at("assetDirectory").data(), false,
|
|
fml::FilePermission::kRead),
|
|
false));
|
|
|
|
// Preserve any original asset resolvers to avoid syncing unchanged assets
|
|
// over the DevFS connection.
|
|
auto old_asset_manager = engine_->GetAssetManager();
|
|
if (old_asset_manager != nullptr) {
|
|
for (auto& old_resolver : old_asset_manager->TakeResolvers()) {
|
|
if (old_resolver->IsValidAfterAssetManagerChange()) {
|
|
asset_manager->PushBack(std::move(old_resolver));
|
|
}
|
|
}
|
|
}
|
|
|
|
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 {
|
|
FML_DLOG(ERROR) << "Could not update asset directory.";
|
|
ServiceProtocolFailureError(response, "Could not update asset directory.");
|
|
return false;
|
|
}
|
|
|
|
FML_DCHECK(false);
|
|
return false;
|
|
}
|
|
|
|
Rasterizer::Screenshot Shell::Screenshot(
|
|
Rasterizer::ScreenshotType screenshot_type,
|
|
bool base64_encode) {
|
|
TRACE_EVENT0("flutter", "Shell::Screenshot");
|
|
fml::AutoResetWaitableEvent latch;
|
|
Rasterizer::Screenshot screenshot;
|
|
fml::TaskRunner::RunNowOrPostTask(
|
|
task_runners_.GetRasterTaskRunner(), [&latch, //
|
|
rasterizer = GetRasterizer(), //
|
|
&screenshot, //
|
|
screenshot_type, //
|
|
base64_encode //
|
|
]() {
|
|
if (rasterizer) {
|
|
screenshot = rasterizer->ScreenshotLastLayerTree(screenshot_type,
|
|
base64_encode);
|
|
}
|
|
latch.Signal();
|
|
});
|
|
latch.Wait();
|
|
return screenshot;
|
|
}
|
|
|
|
fml::Status Shell::WaitForFirstFrame(fml::TimeDelta timeout) {
|
|
FML_DCHECK(is_setup_);
|
|
if (task_runners_.GetUITaskRunner()->RunsTasksOnCurrentThread() ||
|
|
task_runners_.GetRasterTaskRunner()->RunsTasksOnCurrentThread()) {
|
|
return fml::Status(fml::StatusCode::kFailedPrecondition,
|
|
"WaitForFirstFrame called from thread that can't wait "
|
|
"because it is responsible for generating the frame.");
|
|
}
|
|
|
|
// Check for overflow.
|
|
auto now = std::chrono::steady_clock::now();
|
|
auto max_duration = std::chrono::steady_clock::time_point::max() - now;
|
|
auto desired_duration = std::chrono::milliseconds(timeout.ToMilliseconds());
|
|
auto duration =
|
|
now + (desired_duration > max_duration ? max_duration : desired_duration);
|
|
|
|
std::unique_lock<std::mutex> lock(waiting_for_first_frame_mutex_);
|
|
bool success = waiting_for_first_frame_condition_.wait_until(
|
|
lock, duration, [&waiting_for_first_frame = waiting_for_first_frame_] {
|
|
return !waiting_for_first_frame.load();
|
|
});
|
|
if (success) {
|
|
return fml::Status();
|
|
} else {
|
|
return fml::Status(fml::StatusCode::kDeadlineExceeded, "timeout");
|
|
}
|
|
}
|
|
|
|
bool Shell::ReloadSystemFonts() {
|
|
FML_DCHECK(is_setup_);
|
|
FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread());
|
|
|
|
if (!engine_) {
|
|
return false;
|
|
}
|
|
engine_->SetupDefaultFontManager();
|
|
engine_->GetFontCollection().GetFontCollection()->ClearFontFamilyCache();
|
|
// After system fonts are reloaded, we send a system channel message
|
|
// to notify flutter framework.
|
|
rapidjson::Document document;
|
|
document.SetObject();
|
|
auto& allocator = document.GetAllocator();
|
|
rapidjson::Value message_value;
|
|
message_value.SetString(kFontChange, allocator);
|
|
document.AddMember(kTypeKey, message_value, allocator);
|
|
|
|
rapidjson::StringBuffer buffer;
|
|
rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
|
|
document.Accept(writer);
|
|
std::string message = buffer.GetString();
|
|
std::unique_ptr<PlatformMessage> fontsChangeMessage =
|
|
std::make_unique<flutter::PlatformMessage>(
|
|
kSystemChannel,
|
|
fml::MallocMapping::Copy(message.c_str(), message.length()), nullptr);
|
|
|
|
OnPlatformViewDispatchPlatformMessage(std::move(fontsChangeMessage));
|
|
return true;
|
|
}
|
|
|
|
std::shared_ptr<const fml::SyncSwitch> Shell::GetIsGpuDisabledSyncSwitch()
|
|
const {
|
|
return is_gpu_disabled_sync_switch_;
|
|
}
|
|
|
|
void Shell::SetGpuAvailability(GpuAvailability availability) {
|
|
FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread());
|
|
switch (availability) {
|
|
case GpuAvailability::kAvailable:
|
|
is_gpu_disabled_sync_switch_->SetSwitch(false);
|
|
return;
|
|
case GpuAvailability::kFlushAndMakeUnavailable: {
|
|
fml::AutoResetWaitableEvent latch;
|
|
fml::TaskRunner::RunNowOrPostTask(
|
|
task_runners_.GetIOTaskRunner(),
|
|
[io_manager = io_manager_.get(), &latch]() {
|
|
io_manager->GetSkiaUnrefQueue()->Drain();
|
|
latch.Signal();
|
|
});
|
|
latch.Wait();
|
|
}
|
|
// FALLTHROUGH
|
|
case GpuAvailability::kUnavailable:
|
|
is_gpu_disabled_sync_switch_->SetSwitch(true);
|
|
return;
|
|
default:
|
|
FML_DCHECK(false);
|
|
}
|
|
}
|
|
|
|
void Shell::OnDisplayUpdates(DisplayUpdateType update_type,
|
|
std::vector<std::unique_ptr<Display>> displays) {
|
|
display_manager_->HandleDisplayUpdates(update_type, std::move(displays));
|
|
}
|
|
|
|
fml::TimePoint Shell::GetCurrentTimePoint() {
|
|
return fml::TimePoint::Now();
|
|
}
|
|
|
|
const std::shared_ptr<PlatformMessageHandler>&
|
|
Shell::GetPlatformMessageHandler() const {
|
|
return platform_message_handler_;
|
|
}
|
|
|
|
} // namespace flutter
|