blob: 0d1c27d9a1d94fe93bc5d0d5ffcb8109076c9177 [file] [log] [blame]
// 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/content_handler/runtime_holder.h"
#include <utility>
#include "flutter/assets/zip_asset_bundle.h"
#include "flutter/common/threads.h"
#include "flutter/content_handler/rasterizer.h"
#include "flutter/lib/ui/mojo_services.h"
#include "flutter/runtime/asset_font_selector.h"
#include "flutter/runtime/dart_controller.h"
#include "flutter/services/engine/sky_engine.mojom.h"
#include "lib/ftl/functional/make_copyable.h"
#include "lib/ftl/functional/make_runnable.h"
#include "lib/ftl/logging.h"
#include "lib/ftl/time/time_delta.h"
#include "lib/zip/create_unzipper.h"
#include "mojo/public/cpp/application/connect.h"
namespace flutter_content_handler {
namespace {
constexpr char kSnapshotKey[] = "snapshot_blob.bin";
constexpr int kPipelineDepth = 3;
constexpr ftl::TimeDelta kTargetFrameInterval =
ftl::TimeDelta::FromMilliseconds(16);
} // namespace
RuntimeHolder::RuntimeHolder()
: viewport_metrics_(sky::ViewportMetrics::New()), weak_factory_(this) {}
RuntimeHolder::~RuntimeHolder() {
blink::Threads::Gpu()->PostTask(
ftl::MakeCopyable([rasterizer = std::move(rasterizer_)](){
// Deletes rasterizer.
}));
}
void RuntimeHolder::Init(mojo::ApplicationConnectorPtr connector) {
FTL_DCHECK(!rasterizer_);
rasterizer_.reset(new Rasterizer());
mojo::ConnectToService(connector.get(), "mojo:framebuffer",
mojo::GetProxy(&framebuffer_provider_));
framebuffer_provider_->Create(ftl::MakeRunnable([self = GetWeakPtr()](
mojo::InterfaceHandle<mojo::Framebuffer> framebuffer,
mojo::FramebufferInfoPtr info) {
if (self)
self->DidCreateFramebuffer(std::move(framebuffer), std::move(info));
}));
}
void RuntimeHolder::Run(const std::string& script_uri,
std::vector<char> bundle) {
InitRootBundle(std::move(bundle));
std::vector<uint8_t> snapshot;
if (!asset_store_->GetAsBuffer(kSnapshotKey, &snapshot)) {
FTL_LOG(ERROR) << "Unable to load snapshot from root bundle.";
return;
}
runtime_ = blink::RuntimeController::Create(this);
runtime_->CreateDartController(script_uri);
runtime_->SetViewportMetrics(viewport_metrics_);
runtime_->dart_controller()->RunFromSnapshot(snapshot.data(),
snapshot.size());
}
void RuntimeHolder::ScheduleFrame() {
if (runtime_requested_frame_)
return;
runtime_requested_frame_ = true;
FTL_DCHECK(!did_defer_frame_request_);
++outstanding_requests_;
if (outstanding_requests_ >= kPipelineDepth) {
did_defer_frame_request_ = true;
return;
}
ScheduleDelayedFrame();
}
void RuntimeHolder::Render(std::unique_ptr<flow::LayerTree> layer_tree) {
if (!is_ready_to_draw_)
return; // Only draw once per frame.
is_ready_to_draw_ = false;
blink::Threads::Gpu()->PostTask(ftl::MakeCopyable([
rasterizer = rasterizer_.get(), layer_tree = std::move(layer_tree),
self = GetWeakPtr()
]() mutable {
rasterizer->Draw(std::move(layer_tree), [self]() {
if (self)
self->OnFrameComplete();
});
}));
}
void RuntimeHolder::DidCreateMainIsolate(Dart_Isolate isolate) {
blink::MojoServices::Create(isolate, nullptr, nullptr,
std::move(root_bundle_));
if (asset_store_)
blink::AssetFontSelector::Install(asset_store_);
}
void RuntimeHolder::InitRootBundle(std::vector<char> bundle) {
root_bundle_data_ = std::move(bundle);
asset_store_ = ftl::MakeRefCounted<blink::ZipAssetStore>(
GetUnzipperProviderForRootBundle(), blink::Threads::IO());
new blink::ZipAssetBundle(mojo::GetProxy(&root_bundle_), asset_store_);
}
blink::UnzipperProvider RuntimeHolder::GetUnzipperProviderForRootBundle() {
return [self = GetWeakPtr()]() {
if (!self)
return zip::UniqueUnzipper();
// TODO(abarth): The lifetimes aren't quite right here. The unzipper we
// create here might be passed off to an UnzipJob that runs on a background
// thread. The UnzipJob might outlive this object and be referencing a dead
// root_bundle_data_.
return zip::CreateUnzipper(&self->root_bundle_data_);
};
}
void RuntimeHolder::DidCreateFramebuffer(
mojo::InterfaceHandle<mojo::Framebuffer> framebuffer,
mojo::FramebufferInfoPtr info) {
viewport_metrics_->physical_width = info->size->width;
viewport_metrics_->physical_height = info->size->height;
if (runtime_)
runtime_->SetViewportMetrics(viewport_metrics_);
blink::Threads::Gpu()->PostTask(ftl::MakeCopyable([
rasterizer = rasterizer_.get(), framebuffer = std::move(framebuffer),
info = std::move(info)
]() mutable {
rasterizer->SetFramebuffer(std::move(framebuffer), std::move(info));
}));
}
ftl::WeakPtr<RuntimeHolder> RuntimeHolder::GetWeakPtr() {
return weak_factory_.GetWeakPtr();
}
void RuntimeHolder::ScheduleDelayedFrame() {
// TODO(abarth): We should align with vsync or with our own timer pulse.
blink::Threads::UI()->PostDelayedTask(
[self = GetWeakPtr()]() {
if (self)
self->BeginFrame();
},
kTargetFrameInterval);
}
void RuntimeHolder::BeginFrame() {
FTL_DCHECK(outstanding_requests_ > 0);
FTL_DCHECK(outstanding_requests_ <= kPipelineDepth) << outstanding_requests_;
FTL_DCHECK(runtime_requested_frame_);
runtime_requested_frame_ = false;
FTL_DCHECK(!is_ready_to_draw_);
is_ready_to_draw_ = true;
runtime_->BeginFrame(ftl::TimePoint::Now());
const bool was_ready_to_draw = is_ready_to_draw_;
is_ready_to_draw_ = false;
// If we were still ready to draw when done with the frame, that means we
// didn't draw anything this frame and we should acknowledge the frame
// ourselves instead of waiting for the rasterizer to acknowledge it.
if (was_ready_to_draw)
OnFrameComplete();
}
void RuntimeHolder::OnFrameComplete() {
FTL_DCHECK(outstanding_requests_ > 0);
--outstanding_requests_;
if (did_defer_frame_request_) {
did_defer_frame_request_ = false;
ScheduleDelayedFrame();
}
}
} // namespace flutter_content_handler