| // 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 |