fuchsia: create new flutter_runner render path (#19584)
diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter
index a1a2c18..09bc2ba 100755
--- a/ci/licenses_golden/licenses_flutter
+++ b/ci/licenses_golden/licenses_flutter
@@ -1162,6 +1162,8 @@
FILE: ../../../flutter/shell/platform/fuchsia/flutter/flutter_runner_fakes.h
FILE: ../../../flutter/shell/platform/fuchsia/flutter/flutter_runner_product_configuration.cc
FILE: ../../../flutter/shell/platform/fuchsia/flutter/flutter_runner_product_configuration.h
+FILE: ../../../flutter/shell/platform/fuchsia/flutter/fuchsia_external_view_embedder.cc
+FILE: ../../../flutter/shell/platform/fuchsia/flutter/fuchsia_external_view_embedder.h
FILE: ../../../flutter/shell/platform/fuchsia/flutter/fuchsia_intl.cc
FILE: ../../../flutter/shell/platform/fuchsia/flutter/fuchsia_intl.h
FILE: ../../../flutter/shell/platform/fuchsia/flutter/fuchsia_intl_unittest.cc
diff --git a/shell/platform/fuchsia/flutter/BUILD.gn b/shell/platform/fuchsia/flutter/BUILD.gn
index a5da492..d90c996 100644
--- a/shell/platform/fuchsia/flutter/BUILD.gn
+++ b/shell/platform/fuchsia/flutter/BUILD.gn
@@ -53,12 +53,12 @@
"accessibility_bridge.h",
"component.cc",
"component.h",
- "compositor_context.cc",
- "compositor_context.h",
"engine.cc",
"engine.h",
"flutter_runner_product_configuration.cc",
"flutter_runner_product_configuration.h",
+ "fuchsia_external_view_embedder.cc",
+ "fuchsia_external_view_embedder.h",
"fuchsia_intl.cc",
"fuchsia_intl.h",
"isolate_configurator.cc",
@@ -92,6 +92,12 @@
"vulkan_surface_producer.cc",
"vulkan_surface_producer.h",
]
+ if (flutter_enable_legacy_fuchsia_embedder) {
+ sources += [
+ "compositor_context.cc",
+ "compositor_context.h",
+ ]
+ }
public_configs = runner_configs
diff --git a/shell/platform/fuchsia/flutter/component.cc b/shell/platform/fuchsia/flutter/component.cc
index 0106931..f7de2e9 100644
--- a/shell/platform/fuchsia/flutter/component.cc
+++ b/shell/platform/fuchsia/flutter/component.cc
@@ -54,6 +54,7 @@
constexpr char kAssetsKey[] = "assets";
constexpr char kTmpPath[] = "/tmp";
constexpr char kServiceRootPath[] = "/svc";
+constexpr char kRunnerConfigPath[] = "/config/data/flutter_runner_config";
// static
void Application::ParseProgramMetadata(
@@ -346,11 +347,13 @@
}
}
- // Load and use product-specific configuration, if it exists.
+ // Load and use runner-specific configuration, if it exists.
std::string json_string;
- if (dart_utils::ReadFileToString(
- "/config/data/frame_scheduling_performance_values", &json_string)) {
+ if (dart_utils::ReadFileToString(kRunnerConfigPath, &json_string)) {
product_config_ = FlutterRunnerProductConfiguration(json_string);
+ } else {
+ FML_LOG(WARNING) << "Failed to load runner configuration from "
+ << kRunnerConfigPath << "; using default config values.";
}
#if defined(DART_PRODUCT)
diff --git a/shell/platform/fuchsia/flutter/engine.cc b/shell/platform/fuchsia/flutter/engine.cc
index fe4dff0..3d8d53c 100644
--- a/shell/platform/fuchsia/flutter/engine.cc
+++ b/shell/platform/fuchsia/flutter/engine.cc
@@ -8,7 +8,6 @@
#include <zircon/status.h>
#include "../runtime/dart/utils/files.h"
-#include "compositor_context.h"
#include "flutter/common/task_runners.h"
#include "flutter/fml/make_copyable.h"
#include "flutter/fml/synchronization/waitable_event.h"
@@ -16,13 +15,20 @@
#include "flutter/runtime/dart_vm_lifecycle.h"
#include "flutter/shell/common/rasterizer.h"
#include "flutter/shell/common/run_configuration.h"
+#include "third_party/skia/include/ports/SkFontMgr_fuchsia.h"
+
#include "flutter_runner_product_configuration.h"
+#include "fuchsia_external_view_embedder.h"
#include "fuchsia_intl.h"
#include "platform_view.h"
+#include "surface.h"
#include "task_runner_adapter.h"
-#include "third_party/skia/include/ports/SkFontMgr_fuchsia.h"
#include "thread.h"
+#if defined(LEGACY_FUCHSIA_EMBEDDER)
+#include "compositor_context.h" // nogncheck
+#endif
+
namespace flutter_runner {
namespace {
@@ -65,6 +71,9 @@
FlutterRunnerProductConfiguration product_config)
: delegate_(delegate),
thread_label_(std::move(thread_label)),
+#if defined(LEGACY_FUCHSIA_EMBEDDER)
+ use_legacy_renderer_(product_config.use_legacy_renderer()),
+#endif
weak_factory_(this) {
if (zx::event::create(0, &vsync_event_) != ZX_OK) {
FML_DLOG(ERROR) << "Could not create the vsync event.";
@@ -124,9 +133,18 @@
thread_label_, std::move(session),
std::move(session_error_callback), [](auto) {}, vsync_handle);
surface_producer_.emplace(session_connection_->get());
- scene_update_context_.emplace(thread_label_, std::move(view_token),
- std::move(view_ref_pair),
- session_connection_.value());
+#if defined(LEGACY_FUCHSIA_EMBEDDER)
+ if (use_legacy_renderer_) {
+ legacy_external_view_embedder_.emplace(
+ thread_label_, std::move(view_token), std::move(view_ref_pair),
+ session_connection_.value());
+ } else
+#endif
+ {
+ external_view_embedder_.emplace(
+ thread_label_, std::move(view_token), std::move(view_ref_pair),
+ session_connection_.value(), surface_producer_.value());
+ }
}));
// Grab the parent environment services. The platform view may want to
@@ -152,11 +170,8 @@
OnDestroyView on_destroy_view_callback =
std::bind(&Engine::DestroyView, this, std::placeholders::_1);
- OnGetViewEmbedder on_get_view_embedder_callback =
- std::bind(&Engine::GetViewEmbedder, this);
-
- OnGetGrContext on_get_gr_context_callback =
- std::bind(&Engine::GetGrContext, this);
+ OnCreateSurface on_create_surface_callback =
+ std::bind(&Engine::CreateSurface, this);
// SessionListener has a OnScenicError method; invoke this callback on the
// platform thread when that happens. The Session itself should also be
@@ -187,11 +202,9 @@
on_create_view_callback = std::move(on_create_view_callback),
on_update_view_callback = std::move(on_update_view_callback),
on_destroy_view_callback = std::move(on_destroy_view_callback),
- on_get_view_embedder_callback =
- std::move(on_get_view_embedder_callback),
- on_get_gr_context_callback = std::move(on_get_gr_context_callback),
- vsync_handle = vsync_event_.get(),
- product_config = product_config](flutter::Shell& shell) mutable {
+ on_create_surface_callback = std::move(on_create_surface_callback),
+ vsync_offset = product_config.get_vsync_offset(),
+ vsync_handle = vsync_event_.get()](flutter::Shell& shell) mutable {
return std::make_unique<flutter_runner::PlatformView>(
shell, // delegate
debug_label, // debug label
@@ -206,27 +219,35 @@
std::move(on_create_view_callback),
std::move(on_update_view_callback),
std::move(on_destroy_view_callback),
- std::move(on_get_view_embedder_callback),
- std::move(on_get_gr_context_callback),
- vsync_handle, // vsync handle
- product_config);
+ std::move(on_create_surface_callback),
+ std::move(vsync_offset), // vsync offset
+ vsync_handle);
});
// Setup the callback that will instantiate the rasterizer.
- flutter::Shell::CreateCallback<flutter::Rasterizer> on_create_rasterizer =
- fml::MakeCopyable([this](flutter::Shell& shell) mutable {
- FML_DCHECK(session_connection_);
- FML_DCHECK(surface_producer_);
- FML_DCHECK(scene_update_context_);
+ flutter::Shell::CreateCallback<flutter::Rasterizer> on_create_rasterizer;
+#if defined(LEGACY_FUCHSIA_EMBEDDER)
+ on_create_rasterizer = [this](flutter::Shell& shell) {
+ if (use_legacy_renderer_) {
+ FML_DCHECK(session_connection_);
+ FML_DCHECK(surface_producer_);
+ FML_DCHECK(legacy_external_view_embedder_);
- std::unique_ptr<flutter_runner::CompositorContext> compositor_context =
- std::make_unique<flutter_runner::CompositorContext>(
- session_connection_.value(), surface_producer_.value(),
- scene_update_context_.value());
-
- return std::make_unique<flutter::Rasterizer>(
- shell, std::move(compositor_context));
- });
+ auto compositor_context =
+ std::make_unique<flutter_runner::CompositorContext>(
+ session_connection_.value(), surface_producer_.value(),
+ legacy_external_view_embedder_.value());
+ return std::make_unique<flutter::Rasterizer>(
+ shell, std::move(compositor_context));
+ } else {
+ return std::make_unique<flutter::Rasterizer>(shell);
+ }
+ };
+#else
+ on_create_rasterizer = [](flutter::Shell& shell) {
+ return std::make_unique<flutter::Rasterizer>(shell);
+ };
+#endif
settings.root_isolate_create_callback =
std::bind(&Engine::OnMainIsolateStart, this);
@@ -479,59 +500,97 @@
}
void Engine::DebugWireframeSettingsChanged(bool enabled) {
- if (!shell_ || !scene_update_context_) {
- return;
- }
+ FML_CHECK(shell_);
- shell_->GetTaskRunners().GetRasterTaskRunner()->PostTask(
- [this, enabled]() { scene_update_context_->EnableWireframe(enabled); });
+ shell_->GetTaskRunners().GetRasterTaskRunner()->PostTask([this, enabled]() {
+#if defined(LEGACY_FUCHSIA_EMBEDDER)
+ if (use_legacy_renderer_) {
+ FML_CHECK(legacy_external_view_embedder_);
+ legacy_external_view_embedder_->EnableWireframe(enabled);
+ } else
+#endif
+ {
+ FML_CHECK(external_view_embedder_);
+ external_view_embedder_->EnableWireframe(enabled);
+ }
+ });
}
void Engine::CreateView(int64_t view_id, bool hit_testable, bool focusable) {
- if (!shell_ || !scene_update_context_) {
- return;
- }
+ FML_CHECK(shell_);
shell_->GetTaskRunners().GetRasterTaskRunner()->PostTask(
[this, view_id, hit_testable, focusable]() {
- scene_update_context_->CreateView(view_id, hit_testable, focusable);
+#if defined(LEGACY_FUCHSIA_EMBEDDER)
+ if (use_legacy_renderer_) {
+ FML_CHECK(legacy_external_view_embedder_);
+ legacy_external_view_embedder_->CreateView(view_id, hit_testable,
+ focusable);
+ } else
+#endif
+ {
+ FML_CHECK(external_view_embedder_);
+ external_view_embedder_->CreateView(view_id);
+ external_view_embedder_->SetViewProperties(view_id, hit_testable,
+ focusable);
+ }
});
}
void Engine::UpdateView(int64_t view_id, bool hit_testable, bool focusable) {
- if (!shell_ || !scene_update_context_) {
- return;
- }
+ FML_CHECK(shell_);
shell_->GetTaskRunners().GetRasterTaskRunner()->PostTask(
[this, view_id, hit_testable, focusable]() {
- scene_update_context_->UpdateView(view_id, hit_testable, focusable);
+#if defined(LEGACY_FUCHSIA_EMBEDDER)
+ if (use_legacy_renderer_) {
+ FML_CHECK(legacy_external_view_embedder_);
+ legacy_external_view_embedder_->UpdateView(view_id, hit_testable,
+ focusable);
+ } else
+#endif
+ {
+ FML_CHECK(external_view_embedder_);
+ external_view_embedder_->SetViewProperties(view_id, hit_testable,
+ focusable);
+ }
});
}
void Engine::DestroyView(int64_t view_id) {
- if (!shell_ || !scene_update_context_) {
- return;
- }
+ FML_CHECK(shell_);
- shell_->GetTaskRunners().GetRasterTaskRunner()->PostTask(
- [this, view_id]() { scene_update_context_->DestroyView(view_id); });
+ shell_->GetTaskRunners().GetRasterTaskRunner()->PostTask([this, view_id]() {
+#if defined(LEGACY_FUCHSIA_EMBEDDER)
+ if (use_legacy_renderer_) {
+ FML_CHECK(legacy_external_view_embedder_);
+ legacy_external_view_embedder_->DestroyView(view_id);
+ } else
+#endif
+ {
+ FML_CHECK(external_view_embedder_);
+ external_view_embedder_->DestroyView(view_id);
+ }
+ });
}
-flutter::ExternalViewEmbedder* Engine::GetViewEmbedder() {
- if (!scene_update_context_) {
- return nullptr;
+std::unique_ptr<flutter::Surface> Engine::CreateSurface() {
+ flutter::ExternalViewEmbedder* external_view_embedder = nullptr;
+
+#if defined(LEGACY_FUCHSIA_EMBEDDER)
+ if (use_legacy_renderer_) {
+ FML_CHECK(legacy_external_view_embedder_);
+ external_view_embedder = &legacy_external_view_embedder_.value();
+ } else
+#endif
+ {
+ FML_CHECK(external_view_embedder_);
+ external_view_embedder = &external_view_embedder_.value();
}
+ FML_CHECK(external_view_embedder);
- return &scene_update_context_.value();
-}
-
-GrDirectContext* Engine::GetGrContext() {
- // GetGrContext should be called only after rasterizer is created.
- FML_DCHECK(shell_);
- FML_DCHECK(shell_->GetRasterizer());
-
- return surface_producer_->gr_context();
+ return std::make_unique<Surface>(thread_label_, external_view_embedder,
+ surface_producer_->gr_context());
}
#if !defined(DART_PRODUCT)
diff --git a/shell/platform/fuchsia/flutter/engine.h b/shell/platform/fuchsia/flutter/engine.h
index 410ff63..20e14e8 100644
--- a/shell/platform/fuchsia/flutter/engine.h
+++ b/shell/platform/fuchsia/flutter/engine.h
@@ -14,17 +14,21 @@
#include <lib/ui/scenic/cpp/view_ref_pair.h>
#include <lib/zx/event.h>
-#include "flutter/flow/embedded_views.h"
-#include "flutter/flow/scene_update_context.h"
+#include "flutter/flow/surface.h"
#include "flutter/fml/macros.h"
#include "flutter/shell/common/shell.h"
#include "flutter_runner_product_configuration.h"
+#include "fuchsia_external_view_embedder.h"
#include "isolate_configurator.h"
#include "session_connection.h"
#include "thread.h"
#include "vulkan_surface_producer.h"
+#if defined(LEGACY_FUCHSIA_EMBEDDER)
+#include "flutter/flow/scene_update_context.h" // nogncheck
+#endif
+
namespace flutter_runner {
// Represents an instance of running Flutter engine along with the threads
@@ -65,7 +69,10 @@
std::optional<SessionConnection> session_connection_;
std::optional<VulkanSurfaceProducer> surface_producer_;
- std::optional<flutter::SceneUpdateContext> scene_update_context_;
+ std::optional<FuchsiaExternalViewEmbedder> external_view_embedder_;
+#if defined(LEGACY_FUCHSIA_EMBEDDER)
+ std::optional<flutter::SceneUpdateContext> legacy_external_view_embedder_;
+#endif
std::unique_ptr<IsolateConfigurator> isolate_configurator_;
std::unique_ptr<flutter::Shell> shell_;
@@ -74,6 +81,10 @@
zx::event vsync_event_;
+#if defined(LEGACY_FUCHSIA_EMBEDDER)
+ bool use_legacy_renderer_ = true;
+#endif
+
fml::WeakPtrFactory<Engine> weak_factory_;
void OnMainIsolateStart();
@@ -87,9 +98,7 @@
void UpdateView(int64_t view_id, bool hit_testable, bool focusable);
void DestroyView(int64_t view_id);
- flutter::ExternalViewEmbedder* GetViewEmbedder();
-
- GrDirectContext* GetGrContext();
+ std::unique_ptr<flutter::Surface> CreateSurface();
FML_DISALLOW_COPY_AND_ASSIGN(Engine);
};
diff --git a/shell/platform/fuchsia/flutter/flutter_runner_product_configuration.cc b/shell/platform/fuchsia/flutter/flutter_runner_product_configuration.cc
index af7af50..3406faa 100644
--- a/shell/platform/fuchsia/flutter/flutter_runner_product_configuration.cc
+++ b/shell/platform/fuchsia/flutter/flutter_runner_product_configuration.cc
@@ -20,6 +20,11 @@
if (auto& val = document["vsync_offset_in_us"]; val.IsInt()) {
vsync_offset_ = fml::TimeDelta::FromMicroseconds(val.GetInt());
}
+#if defined(LEGACY_FUCHSIA_EMBEDDER)
+ if (auto& val = document["use_legacy_renderer"]; val.IsBool()) {
+ use_legacy_renderer_ = val.GetBool();
+ }
+#endif
}
} // namespace flutter_runner
diff --git a/shell/platform/fuchsia/flutter/flutter_runner_product_configuration.h b/shell/platform/fuchsia/flutter/flutter_runner_product_configuration.h
index 29958f5..57313d7 100644
--- a/shell/platform/fuchsia/flutter/flutter_runner_product_configuration.h
+++ b/shell/platform/fuchsia/flutter/flutter_runner_product_configuration.h
@@ -4,6 +4,7 @@
#ifndef FLUTTER_SHELL_PLATFORM_FUCHSIA_FLUTTER_RUNNER_PRODUCT_CONFIGURATION_H_
#define FLUTTER_SHELL_PLATFORM_FUCHSIA_FLUTTER_RUNNER_PRODUCT_CONFIGURATION_H_
+
#include "flutter/fml/time/time_delta.h"
namespace flutter_runner {
@@ -14,9 +15,15 @@
FlutterRunnerProductConfiguration(std::string path);
fml::TimeDelta get_vsync_offset() { return vsync_offset_; }
+#if defined(LEGACY_FUCHSIA_EMBEDDER)
+ bool use_legacy_renderer() { return use_legacy_renderer_; }
+#endif
private:
fml::TimeDelta vsync_offset_ = fml::TimeDelta::Zero();
+#if defined(LEGACY_FUCHSIA_EMBEDDER)
+ bool use_legacy_renderer_ = true;
+#endif
};
} // namespace flutter_runner
diff --git a/shell/platform/fuchsia/flutter/fuchsia_external_view_embedder.cc b/shell/platform/fuchsia/flutter/fuchsia_external_view_embedder.cc
new file mode 100644
index 0000000..96fd028
--- /dev/null
+++ b/shell/platform/fuchsia/flutter/fuchsia_external_view_embedder.cc
@@ -0,0 +1,442 @@
+// 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.
+
+#include "fuchsia_external_view_embedder.h"
+
+#include <lib/ui/scenic/cpp/commands.h>
+#include <lib/ui/scenic/cpp/view_token_pair.h>
+#include <zircon/types.h>
+
+#include <algorithm> // For std::clamp
+
+#include "flutter/fml/trace_event.h"
+#include "third_party/skia/include/core/SkPicture.h"
+#include "third_party/skia/include/core/SkSurface.h"
+
+namespace flutter_runner {
+namespace {
+
+// Layer separation is as infinitesimal as possible without introducing
+// Z-fighting.
+constexpr float kScenicZElevationBetweenLayers = 0.0001f;
+constexpr float kScenicZElevationForPlatformView = 100.f;
+
+} // namespace
+
+FuchsiaExternalViewEmbedder::FuchsiaExternalViewEmbedder(
+ std::string debug_label,
+ fuchsia::ui::views::ViewToken view_token,
+ scenic::ViewRefPair view_ref_pair,
+ SessionConnection& session,
+ VulkanSurfaceProducer& surface_producer)
+ : session_(session),
+ surface_producer_(surface_producer),
+ root_view_(session_.get(),
+ std::move(view_token),
+ std::move(view_ref_pair.control_ref),
+ std::move(view_ref_pair.view_ref),
+ debug_label),
+ metrics_node_(session_.get()),
+ root_node_(session_.get()) {
+ root_view_.AddChild(metrics_node_);
+ metrics_node_.SetEventMask(fuchsia::ui::gfx::kMetricsEventMask);
+ metrics_node_.SetLabel("Flutter::MetricsWatcher");
+ metrics_node_.AddChild(root_node_);
+ root_node_.SetLabel("Flutter::LayerTree");
+
+ session_.Present();
+}
+
+FuchsiaExternalViewEmbedder::~FuchsiaExternalViewEmbedder() = default;
+
+SkCanvas* FuchsiaExternalViewEmbedder::GetRootCanvas() {
+ auto found = frame_layers_.find(kRootLayerId);
+ if (found == frame_layers_.end()) {
+ FML_DLOG(WARNING)
+ << "No root canvas could be found. This is extremely unlikely and "
+ "indicates that the external view embedder did not receive the "
+ "notification to begin the frame.";
+ return nullptr;
+ }
+
+ return found->second.canvas_spy->GetSpyingCanvas();
+}
+
+std::vector<SkCanvas*> FuchsiaExternalViewEmbedder::GetCurrentCanvases() {
+ std::vector<SkCanvas*> canvases;
+ for (const auto& layer : frame_layers_) {
+ // This method (for legacy reasons) expects non-root current canvases.
+ if (layer.first.has_value()) {
+ canvases.push_back(layer.second.canvas_spy->GetSpyingCanvas());
+ }
+ }
+ return canvases;
+}
+
+void FuchsiaExternalViewEmbedder::PrerollCompositeEmbeddedView(
+ int view_id,
+ std::unique_ptr<flutter::EmbeddedViewParams> params) {
+ zx_handle_t handle = static_cast<zx_handle_t>(view_id);
+ FML_DCHECK(frame_layers_.count(handle) == 0);
+
+ frame_layers_.emplace(std::make_pair(EmbedderLayerId{handle},
+ EmbedderLayer(frame_size_, *params)));
+ frame_composition_order_.push_back(handle);
+}
+
+SkCanvas* FuchsiaExternalViewEmbedder::CompositeEmbeddedView(int view_id) {
+ zx_handle_t handle = static_cast<zx_handle_t>(view_id);
+ auto found = frame_layers_.find(handle);
+ FML_DCHECK(found != frame_layers_.end());
+
+ return found->second.canvas_spy->GetSpyingCanvas();
+}
+
+flutter::PostPrerollResult FuchsiaExternalViewEmbedder::PostPrerollAction(
+ fml::RefPtr<fml::RasterThreadMerger> raster_thread_merger) {
+ return flutter::PostPrerollResult::kSuccess;
+}
+
+void FuchsiaExternalViewEmbedder::BeginFrame(
+ SkISize frame_size,
+ GrDirectContext* context,
+ double device_pixel_ratio,
+ fml::RefPtr<fml::RasterThreadMerger> raster_thread_merger) {
+ TRACE_EVENT0("flutter", "FuchsiaExternalViewEmbedder::BeginFrame");
+
+ // Reset for new frame.
+ Reset();
+ frame_size_ = frame_size;
+ frame_dpr_ = device_pixel_ratio;
+
+ // Create the root layer.
+ frame_layers_.emplace(
+ std::make_pair(kRootLayerId, EmbedderLayer(frame_size, std::nullopt)));
+ frame_composition_order_.push_back(kRootLayerId);
+}
+
+void FuchsiaExternalViewEmbedder::EndFrame(
+ bool should_resubmit_frame,
+ fml::RefPtr<fml::RasterThreadMerger> raster_thread_merger) {
+ TRACE_EVENT0("flutter", "FuchsiaExternalViewEmbedder::EndFrame");
+}
+
+void FuchsiaExternalViewEmbedder::SubmitFrame(
+ GrDirectContext* context,
+ std::unique_ptr<flutter::SurfaceFrame> frame) {
+ TRACE_EVENT0("flutter", "FuchsiaExternalViewEmbedder::SubmitFrame");
+
+ std::vector<std::unique_ptr<SurfaceProducerSurface>> frame_surfaces;
+ std::unordered_map<EmbedderLayerId, size_t> frame_surface_indices;
+
+ // Create surfaces for the frame and associate them with layer IDs.
+ {
+ TRACE_EVENT0("flutter", "CreateSurfaces");
+
+ for (const auto& layer : frame_layers_) {
+ if (!layer.second.canvas_spy->DidDrawIntoCanvas()) {
+ continue;
+ }
+
+ auto surface =
+ surface_producer_.ProduceSurface(layer.second.surface_size);
+ FML_DCHECK(surface)
+ << "Embedder did not return a valid render target of size ("
+ << layer.second.surface_size.width() << ", "
+ << layer.second.surface_size.height() << ")";
+
+ frame_surface_indices.emplace(
+ std::make_pair(layer.first, frame_surfaces.size()));
+ frame_surfaces.emplace_back(std::move(surface));
+ }
+ }
+
+ // Submit layers and platform views to Scenic in composition order.
+ {
+ TRACE_EVENT0("flutter", "SubmitLayers");
+
+ std::unordered_map<uint64_t, size_t> scenic_rect_indices;
+ size_t scenic_layer_index = 0;
+ float embedded_views_height = 0.0f;
+
+ // First re-scale everything according to the DPR.
+ const float inv_dpr = 1.0f / frame_dpr_;
+ root_node_.SetScale(inv_dpr, inv_dpr, 1.0f);
+
+ for (const auto& layer_id : frame_composition_order_) {
+ const auto& layer = frame_layers_.find(layer_id);
+ FML_DCHECK(layer != frame_layers_.end());
+
+ // Draw the PlatformView associated with each layer first.
+ if (layer_id.has_value()) {
+ FML_DCHECK(layer->second.embedded_view_params.has_value());
+ auto& view_params = layer->second.embedded_view_params.value();
+
+ // Compute offset and size for the platform view.
+ SkPoint view_offset =
+ SkPoint::Make(view_params.finalBoundingRect().left(),
+ view_params.finalBoundingRect().top());
+ SkSize view_size =
+ SkSize::Make(view_params.finalBoundingRect().width(),
+ view_params.finalBoundingRect().height());
+
+ // Compute opacity for the platform view.
+ float view_opacity = 1.0f;
+ for (auto i = view_params.mutatorsStack().Bottom();
+ i != view_params.mutatorsStack().Top(); ++i) {
+ const auto& mutator = *i;
+ switch (mutator->GetType()) {
+ case flutter::MutatorType::opacity: {
+ view_opacity *= std::clamp(mutator->GetAlphaFloat(), 0.0f, 1.0f);
+ } break;
+ default: {
+ break;
+ }
+ }
+ }
+
+ auto found = scenic_views_.find(layer_id.value());
+ FML_DCHECK(found != scenic_views_.end());
+ auto& view_holder = found->second;
+
+ // Set opacity.
+ if (view_opacity != view_holder.opacity) {
+ view_holder.opacity_node.SetOpacity(view_opacity);
+ view_holder.opacity = view_opacity;
+ }
+
+ // Set offset and elevation.
+ const float view_elevation =
+ kScenicZElevationBetweenLayers * scenic_layer_index +
+ embedded_views_height;
+ if (view_offset != view_holder.offset ||
+ view_elevation != view_holder.elevation) {
+ view_holder.entity_node.SetTranslation(view_offset.fX, view_offset.fY,
+ -view_elevation);
+ view_holder.elevation = view_elevation;
+ }
+
+ // Set HitTestBehavior.
+ if (view_holder.pending_hit_testable != view_holder.hit_testable) {
+ view_holder.entity_node.SetHitTestBehavior(
+ view_holder.pending_hit_testable
+ ? fuchsia::ui::gfx::HitTestBehavior::kDefault
+ : fuchsia::ui::gfx::HitTestBehavior::kSuppress);
+ view_holder.hit_testable = view_holder.pending_hit_testable;
+ }
+
+ // Set size and focusable.
+ //
+ // Scenic rejects `SetViewProperties` calls with a zero size.
+ if (!view_size.isEmpty() &&
+ (view_size != view_holder.size ||
+ view_holder.pending_focusable != view_holder.focusable)) {
+ view_holder.view_holder.SetViewProperties({
+ .bounding_box =
+ {
+ .min = {.x = 0.f, .y = 0.f, .z = -1000.f},
+ .max = {.x = view_size.width(),
+ .y = view_size.height(),
+ .z = 0.f},
+ },
+ .inset_from_min = {.x = 0.f, .y = 0.f, .z = 0.f},
+ .inset_from_max = {.x = 0.f, .y = 0.f, .z = 0.f},
+ .focus_change = view_holder.pending_focusable,
+ });
+ view_holder.size = view_size;
+ view_holder.focusable = view_holder.pending_focusable;
+ }
+
+ // Attach the ScenicView to the main scene graph.
+ root_node_.AddChild(view_holder.opacity_node);
+
+ // Account for the ScenicView's height when positioning the next layer.
+ embedded_views_height += kScenicZElevationForPlatformView;
+ }
+
+ if (layer->second.canvas_spy->DidDrawIntoCanvas()) {
+ const auto& surface_index = frame_surface_indices.find(layer_id);
+ FML_DCHECK(surface_index != frame_surface_indices.end());
+ scenic::Image* surface_image =
+ frame_surfaces[surface_index->second]->GetImage();
+
+ // Create a new layer if needed for the surface.
+ FML_DCHECK(scenic_layer_index <= scenic_layers_.size());
+ if (scenic_layer_index == scenic_layers_.size()) {
+ ScenicLayer new_layer{
+ .shape_node = scenic::ShapeNode(session_.get()),
+ .material = scenic::Material(session_.get()),
+ };
+ new_layer.shape_node.SetMaterial(new_layer.material);
+ scenic_layers_.emplace_back(std::move(new_layer));
+ }
+
+ // Compute a hash and index for the rect.
+ const uint64_t rect_hash =
+ (static_cast<uint64_t>(layer->second.surface_size.width()) << 32) +
+ layer->second.surface_size.height();
+ size_t rect_index = 0;
+ auto found_index = scenic_rect_indices.find(rect_hash);
+ if (found_index == scenic_rect_indices.end()) {
+ scenic_rect_indices.emplace(std::make_pair(rect_hash, 0));
+ } else {
+ rect_index = found_index->second + 1;
+ scenic_rect_indices[rect_hash] = rect_index;
+ }
+
+ // Create a new rect if needed for the surface.
+ auto found_rects = scenic_rects_.find(rect_hash);
+ if (found_rects == scenic_rects_.end()) {
+ auto [emplaced_rects, success] = scenic_rects_.emplace(
+ std::make_pair(rect_hash, std::vector<scenic::Rectangle>()));
+ FML_DCHECK(success);
+
+ found_rects = std::move(emplaced_rects);
+ }
+ FML_DCHECK(rect_index <= found_rects->second.size());
+ if (rect_index == found_rects->second.size()) {
+ found_rects->second.emplace_back(scenic::Rectangle(
+ session_.get(), layer->second.surface_size.width(),
+ layer->second.surface_size.height()));
+ }
+
+ // Set layer shape and texture.
+ // Scenic currently lacks an API to enable rendering of alpha channel;
+ // Flutter Embedder also lacks an API to detect if a layer has alpha or
+ // not. Alpha channels are only rendered if there is a OpacityNode
+ // higher in the tree with opacity != 1. For now, always assume t he
+ // layer has alpha and clamp to a infinitesimally smaller value than 1.
+ //
+ // This does not cause visual problems in practice, but probably has
+ // performance implications.
+ auto& scenic_layer = scenic_layers_[scenic_layer_index];
+ auto& scenic_rect = found_rects->second[rect_index];
+ const float layer_elevation =
+ kScenicZElevationBetweenLayers * scenic_layer_index +
+ embedded_views_height;
+ scenic_layer.shape_node.SetLabel("Flutter::Layer");
+ scenic_layer.shape_node.SetShape(scenic_rect);
+ scenic_layer.shape_node.SetTranslation(
+ layer->second.surface_size.width() * 0.5f,
+ layer->second.surface_size.height() * 0.5f, -layer_elevation);
+ scenic_layer.material.SetColor(SK_AlphaOPAQUE, SK_AlphaOPAQUE,
+ SK_AlphaOPAQUE, SK_AlphaOPAQUE - 1);
+ scenic_layer.material.SetTexture(*surface_image);
+
+ // Attach the ScenicLayer to the main scene graph.
+ root_node_.AddChild(scenic_layer.shape_node);
+
+ // Account for the ScenicLayer's height when positioning the next layer.
+ scenic_layer_index++;
+ }
+ }
+ }
+
+ // Present the session to Scenic, along with surface acquire/release fencess.
+ {
+ TRACE_EVENT0("flutter", "SessionPresent");
+
+ session_.Present();
+ }
+
+ // Render the recorded SkPictures into the surfaces.
+ {
+ TRACE_EVENT0("flutter", "RasterizeSurfaces");
+
+ for (const auto& surface_index : frame_surface_indices) {
+ TRACE_EVENT0("flutter", "RasterizeSurface");
+
+ const auto& layer = frame_layers_.find(surface_index.first);
+ FML_DCHECK(layer != frame_layers_.end());
+ sk_sp<SkPicture> picture =
+ layer->second.recorder->finishRecordingAsPicture();
+ FML_DCHECK(picture);
+
+ sk_sp<SkSurface> sk_surface =
+ frame_surfaces[surface_index.second]->GetSkiaSurface();
+ FML_DCHECK(sk_surface);
+ FML_DCHECK(SkISize::Make(sk_surface->width(), sk_surface->height()) ==
+ frame_size_);
+
+ SkCanvas* canvas = sk_surface->getCanvas();
+ FML_DCHECK(canvas);
+
+ canvas->setMatrix(SkMatrix::I());
+ canvas->clear(SK_ColorTRANSPARENT);
+ canvas->drawPicture(picture);
+ canvas->flush();
+ }
+ }
+
+ // Flush deferred Skia work and inform Scenic that render targets are ready.
+ {
+ TRACE_EVENT0("flutter", "PresentSurfaces");
+
+ surface_producer_.OnSurfacesPresented(std::move(frame_surfaces));
+ }
+
+ // Submit the underlying render-backend-specific frame for processing.
+ frame->Submit();
+}
+
+void FuchsiaExternalViewEmbedder::EnableWireframe(bool enable) {
+ session_.get()->Enqueue(
+ scenic::NewSetEnableDebugViewBoundsCmd(root_view_.id(), enable));
+ session_.Present();
+}
+
+void FuchsiaExternalViewEmbedder::CreateView(int64_t view_id) {
+ FML_DCHECK(scenic_views_.find(view_id) == scenic_views_.end());
+
+ ScenicView new_view = {
+ .opacity_node = scenic::OpacityNodeHACK(session_.get()),
+ .entity_node = scenic::EntityNode(session_.get()),
+ .view_holder = scenic::ViewHolder(
+ session_.get(),
+ scenic::ToViewHolderToken(zx::eventpair((zx_handle_t)view_id)),
+ "Flutter::PlatformView"),
+ };
+
+ new_view.opacity_node.SetLabel("flutter::PlatformView::OpacityMutator");
+ new_view.entity_node.SetLabel("flutter::PlatformView::TransformMutator");
+ new_view.opacity_node.AddChild(new_view.entity_node);
+ new_view.entity_node.Attach(new_view.view_holder);
+ new_view.entity_node.SetTranslation(0.f, 0.f,
+ -kScenicZElevationBetweenLayers);
+
+ scenic_views_.emplace(std::make_pair(view_id, std::move(new_view)));
+}
+
+void FuchsiaExternalViewEmbedder::DestroyView(int64_t view_id) {
+ size_t erased = scenic_views_.erase(view_id);
+ FML_DCHECK(erased == 1);
+}
+
+void FuchsiaExternalViewEmbedder::SetViewProperties(int64_t view_id,
+ bool hit_testable,
+ bool focusable) {
+ auto found = scenic_views_.find(view_id);
+ FML_DCHECK(found != scenic_views_.end());
+ auto& view_holder = found->second;
+
+ view_holder.pending_hit_testable = hit_testable;
+ view_holder.pending_focusable = focusable;
+}
+
+void FuchsiaExternalViewEmbedder::Reset() {
+ frame_layers_.clear();
+ frame_composition_order_.clear();
+ frame_size_ = SkISize::Make(0, 0);
+ frame_dpr_ = 1.f;
+
+ // Detach the root node to prepare for the next frame.
+ session_.get()->Enqueue(scenic::NewDetachChildrenCmd(root_node_.id()));
+
+ // Clear images on all layers so they aren't cached unnecesarily.
+ for (auto& layer : scenic_layers_) {
+ layer.material.SetTexture(0);
+ }
+}
+
+} // namespace flutter_runner
diff --git a/shell/platform/fuchsia/flutter/fuchsia_external_view_embedder.h b/shell/platform/fuchsia/flutter/fuchsia_external_view_embedder.h
new file mode 100644
index 0000000..f299a2f
--- /dev/null
+++ b/shell/platform/fuchsia/flutter/fuchsia_external_view_embedder.h
@@ -0,0 +1,158 @@
+// 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.
+
+#ifndef FLUTTER_SHELL_PLATFORM_FUCHSIA_FLUTTER_FUCHSIA_EXTERNAL_VIEW_EMBEDDER_H_
+#define FLUTTER_SHELL_PLATFORM_FUCHSIA_FLUTTER_FUCHSIA_EXTERNAL_VIEW_EMBEDDER_H_
+
+#include <fuchsia/ui/views/cpp/fidl.h>
+#include <lib/ui/scenic/cpp/resources.h>
+#include <lib/ui/scenic/cpp/view_ref_pair.h>
+
+#include <cstdint> // For uint32_t & uint64_t
+#include <memory>
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+#include "flutter/flow/embedded_views.h"
+#include "flutter/fml/logging.h"
+#include "flutter/fml/macros.h"
+#include "flutter/shell/common/canvas_spy.h"
+#include "third_party/skia/include/core/SkCanvas.h"
+#include "third_party/skia/include/core/SkPictureRecorder.h"
+#include "third_party/skia/include/core/SkPoint.h"
+#include "third_party/skia/include/core/SkSize.h"
+#include "third_party/skia/include/gpu/GrDirectContext.h"
+
+#include "session_connection.h"
+#include "vulkan_surface_producer.h"
+
+namespace flutter_runner {
+
+// This class orchestrates interaction with the Scenic compositor on Fuchsia. It
+// ensures that flutter content and platform view content are both rendered
+// correctly in a unified scene.
+class FuchsiaExternalViewEmbedder final : public flutter::ExternalViewEmbedder {
+ public:
+ FuchsiaExternalViewEmbedder(std::string debug_label,
+ fuchsia::ui::views::ViewToken view_token,
+ scenic::ViewRefPair view_ref_pair,
+ SessionConnection& session,
+ VulkanSurfaceProducer& surface_producer);
+ ~FuchsiaExternalViewEmbedder();
+
+ // |ExternalViewEmbedder|
+ SkCanvas* GetRootCanvas() override;
+
+ // |ExternalViewEmbedder|
+ std::vector<SkCanvas*> GetCurrentCanvases() override;
+
+ // |ExternalViewEmbedder|
+ void PrerollCompositeEmbeddedView(
+ int view_id,
+ std::unique_ptr<flutter::EmbeddedViewParams> params) override;
+
+ // |ExternalViewEmbedder|
+ SkCanvas* CompositeEmbeddedView(int view_id) override;
+
+ // |ExternalViewEmbedder|
+ flutter::PostPrerollResult PostPrerollAction(
+ fml::RefPtr<fml::RasterThreadMerger> raster_thread_merger) override;
+
+ // |ExternalViewEmbedder|
+ void BeginFrame(
+ SkISize frame_size,
+ GrDirectContext* context,
+ double device_pixel_ratio,
+ fml::RefPtr<fml::RasterThreadMerger> raster_thread_merger) override;
+
+ // |ExternalViewEmbedder|
+ void EndFrame(
+ bool should_resubmit_frame,
+ fml::RefPtr<fml::RasterThreadMerger> raster_thread_merger) override;
+
+ // |ExternalViewEmbedder|
+ void SubmitFrame(GrDirectContext* context,
+ std::unique_ptr<flutter::SurfaceFrame> frame) override;
+
+ // |ExternalViewEmbedder|
+ void CancelFrame() override { Reset(); }
+
+ // |ExternalViewEmbedder|
+ bool SupportsDynamicThreadMerging() override { return false; }
+
+ // View manipulation.
+ // |SetViewProperties| doesn't manipulate the view directly -- it sets
+ // prending properties for the next |UpdateView| call.
+ void EnableWireframe(bool enable);
+ void CreateView(int64_t view_id);
+ void DestroyView(int64_t view_id);
+ void SetViewProperties(int64_t view_id, bool hit_testable, bool focusable);
+
+ private:
+ // Reset state for a new frame.
+ void Reset();
+
+ struct EmbedderLayer {
+ EmbedderLayer(const SkISize& frame_size,
+ std::optional<flutter::EmbeddedViewParams> view_params)
+ : embedded_view_params(std::move(view_params)),
+ recorder(std::make_unique<SkPictureRecorder>()),
+ canvas_spy(std::make_unique<flutter::CanvasSpy>(
+ recorder->beginRecording(frame_size.width(),
+ frame_size.height()))),
+ surface_size(frame_size) {}
+
+ std::optional<flutter::EmbeddedViewParams> embedded_view_params;
+ std::unique_ptr<SkPictureRecorder> recorder;
+ std::unique_ptr<flutter::CanvasSpy> canvas_spy;
+ SkISize surface_size;
+ };
+
+ struct ScenicView {
+ scenic::OpacityNodeHACK opacity_node;
+ scenic::EntityNode entity_node;
+ scenic::ViewHolder view_holder;
+
+ SkPoint offset = SkPoint::Make(0.f, 0.f);
+ SkSize size = SkSize::MakeEmpty();
+ float elevation = 0.f;
+ float opacity = 1.f;
+ bool hit_testable = false;
+ bool focusable = false;
+
+ bool pending_hit_testable = false;
+ bool pending_focusable = false;
+ };
+
+ struct ScenicLayer {
+ scenic::ShapeNode shape_node;
+ scenic::Material material;
+ };
+
+ using EmbedderLayerId = std::optional<uint32_t>;
+ constexpr static EmbedderLayerId kRootLayerId = EmbedderLayerId{};
+
+ SessionConnection& session_;
+ VulkanSurfaceProducer& surface_producer_;
+
+ scenic::View root_view_;
+ scenic::EntityNode metrics_node_;
+ scenic::EntityNode root_node_;
+
+ std::unordered_map<uint64_t, std::vector<scenic::Rectangle>> scenic_rects_;
+ std::unordered_map<int64_t, ScenicView> scenic_views_;
+ std::vector<ScenicLayer> scenic_layers_;
+
+ std::unordered_map<EmbedderLayerId, EmbedderLayer> frame_layers_;
+ std::vector<EmbedderLayerId> frame_composition_order_;
+ SkISize frame_size_ = SkISize::Make(0, 0);
+ float frame_dpr_ = 1.f;
+
+ FML_DISALLOW_COPY_AND_ASSIGN(FuchsiaExternalViewEmbedder);
+};
+
+} // namespace flutter_runner
+
+#endif // FLUTTER_SHELL_PLATFORM_FUCHSIA_FLUTTER_FUCHSIA_EXTERNAL_VIEW_EMBEDDER_H_
diff --git a/shell/platform/fuchsia/flutter/platform_view.cc b/shell/platform/fuchsia/flutter/platform_view.cc
index 5359c1e..0d289f5 100644
--- a/shell/platform/fuchsia/flutter/platform_view.cc
+++ b/shell/platform/fuchsia/flutter/platform_view.cc
@@ -10,10 +10,8 @@
#include <sstream>
#include "flutter/fml/logging.h"
-#include "flutter/lib/ui/compositing/scene_host.h"
#include "flutter/lib/ui/window/pointer_data.h"
#include "flutter/lib/ui/window/window.h"
-#include "flutter_runner_product_configuration.h"
#include "logging.h"
#include "rapidjson/document.h"
#include "rapidjson/stringbuffer.h"
@@ -21,6 +19,10 @@
#include "runtime/dart/utils/inlines.h"
#include "vsync_waiter.h"
+#if defined(LEGACY_FUCHSIA_EMBEDDER)
+#include "flutter/lib/ui/compositing/scene_host.h"
+#endif
+
namespace flutter_runner {
static constexpr char kFlutterPlatformChannel[] = "flutter/platform";
@@ -60,10 +62,9 @@
OnCreateView on_create_view_callback,
OnUpdateView on_update_view_callback,
OnDestroyView on_destroy_view_callback,
- OnGetViewEmbedder on_get_view_embedder_callback,
- OnGetGrContext on_get_gr_context_callback,
- zx_handle_t vsync_event_handle,
- FlutterRunnerProductConfiguration product_config)
+ OnCreateSurface on_create_surface_callback,
+ fml::TimeDelta vsync_offset,
+ zx_handle_t vsync_event_handle)
: flutter::PlatformView(delegate, std::move(task_runners)),
debug_label_(std::move(debug_label)),
view_ref_(std::move(view_ref)),
@@ -75,11 +76,10 @@
on_create_view_callback_(std::move(on_create_view_callback)),
on_update_view_callback_(std::move(on_update_view_callback)),
on_destroy_view_callback_(std::move(on_destroy_view_callback)),
- on_get_view_embedder_callback_(std::move(on_get_view_embedder_callback)),
- on_get_gr_context_callback_(std::move(on_get_gr_context_callback)),
+ on_create_surface_callback_(std::move(on_create_surface_callback)),
ime_client_(this),
- vsync_event_handle_(vsync_event_handle),
- product_config_(product_config) {
+ vsync_offset_(std::move(vsync_offset)),
+ vsync_event_handle_(vsync_event_handle) {
// Register all error handlers.
SetInterfaceErrorHandler(session_listener_binding_, "SessionListener");
SetInterfaceErrorHandler(ime_, "Input Method Editor");
@@ -248,6 +248,7 @@
}
break;
}
+#if defined(LEGACY_FUCHSIA_EMBEDDER)
case fuchsia::ui::gfx::Event::Tag::kViewConnected:
OnChildViewConnected(event.gfx().view_connected().view_holder_id);
break;
@@ -260,6 +261,7 @@
event.gfx().view_state_changed().view_holder_id,
event.gfx().view_state_changed().state.is_rendering);
break;
+#endif
case fuchsia::ui::gfx::Event::Tag::Invalid:
FML_DCHECK(false) << "Flutter PlatformView::OnScenicEvent: Got "
"an invalid GFX event.";
@@ -317,6 +319,7 @@
}
}
+#if defined(LEGACY_FUCHSIA_EMBEDDER)
void PlatformView::OnChildViewConnected(scenic::ResourceId view_holder_id) {
task_runners_.GetUITaskRunner()->PostTask([view_holder_id]() {
flutter::SceneHost::OnViewConnected(view_holder_id);
@@ -335,6 +338,7 @@
flutter::SceneHost::OnViewStateChanged(view_holder_id, state);
});
}
+#endif
static flutter::PointerData::Change GetChangeFromPointerEventPhase(
fuchsia::ui::input::PointerEventPhase phase) {
@@ -513,21 +517,12 @@
// |flutter::PlatformView|
std::unique_ptr<flutter::VsyncWaiter> PlatformView::CreateVSyncWaiter() {
return std::make_unique<flutter_runner::VsyncWaiter>(
- debug_label_, vsync_event_handle_, task_runners_,
- product_config_.get_vsync_offset());
+ debug_label_, vsync_event_handle_, task_runners_, vsync_offset_);
}
// |flutter::PlatformView|
std::unique_ptr<flutter::Surface> PlatformView::CreateRenderingSurface() {
- // This platform does not repeatly lose and gain a surface connection. So the
- // surface is setup once during platform view setup and returned to the
- // shell on the initial (and only) |NotifyCreated| call.
- auto view_embedder = on_get_view_embedder_callback_
- ? on_get_view_embedder_callback_()
- : nullptr;
- auto gr_context =
- on_get_gr_context_callback_ ? on_get_gr_context_callback_() : nullptr;
- return std::make_unique<Surface>(debug_label_, view_embedder, gr_context);
+ return on_create_surface_callback_ ? on_create_surface_callback_() : nullptr;
}
// |flutter::PlatformView|
diff --git a/shell/platform/fuchsia/flutter/platform_view.h b/shell/platform/fuchsia/flutter/platform_view.h
index 35275b3..431272b 100644
--- a/shell/platform/fuchsia/flutter/platform_view.h
+++ b/shell/platform/fuchsia/flutter/platform_view.h
@@ -7,18 +7,21 @@
#include <fuchsia/ui/input/cpp/fidl.h>
#include <fuchsia/ui/scenic/cpp/fidl.h>
+#include <lib/fidl/cpp/binding.h>
#include <lib/fit/function.h>
#include <map>
#include <set>
#include "flutter/fml/macros.h"
+#include "flutter/fml/time/time_delta.h"
#include "flutter/shell/common/platform_view.h"
-#include "flutter/shell/platform/fuchsia/flutter/accessibility_bridge.h"
-#include "flutter_runner_product_configuration.h"
-#include "lib/fidl/cpp/binding.h"
-#include "lib/ui/scenic/cpp/id.h"
-#include "surface.h"
+
+#include "accessibility_bridge.h"
+
+#if defined(LEGACY_FUCHSIA_EMBEDDER)
+#include <lib/ui/scenic/cpp/id.h> // nogncheck
+#endif
namespace flutter_runner {
@@ -26,8 +29,7 @@
using OnCreateView = fit::function<void(int64_t, bool, bool)>;
using OnUpdateView = fit::function<void(int64_t, bool, bool)>;
using OnDestroyView = fit::function<void(int64_t)>;
-using OnGetViewEmbedder = fit::function<flutter::ExternalViewEmbedder*()>;
-using OnGetGrContext = fit::function<GrDirectContext*()>;
+using OnCreateSurface = fit::function<std::unique_ptr<flutter::Surface>()>;
// The per engine component residing on the platform thread is responsible for
// all platform specific integrations.
@@ -55,10 +57,9 @@
OnCreateView on_create_view_callback,
OnUpdateView on_update_view_callback,
OnDestroyView on_destroy_view_callback,
- OnGetViewEmbedder on_get_view_embedder_callback,
- OnGetGrContext on_get_gr_context_callback,
- zx_handle_t vsync_event_handle,
- FlutterRunnerProductConfiguration product_config);
+ OnCreateSurface on_create_surface_callback,
+ fml::TimeDelta vsync_offset,
+ zx_handle_t vsync_event_handle);
~PlatformView();
@@ -87,8 +88,7 @@
OnCreateView on_create_view_callback_;
OnUpdateView on_update_view_callback_;
OnDestroyView on_destroy_view_callback_;
- OnGetViewEmbedder on_get_view_embedder_callback_;
- OnGetGrContext on_get_gr_context_callback_;
+ OnCreateSurface on_create_surface_callback_;
int current_text_input_client_ = 0;
fidl::Binding<fuchsia::ui::input::InputMethodEditorClient> ime_client_;
@@ -112,14 +112,14 @@
// such. Notifying via logs multiple times results in log-spam. See:
// https://github.com/flutter/flutter/issues/55966
std::set<std::string /* channel */> unregistered_channels_;
+
+ fml::TimeDelta vsync_offset_;
zx_handle_t vsync_event_handle_ = 0;
float view_width_ = 0.0f; // Width in logical pixels.
float view_height_ = 0.0f; // Height in logical pixels.
float view_pixel_ratio_ = 0.0f; // Logical / physical pixel ratio.
- FlutterRunnerProductConfiguration product_config_;
-
void RegisterPlatformMessageHandlers();
// |fuchsia::ui::input::InputMethodEditorClient|
@@ -134,9 +134,11 @@
void OnScenicError(std::string error) override;
void OnScenicEvent(std::vector<fuchsia::ui::scenic::Event> events) override;
+#if defined(LEGACY_FUCHSIA_EMBEDDER)
void OnChildViewConnected(scenic::ResourceId view_holder_id);
void OnChildViewDisconnected(scenic::ResourceId view_holder_id);
void OnChildViewStateChanged(scenic::ResourceId view_holder_id, bool state);
+#endif
bool OnHandlePointerEvent(const fuchsia::ui::input::PointerEvent& pointer);
diff --git a/shell/platform/fuchsia/flutter/platform_view_unittest.cc b/shell/platform/fuchsia/flutter/platform_view_unittest.cc
index 6d236e8..1810c4c 100644
--- a/shell/platform/fuchsia/flutter/platform_view_unittest.cc
+++ b/shell/platform/fuchsia/flutter/platform_view_unittest.cc
@@ -20,6 +20,7 @@
#include "flutter/lib/ui/window/window.h"
#include "gtest/gtest.h"
+#include "surface.h"
#include "task_runner_adapter.h"
namespace flutter_runner_test::flutter_runner_a11y_test {
@@ -116,10 +117,7 @@
bool SemanticsEnabled() const { return semantics_enabled_; }
int32_t SemanticsFeatures() const { return semantics_features_; }
- flutter::ExternalViewEmbedder* get_view_embedder() {
- return surface_->GetExternalViewEmbedder();
- }
- GrDirectContext* get_gr_context() { return surface_->GetContext(); }
+ flutter::Surface* surface() const { return surface_.get(); }
private:
std::unique_ptr<flutter::Surface> surface_;
@@ -162,18 +160,17 @@
std::move(view_ref), // view_ref
std::move(task_runners), // task_runners
services_provider.service_directory(), // runner_services
- nullptr, // parent_environment_service_provider_handle
- nullptr, // session_listener_request
- nullptr, // focuser,
- nullptr, // on_session_listener_error_callback
- nullptr, // on_enable_wireframe_callback,
- nullptr, // on_create_view_callback,
- nullptr, // on_update_view_callback,
- nullptr, // on_destroy_view_callback,
- nullptr, // on_get_view_embedder_callback,
- nullptr, // on_get_gr_context_callback,
- 0u, // vsync_event_handle
- {} // product_config
+ nullptr, // parent_environment_service_provider_handle
+ nullptr, // session_listener_request
+ nullptr, // focuser,
+ nullptr, // on_session_listener_error_callback
+ nullptr, // on_enable_wireframe_callback,
+ nullptr, // on_create_view_callback,
+ nullptr, // on_update_view_callback,
+ nullptr, // on_destroy_view_callback,
+ nullptr, // on_create_surface_callback,
+ fml::TimeDelta::Zero(), // vsync_offset
+ ZX_HANDLE_INVALID // vsync_event_handle
);
RunLoopUntilIdle();
@@ -227,10 +224,9 @@
nullptr, // on_create_view_callback,
nullptr, // on_update_view_callback,
nullptr, // on_destroy_view_callback,
- nullptr, // on_get_view_embedder_callback,
- nullptr, // on_get_gr_context_callback,
- 0u, // vsync_event_handle
- {} // product_config
+ nullptr, // on_create_surface_callback,
+ fml::TimeDelta::Zero(), // vsync_offset
+ ZX_HANDLE_INVALID // vsync_event_handle
);
// Cast platform_view to its base view so we can have access to the public
@@ -287,18 +283,17 @@
std::move(view_ref), // view_refs
std::move(task_runners), // task_runners
services_provider.service_directory(), // runner_services
- nullptr, // parent_environment_service_provider_handle
- nullptr, // session_listener_request
- nullptr, // focuser,
- nullptr, // on_session_listener_error_callback
- nullptr, // on_enable_wireframe_callback,
- CreateViewCallback, // on_create_view_callback,
- nullptr, // on_update_view_callback,
- nullptr, // on_destroy_view_callback,
- nullptr, // on_get_view_embedder_callback,
- nullptr, // on_get_gr_context_callback,
- 0u, // vsync_event_handle
- {} // product_config
+ nullptr, // parent_environment_service_provider_handle
+ nullptr, // session_listener_request
+ nullptr, // focuser,
+ nullptr, // on_session_listener_error_callback
+ nullptr, // on_enable_wireframe_callback,
+ CreateViewCallback, // on_create_view_callback,
+ nullptr, // on_update_view_callback,
+ nullptr, // on_destroy_view_callback,
+ nullptr, // on_create_surface_callback,
+ fml::TimeDelta::Zero(), // vsync_offset
+ ZX_HANDLE_INVALID // vsync_event_handle
);
// Cast platform_view to its base view so we can have access to the public
@@ -357,18 +352,17 @@
std::move(view_ref), // view_refs
std::move(task_runners), // task_runners
services_provider.service_directory(), // runner_services
- nullptr, // parent_environment_service_provider_handle
- nullptr, // session_listener_request
- nullptr, // focuser,
- nullptr, // on_session_listener_error_callback
- nullptr, // on_enable_wireframe_callback,
- nullptr, // on_create_view_callback,
- UpdateViewCallback, // on_update_view_callback,
- nullptr, // on_destroy_view_callback,
- nullptr, // on_get_view_embedder_callback,
- nullptr, // on_get_gr_context_callback,
- 0u, // vsync_event_handle
- {} // product_config
+ nullptr, // parent_environment_service_provider_handle
+ nullptr, // session_listener_request
+ nullptr, // focuser,
+ nullptr, // on_session_listener_error_callback
+ nullptr, // on_enable_wireframe_callback,
+ nullptr, // on_create_view_callback,
+ UpdateViewCallback, // on_update_view_callback,
+ nullptr, // on_destroy_view_callback,
+ nullptr, // on_create_surface_callback,
+ fml::TimeDelta::Zero(), // vsync_offset
+ ZX_HANDLE_INVALID // vsync_event_handle
);
// Cast platform_view to its base view so we can have access to the public
@@ -427,18 +421,17 @@
std::move(view_ref), // view_refs
std::move(task_runners), // task_runners
services_provider.service_directory(), // runner_services
- nullptr, // parent_environment_service_provider_handle
- nullptr, // session_listener_request
- nullptr, // focuser,
- nullptr, // on_session_listener_error_callback
- nullptr, // on_enable_wireframe_callback,
- nullptr, // on_create_view_callback,
- nullptr, // on_update_view_callback,
- DestroyViewCallback, // on_destroy_view_callback,
- nullptr, // on_get_view_embedder_callback,
- nullptr, // on_get_gr_context_callback,
- 0u, // vsync_event_handle
- {} // product_config
+ nullptr, // parent_environment_service_provider_handle
+ nullptr, // session_listener_request
+ nullptr, // focuser,
+ nullptr, // on_session_listener_error_callback
+ nullptr, // on_enable_wireframe_callback,
+ nullptr, // on_create_view_callback,
+ nullptr, // on_update_view_callback,
+ DestroyViewCallback, // on_destroy_view_callback,
+ nullptr, // on_create_surface_callback,
+ fml::TimeDelta::Zero(), // vsync_offset
+ ZX_HANDLE_INVALID // vsync_event_handle
);
// Cast platform_view to its base view so we can have access to the public
@@ -499,10 +492,9 @@
nullptr, // on_create_view_callback,
nullptr, // on_update_view_callback,
nullptr, // on_destroy_view_callback,
- nullptr, // on_get_gr_context_callback,
- nullptr, // on_get_view_embedder_callback,
- 0u, // vsync_event_handle
- {} // product_config
+ nullptr, // on_create_surface_callback,
+ fml::TimeDelta::Zero(), // vsync_offset
+ ZX_HANDLE_INVALID // vsync_event_handle
);
// Cast platform_view to its base view so we can have access to the public
@@ -534,8 +526,8 @@
}
// Test to make sure that PlatformView correctly returns a Surface instance
-// that can surface the view_embedder provided from GetViewEmbedderCallback.
-TEST_F(PlatformViewTests, GetViewEmbedderTest) {
+// that can surface the provided gr_context and view_embedder.
+TEST_F(PlatformViewTests, CreateSurfaceTest) {
sys::testing::ServiceDirectoryProvider services_provider(dispatcher());
MockPlatformViewDelegate delegate;
zx::eventpair a, b;
@@ -552,63 +544,13 @@
nullptr // io
);
- // Test get view embedder callback function.
- MockExternalViewEmbedder view_embedder;
- auto GetViewEmbedderCallback = [&view_embedder]() { return &view_embedder; };
-
- auto platform_view = flutter_runner::PlatformView(
- delegate, // delegate
- "test_platform_view", // label
- std::move(view_ref), // view_refs
- std::move(task_runners), // task_runners
- services_provider.service_directory(), // runner_services
- nullptr, // parent_environment_service_provider_handle
- nullptr, // session_listener_request
- nullptr, // focuser,
- nullptr, // on_session_listener_error_callback
- nullptr, // on_enable_wireframe_callback,
- nullptr, // on_create_view_callback,
- nullptr, // on_update_view_callback,
- nullptr, // on_destroy_view_callback,
- GetViewEmbedderCallback, // on_get_view_embedder_callback,
- nullptr, // on_get_gr_context_callback,
- 0u, // vsync_event_handle
- {} // product_config
- );
-
- RunLoopUntilIdle();
-
- platform_view.NotifyCreated();
-
- RunLoopUntilIdle();
-
- EXPECT_EQ(&view_embedder, delegate.get_view_embedder());
-}
-
-// Test to make sure that PlatformView correctly returns a Surface instance
-// that can surface the GrContext provided from GetGrContextCallback.
-TEST_F(PlatformViewTests, GetGrContextTest) {
- sys::testing::ServiceDirectoryProvider services_provider(dispatcher());
- MockPlatformViewDelegate delegate;
- zx::eventpair a, b;
- zx::eventpair::create(/* flags */ 0u, &a, &b);
- auto view_ref = fuchsia::ui::views::ViewRef({
- .reference = std::move(a),
- });
- flutter::TaskRunners task_runners =
- flutter::TaskRunners("test_runners", // label
- nullptr, // platform
- flutter_runner::CreateFMLTaskRunner(
- async_get_default_dispatcher()), // raster
- nullptr, // ui
- nullptr // io
- );
-
- // Test get GrContext callback function.
+ // Test create surface callback function.
sk_sp<GrDirectContext> gr_context =
GrDirectContext::MakeMock(nullptr, GrContextOptions());
- auto GetGrContextCallback = [gr_context = gr_context.get()]() {
- return gr_context;
+ MockExternalViewEmbedder view_embedder;
+ auto CreateSurfaceCallback = [&view_embedder, gr_context]() {
+ return std::make_unique<flutter_runner::Surface>(
+ "PlatformViewTest", &view_embedder, gr_context.get());
};
auto platform_view = flutter_runner::PlatformView(
@@ -617,27 +559,24 @@
std::move(view_ref), // view_refs
std::move(task_runners), // task_runners
services_provider.service_directory(), // runner_services
- nullptr, // parent_environment_service_provider_handle
- nullptr, // session_listener_request
- nullptr, // focuser
- nullptr, // on_session_listener_error_callback
- nullptr, // on_enable_wireframe_callback,
- nullptr, // on_create_view_callback,
- nullptr, // on_update_view_callback,
- nullptr, // on_destroy_view_callback,
- nullptr, // on_get_view_embedder_callback,
- GetGrContextCallback, // on_get_gr_context_callback,
- 0u, // vsync_event_handle
- {} // product_config
+ nullptr, // parent_environment_service_provider_handle
+ nullptr, // session_listener_request
+ nullptr, // focuser,
+ nullptr, // on_session_listener_error_callback
+ nullptr, // on_enable_wireframe_callback,
+ nullptr, // on_create_view_callback,
+ nullptr, // on_update_view_callback,
+ nullptr, // on_destroy_view_callback,
+ CreateSurfaceCallback, // on_create_surface_callback,
+ fml::TimeDelta::Zero(), // vsync_offset
+ ZX_HANDLE_INVALID // vsync_event_handle
);
-
- RunLoopUntilIdle();
-
platform_view.NotifyCreated();
RunLoopUntilIdle();
- EXPECT_EQ(gr_context.get(), delegate.get_gr_context());
+ EXPECT_EQ(gr_context.get(), delegate.surface()->GetContext());
+ EXPECT_EQ(&view_embedder, delegate.surface()->GetExternalViewEmbedder());
}
} // namespace flutter_runner_test::flutter_runner_a11y_test