diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index a1a2c18dccbd9d4b7b68c0e5ad7c9bd73d955372..09bc2ba6555c4a7ce3ce10607207418d704489fa 100755 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -1162,6 +1162,8 @@ FILE: ../../../flutter/shell/platform/fuchsia/flutter/engine.h 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 a5da4925a2eff9aa118b6654c0291b4025d630f0..d90c9968612432bc7439862ec5863d372182019d 100644 --- a/shell/platform/fuchsia/flutter/BUILD.gn +++ b/shell/platform/fuchsia/flutter/BUILD.gn @@ -53,12 +53,12 @@ template("runner_sources") { "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 @@ template("runner_sources") { "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 01069318208874bd28e5d7731ae8b2d80a1200a6..f7de2e9990bbec84430e323e84bdbeefff5b376b 100644 --- a/shell/platform/fuchsia/flutter/component.cc +++ b/shell/platform/fuchsia/flutter/component.cc @@ -54,6 +54,7 @@ constexpr char kDataKey[] = "data"; 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 @@ Application::Application( } } - // 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 fe4dff020a8c63a5e5147573adfabb883223c800..3d8d53cddc6e94fa2033334edb233dc3c35568b6 100644 --- a/shell/platform/fuchsia/flutter/engine.cc +++ b/shell/platform/fuchsia/flutter/engine.cc @@ -8,7 +8,6 @@ #include #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 @@ Engine::Engine(Delegate& delegate, 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 @@ Engine::Engine(Delegate& delegate, 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 @@ Engine::Engine(Delegate& delegate, 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 @@ Engine::Engine(Delegate& delegate, 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( shell, // delegate debug_label, // debug label @@ -206,27 +219,35 @@ Engine::Engine(Delegate& delegate, 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 on_create_rasterizer = - fml::MakeCopyable([this](flutter::Shell& shell) mutable { - FML_DCHECK(session_connection_); - FML_DCHECK(surface_producer_); - FML_DCHECK(scene_update_context_); - - std::unique_ptr compositor_context = - std::make_unique( - session_connection_.value(), surface_producer_.value(), - scene_update_context_.value()); - - return std::make_unique( - shell, std::move(compositor_context)); - }); + flutter::Shell::CreateCallback 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_); + + auto compositor_context = + std::make_unique( + session_connection_.value(), surface_producer_.value(), + legacy_external_view_embedder_.value()); + return std::make_unique( + shell, std::move(compositor_context)); + } else { + return std::make_unique(shell); + } + }; +#else + on_create_rasterizer = [](flutter::Shell& shell) { + return std::make_unique(shell); + }; +#endif settings.root_isolate_create_callback = std::bind(&Engine::OnMainIsolateStart, this); @@ -479,59 +500,97 @@ void Engine::Terminate() { } void Engine::DebugWireframeSettingsChanged(bool enabled) { - if (!shell_ || !scene_update_context_) { - return; - } - - shell_->GetTaskRunners().GetRasterTaskRunner()->PostTask( - [this, enabled]() { scene_update_context_->EnableWireframe(enabled); }); + FML_CHECK(shell_); + + 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; - } - - shell_->GetTaskRunners().GetRasterTaskRunner()->PostTask( - [this, view_id]() { scene_update_context_->DestroyView(view_id); }); + FML_CHECK(shell_); + + 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; - } - - return &scene_update_context_.value(); -} +std::unique_ptr Engine::CreateSurface() { + flutter::ExternalViewEmbedder* external_view_embedder = nullptr; -GrDirectContext* Engine::GetGrContext() { - // GetGrContext should be called only after rasterizer is created. - FML_DCHECK(shell_); - FML_DCHECK(shell_->GetRasterizer()); +#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 surface_producer_->gr_context(); + return std::make_unique(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 410ff63151b7aa5b93a89d6d8dc143d96f58c0f9..20e14e849fc17513a210886c062f12358e945882 100644 --- a/shell/platform/fuchsia/flutter/engine.h +++ b/shell/platform/fuchsia/flutter/engine.h @@ -14,17 +14,21 @@ #include #include -#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 @@ class Engine final { std::optional session_connection_; std::optional surface_producer_; - std::optional scene_update_context_; + std::optional external_view_embedder_; +#if defined(LEGACY_FUCHSIA_EMBEDDER) + std::optional legacy_external_view_embedder_; +#endif std::unique_ptr isolate_configurator_; std::unique_ptr shell_; @@ -74,6 +81,10 @@ class Engine final { zx::event vsync_event_; +#if defined(LEGACY_FUCHSIA_EMBEDDER) + bool use_legacy_renderer_ = true; +#endif + fml::WeakPtrFactory weak_factory_; void OnMainIsolateStart(); @@ -87,9 +98,7 @@ class Engine final { 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 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 af7af50497618b5ecf0ee85b8bde91a511399c57..3406faa6c0c73d91ab5de99489766ed9dc920cef 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 @@ FlutterRunnerProductConfiguration::FlutterRunnerProductConfiguration( 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 29958f557e3bf76dfdac14b8027d87d17b66442e..57313d7a58c8359670c84d6317f5f47c90c360ad 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 @@ class FlutterRunnerProductConfiguration { 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 0000000000000000000000000000000000000000..96fd028d4dd9927f5425977856537db8f837d4ee --- /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 +#include +#include + +#include // 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 FuchsiaExternalViewEmbedder::GetCurrentCanvases() { + std::vector 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 params) { + zx_handle_t handle = static_cast(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(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 raster_thread_merger) { + return flutter::PostPrerollResult::kSuccess; +} + +void FuchsiaExternalViewEmbedder::BeginFrame( + SkISize frame_size, + GrDirectContext* context, + double device_pixel_ratio, + fml::RefPtr 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 raster_thread_merger) { + TRACE_EVENT0("flutter", "FuchsiaExternalViewEmbedder::EndFrame"); +} + +void FuchsiaExternalViewEmbedder::SubmitFrame( + GrDirectContext* context, + std::unique_ptr frame) { + TRACE_EVENT0("flutter", "FuchsiaExternalViewEmbedder::SubmitFrame"); + + std::vector> frame_surfaces; + std::unordered_map 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 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(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())); + 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 picture = + layer->second.recorder->finishRecordingAsPicture(); + FML_DCHECK(picture); + + sk_sp 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 0000000000000000000000000000000000000000..f299a2f91f62f5518c6f10f600adb28d4a8c1e73 --- /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 +#include +#include + +#include // For uint32_t & uint64_t +#include +#include +#include +#include + +#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 GetCurrentCanvases() override; + + // |ExternalViewEmbedder| + void PrerollCompositeEmbeddedView( + int view_id, + std::unique_ptr params) override; + + // |ExternalViewEmbedder| + SkCanvas* CompositeEmbeddedView(int view_id) override; + + // |ExternalViewEmbedder| + flutter::PostPrerollResult PostPrerollAction( + fml::RefPtr raster_thread_merger) override; + + // |ExternalViewEmbedder| + void BeginFrame( + SkISize frame_size, + GrDirectContext* context, + double device_pixel_ratio, + fml::RefPtr raster_thread_merger) override; + + // |ExternalViewEmbedder| + void EndFrame( + bool should_resubmit_frame, + fml::RefPtr raster_thread_merger) override; + + // |ExternalViewEmbedder| + void SubmitFrame(GrDirectContext* context, + std::unique_ptr 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 view_params) + : embedded_view_params(std::move(view_params)), + recorder(std::make_unique()), + canvas_spy(std::make_unique( + recorder->beginRecording(frame_size.width(), + frame_size.height()))), + surface_size(frame_size) {} + + std::optional embedded_view_params; + std::unique_ptr recorder; + std::unique_ptr 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; + 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> scenic_rects_; + std::unordered_map scenic_views_; + std::vector scenic_layers_; + + std::unordered_map frame_layers_; + std::vector 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 5359c1e809cebcca57bf578c298710de2d2c6dc7..0d289f5efbfefbbc3abc97483f68279c9211ad16 100644 --- a/shell/platform/fuchsia/flutter/platform_view.cc +++ b/shell/platform/fuchsia/flutter/platform_view.cc @@ -10,10 +10,8 @@ #include #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 @@ PlatformView::PlatformView( 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 @@ PlatformView::PlatformView( 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 @@ void PlatformView::OnScenicEvent( } 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 @@ void PlatformView::OnScenicEvent( 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 @@ void PlatformView::OnScenicEvent( } } +#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 @@ void PlatformView::OnChildViewStateChanged(scenic::ResourceId view_holder_id, flutter::SceneHost::OnViewStateChanged(view_holder_id, state); }); } +#endif static flutter::PointerData::Change GetChangeFromPointerEventPhase( fuchsia::ui::input::PointerEventPhase phase) { @@ -513,21 +517,12 @@ void PlatformView::DeactivateIme() { // |flutter::PlatformView| std::unique_ptr PlatformView::CreateVSyncWaiter() { return std::make_unique( - 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 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(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 35275b3b84c033d6dbb22e09d854236d60dbef30..431272b774c6df42a2dc8b702b721607887ec16c 100644 --- a/shell/platform/fuchsia/flutter/platform_view.h +++ b/shell/platform/fuchsia/flutter/platform_view.h @@ -7,18 +7,21 @@ #include #include +#include #include #include #include #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 // nogncheck +#endif namespace flutter_runner { @@ -26,8 +29,7 @@ using OnEnableWireframe = fit::function; using OnCreateView = fit::function; using OnUpdateView = fit::function; using OnDestroyView = fit::function; -using OnGetViewEmbedder = fit::function; -using OnGetGrContext = fit::function; +using OnCreateSurface = fit::function()>; // The per engine component residing on the platform thread is responsible for // all platform specific integrations. @@ -55,10 +57,9 @@ class PlatformView final : public flutter::PlatformView, 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 @@ class PlatformView final : public flutter::PlatformView, 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 ime_client_; @@ -112,14 +112,14 @@ class PlatformView final : public flutter::PlatformView, // such. Notifying via logs multiple times results in log-spam. See: // https://github.com/flutter/flutter/issues/55966 std::set 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 @@ class PlatformView final : public flutter::PlatformView, void OnScenicError(std::string error) override; void OnScenicEvent(std::vector 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 6d236e8c8a371343b154d61e158f3e668b990de0..1810c4cacac01bbeffa49daa2e9021e486111cb2 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 @@ class MockPlatformViewDelegate : public flutter::PlatformView::Delegate { 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 surface_; @@ -162,18 +160,17 @@ TEST_F(PlatformViewTests, ChangesAccessibilitySettings) { 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 @@ TEST_F(PlatformViewTests, EnableWireframeTest) { 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 @@ TEST_F(PlatformViewTests, CreateViewTest) { 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 @@ TEST_F(PlatformViewTests, UpdateViewTest) { 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 @@ TEST_F(PlatformViewTests, DestroyViewTest) { 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 @@ TEST_F(PlatformViewTests, RequestFocusTest) { 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_F(PlatformViewTests, RequestFocusTest) { } // 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 @@ TEST_F(PlatformViewTests, GetViewEmbedderTest) { 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 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( + "PlatformViewTest", &view_embedder, gr_context.get()); }; auto platform_view = flutter_runner::PlatformView( @@ -617,27 +559,24 @@ TEST_F(PlatformViewTests, GetGrContextTest) { 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