#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/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/lib/ui/text/font_collection.h"
#include "flutter/lib/ui/ui_dart_state.h"
#include "flutter/lib/ui/window/window.h"
#include "flutter/runtime/start_up.h"
#include "flutter/shell/common/engine.h"
#include "flutter/shell/common/persistent_cache.h"
#include "flutter/shell/common/skia_event_tracer_impl.h"
#include "flutter/shell/common/switches.h"
#include "flutter/shell/common/vsync_waiter.h"
#include "include/core/SkGraphics.h"
namespace {
* Preload font collection for accelerate text layout.
*
* txt::FontCollection uses font_collections_cache_ to cache minikin::FontCollection.
* ParagraphTxt::Layout will calling `getFamilyForChar` for every character. While first encountering
* characters like Chinese, a fallback will occured. So manually fallback.
*/
void FontCollectionPreload(flutter::Window* window) {
if (window && window->client()) {
window->client()->GetFontCollection().GetFontCollection();
}
}
}
namespace flutter {
constexpr char kSkiaChannel[] = "flutter/skia";
std::unique_ptr<Shell> Shell::CreateShellOnPlatformThread(
TaskRunners task_runners,
Settings settings,
Shell::CreateCallback<PlatformView> on_create_platform_view,
Shell::CreateCallback<Rasterizer> on_create_rasterizer) {
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(task_runners, settings));
auto platform_view = on_create_platform_view(*shell.get());
if (!platform_view || !platform_view->GetWeakPtr()) {
return nullptr;
}
auto vsync_waiter = platform_view->CreateVSyncWaiter(static_cast<int32_t>(settings.platform));
if (!vsync_waiter) {
return nullptr;
}
fml::AutoResetWaitableEvent io_latch;
std::unique_ptr<ShellIOManager> io_manager;
auto io_task_runner = shell->GetTaskRunners().GetIOTaskRunner();
fml::TaskRunner::RunNowOrPostTask(
io_task_runner,
[&io_latch,
&io_manager,
&platform_view,
io_task_runner
]() {
TRACE_EVENT0("flutter", "ShellSetupIOSubsystem");
io_manager = std::make_unique<ShellIOManager>(
platform_view->CreateResourceContext(), io_task_runner);
io_latch.Signal();
});
io_latch.Wait();
fml::AutoResetWaitableEvent gpu_latch;
std::unique_ptr<Rasterizer> rasterizer;
fml::TaskRunner::RunNowOrPostTask(
task_runners.GetGPUTaskRunner(), [&gpu_latch,
&rasterizer,
on_create_rasterizer,
shell = shell.get()
]() {
TRACE_EVENT0("flutter", "ShellSetupGPUSubsystem");
if (auto new_rasterizer = on_create_rasterizer(*shell)) {
rasterizer = std::move(new_rasterizer);
}
gpu_latch.Signal();
});
gpu_latch.Wait();
fml::AutoResetWaitableEvent ui_latch;
std::unique_ptr<Engine> engine;
Window* window = nullptr;
fml::TaskRunner::RunNowOrPostTask(
shell->GetTaskRunners().GetUITaskRunner(),
fml::MakeCopyable([&ui_latch,
&engine,
shell = shell.get(),
vsync_waiter = std::move(vsync_waiter),
io_manager = io_manager->GetWeakPtr(),
&window
]() mutable {
TRACE_EVENT0("flutter", "ShellSetupUISubsystem");
const auto& task_runners = shell->GetTaskRunners();
auto animator = std::make_unique<Animator>(*shell, task_runners,
std::move(vsync_waiter));
engine = std::make_unique<Engine>(*shell,
task_runners,
shell->GetSettings(),
std::move(animator),
std::move(io_manager)
);
window = flutter::UIDartState::Current()->window();
ui_latch.Signal();
}));
ui_latch.Wait();
fml::TaskRunner::RunNowOrPostTask(
io_task_runner,
[window]() {
TRACE_EVENT0("flutter", "FontCollectionPreload");
FontCollectionPreload(window);
});
if (!shell->Setup(std::move(platform_view),
std::move(engine),
std::move(rasterizer),
std::move(io_manager))
) {
return nullptr;
}
return shell;
}
static void RecordStartupTimestamp() {
}
static void PerformInitializationTasks(const 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] {
RecordStartupTimestamp();
if (!settings.skia_deterministic_rendering_on_cpu) {
SkGraphics::Init();
} else {
FML_DLOG(INFO) << "Skia deterministic rendering is enabled.";
}
#ifdef ACE_MISSING
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.";
}
}
#endif
});
}
std::unique_ptr<Shell> Shell::Create(
TaskRunners task_runners,
Settings settings,
Shell::CreateCallback<PlatformView> on_create_platform_view,
Shell::CreateCallback<Rasterizer> on_create_rasterizer) {
PerformInitializationTasks(settings);
TRACE_EVENT0("flutter", "Shell::CreateWithSnapshots");
if (!task_runners.IsValid() || !on_create_platform_view ||
!on_create_rasterizer) {
return nullptr;
}
fml::AutoResetWaitableEvent latch;
std::unique_ptr<Shell> shell;
fml::TaskRunner::RunNowOrPostTask(
task_runners.GetPlatformTaskRunner(),
fml::MakeCopyable([&latch,
&shell,
task_runners = std::move(task_runners),
settings,
on_create_platform_view,
on_create_rasterizer
]() mutable {
shell = CreateShellOnPlatformThread(std::move(task_runners),
settings,
on_create_platform_view,
on_create_rasterizer
);
latch.Signal();
}));
latch.Wait();
return shell;
}
Shell::Shell(TaskRunners task_runners, Settings settings)
: task_runners_(std::move(task_runners)),
settings_(std::move(settings)),
weak_factory_(this) {
FML_DCHECK(task_runners_.IsValid());
FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread());
}
Shell::~Shell() {
fml::AutoResetWaitableEvent ui_latch, gpu_latch, platform_latch, io_latch;
fml::AutoResetWaitableEvent wait_io_latch;
fml::TaskRunner::RunNowOrPostTask(
task_runners_.GetIOTaskRunner(), [&wait_io_latch]() {
wait_io_latch.Signal();
});
wait_io_latch.Wait();
fml::TaskRunner::RunNowOrPostTask(
task_runners_.GetUITaskRunner(),
fml::MakeCopyable([engine = std::move(engine_), &ui_latch]() mutable {
engine.reset();
ui_latch.Signal();
}));
ui_latch.Wait();
fml::TaskRunner::RunNowOrPostTask(
task_runners_.GetGPUTaskRunner(),
fml::MakeCopyable(
[rasterizer = std::move(rasterizer_), &gpu_latch]() mutable {
rasterizer.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();
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();
}
void Shell::NotifyLowMemoryWarning() const {
task_runners_.GetGPUTaskRunner()->PostTask(
[rasterizer = rasterizer_->GetWeakPtr()]() {
if (rasterizer) {
rasterizer->NotifyLowMemoryWarning();
}
});
}
void Shell::RunEngine(RunConfiguration run_configuration) {
RunEngine(std::move(run_configuration), nullptr);
}
void Shell::RunEngine(RunConfiguration run_configuration,
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());
if (!weak_engine_) {
result(Engine::RunStatus::Failure);
}
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);
}));
}
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);
engine_ = std::move(engine);
rasterizer_ = std::move(rasterizer);
io_manager_ = std::move(io_manager);
weak_engine_ = engine_->GetWeakPtr();
weak_rasterizer_ = rasterizer_->GetWeakPtr();
weak_platform_view_ = platform_view_->GetWeakPtr();
is_setup_ = true;
return true;
}
const Settings& Shell::GetSettings() const {
return settings_;
}
const TaskRunners& Shell::GetTaskRunners() const {
return task_runners_;
}
fml::WeakPtr<Rasterizer> Shell::GetRasterizer() {
FML_DCHECK(is_setup_);
return weak_rasterizer_;
}
#ifdef OS_FUCHSIA
fml::WeakPtr<Engine> Shell::GetEngine() {
FML_DCHECK(is_setup_);
return weak_engine_;
}
#endif
fml::WeakPtr<PlatformView> Shell::GetPlatformView() {
FML_DCHECK(is_setup_);
return weak_platform_view_;
}
void Shell::OnPlatformViewCreated(std::unique_ptr<Surface> surface) {
TRACE_EVENT0("flutter", "Shell::OnPlatformViewCreated");
FML_DCHECK(is_setup_);
FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread());
fml::AutoResetWaitableEvent latch;
auto gpu_task =
fml::MakeCopyable([& waiting_for_first_frame = waiting_for_first_frame_,
rasterizer = rasterizer_->GetWeakPtr(),
surface = std::move(surface),
&latch]() mutable {
if (rasterizer) {
rasterizer->Setup(std::move(surface));
}
waiting_for_first_frame.store(true);
latch.Signal();
});
bool should_post_gpu_task =
task_runners_.GetGPUTaskRunner() != task_runners_.GetPlatformTaskRunner();
auto ui_task = [engine = engine_->GetWeakPtr()] {
if (engine) {
engine->OnOutputSurfaceCreated();
}
};
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,
should_post_gpu_task, gpu_task_runner = task_runners_.GetGPUTaskRunner(),
gpu_task, &latch] {
if (io_manager && !io_manager->GetResourceContext()) {
io_manager->NotifyResourceContextAvailable(
platform_view->CreateResourceContext());
}
fml::TaskRunner::RunNowOrPostTask(ui_task_runner, ui_task);
if (should_post_gpu_task) {
fml::TaskRunner::RunNowOrPostTask(gpu_task_runner, gpu_task);
} else {
latch.Signal();
}
};
fml::TaskRunner::RunNowOrPostTask(task_runners_.GetIOTaskRunner(), io_task);
latch.Wait();
if (!should_post_gpu_task) {
gpu_task();
}
}
void Shell::OnPlatformViewDestroyed() {
TRACE_EVENT0("flutter", "Shell::OnPlatformViewDestroyed");
FML_DCHECK(is_setup_);
FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread());
fml::AutoResetWaitableEvent latch;
auto io_task = [io_manager = io_manager_.get(), &latch]() {
io_manager->GetSkiaUnrefQueue()->Drain();
latch.Signal();
};
auto gpu_task = [rasterizer = rasterizer_->GetWeakPtr(),
io_task_runner = task_runners_.GetIOTaskRunner(),
io_task]() {
if (rasterizer) {
rasterizer->Teardown();
}
fml::TaskRunner::RunNowOrPostTask(io_task_runner, io_task);
};
bool should_post_gpu_task =
task_runners_.GetGPUTaskRunner() != task_runners_.GetPlatformTaskRunner();
auto ui_task = [engine = engine_->GetWeakPtr(),
gpu_task_runner = task_runners_.GetGPUTaskRunner(), gpu_task,
should_post_gpu_task, &latch]() {
if (engine) {
engine->OnOutputSurfaceDestroyed();
}
if (should_post_gpu_task) {
fml::TaskRunner::RunNowOrPostTask(gpu_task_runner, gpu_task);
} else {
latch.Signal();
}
};
fml::TaskRunner::RunNowOrPostTask(task_runners_.GetUITaskRunner(), ui_task);
latch.Wait();
if (!should_post_gpu_task) {
gpu_task();
latch.Wait();
}
}
void Shell::OnSetIdleNotificationCallback(const Engine::IdleCallback& idleCallback) {
task_runners_.GetUITaskRunner()->PostTask(
[engine = engine_->GetWeakPtr(), idleCallback]() {
if (engine) {
engine->SetIdleNotificationCallback(idleCallback);
}
});
}
void Shell::OnPlatformViewSetViewportMetrics(const ViewportMetrics& metrics) {
FML_DCHECK(is_setup_);
FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread());
size_t max_bytes = metrics.physical_width * metrics.physical_height * 12 * 4;
task_runners_.GetGPUTaskRunner()->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);
}
});
}
void Shell::OnPlatformViewDispatchPlatformMessage(
fml::RefPtr<PlatformMessage> message) {
FML_DCHECK(is_setup_);
FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread());
task_runners_.GetUITaskRunner()->PostTask(
[engine = engine_->GetWeakPtr(), message = std::move(message)] {
if (engine) {
engine->DispatchPlatformMessage(std::move(message));
}
});
}
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 = engine_->GetWeakPtr(), packet = std::move(packet),
flow_id = next_pointer_flow_id_] {
if (engine) {
engine->DispatchPointerDataPacket(*packet, flow_id);
}
}));
next_pointer_flow_id_++;
}
void Shell::OnPlatformViewRegisterTexture(
std::shared_ptr<flutter::Texture> texture) {
FML_DCHECK(is_setup_);
FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread());
task_runners_.GetGPUTaskRunner()->PostTask(
[rasterizer = rasterizer_->GetWeakPtr(), texture] {
if (rasterizer) {
if (auto* registry = rasterizer->GetTextureRegistry()) {
registry->RegisterTexture(texture);
}
}
});
}
void Shell::OnPlatformViewUnregisterTexture(int64_t texture_id) {
FML_DCHECK(is_setup_);
FML_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);
}
}
});
}
void Shell::OnPlatformViewMarkTextureFrameAvailable(int64_t texture_id) {
FML_DCHECK(is_setup_);
FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread());
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();
});
task_runners_.GetUITaskRunner()->PostTask([engine = engine_->GetWeakPtr()]() {
if (engine) {
engine->ScheduleFrame(false);
}
});
}
void Shell::OnPlatformViewSetNextFrameCallback(fml::closure closure) {
FML_DCHECK(is_setup_);
FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread());
task_runners_.GetGPUTaskRunner()->PostTask(
[rasterizer = rasterizer_->GetWeakPtr(), closure = std::move(closure)]() {
if (rasterizer) {
rasterizer->SetNextFrameCallback(std::move(closure));
}
});
}
void Shell::OnAnimatorBeginFrame(fml::TimePoint frame_time) {
FML_DCHECK(is_setup_);
FML_DCHECK(task_runners_.GetUITaskRunner()->RunsTasksOnCurrentThread());
if (engine_) {
engine_->BeginFrame(frame_time);
}
}
void Shell::OnAnimatorNotifyIdle(int64_t deadline) {
FML_DCHECK(is_setup_);
FML_DCHECK(task_runners_.GetUITaskRunner()->RunsTasksOnCurrentThread());
if (engine_) {
engine_->NotifyIdle(deadline);
}
}
void Shell::OnAnimatorDraw(fml::RefPtr<Pipeline<flutter::LayerTree>> pipeline) {
FML_DCHECK(is_setup_);
task_runners_.GetGPUTaskRunner()->PostTask(
[& waiting_for_first_frame = waiting_for_first_frame_,
&waiting_for_first_frame_condition = waiting_for_first_frame_condition_,
rasterizer = rasterizer_->GetWeakPtr(),
pipeline = std::move(pipeline)]() {
if (rasterizer) {
rasterizer->Draw(pipeline);
if (waiting_for_first_frame.load()) {
waiting_for_first_frame.store(false);
waiting_for_first_frame_condition.notify_all();
}
}
});
}
void Shell::OnAnimatorDrawLastLayerTree() {
FML_DCHECK(is_setup_);
task_runners_.GetGPUTaskRunner()->PostTask(
[rasterizer = rasterizer_->GetWeakPtr()]() {
if (rasterizer) {
rasterizer->DrawLastLayerTree();
}
});
}
void Shell::OnEngineHandlePlatformMessage(
fml::RefPtr<PlatformMessage> message) {
FML_DCHECK(is_setup_);
FML_DCHECK(task_runners_.GetUITaskRunner()->RunsTasksOnCurrentThread());
if (message->channel() == kSkiaChannel) {
HandleEngineSkiaMessage(std::move(message));
return;
}
task_runners_.GetPlatformTaskRunner()->PostTask(
[view = platform_view_->GetWeakPtr(), message = std::move(message)]() {
if (view) {
view->HandlePlatformMessage(std::move(message));
}
});
}
void Shell::HandleEngineSkiaMessage(fml::RefPtr<PlatformMessage> message) {
}
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();
});
latch.Wait();
}
void Shell::SetNeedsReportTimings(bool value) {
needs_report_timings_ = value;
}
void Shell::ReportTimings() {
FML_DCHECK(is_setup_);
FML_DCHECK(task_runners_.GetGPUTaskRunner()->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 {
FML_DCHECK(task_runners_.GetGPUTaskRunner()->RunsTasksOnCurrentThread());
FML_DCHECK(unreported_timings_.size() % FrameTiming::kCount == 0);
return unreported_timings_.size() / FrameTiming::kCount;
}
void Shell::OnFrameRasterized(const FrameTiming& timing) {
FML_DCHECK(is_setup_);
FML_DCHECK(task_runners_.GetGPUTaskRunner()->RunsTasksOnCurrentThread());
if (settings_.frame_rasterized_callback) {
settings_.frame_rasterized_callback(timing);
}
if (!needs_report_timings_) {
return;
}
for (auto phase : FrameTiming::kPhases) {
unreported_timings_.push_back(
timing.Get(phase).ToEpochDelta().ToMicroseconds());
}
if (!first_frame_rasterized_ || UnreportedFramesCount() >= 100) {
first_frame_rasterized_ = true;
ReportTimings();
} else if (!frame_timings_report_scheduled_) {
#if FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_RELEASE
constexpr int kBatchTimeInMilliseconds = 1000;
#else
constexpr int kBatchTimeInMilliseconds = 100;
#endif
frame_timings_report_scheduled_ = true;
task_runners_.GetGPUTaskRunner()->PostDelayedTask(
[self = weak_factory_.GetWeakPtr()]() {
if (!self.get()) {
return;
}
self->frame_timings_report_scheduled_ = false;
if (self->UnreportedFramesCount() > 0) {
self->ReportTimings();
}
},
fml::TimeDelta::FromMilliseconds(kBatchTimeInMilliseconds));
}
}
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_.GetGPUTaskRunner(), [&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_.GetGPUTaskRunner()->RunsTasksOnCurrentThread()) {
return fml::Status(fml::StatusCode::kFailedPrecondition,
"WaitForFirstFrame called from thread that can't wait "
"because it is responsible for generating the frame.");
}
std::unique_lock<std::mutex> lock(waiting_for_first_frame_mutex_);
bool success = waiting_for_first_frame_condition_.wait_for(
lock, std::chrono::milliseconds(timeout.ToMilliseconds()),
[& 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");
}
}
}