From c1e33b58a5cd28eb89b5d369bf401ef0a564a122 Mon Sep 17 00:00:00 2001 From: David Worsham Date: Thu, 2 Dec 2021 12:09:51 -0800 Subject: [PATCH] fuchsia: Add a SoftwareSurfaceProducer for debug (flutter/engine#29657) --- .../ci/licenses_golden/licenses_flutter | 4 + .../shell/platform/fuchsia/flutter/BUILD.gn | 4 + .../shell/platform/fuchsia/flutter/engine.cc | 102 +++-- .../shell/platform/fuchsia/flutter/engine.h | 9 +- .../flatland_external_view_embedder.cc | 69 +-- .../flutter/flatland_external_view_embedder.h | 9 +- .../flutter_runner_product_configuration.cc | 21 +- .../flutter_runner_product_configuration.h | 6 +- .../flutter/gfx_external_view_embedder.cc | 40 +- .../flutter/gfx_external_view_embedder.h | 8 +- .../fuchsia/flutter/software_surface.cc | 428 ++++++++++++++++++ .../fuchsia/flutter/software_surface.h | 127 ++++++ .../flutter/software_surface_producer.cc | 245 ++++++++++ .../flutter/software_surface_producer.h | 77 ++++ .../fuchsia/flutter/surface_producer.h | 5 + .../fuchsia/flutter/tests/engine_unittests.cc | 23 +- ...atland_external_view_embedder_unittests.cc | 36 +- .../gfx_external_view_embedder_unittests.cc | 60 ++- .../flutter/vulkan_surface_producer.cc | 1 - .../fuchsia/flutter/vulkan_surface_producer.h | 12 +- 20 files changed, 1126 insertions(+), 160 deletions(-) create mode 100644 engine/src/flutter/shell/platform/fuchsia/flutter/software_surface.cc create mode 100644 engine/src/flutter/shell/platform/fuchsia/flutter/software_surface.h create mode 100644 engine/src/flutter/shell/platform/fuchsia/flutter/software_surface_producer.cc create mode 100644 engine/src/flutter/shell/platform/fuchsia/flutter/software_surface_producer.h diff --git a/engine/src/flutter/ci/licenses_golden/licenses_flutter b/engine/src/flutter/ci/licenses_golden/licenses_flutter index 4308d1df37..9016692375 100755 --- a/engine/src/flutter/ci/licenses_golden/licenses_flutter +++ b/engine/src/flutter/ci/licenses_golden/licenses_flutter @@ -1504,6 +1504,10 @@ FILE: ../../../flutter/shell/platform/fuchsia/flutter/runner.cc FILE: ../../../flutter/shell/platform/fuchsia/flutter/runner.h FILE: ../../../flutter/shell/platform/fuchsia/flutter/runner_tzdata_unittest.cc FILE: ../../../flutter/shell/platform/fuchsia/flutter/runner_unittest.cc +FILE: ../../../flutter/shell/platform/fuchsia/flutter/software_surface.cc +FILE: ../../../flutter/shell/platform/fuchsia/flutter/software_surface.h +FILE: ../../../flutter/shell/platform/fuchsia/flutter/software_surface_producer.cc +FILE: ../../../flutter/shell/platform/fuchsia/flutter/software_surface_producer.h FILE: ../../../flutter/shell/platform/fuchsia/flutter/surface.cc FILE: ../../../flutter/shell/platform/fuchsia/flutter/surface.h FILE: ../../../flutter/shell/platform/fuchsia/flutter/surface_producer.h diff --git a/engine/src/flutter/shell/platform/fuchsia/flutter/BUILD.gn b/engine/src/flutter/shell/platform/fuchsia/flutter/BUILD.gn index ea2de3f2c4..9480c64335 100644 --- a/engine/src/flutter/shell/platform/fuchsia/flutter/BUILD.gn +++ b/engine/src/flutter/shell/platform/fuchsia/flutter/BUILD.gn @@ -95,6 +95,10 @@ template("runner_sources") { "program_metadata.h", "runner.cc", "runner.h", + "software_surface.cc", + "software_surface.h", + "software_surface_producer.cc", + "software_surface_producer.h", "surface.cc", "surface.h", "surface_producer.h", diff --git a/engine/src/flutter/shell/platform/fuchsia/flutter/engine.cc b/engine/src/flutter/shell/platform/fuchsia/flutter/engine.cc index 5fd6bac268..011df3aa4b 100644 --- a/engine/src/flutter/shell/platform/fuchsia/flutter/engine.cc +++ b/engine/src/flutter/shell/platform/fuchsia/flutter/engine.cc @@ -31,8 +31,10 @@ #include "focus_delegate.h" #include "fuchsia_intl.h" #include "gfx_platform_view.h" +#include "software_surface_producer.h" #include "surface.h" #include "vsync_waiter.h" +#include "vulkan_surface_producer.h" namespace flutter_runner { namespace { @@ -252,13 +254,22 @@ void Engine::Initialize( request = parent_viewport_watcher.NewRequest(), view_ref_pair = std::move(view_ref_pair), max_frames_in_flight = product_config.get_max_frames_in_flight(), - vsync_offset = product_config.get_vsync_offset()]() mutable { + vsync_offset = product_config.get_vsync_offset(), + software_rendering = product_config.software_rendering()]() mutable { if (use_flatland) { + if (software_rendering) { + surface_producer_ = std::make_shared( + /*scenic_session=*/nullptr); + } else { + surface_producer_ = std::make_shared( + /*scenic_session=*/nullptr); + } + flatland_connection_ = std::make_shared( thread_label_, std::move(flatland), std::move(session_error_callback), [](auto) {}, max_frames_in_flight, vsync_offset); - surface_producer_.emplace(/*scenic_session=*/nullptr); + fuchsia::ui::views::ViewIdentityOnCreation view_identity = { .view_ref = std::move(view_ref_pair.view_ref), .view_ref_control = std::move(view_ref_pair.control_ref)}; @@ -266,18 +277,25 @@ void Engine::Initialize( std::make_shared( std::move(view_creation_token), std::move(view_identity), std::move(flatland_view_protocols), std::move(request), - *flatland_connection_.get(), surface_producer_.value(), + flatland_connection_, surface_producer_, intercept_all_input_); } else { session_connection_ = std::make_shared( thread_label_, std::move(session_inspect_node), std::move(session), std::move(session_error_callback), [](auto) {}, max_frames_in_flight, vsync_offset); - surface_producer_.emplace(session_connection_->get()); + + if (software_rendering) { + surface_producer_ = std::make_shared( + session_connection_->get()); + } else { + surface_producer_ = std::make_shared( + session_connection_->get()); + } + external_view_embedder_ = std::make_shared( thread_label_, std::move(view_token), std::move(view_ref_pair), - *session_connection_.get(), surface_producer_.value(), - intercept_all_input_); + session_connection_, surface_producer_, intercept_all_input_); } view_embedder_latch.Signal(); })); @@ -432,7 +450,7 @@ void Engine::Initialize( ->GetTaskRunner() .get(), shell.GetTaskRunners().GetRasterTaskRunner().get(), - surface_producer_.value(), width, height, + surface_producer_, SkISize::Make(width, height), flutter::PersistentCache::GetCacheForProcess() ->asset_manager(), skp_names, completion_callback); @@ -443,7 +461,7 @@ void Engine::Initialize( ->GetTaskRunner() .get(), shell.GetTaskRunners().GetRasterTaskRunner().get(), - surface_producer_.value(), 1024, 600, + surface_producer_, SkISize::Make(1024, 600), flutter::PersistentCache::GetCacheForProcess() ->asset_manager(), std::nullopt, std::nullopt); @@ -658,12 +676,15 @@ Engine::~Engine() { fml::AutoResetWaitableEvent view_embedder_latch; thread_host_.raster_thread->GetTaskRunner()->PostTask( fml::MakeCopyable([this, &view_embedder_latch]() mutable { - flatland_view_embedder_.reset(); - external_view_embedder_.reset(); - surface_producer_.reset(); - flatland_connection_.reset(); - session_connection_.reset(); - + if (flatland_view_embedder_ != nullptr) { + flatland_view_embedder_.reset(); + flatland_connection_.reset(); + surface_producer_.reset(); + } else { + external_view_embedder_.reset(); + surface_producer_.reset(); + session_connection_.reset(); + } view_embedder_latch.Signal(); })); view_embedder_latch.Wait(); @@ -835,19 +856,16 @@ void Engine::WriteProfileToTrace() const { void Engine::WarmupSkps( fml::BasicTaskRunner* concurrent_task_runner, fml::BasicTaskRunner* raster_task_runner, - VulkanSurfaceProducer& surface_producer, - uint64_t width, - uint64_t height, + std::shared_ptr surface_producer, + SkISize size, std::shared_ptr asset_manager, std::optional> skp_names, std::optional> completion_callback) { - SkISize size = SkISize::Make(width, height); - // We use a raw pointer here because we want to keep this alive until all gpu // work is done and the callbacks skia takes for this are function pointers // so we are unable to use a lambda that captures the smart pointer. SurfaceProducerSurface* skp_warmup_surface = - surface_producer.ProduceOffscreenSurface(size).release(); + surface_producer->ProduceOffscreenSurface(size).release(); if (!skp_warmup_surface) { FML_LOG(ERROR) << "Failed to create offscreen warmup surface"; // Tell client that zero shaders were warmed up because warmup failed. @@ -860,7 +878,7 @@ void Engine::WarmupSkps( // tell concurrent task runner to deserialize all skps available from // the asset manager concurrent_task_runner->PostTask([raster_task_runner, skp_warmup_surface, - &surface_producer, asset_manager, skp_names, + surface_producer, asset_manager, skp_names, completion_callback]() { TRACE_DURATION("flutter", "DeserializeSkps"); std::vector> skp_mappings; @@ -903,27 +921,13 @@ void Engine::WarmupSkps( // Tell raster task runner to warmup have the compositor // context warm up the newly deserialized picture raster_task_runner->PostTask([skp_warmup_surface, picture, - &surface_producer, completion_callback, i, + surface_producer, completion_callback, i, count = skp_mappings.size()] { TRACE_DURATION("flutter", "WarmupSkp"); skp_warmup_surface->GetSkiaSurface()->getCanvas()->drawPicture(picture); - if (i < count - 1) { - // For all but the last skp we fire and forget - surface_producer.gr_context()->flushAndSubmit(); - } else { - // For the last skp we provide a callback that frees the warmup - // surface and calls the completion callback - struct GrFlushInfo flush_info; - flush_info.fFinishedContext = skp_warmup_surface; - flush_info.fFinishedProc = [](void* skp_warmup_surface) { - delete static_cast(skp_warmup_surface); - }; - - surface_producer.gr_context()->flush(flush_info); - surface_producer.gr_context()->submit(); - - // We call this here instead of inside fFinishedProc above because + if (i == count - 1) { + // We call this here instead of inside fFinishedProc below because // we want to unblock the dart animation code as soon as the raster // thread is free to enque work, rather than waiting for the GPU work // itself to finish. @@ -931,6 +935,28 @@ void Engine::WarmupSkps( completion_callback.value()(count); } } + + if (surface_producer->gr_context()) { + if (i < count - 1) { + // For all but the last skp we fire and forget + surface_producer->gr_context()->flushAndSubmit(); + } else { + // For the last skp we provide a callback that frees the warmup + // surface and calls the completion callback + struct GrFlushInfo flush_info; + flush_info.fFinishedContext = skp_warmup_surface; + flush_info.fFinishedProc = [](void* skp_warmup_surface) { + delete static_cast(skp_warmup_surface); + }; + + surface_producer->gr_context()->flush(flush_info); + surface_producer->gr_context()->submit(); + } + } else { + if (i == count - 1) { + delete skp_warmup_surface; + } + } }); i++; } diff --git a/engine/src/flutter/shell/platform/fuchsia/flutter/engine.h b/engine/src/flutter/shell/platform/fuchsia/flutter/engine.h index 7f49589ebf..76e4caa16c 100644 --- a/engine/src/flutter/shell/platform/fuchsia/flutter/engine.h +++ b/engine/src/flutter/shell/platform/fuchsia/flutter/engine.h @@ -31,7 +31,7 @@ #include "gfx_external_view_embedder.h" #include "gfx_session_connection.h" #include "isolate_configurator.h" -#include "vulkan_surface_producer.h" +#include "surface_producer.h" namespace flutter_runner { @@ -104,9 +104,8 @@ class Engine final : public fuchsia::memorypressure::Watcher { static void WarmupSkps( fml::BasicTaskRunner* concurrent_task_runner, fml::BasicTaskRunner* raster_task_runner, - VulkanSurfaceProducer& surface_producer, - uint64_t width, - uint64_t height, + std::shared_ptr surface_producer, + SkISize size, std::shared_ptr asset_manager, std::optional> skp_names, std::optional> completion_callback); @@ -157,7 +156,7 @@ class Engine final : public fuchsia::memorypressure::Watcher { session_connection_; // Must come before surface_producer_ std::shared_ptr flatland_connection_; // Must come before surface_producer_ - std::optional surface_producer_; + std::shared_ptr surface_producer_; std::shared_ptr external_view_embedder_; std::shared_ptr flatland_view_embedder_; diff --git a/engine/src/flutter/shell/platform/fuchsia/flutter/flatland_external_view_embedder.cc b/engine/src/flutter/shell/platform/fuchsia/flutter/flatland_external_view_embedder.cc index 91e7d7a816..b250ffe8cf 100644 --- a/engine/src/flutter/shell/platform/fuchsia/flutter/flatland_external_view_embedder.cc +++ b/engine/src/flutter/shell/platform/fuchsia/flutter/flatland_external_view_embedder.cc @@ -17,17 +17,17 @@ FlatlandExternalViewEmbedder::FlatlandExternalViewEmbedder( fuchsia::ui::composition::ViewBoundProtocols view_protocols, fidl::InterfaceRequest parent_viewport_watcher_request, - FlatlandConnection& flatland, - SurfaceProducer& surface_producer, + std::shared_ptr flatland, + std::shared_ptr surface_producer, bool intercept_all_input) : flatland_(flatland), surface_producer_(surface_producer) { - flatland_.flatland()->CreateView2( + flatland_->flatland()->CreateView2( std::move(view_creation_token), std::move(view_identity), std::move(view_protocols), std::move(parent_viewport_watcher_request)); - root_transform_id_ = flatland_.NextTransformId(); - flatland_.flatland()->CreateTransform(root_transform_id_); - flatland_.flatland()->SetRootTransform(root_transform_id_); + root_transform_id_ = flatland_->NextTransformId(); + flatland_->flatland()->CreateTransform(root_transform_id_); + flatland_->flatland()->SetRootTransform(root_transform_id_); } FlatlandExternalViewEmbedder::~FlatlandExternalViewEmbedder() = default; @@ -122,7 +122,7 @@ void FlatlandExternalViewEmbedder::SubmitFrame( } auto surface = - surface_producer_.ProduceSurface(layer.second.surface_size); + surface_producer_->ProduceSurface(layer.second.surface_size); if (!surface) { const std::string layer_id_str = layer.first.has_value() ? std::to_string(layer.first.value()) @@ -137,24 +137,24 @@ void FlatlandExternalViewEmbedder::SubmitFrame( // If we receive an unitialized surface, we need to first create flatland // resource. if (surface->GetImageId() == 0) { - auto image_id = flatland_.NextContentId().value; + auto image_id = flatland_->NextContentId().value; const auto& size = surface->GetSize(); fuchsia::ui::composition::ImageProperties image_properties; image_properties.set_size({static_cast(size.width()), static_cast(size.height())}); - flatland_.flatland()->CreateImage( + flatland_->flatland()->CreateImage( {image_id}, surface->GetBufferCollectionImportToken(), 0, std::move(image_properties)); surface->SetImageId(image_id); - surface->SetReleaseImageCallback([flatland = &flatland_, image_id]() { + surface->SetReleaseImageCallback([flatland = flatland_, image_id]() { flatland->flatland()->ReleaseImage({image_id}); }); } // Enqueue fences for the next present. - flatland_.EnqueueAcquireFence(surface->GetAcquireFence()); - flatland_.EnqueueReleaseFence(surface->GetReleaseFence()); + flatland_->EnqueueAcquireFence(surface->GetAcquireFence()); + flatland_->EnqueueReleaseFence(surface->GetReleaseFence()); frame_surface_indices.emplace( std::make_pair(layer.first, frame_surfaces.size())); @@ -193,7 +193,7 @@ void FlatlandExternalViewEmbedder::SubmitFrame( // Set transform for the viewport. // TODO(fxbug.dev/64201): Handle scaling. if (view_mutators.transform != viewport.mutators.transform) { - flatland_.flatland()->SetTranslation( + flatland_->flatland()->SetTranslation( viewport.transform_id, {static_cast(view_mutators.transform.getTranslateX()), static_cast(view_mutators.transform.getTranslateY())}); @@ -210,14 +210,14 @@ void FlatlandExternalViewEmbedder::SubmitFrame( properties.set_logical_size( {static_cast(view_size.fWidth), static_cast(view_size.fHeight)}); - flatland_.flatland()->SetViewportProperties(viewport.viewport_id, - std::move(properties)); + flatland_->flatland()->SetViewportProperties(viewport.viewport_id, + std::move(properties)); viewport.size = view_size; } // Attach the FlatlandView to the main scene graph. - flatland_.flatland()->AddChild(root_transform_id_, - viewport.transform_id); + flatland_->flatland()->AddChild(root_transform_id_, + viewport.transform_id); child_transforms_.emplace_back(viewport.transform_id); } @@ -244,22 +244,22 @@ void FlatlandExternalViewEmbedder::SubmitFrame( // Create a new layer if needed for the surface. FML_CHECK(flatland_layer_index <= flatland_layers_.size()); if (flatland_layer_index == flatland_layers_.size()) { - FlatlandLayer new_layer{.transform_id = flatland_.NextTransformId()}; - flatland_.flatland()->CreateTransform(new_layer.transform_id); + FlatlandLayer new_layer{.transform_id = flatland_->NextTransformId()}; + flatland_->flatland()->CreateTransform(new_layer.transform_id); flatland_layers_.emplace_back(std::move(new_layer)); } // Update the image content and set size. - flatland_.flatland()->SetContent( + flatland_->flatland()->SetContent( flatland_layers_[flatland_layer_index].transform_id, {surface_for_layer->GetImageId()}); - flatland_.flatland()->SetImageDestinationSize( + flatland_->flatland()->SetImageDestinationSize( {surface_for_layer->GetImageId()}, {static_cast(surface_for_layer->GetSize().width()), static_cast(surface_for_layer->GetSize().height())}); // Attach the FlatlandLayer to the main scene graph. - flatland_.flatland()->AddChild( + flatland_->flatland()->AddChild( root_transform_id_, flatland_layers_[flatland_layer_index].transform_id); child_transforms_.emplace_back( @@ -276,7 +276,7 @@ void FlatlandExternalViewEmbedder::SubmitFrame( { TRACE_EVENT0("flutter", "SessionPresent"); - flatland_.Present(); + flatland_->Present(); } // Render the recorded SkPictures into the surfaces. @@ -315,7 +315,7 @@ void FlatlandExternalViewEmbedder::SubmitFrame( { TRACE_EVENT0("flutter", "PresentSurfaces"); - surface_producer_.SubmitSurfaces(std::move(frame_surfaces)); + surface_producer_->SubmitSurfaces(std::move(frame_surfaces)); } // Submit the underlying render-backend-specific frame for processing. @@ -328,19 +328,20 @@ void FlatlandExternalViewEmbedder::CreateView( FlatlandViewCreatedCallback on_view_bound) { FML_CHECK(flatland_views_.find(view_id) == flatland_views_.end()); - FlatlandView new_view = {.transform_id = flatland_.NextTransformId(), - .viewport_id = flatland_.NextContentId()}; - flatland_.flatland()->CreateTransform(new_view.transform_id); + FlatlandView new_view = {.transform_id = flatland_->NextTransformId(), + .viewport_id = flatland_->NextContentId()}; + flatland_->flatland()->CreateTransform(new_view.transform_id); fuchsia::ui::composition::ViewportProperties properties; // TODO(fxbug.dev/64201): Investigate if it is possible to avoid using a // default size by finding the size before creation. properties.set_logical_size( {kFlatlandDefaultViewportSize, kFlatlandDefaultViewportSize}); fuchsia::ui::composition::ChildViewWatcherPtr child_view_watcher; - flatland_.flatland()->CreateViewport( + flatland_->flatland()->CreateViewport( new_view.viewport_id, {zx::channel((zx_handle_t)view_id)}, std::move(properties), child_view_watcher.NewRequest()); - flatland_.flatland()->SetContent(new_view.transform_id, new_view.viewport_id); + flatland_->flatland()->SetContent(new_view.transform_id, + new_view.viewport_id); on_view_created(); on_view_bound(new_view.viewport_id, std::move(child_view_watcher)); @@ -355,17 +356,17 @@ void FlatlandExternalViewEmbedder::DestroyView( auto viewport_id = flatland_view->second.viewport_id; auto transform_id = flatland_view->second.transform_id; - flatland_.flatland()->ReleaseViewport(viewport_id, [](auto) {}); + flatland_->flatland()->ReleaseViewport(viewport_id, [](auto) {}); auto itr = std::find_if(child_transforms_.begin(), child_transforms_.end(), [transform_id](fuchsia::ui::composition::TransformId id) { return id.value == transform_id.value; }); if (itr != child_transforms_.end()) { - flatland_.flatland()->RemoveChild(root_transform_id_, transform_id); + flatland_->flatland()->RemoveChild(root_transform_id_, transform_id); child_transforms_.erase(itr); } - flatland_.flatland()->ReleaseTransform(transform_id); + flatland_->flatland()->ReleaseTransform(transform_id); flatland_views_.erase(flatland_view); on_view_unbound(viewport_id); @@ -389,13 +390,13 @@ void FlatlandExternalViewEmbedder::Reset() { // Clear all children from root. for (const auto& transform : child_transforms_) { - flatland_.flatland()->RemoveChild(root_transform_id_, transform); + flatland_->flatland()->RemoveChild(root_transform_id_, transform); } child_transforms_.clear(); // Clear images on all layers so they aren't cached unnecessarily. for (const auto& layer : flatland_layers_) { - flatland_.flatland()->SetContent(layer.transform_id, {0}); + flatland_->flatland()->SetContent(layer.transform_id, {0}); } } diff --git a/engine/src/flutter/shell/platform/fuchsia/flutter/flatland_external_view_embedder.h b/engine/src/flutter/shell/platform/fuchsia/flutter/flatland_external_view_embedder.h index c8017a1d24..960191f5d2 100644 --- a/engine/src/flutter/shell/platform/fuchsia/flutter/flatland_external_view_embedder.h +++ b/engine/src/flutter/shell/platform/fuchsia/flutter/flatland_external_view_embedder.h @@ -52,8 +52,8 @@ class FlatlandExternalViewEmbedder final fuchsia::ui::composition::ViewBoundProtocols endpoints, fidl::InterfaceRequest parent_viewport_watcher_request, - FlatlandConnection& flatland, - SurfaceProducer& surface_producer, + std::shared_ptr flatland, + std::shared_ptr surface_producer, bool intercept_all_input = false); ~FlatlandExternalViewEmbedder(); @@ -168,8 +168,9 @@ class FlatlandExternalViewEmbedder final fuchsia::ui::composition::TransformId transform_id; }; - FlatlandConnection& flatland_; - SurfaceProducer& surface_producer_; + std::shared_ptr flatland_; + std::shared_ptr surface_producer_; + fuchsia::ui::composition::ParentViewportWatcherPtr parent_viewport_watcher_; fuchsia::ui::composition::TransformId root_transform_id_; diff --git a/engine/src/flutter/shell/platform/fuchsia/flutter/flutter_runner_product_configuration.cc b/engine/src/flutter/shell/platform/fuchsia/flutter/flutter_runner_product_configuration.cc index e4af767231..f0137c355d 100644 --- a/engine/src/flutter/shell/platform/fuchsia/flutter/flutter_runner_product_configuration.cc +++ b/engine/src/flutter/shell/platform/fuchsia/flutter/flutter_runner_product_configuration.cc @@ -25,28 +25,39 @@ FlutterRunnerProductConfiguration::FlutterRunnerProductConfiguration( // Parse out all values we're expecting. if (document.HasMember("vsync_offset_in_us")) { auto& val = document["vsync_offset_in_us"]; - if (val.IsInt()) + if (val.IsInt()) { vsync_offset_ = fml::TimeDelta::FromMicroseconds(val.GetInt()); + } } if (document.HasMember("max_frames_in_flight")) { auto& val = document["max_frames_in_flight"]; - if (val.IsInt()) + if (val.IsInt()) { max_frames_in_flight_ = val.GetInt(); + } } if (document.HasMember("intercept_all_input")) { auto& val = document["intercept_all_input"]; - if (val.IsBool()) + if (val.IsBool()) { intercept_all_input_ = val.GetBool(); + } + } + if (document.HasMember("software_rendering")) { + auto& val = document["software_rendering"]; + if (val.IsBool()) { + software_rendering_ = val.GetBool(); + } } if (document.HasMember("enable_shader_warmup")) { auto& val = document["enable_shader_warmup"]; - if (val.IsBool()) + if (val.IsBool()) { enable_shader_warmup_ = val.GetBool(); + } } if (document.HasMember("enable_shader_warmup_dart_hooks")) { auto& val = document["enable_shader_warmup_dart_hooks"]; - if (val.IsBool()) + if (val.IsBool()) { enable_shader_warmup_dart_hooks_ = val.GetBool(); + } } } diff --git a/engine/src/flutter/shell/platform/fuchsia/flutter/flutter_runner_product_configuration.h b/engine/src/flutter/shell/platform/fuchsia/flutter/flutter_runner_product_configuration.h index eb0178d8a7..3b6fceb7e4 100644 --- a/engine/src/flutter/shell/platform/fuchsia/flutter/flutter_runner_product_configuration.h +++ b/engine/src/flutter/shell/platform/fuchsia/flutter/flutter_runner_product_configuration.h @@ -5,6 +5,8 @@ #ifndef FLUTTER_SHELL_PLATFORM_FUCHSIA_FLUTTER_RUNNER_PRODUCT_CONFIGURATION_H_ #define FLUTTER_SHELL_PLATFORM_FUCHSIA_FLUTTER_RUNNER_PRODUCT_CONFIGURATION_H_ +#include + #include "flutter/fml/time/time_delta.h" namespace flutter_runner { @@ -12,11 +14,12 @@ namespace flutter_runner { class FlutterRunnerProductConfiguration { public: FlutterRunnerProductConfiguration() {} - FlutterRunnerProductConfiguration(std::string path); + explicit FlutterRunnerProductConfiguration(std::string json_string); fml::TimeDelta get_vsync_offset() { return vsync_offset_; } uint64_t get_max_frames_in_flight() { return max_frames_in_flight_; } bool get_intercept_all_input() { return intercept_all_input_; } + bool software_rendering() { return software_rendering_; } bool enable_shader_warmup() { return enable_shader_warmup_; } bool enable_shader_warmup_dart_hooks() { return enable_shader_warmup_dart_hooks_; @@ -26,6 +29,7 @@ class FlutterRunnerProductConfiguration { fml::TimeDelta vsync_offset_ = fml::TimeDelta::Zero(); uint64_t max_frames_in_flight_ = 3; bool intercept_all_input_ = false; + bool software_rendering_ = false; bool enable_shader_warmup_ = false; bool enable_shader_warmup_dart_hooks_ = true; }; diff --git a/engine/src/flutter/shell/platform/fuchsia/flutter/gfx_external_view_embedder.cc b/engine/src/flutter/shell/platform/fuchsia/flutter/gfx_external_view_embedder.cc index 7fea907623..b6f742aaf2 100644 --- a/engine/src/flutter/shell/platform/fuchsia/flutter/gfx_external_view_embedder.cc +++ b/engine/src/flutter/shell/platform/fuchsia/flutter/gfx_external_view_embedder.cc @@ -103,18 +103,18 @@ GfxExternalViewEmbedder::GfxExternalViewEmbedder( std::string debug_label, fuchsia::ui::views::ViewToken view_token, scenic::ViewRefPair view_ref_pair, - GfxSessionConnection& session, - SurfaceProducer& surface_producer, + std::shared_ptr session, + std::shared_ptr surface_producer, bool intercept_all_input) : session_(session), surface_producer_(surface_producer), - root_view_(session_.get(), + 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()), - layer_tree_node_(session_.get()) { + metrics_node_(session_->get()), + layer_tree_node_(session_->get()) { layer_tree_node_.SetLabel("Flutter::LayerTree"); metrics_node_.SetLabel("Flutter::MetricsWatcher"); metrics_node_.SetEventMask(fuchsia::ui::gfx::kMetricsEventMask); @@ -125,7 +125,7 @@ GfxExternalViewEmbedder::GfxExternalViewEmbedder( // will capture all input, and any unwanted input will be reinjected into // embedded views. if (intercept_all_input) { - input_interceptor_node_.emplace(session_.get()); + input_interceptor_node_.emplace(session_->get()); input_interceptor_node_->SetLabel("Flutter::InputInterceptor"); input_interceptor_node_->SetHitTestBehavior( fuchsia::ui::gfx::HitTestBehavior::kDefault); @@ -134,7 +134,7 @@ GfxExternalViewEmbedder::GfxExternalViewEmbedder( metrics_node_.AddChild(input_interceptor_node_.value()); } - session_.Present(); + session_->Present(); } GfxExternalViewEmbedder::~GfxExternalViewEmbedder() = default; @@ -215,7 +215,7 @@ void GfxExternalViewEmbedder::BeginFrame( if (found_rect == scenic_interceptor_rects_.end()) { auto [emplaced_rect, success] = scenic_interceptor_rects_.emplace(std::make_pair( - rect_hash, scenic::Rectangle(session_.get(), frame_size_.width(), + rect_hash, scenic::Rectangle(session_->get(), frame_size_.width(), frame_size_.height()))); FML_CHECK(success); @@ -253,7 +253,7 @@ void GfxExternalViewEmbedder::SubmitFrame( } auto surface = - surface_producer_.ProduceSurface(layer.second.surface_size); + surface_producer_->ProduceSurface(layer.second.surface_size); if (!surface) { const std::string layer_id_str = layer.first.has_value() ? std::to_string(layer.first.value()) @@ -313,7 +313,7 @@ void GfxExternalViewEmbedder::SubmitFrame( // Expand the clip_nodes array to fit any new nodes. while (view_holder.clip_nodes.size() < view_mutators.clips.size()) { view_holder.clip_nodes.emplace_back( - scenic::EntityNode(session_.get())); + scenic::EntityNode(session_->get())); } FML_CHECK(view_holder.clip_nodes.size() >= view_mutators.clips.size()); @@ -437,8 +437,8 @@ void GfxExternalViewEmbedder::SubmitFrame( FML_CHECK(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()), + .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)); @@ -469,7 +469,7 @@ void GfxExternalViewEmbedder::SubmitFrame( FML_CHECK(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(), + session_->get(), layer->second.surface_size.width(), layer->second.surface_size.height())); } @@ -524,7 +524,7 @@ void GfxExternalViewEmbedder::SubmitFrame( { TRACE_EVENT0("flutter", "SessionPresent"); - session_.Present(); + session_->Present(); } // Render the recorded SkPictures into the surfaces. @@ -563,7 +563,7 @@ void GfxExternalViewEmbedder::SubmitFrame( { TRACE_EVENT0("flutter", "PresentSurfaces"); - surface_producer_.SubmitSurfaces(std::move(frame_surfaces)); + surface_producer_->SubmitSurfaces(std::move(frame_surfaces)); } // Submit the underlying render-backend-specific frame for processing. @@ -571,9 +571,9 @@ void GfxExternalViewEmbedder::SubmitFrame( } void GfxExternalViewEmbedder::EnableWireframe(bool enable) { - session_.get()->Enqueue( + session_->get()->Enqueue( scenic::NewSetEnableDebugViewBoundsCmd(root_view_.id(), enable)); - session_.Present(); + session_->Present(); } void GfxExternalViewEmbedder::CreateView(int64_t view_id, @@ -582,10 +582,10 @@ void GfxExternalViewEmbedder::CreateView(int64_t view_id, FML_CHECK(scenic_views_.find(view_id) == scenic_views_.end()); ScenicView new_view = { - .opacity_node = scenic::OpacityNodeHACK(session_.get()), - .transform_node = scenic::EntityNode(session_.get()), + .opacity_node = scenic::OpacityNodeHACK(session_->get()), + .transform_node = scenic::EntityNode(session_->get()), .view_holder = scenic::ViewHolder( - session_.get(), + session_->get(), scenic::ToViewHolderToken(zx::eventpair((zx_handle_t)view_id)), "Flutter::PlatformView"), }; diff --git a/engine/src/flutter/shell/platform/fuchsia/flutter/gfx_external_view_embedder.h b/engine/src/flutter/shell/platform/fuchsia/flutter/gfx_external_view_embedder.h index 6c726ab75a..1e0a5e3eab 100644 --- a/engine/src/flutter/shell/platform/fuchsia/flutter/gfx_external_view_embedder.h +++ b/engine/src/flutter/shell/platform/fuchsia/flutter/gfx_external_view_embedder.h @@ -75,8 +75,8 @@ class GfxExternalViewEmbedder final : public flutter::ExternalViewEmbedder { GfxExternalViewEmbedder(std::string debug_label, fuchsia::ui::views::ViewToken view_token, scenic::ViewRefPair view_ref_pair, - GfxSessionConnection& session, - SurfaceProducer& surface_producer, + std::shared_ptr session, + std::shared_ptr surface_producer, bool intercept_all_input = false); ~GfxExternalViewEmbedder(); @@ -177,8 +177,8 @@ class GfxExternalViewEmbedder final : public flutter::ExternalViewEmbedder { scenic::Material material; }; - GfxSessionConnection& session_; - SurfaceProducer& surface_producer_; + std::shared_ptr session_; + std::shared_ptr surface_producer_; scenic::View root_view_; scenic::EntityNode metrics_node_; diff --git a/engine/src/flutter/shell/platform/fuchsia/flutter/software_surface.cc b/engine/src/flutter/shell/platform/fuchsia/flutter/software_surface.cc new file mode 100644 index 0000000000..7c38b9502f --- /dev/null +++ b/engine/src/flutter/shell/platform/fuchsia/flutter/software_surface.cc @@ -0,0 +1,428 @@ +// 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 "software_surface.h" + +#include +#include +#include +#include + +#include + +#include "flutter/fml/logging.h" +#include "flutter/fml/trace_event.h" +#include "fuchsia/sysmem/cpp/fidl.h" +#include "include/core/SkImageInfo.h" +#include "third_party/skia/include/core/SkColorSpace.h" +#include "third_party/skia/include/core/SkImageInfo.h" + +#include "../runtime/dart/utils/inlines.h" +#include "zx/cpp/fidl.h" + +namespace flutter_runner { + +namespace { + +constexpr SkColorType kSkiaColorType = kRGBA_8888_SkColorType; + +uint32_t BytesPerRow(const fuchsia::sysmem::SingleBufferSettings& settings, + uint32_t bytes_per_pixel, + uint32_t image_width) { + const uint32_t bytes_per_row_divisor = + settings.image_format_constraints.bytes_per_row_divisor; + const uint32_t min_bytes_per_row = + settings.image_format_constraints.min_bytes_per_row; + const uint32_t unrounded_bytes_per_row = + std::max(image_width * bytes_per_pixel, min_bytes_per_row); + const uint32_t roundup_bytes = + unrounded_bytes_per_row % bytes_per_row_divisor; + + return unrounded_bytes_per_row + roundup_bytes; +} + +} // namespace + +uint32_t SoftwareSurface::sNextBufferId = 1; // 0 is invalid; start at 1. + +SoftwareSurface::SoftwareSurface( + fuchsia::sysmem::AllocatorSyncPtr& sysmem_allocator, + fuchsia::ui::composition::AllocatorPtr& flatland_allocator, + scenic::Session* session, + const SkISize& size) + : session_(session), wait_for_surface_read_finished_(this) { + FML_CHECK((session_ || flatland_allocator.is_bound()) && + !(session_ && flatland_allocator.is_bound())); + + if (!SetupSkiaSurface(sysmem_allocator, flatland_allocator, size)) { + FML_LOG(ERROR) << "Could not create render surface."; + return; + } + + if (!CreateFences()) { + FML_LOG(ERROR) << "Could not create signal fences."; + return; + } + + if (session) { + if (image_id_ == 0) { + image_id_ = session->AllocResourceId(); + } + session->Enqueue(scenic::NewCreateImage2Cmd( + image_id_, sk_surface_->width(), sk_surface_->height(), buffer_id_, 0)); + } + + wait_for_surface_read_finished_.set_object(release_event_.get()); + wait_for_surface_read_finished_.set_trigger(ZX_EVENT_SIGNALED); + Reset(); + + valid_ = true; +} + +SoftwareSurface::~SoftwareSurface() { + if (session_) { + if (image_id_) { + session_->Enqueue(scenic::NewReleaseResourceCmd(image_id_)); + } + if (buffer_id_) { + session_->DeregisterBufferCollection(buffer_id_); + } + } else { + release_image_callback_(); + } + wait_for_surface_read_finished_.Cancel(); + wait_for_surface_read_finished_.set_object(ZX_HANDLE_INVALID); +} + +bool SoftwareSurface::IsValid() const { + return valid_; +} + +SkISize SoftwareSurface::GetSize() const { + if (!valid_) { + return SkISize::Make(0, 0); + } + + return SkISize::Make(sk_surface_->width(), sk_surface_->height()); +} + +bool SoftwareSurface::CreateFences() { + if (zx::event::create(0, &acquire_event_) != ZX_OK) { + FML_LOG(ERROR) << "Failed to create acquire event."; + return false; + } + + if (zx::event::create(0, &release_event_) != ZX_OK) { + FML_LOG(ERROR) << "Failed to create release event."; + return false; + } + + return true; +} + +bool SoftwareSurface::SetupSkiaSurface( + fuchsia::sysmem::AllocatorSyncPtr& sysmem_allocator, + fuchsia::ui::composition::AllocatorPtr& flatland_allocator, + const SkISize& size) { + if (size.isEmpty()) { + FML_LOG(ERROR) << "Failed to allocate surface, size is empty."; + return false; + } + + // Allocate a "local" sysmem token to represent flutter's handle to the + // sysmem buffer. + fuchsia::sysmem::BufferCollectionTokenSyncPtr local_token; + zx_status_t allocate_status = + sysmem_allocator->AllocateSharedCollection(local_token.NewRequest()); + if (allocate_status != ZX_OK) { + FML_LOG(ERROR) << "Failed to allocate collection: " + << zx_status_get_string(allocate_status); + return false; + } + + // Create a single Duplicate of the token and Sync it; the single duplicate + // token represents scenic's handle to the sysmem buffer. + std::vector duplicate_tokens; + zx_status_t duplicate_status = local_token->DuplicateSync( + std::vector{zx::rights::SAME_RIGHTS}, &duplicate_tokens); + if (duplicate_status != ZX_OK) { + FML_LOG(ERROR) << "Failed to duplicate collection token: " + << zx_status_get_string(duplicate_status); + return false; + } + if (duplicate_tokens.size() != 1u) { + FML_LOG(ERROR) << "Failed to duplicate collection token: Incorrect number " + "of tokens returned."; + return false; + } + auto scenic_token = std::move(duplicate_tokens[0]); + + // Register the sysmem token with flatland (or scenic's legacy gfx interface). + // + // This binds the sysmem token to a composition token, which is used later + // to associate the rendering surface with a specific flatland Image. + // + // Under gfx, scenic uses an integral `buffer_id` instead of the composition + // token. + if (session_) { + buffer_id_ = sNextBufferId++; + session_->RegisterBufferCollection(buffer_id_, std::move(scenic_token)); + } else { + fuchsia::ui::composition::BufferCollectionExportToken export_token; + zx_status_t token_create_status = + zx::eventpair::create(0, &export_token.value, &import_token_.value); + if (token_create_status != ZX_OK) { + FML_LOG(ERROR) << "Failed to create flatland export token: " + << zx_status_get_string(token_create_status); + return false; + } + + fuchsia::ui::composition::RegisterBufferCollectionArgs args; + args.set_export_token(std::move(export_token)); + args.set_buffer_collection_token(std::move(scenic_token)); + args.set_usage( + fuchsia::ui::composition::RegisterBufferCollectionUsage::DEFAULT); + flatland_allocator->RegisterBufferCollection( + std::move(args), + [](fuchsia::ui::composition::Allocator_RegisterBufferCollection_Result + result) { + if (result.is_err()) { + FML_LOG(ERROR) + << "RegisterBufferCollection call to Scenic Allocator failed."; + } + }); + } + + // Acquire flutter's local handle to the sysmem buffer. + fuchsia::sysmem::BufferCollectionSyncPtr buffer_collection; + zx_status_t bind_status = sysmem_allocator->BindSharedCollection( + std::move(local_token), buffer_collection.NewRequest()); + if (bind_status != ZX_OK) { + FML_LOG(ERROR) << "Failed to bind collection token: " + << zx_status_get_string(bind_status); + return false; + } + + // Set flutter's constraints on the sysmem buffer. Software rendering only + // requires CPU access to the surface and a basic R8G8B8A8 pixel format. + fuchsia::sysmem::BufferCollectionConstraints constraints; + constraints.min_buffer_count = 1; + constraints.usage.cpu = + fuchsia::sysmem::cpuUsageWrite | fuchsia::sysmem::cpuUsageWriteOften; + constraints.has_buffer_memory_constraints = true; + constraints.buffer_memory_constraints.physically_contiguous_required = false; + constraints.buffer_memory_constraints.secure_required = false; + constraints.buffer_memory_constraints.ram_domain_supported = true; + constraints.buffer_memory_constraints.cpu_domain_supported = true; + constraints.buffer_memory_constraints.inaccessible_domain_supported = false; + constraints.image_format_constraints_count = 1; + fuchsia::sysmem::ImageFormatConstraints& image_constraints = + constraints.image_format_constraints[0]; + image_constraints = fuchsia::sysmem::ImageFormatConstraints(); + image_constraints.min_coded_width = static_cast(size.fWidth); + image_constraints.min_coded_height = static_cast(size.fHeight); + image_constraints.min_bytes_per_row = static_cast(size.fWidth) * 4; + image_constraints.pixel_format.type = + fuchsia::sysmem::PixelFormatType::R8G8B8A8; + image_constraints.color_spaces_count = 1; + image_constraints.color_space[0].type = fuchsia::sysmem::ColorSpaceType::SRGB; + image_constraints.pixel_format.has_format_modifier = true; + image_constraints.pixel_format.format_modifier.value = + fuchsia::sysmem::FORMAT_MODIFIER_LINEAR; + zx_status_t set_constraints_status = + buffer_collection->SetConstraints(true, constraints); + if (set_constraints_status != ZX_OK) { + FML_LOG(ERROR) << "Failed to set constraints: " + << zx_status_get_string(set_constraints_status); + return false; + } + + // Wait for sysmem to allocate, now that constraints are set. + fuchsia::sysmem::BufferCollectionInfo_2 buffer_collection_info; + zx_status_t allocation_status = ZX_OK; + zx_status_t wait_for_allocated_status = + buffer_collection->WaitForBuffersAllocated(&allocation_status, + &buffer_collection_info); + if (allocation_status != ZX_OK) { + FML_LOG(ERROR) << "Failed to allocate: " + << zx_status_get_string(allocation_status); + return false; + } + if (wait_for_allocated_status != ZX_OK) { + FML_LOG(ERROR) << "Failed to wait for allocate: " + << zx_status_get_string(wait_for_allocated_status); + return false; + } + + // Cache the allocated surface VMO and metadata. + FML_CHECK(buffer_collection_info.settings.buffer_settings.size_bytes != 0); + FML_CHECK(buffer_collection_info.buffers[0].vmo != ZX_HANDLE_INVALID); + surface_vmo_ = std::move(buffer_collection_info.buffers[0].vmo); + surface_size_bytes_ = + buffer_collection_info.settings.buffer_settings.size_bytes; + if (buffer_collection_info.settings.buffer_settings.coherency_domain == + fuchsia::sysmem::CoherencyDomain::RAM) { + // RAM coherency domain requires a cache clean when writes are finished. + needs_cache_clean_ = true; + } + + // Map the allocated buffer to the CPU. + uint8_t* vmo_base = nullptr; + zx_status_t buffer_map_status = zx::vmar::root_self()->map( + ZX_VM_PERM_WRITE | ZX_VM_PERM_READ, 0, surface_vmo_, 0, + surface_size_bytes_, reinterpret_cast(&vmo_base)); + if (buffer_map_status != ZX_OK) { + FML_LOG(ERROR) << "Failed to map buffer memory: " + << zx_status_get_string(buffer_map_status); + return false; + } + + // Now that the buffer is CPU-readable, it's safe to discard flutter's + // connection to sysmem. + zx_status_t close_status = buffer_collection->Close(); + if (close_status != ZX_OK) { + FML_LOG(ERROR) << "Failed to close buffer: " + << zx_status_get_string(close_status); + return false; + } + + // Wrap the buffer in a software-rendered Skia surface. + const uint64_t vmo_offset = + buffer_collection_info.buffers[0].vmo_usable_start; + const size_t vmo_stride = + BytesPerRow(buffer_collection_info.settings, 4u, size.width()); + SkSurfaceProps sk_surface_props(0, kUnknown_SkPixelGeometry); + sk_surface_ = SkSurface::MakeRasterDirect( + SkImageInfo::Make(size, kSkiaColorType, kPremul_SkAlphaType, + SkColorSpace::MakeSRGB()), + vmo_base + vmo_offset, vmo_stride, &sk_surface_props); + if (!sk_surface_ || sk_surface_->getCanvas() == nullptr) { + FML_LOG(ERROR) << "SkSurface::MakeRasterDirect failed."; + return false; + } + + return true; +} + +void SoftwareSurface::SetImageId(uint32_t image_id) { + FML_CHECK(image_id_ == 0); + FML_CHECK(!session_); + image_id_ = image_id; +} + +uint32_t SoftwareSurface::GetImageId() { + return image_id_; +} + +sk_sp SoftwareSurface::GetSkiaSurface() const { + return valid_ ? sk_surface_ : nullptr; +} + +fuchsia::ui::composition::BufferCollectionImportToken +SoftwareSurface::GetBufferCollectionImportToken() { + FML_CHECK(!session_); + fuchsia::ui::composition::BufferCollectionImportToken import_dup; + import_token_.value.duplicate(ZX_RIGHT_SAME_RIGHTS, &import_dup.value); + return import_dup; +} + +zx::event SoftwareSurface::GetAcquireFence() { + FML_CHECK(!session_); + zx::event fence; + acquire_event_.duplicate(ZX_RIGHT_SAME_RIGHTS, &fence); + return fence; +} + +zx::event SoftwareSurface::GetReleaseFence() { + FML_CHECK(!session_); + zx::event fence; + release_event_.duplicate(ZX_RIGHT_SAME_RIGHTS, &fence); + return fence; +} +void SoftwareSurface::SetReleaseImageCallback( + ReleaseImageCallback release_image_callback) { + FML_CHECK(!session_); + release_image_callback_ = release_image_callback; +} + +size_t SoftwareSurface::AdvanceAndGetAge() { + return ++age_; +} + +bool SoftwareSurface::FlushSessionAcquireAndReleaseEvents() { + if (session_) { + zx::event acquire, release; + if (acquire_event_.duplicate(ZX_RIGHT_SAME_RIGHTS, &acquire) != ZX_OK || + release_event_.duplicate(ZX_RIGHT_SAME_RIGHTS, &release) != ZX_OK) { + return false; + } + session_->EnqueueAcquireFence(std::move(acquire)); + session_->EnqueueReleaseFence(std::move(release)); + } + + age_ = 0; + return true; +} + +void SoftwareSurface::SignalWritesFinished( + const std::function& on_surface_read_finished) { + FML_CHECK(on_surface_read_finished); + + if (!valid_) { + on_surface_read_finished(); + return; + } + + dart_utils::Check(surface_read_finished_callback_ == nullptr, + "Attempted to signal a write on the surface when the " + "previous write has not yet been acknowledged by the " + "compositor."); + surface_read_finished_callback_ = on_surface_read_finished; + + // Sysmem *may* require the cache to be cleared after writes to the surface + // are complete. + if (needs_cache_clean_) { + surface_vmo_.op_range(ZX_VMO_OP_CACHE_CLEAN, 0, surface_size_bytes_, + /*buffer*/ nullptr, + /*buffer_size*/ 0); + } + + // Inform scenic that flutter is finished writing to the surface. + zx_status_t signal_status = acquire_event_.signal(0u, ZX_EVENT_SIGNALED); + if (signal_status != ZX_OK) { + FML_LOG(ERROR) << "Failed to signal acquire event; " + << zx_status_get_string(signal_status); + } +} + +void SoftwareSurface::Reset() { + if (acquire_event_.signal(ZX_EVENT_SIGNALED, 0u) != ZX_OK || + release_event_.signal(ZX_EVENT_SIGNALED, 0u) != ZX_OK) { + valid_ = false; + FML_LOG(ERROR) << "Could not reset fences. The surface is no longer valid."; + } + + wait_for_surface_read_finished_.Begin(async_get_default_dispatcher()); + + // It is safe for the caller to collect the surface in the callback. + auto callback = surface_read_finished_callback_; + surface_read_finished_callback_ = nullptr; + if (callback) { + callback(); + } +} + +void SoftwareSurface::OnSurfaceReadFinished(async_dispatcher_t* dispatcher, + async::WaitBase* wait, + zx_status_t status, + const zx_packet_signal_t* signal) { + if (status != ZX_OK) { + return; + } + FML_DCHECK(signal->observed & ZX_EVENT_SIGNALED); + + Reset(); +} + +} // namespace flutter_runner diff --git a/engine/src/flutter/shell/platform/fuchsia/flutter/software_surface.h b/engine/src/flutter/shell/platform/fuchsia/flutter/software_surface.h new file mode 100644 index 0000000000..c1af8fb715 --- /dev/null +++ b/engine/src/flutter/shell/platform/fuchsia/flutter/software_surface.h @@ -0,0 +1,127 @@ +// 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. + +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "flutter/fml/macros.h" +#include "third_party/skia/include/core/SkSize.h" +#include "third_party/skia/include/core/SkSurface.h" + +#include "surface_producer.h" + +namespace flutter_runner { + +class SoftwareSurface final : public SurfaceProducerSurface { + public: + SoftwareSurface(fuchsia::sysmem::AllocatorSyncPtr& sysmem_allocator, + fuchsia::ui::composition::AllocatorPtr& flatland_allocator, + scenic::Session* session, + const SkISize& size); + + ~SoftwareSurface() override; + + size_t GetAllocationSize() const { return surface_size_bytes_; } + + // |SurfaceProducerSurface| + size_t AdvanceAndGetAge() override; + + // |SurfaceProducerSurface| + bool FlushSessionAcquireAndReleaseEvents() override; + + // |SurfaceProducerSurface| + bool IsValid() const override; + + // |SurfaceProducerSurface| + SkISize GetSize() const override; + + // |SurfaceProducerSurface| + void SignalWritesFinished( + const std::function& on_surface_read_finished) override; + + // |SurfaceProducerSurface| + void SetImageId(uint32_t image_id) override; + + // |SurfaceProducerSurface| + uint32_t GetImageId() override; + + // |SurfaceProducerSurface| + sk_sp GetSkiaSurface() const override; + + // |SurfaceProducerSurface| + fuchsia::ui::composition::BufferCollectionImportToken + GetBufferCollectionImportToken() override; + + // |SurfaceProducerSurface| + zx::event GetAcquireFence() override; + + // |SurfaceProducerSurface| + zx::event GetReleaseFence() override; + + // |SurfaceProducerSurface| + void SetReleaseImageCallback( + ReleaseImageCallback release_image_callback) override; + + private: + void OnSurfaceReadFinished(async_dispatcher_t* dispatcher, + async::WaitBase* wait, + zx_status_t status, + const zx_packet_signal_t* signal); + + bool SetupSkiaSurface( + fuchsia::sysmem::AllocatorSyncPtr& sysmem_allocator, + fuchsia::ui::composition::AllocatorPtr& flatland_allocator, + const SkISize& size); + + bool CreateFences(); + + void Reset(); + + static uint32_t sNextBufferId; // For legacy gfx; counter for `buffer_id_`. + + scenic::Session* session_; // Legacy gfx API endpoint. + scenic::ResourceId image_id_{0}; // Legacy gfx image ID. + + sk_sp sk_surface_; + + // This is associated with `release_event_` and allows detection of when + // scenic has finished reading from the surface (and thus it is safe to re-use + // for writing). + async::WaitMethod + wait_for_surface_read_finished_; + // Called when scenic has finished reading from the surface, to allow + // `SoftwareSurfaceProducer` to re-use the surface. + std::function surface_read_finished_callback_; + // Called when the surface is destroyed, to allow + // `FlatlandExternalViewEmbedder` to release the associated Flatland image. + ReleaseImageCallback release_image_callback_; + + // Allows Flatland to associate this surface with a Flatland Image. + fuchsia::ui::composition::BufferCollectionImportToken import_token_; + uint32_t buffer_id_{0}; // Legacy gfx version of the import_token_. + zx::event acquire_event_; // Signals to scenic that writing is finished. + zx::event release_event_; // Signalled by scenic that reading is finished. + zx::vmo surface_vmo_; // VMO that is backing the surface memory. + + uint32_t surface_size_bytes_; // Size of the surface memory, in bytes. + size_t age_{0}; // Number of frames since surface was last written to. + + bool needs_cache_clean_{false}; + bool valid_{false}; + + FML_DISALLOW_COPY_AND_ASSIGN(SoftwareSurface); +}; + +} // namespace flutter_runner diff --git a/engine/src/flutter/shell/platform/fuchsia/flutter/software_surface_producer.cc b/engine/src/flutter/shell/platform/fuchsia/flutter/software_surface_producer.cc new file mode 100644 index 0000000000..370307c227 --- /dev/null +++ b/engine/src/flutter/shell/platform/fuchsia/flutter/software_surface_producer.cc @@ -0,0 +1,245 @@ +// 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 "software_surface_producer.h" + +#include +#include + +#include // Foor std::remove_if +#include +#include +#include +#include + +#include "flutter/fml/logging.h" +#include "flutter/fml/trace_event.h" + +namespace flutter_runner { + +namespace { + +std::string GetCurrentProcessName() { + char name[ZX_MAX_NAME_LEN]; + zx_status_t status = + zx::process::self()->get_property(ZX_PROP_NAME, name, sizeof(name)); + if (status != ZX_OK) { + FML_LOG(ERROR) << "Failed to get process name for sysmem; using \"\"."; + return std::string(); + } + + return std::string(name); +} + +zx_koid_t GetCurrentProcessId() { + zx_info_handle_basic_t info; + zx_status_t status = zx::process::self()->get_info( + ZX_INFO_HANDLE_BASIC, &info, sizeof(info), /*actual_count*/ nullptr, + /*avail_count*/ nullptr); + if (status != ZX_OK) { + FML_LOG(ERROR) << "Failed to get process ID for sysmem; using 0."; + return ZX_KOID_INVALID; + } + + return info.koid; +} + +} // namespace + +SoftwareSurfaceProducer::SoftwareSurfaceProducer( + scenic::Session* scenic_session) + : scenic_session_(scenic_session) { + zx_status_t status = fdio_service_connect( + "/svc/fuchsia.sysmem.Allocator", + sysmem_allocator_.NewRequest().TakeChannel().release()); + sysmem_allocator_->SetDebugClientInfo(GetCurrentProcessName(), + GetCurrentProcessId()); + FML_DCHECK(status != ZX_OK); + + if (!scenic_session_) { + status = fdio_service_connect( + "/svc/fuchsia.ui.composition.Allocator", + flatland_allocator_.NewRequest().TakeChannel().release()); + FML_DCHECK(status != ZX_OK); + } + + valid_ = true; +} + +SoftwareSurfaceProducer::~SoftwareSurfaceProducer() = default; + +std::unique_ptr +SoftwareSurfaceProducer::ProduceOffscreenSurface(const SkISize& size) { + FML_CHECK(valid_); + + return CreateSurface(size); +} + +std::unique_ptr SoftwareSurfaceProducer::ProduceSurface( + const SkISize& size) { + TRACE_EVENT2("flutter", "SoftwareSurfacePool::ProduceSurface", "width", + size.width(), "height", size.height()); + FML_CHECK(valid_); + + std::unique_ptr surface; + auto exact_match_it = + std::find_if(available_surfaces_.begin(), available_surfaces_.end(), + [&size](const auto& surface) { + return surface->IsValid() && surface->GetSize() == size; + }); + if (exact_match_it != available_surfaces_.end()) { + TRACE_EVENT_INSTANT0("flutter", "Exact match found"); + surface = std::move(*exact_match_it); + available_surfaces_.erase(exact_match_it); + } else { + surface = CreateSurface(size); + } + + if (surface == nullptr) { + FML_LOG(ERROR) << "Could not acquire surface."; + return nullptr; + } + + if (!surface->FlushSessionAcquireAndReleaseEvents()) { + FML_LOG(ERROR) << "Could not flush acquire/release events for buffer."; + return nullptr; + } + + return surface; +} + +void SoftwareSurfaceProducer::SubmitSurfaces( + std::vector> surfaces) { + TRACE_EVENT0("flutter", "SoftwareSurfaceProducer::SubmitSurfaces"); + + // Submit surface + for (auto& surface : surfaces) { + SubmitSurface(std::move(surface)); + } + + // Buffer management. + AgeAndCollectOldBuffers(); +} + +void SoftwareSurfaceProducer::SubmitSurface( + std::unique_ptr surface) { + TRACE_EVENT0("flutter", "SoftwareSurfacePool::SubmitSurface"); + FML_CHECK(valid_); + + // This cast is safe because |SoftwareSurface| is the only implementation of + // |SurfaceProducerSurface| for Flutter on Fuchsia. Additionally, it is + // required, because we need to access |SoftwareSurface| specific information + // of the surface (such as the amount of memory it contains). + auto software_surface = std::unique_ptr( + static_cast(surface.release())); + if (!software_surface) { + return; + } + + uintptr_t surface_key = reinterpret_cast(software_surface.get()); + auto insert_iterator = pending_surfaces_.insert(std::make_pair( + surface_key, // key + std::move(software_surface) // value + )); + if (insert_iterator.second) { + insert_iterator.first->second->SignalWritesFinished(std::bind( + &SoftwareSurfaceProducer::RecyclePendingSurface, this, surface_key)); + } +} + +std::unique_ptr SoftwareSurfaceProducer::CreateSurface( + const SkISize& size) { + TRACE_EVENT2("flutter", "SoftwareSurfacePool::CreateSurface", "width", + size.width(), "height", size.height()); + auto surface = std::make_unique( + sysmem_allocator_, flatland_allocator_, scenic_session_, size); + if (!surface->IsValid()) { + FML_LOG(ERROR) << "Created surface is invalid."; + return nullptr; + } + trace_surfaces_created_++; + return surface; +} + +void SoftwareSurfaceProducer::RecycleSurface( + std::unique_ptr surface) { + // The surface may have become invalid (for example if the fences could + // not be reset). + if (!surface->IsValid()) { + FML_LOG(ERROR) << "Attempted to recycle invalid surface."; + return; + } + + TRACE_EVENT0("flutter", "SoftwareSurfacePool::RecycleSurface"); + // Recycle the buffer by putting it in the list of available surfaces if we + // have not reached the maximum amount of cached surfaces. + if (available_surfaces_.size() < kMaxSurfaces) { + available_surfaces_.push_back(std::move(surface)); + } else { + TRACE_EVENT_INSTANT0("flutter", "Too many surfaces in pool, dropping"); + } + TraceStats(); +} + +void SoftwareSurfaceProducer::RecyclePendingSurface(uintptr_t surface_key) { + // Before we do anything, we must clear the surface from the collection of + // pending surfaces. + auto found_in_pending = pending_surfaces_.find(surface_key); + if (found_in_pending == pending_surfaces_.end()) { + FML_LOG(ERROR) << "Attempted to recycle a surface that wasn't pending."; + return; + } + + // Grab a hold of the surface to recycle and clear the entry in the pending + // surfaces collection. + auto surface_to_recycle = std::move(found_in_pending->second); + pending_surfaces_.erase(found_in_pending); + + RecycleSurface(std::move(surface_to_recycle)); +} + +void SoftwareSurfaceProducer::AgeAndCollectOldBuffers() { + TRACE_EVENT0("flutter", "SoftwareSurfacePool::AgeAndCollectOldBuffers"); + + // Remove all surfaces that are no longer valid or are too old. + size_t size_before = available_surfaces_.size(); + available_surfaces_.erase( + std::remove_if(available_surfaces_.begin(), available_surfaces_.end(), + [&](auto& surface) { + return !surface->IsValid() || + surface->AdvanceAndGetAge() >= kMaxSurfaceAge; + }), + available_surfaces_.end()); + TRACE_EVENT1("flutter", "AgeAndCollect", "aged surfaces", + (size_before - available_surfaces_.size())); + + TraceStats(); +} + +void SoftwareSurfaceProducer::TraceStats() { + // Resources held in cached buffers. + size_t cached_surfaces_bytes = 0; + for (const auto& surface : available_surfaces_) { + cached_surfaces_bytes += surface->GetAllocationSize(); + } + + TRACE_COUNTER("flutter", "SurfacePoolCounts", 0u, "CachedCount", + available_surfaces_.size(), // + "Created", trace_surfaces_created_, // + "Reused", trace_surfaces_reused_, // + "PendingInCompositor", pending_surfaces_.size(), // + "Retained", 0 // + ); + + TRACE_COUNTER("flutter", "SurfacePoolBytes", 0u, // + "CachedBytes", cached_surfaces_bytes, // + "RetainedBytes", 0 // + ); + + // Reset per present/frame stats. + trace_surfaces_created_ = 0; + trace_surfaces_reused_ = 0; +} + +} // namespace flutter_runner diff --git a/engine/src/flutter/shell/platform/fuchsia/flutter/software_surface_producer.h b/engine/src/flutter/shell/platform/fuchsia/flutter/software_surface_producer.h new file mode 100644 index 0000000000..9e0ddbcf27 --- /dev/null +++ b/engine/src/flutter/shell/platform/fuchsia/flutter/software_surface_producer.h @@ -0,0 +1,77 @@ +// 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. + +#pragma once + +#include +#include +#include +#include + +#include + +#include "flutter/fml/macros.h" + +#include "software_surface.h" + +namespace flutter_runner { + +class SoftwareSurfaceProducer final : public SurfaceProducer { + public: + // Only keep 12 surfaces at a time. + static constexpr int kMaxSurfaces = 12; + // If a surface doesn't get used for 3 or more generations, we discard it. + static constexpr int kMaxSurfaceAge = 3; + + explicit SoftwareSurfaceProducer(scenic::Session* scenic_session); + ~SoftwareSurfaceProducer() override; + + bool IsValid() const { return valid_; } + + // |SurfaceProducer| + GrDirectContext* gr_context() const override { return nullptr; } + + // |SurfaceProducer| + std::unique_ptr ProduceOffscreenSurface( + const SkISize& size) override; + + // |SurfaceProducer| + std::unique_ptr ProduceSurface( + const SkISize& size) override; + + // |SurfaceProducer| + void SubmitSurfaces( + std::vector> surfaces) override; + + private: + void SubmitSurface(std::unique_ptr surface); + std::unique_ptr CreateSurface(const SkISize& size); + void RecycleSurface(std::unique_ptr surface); + + void RecyclePendingSurface(uintptr_t surface_key); + + void AgeAndCollectOldBuffers(); + + void TraceStats(); + + scenic::Session* scenic_session_; // Legacy gfx API endpoint. + fuchsia::sysmem::AllocatorSyncPtr sysmem_allocator_; + fuchsia::ui::composition::AllocatorPtr flatland_allocator_; + + // These surfaces are available for re-use. + std::vector> available_surfaces_; + // These surfaces have been written to, but scenic is not finished reading + // from them yet. + std::unordered_map> + pending_surfaces_; + + size_t trace_surfaces_created_ = 0; + size_t trace_surfaces_reused_ = 0; + + bool valid_ = false; + + FML_DISALLOW_COPY_AND_ASSIGN(SoftwareSurfaceProducer); +}; + +} // namespace flutter_runner diff --git a/engine/src/flutter/shell/platform/fuchsia/flutter/surface_producer.h b/engine/src/flutter/shell/platform/fuchsia/flutter/surface_producer.h index c09cb187ce..bd03531c16 100644 --- a/engine/src/flutter/shell/platform/fuchsia/flutter/surface_producer.h +++ b/engine/src/flutter/shell/platform/fuchsia/flutter/surface_producer.h @@ -14,6 +14,7 @@ #include "third_party/skia/include/core/SkSize.h" #include "third_party/skia/include/core/SkSurface.h" +#include "third_party/skia/include/gpu/GrDirectContext.h" namespace flutter_runner { @@ -66,6 +67,10 @@ class SurfaceProducer { public: virtual ~SurfaceProducer() = default; + virtual GrDirectContext* gr_context() const = 0; + + virtual std::unique_ptr ProduceOffscreenSurface( + const SkISize& size) = 0; virtual std::unique_ptr ProduceSurface( const SkISize& size) = 0; diff --git a/engine/src/flutter/shell/platform/fuchsia/flutter/tests/engine_unittests.cc b/engine/src/flutter/shell/platform/fuchsia/flutter/tests/engine_unittests.cc index 534aa6a7c7..10afd5fae5 100644 --- a/engine/src/flutter/shell/platform/fuchsia/flutter/tests/engine_unittests.cc +++ b/engine/src/flutter/shell/platform/fuchsia/flutter/tests/engine_unittests.cc @@ -6,22 +6,23 @@ #include #include -#include "assets/directory_asset_bundle.h" +#include "flutter/assets/directory_asset_bundle.h" #include "flutter/common/graphics/persistent_cache.h" #include "flutter/fml/memory/ref_ptr.h" #include "flutter/fml/message_loop_impl.h" #include "flutter/fml/task_runner.h" #include "flutter/shell/common/serialization_callbacks.h" #include "flutter/shell/common/thread_host.h" -#include "flutter/shell/platform/fuchsia/flutter/gfx_session_connection.h" +#include "third_party/skia/include/core/SkPicture.h" +#include "third_party/skia/include/core/SkPictureRecorder.h" +#include "third_party/skia/include/core/SkSerialProcs.h" + #include "flutter/shell/platform/fuchsia/flutter/logging.h" #include "flutter/shell/platform/fuchsia/flutter/runner.h" -#include "gtest/gtest.h" -#include "include/core/SkPicture.h" -#include "include/core/SkPictureRecorder.h" -#include "include/core/SkSerialProcs.h" +#include "flutter/shell/platform/fuchsia/flutter/vulkan_surface_producer.h" + +#include "gtest/gtest.h" -using namespace flutter_runner; using namespace flutter; namespace flutter_runner { @@ -74,17 +75,17 @@ class EngineTest : public ::testing::Test { scenic_ = context_->svc()->Connect(); session_.emplace(scenic_.get()); surface_producer_ = - std::make_unique(&session_.value()); + std::make_shared(&session_.value()); Engine::WarmupSkps(&concurrent_task_runner_, &raster_task_runner_, - *surface_producer_, width, height, asset_manager, - std::nullopt, std::nullopt); + surface_producer_, SkISize::Make(width, height), + asset_manager, std::nullopt, std::nullopt); } protected: MockTaskRunner concurrent_task_runner_; MockTaskRunner raster_task_runner_; - std::unique_ptr surface_producer_; + std::shared_ptr surface_producer_; std::unique_ptr context_; fuchsia::ui::scenic::ScenicPtr scenic_; diff --git a/engine/src/flutter/shell/platform/fuchsia/flutter/tests/flatland_external_view_embedder_unittests.cc b/engine/src/flutter/shell/platform/fuchsia/flutter/tests/flatland_external_view_embedder_unittests.cc index efa5ef54f8..53a0f5a88b 100644 --- a/engine/src/flutter/shell/platform/fuchsia/flutter/tests/flatland_external_view_embedder_unittests.cc +++ b/engine/src/flutter/shell/platform/fuchsia/flutter/tests/flatland_external_view_embedder_unittests.cc @@ -128,6 +128,16 @@ class FakeSurfaceProducer : public SurfaceProducer { : flatland_allocator_(flatland_allocator.Bind()) {} ~FakeSurfaceProducer() override = default; + // |SurfaceProducer| + GrDirectContext* gr_context() const override { return nullptr; } + + // |SurfaceProducer| + std::unique_ptr ProduceOffscreenSurface( + const SkISize& size) override { + return nullptr; + } + + // |SurfaceProducer| std::unique_ptr ProduceSurface( const SkISize& size) override { auto [buffer_export_token, buffer_import_token] = @@ -156,6 +166,7 @@ class FakeSurfaceProducer : public SurfaceProducer { std::move(sysmem_token_request), std::move(buffer_import_token), size); } + // |SurfaceProducer| void SubmitSurfaces( std::vector> surfaces) override {} @@ -305,19 +316,22 @@ class FlatlandExternalViewEmbedderTest : public ::testing::Test { protected: FlatlandExternalViewEmbedderTest() : session_subloop_(loop_.StartNewLoop()), - fake_surface_producer_(CreateFlatlandAllocator()), - flatland_connection_(CreateFlatlandConnection()) {} + flatland_connection_(CreateFlatlandConnection()), + fake_surface_producer_( + std::make_shared(CreateFlatlandAllocator())) {} ~FlatlandExternalViewEmbedderTest() override = default; async::TestLoop& loop() { return loop_; } - FakeSurfaceProducer& fake_surface_producer() { + std::shared_ptr fake_surface_producer() { return fake_surface_producer_; } FakeFlatland& fake_flatland() { return fake_flatland_; } - FlatlandConnection& flatland_connection() { return flatland_connection_; } + std::shared_ptr flatland_connection() { + return flatland_connection_; + } private: fuchsia::ui::composition::AllocatorHandle CreateFlatlandAllocator() { @@ -328,14 +342,14 @@ class FlatlandExternalViewEmbedderTest : public ::testing::Test { return flatland_allocator; } - FlatlandConnection CreateFlatlandConnection() { + std::shared_ptr CreateFlatlandConnection() { FML_CHECK(!fake_flatland_.is_flatland_connected()); fuchsia::ui::composition::FlatlandHandle flatland = fake_flatland_.ConnectFlatland(session_subloop_->dispatcher()); auto test_name = ::testing::UnitTest::GetInstance()->current_test_info()->name(); - return FlatlandConnection( + return std::make_shared( std::move(test_name), std::move(flatland), []() { FAIL(); }, [](auto...) {}, 1, fml::TimeDelta::Zero()); } @@ -349,9 +363,9 @@ class FlatlandExternalViewEmbedderTest : public ::testing::Test { std::unique_ptr session_subloop_; FakeFlatland fake_flatland_; - FakeSurfaceProducer fake_surface_producer_; - FlatlandConnection flatland_connection_; + std::shared_ptr flatland_connection_; + std::shared_ptr fake_surface_producer_; }; TEST_F(FlatlandExternalViewEmbedderTest, RootScene) { @@ -382,7 +396,7 @@ TEST_F(FlatlandExternalViewEmbedderTest, RootScene) { EXPECT_THAT(fake_flatland().graph(), IsEmptyGraph()); // Pump the loop; the contents of the initial `Present` should be processed. - flatland_connection().Present(); + flatland_connection()->Present(); loop().RunUntilIdle(); EXPECT_THAT(fake_flatland().graph(), IsFlutterGraph(parent_viewport_watcher, viewport_creation_token, @@ -411,7 +425,7 @@ TEST_F(FlatlandExternalViewEmbedderTest, SimpleScene) { fuchsia::ui::composition::ViewBoundProtocols{}, parent_viewport_watcher.NewRequest(), flatland_connection(), fake_surface_producer()); - flatland_connection().Present(); + flatland_connection()->Present(); loop().RunUntilIdle(); fake_flatland().FireOnNextFrameBeginEvent(WithPresentCredits(1u)); loop().RunUntilIdle(); @@ -471,7 +485,7 @@ TEST_F(FlatlandExternalViewEmbedderTest, SceneWithOneView) { fuchsia::ui::composition::ViewBoundProtocols{}, parent_viewport_watcher.NewRequest(), flatland_connection(), fake_surface_producer()); - flatland_connection().Present(); + flatland_connection()->Present(); loop().RunUntilIdle(); fake_flatland().FireOnNextFrameBeginEvent(WithPresentCredits(1u)); loop().RunUntilIdle(); diff --git a/engine/src/flutter/shell/platform/fuchsia/flutter/tests/gfx_external_view_embedder_unittests.cc b/engine/src/flutter/shell/platform/fuchsia/flutter/tests/gfx_external_view_embedder_unittests.cc index 47eadc3e14..58690960d0 100644 --- a/engine/src/flutter/shell/platform/fuchsia/flutter/tests/gfx_external_view_embedder_unittests.cc +++ b/engine/src/flutter/shell/platform/fuchsia/flutter/tests/gfx_external_view_embedder_unittests.cc @@ -57,25 +57,26 @@ namespace { class FakeSurfaceProducerSurface : public SurfaceProducerSurface { public: - explicit FakeSurfaceProducerSurface(scenic::Session& session, + explicit FakeSurfaceProducerSurface(scenic::Session* session, const SkISize& size, uint32_t buffer_id) : session_(session), surface_(SkSurface::MakeNull(size.width(), size.height())), - image_id_(session_.AllocResourceId()), buffer_id_(buffer_id) { + FML_CHECK(session_); FML_CHECK(buffer_id_ != 0); fuchsia::sysmem::BufferCollectionTokenSyncPtr token; buffer_binding_ = token.NewRequest(); - session_.RegisterBufferCollection(buffer_id_, std::move(token)); - session_.Enqueue(scenic::NewCreateImage2Cmd( + image_id_ = session_->AllocResourceId(); + session_->RegisterBufferCollection(buffer_id_, std::move(token)); + session_->Enqueue(scenic::NewCreateImage2Cmd( image_id_, surface_->width(), surface_->height(), buffer_id_, 0)); } ~FakeSurfaceProducerSurface() override { - session_.DeregisterBufferCollection(buffer_id_); - session_.Enqueue(scenic::NewReleaseResourceCmd(image_id_)); + session_->DeregisterBufferCollection(buffer_id_); + session_->Enqueue(scenic::NewReleaseResourceCmd(image_id_)); } bool IsValid() const override { return true; } @@ -107,7 +108,7 @@ class FakeSurfaceProducerSurface : public SurfaceProducerSurface { const std::function& on_writes_committed) override {} private: - scenic::Session& session_; + scenic::Session* session_; sk_sp surface_; @@ -119,20 +120,31 @@ class FakeSurfaceProducerSurface : public SurfaceProducerSurface { class FakeSurfaceProducer : public SurfaceProducer { public: - explicit FakeSurfaceProducer(scenic::Session& session) : session_(session) {} + explicit FakeSurfaceProducer(scenic::Session* session) : session_(session) {} ~FakeSurfaceProducer() override = default; + // |SurfaceProducer| + GrDirectContext* gr_context() const override { return nullptr; } + + // |SurfaceProducer| + std::unique_ptr ProduceOffscreenSurface( + const SkISize& size) override { + return nullptr; + } + + // |SurfaceProducer| std::unique_ptr ProduceSurface( const SkISize& size) override { return std::make_unique(session_, size, buffer_id_++); } + // |SurfaceProducer| void SubmitSurfaces( std::vector> surfaces) override {} private: - scenic::Session& session_; + scenic::Session* session_; uint32_t buffer_id_{1}; }; @@ -458,21 +470,25 @@ class GfxExternalViewEmbedderTest public fuchsia::ui::scenic::SessionListener { protected: GfxExternalViewEmbedderTest() - : session_listener_(this), - session_subloop_(loop_.StartNewLoop()), + : session_subloop_(loop_.StartNewLoop()), + session_listener_(this), session_connection_(CreateSessionConnection()), - fake_surface_producer_(*session_connection_.get()) {} + fake_surface_producer_( + std::make_shared(session_connection_->get())) { + } ~GfxExternalViewEmbedderTest() override = default; async::TestLoop& loop() { return loop_; } FakeSession& fake_session() { return fake_session_; } - FakeSurfaceProducer& fake_surface_producer() { + std::shared_ptr fake_surface_producer() { return fake_surface_producer_; } - GfxSessionConnection& session_connection() { return session_connection_; } + std::shared_ptr session_connection() { + return session_connection_; + } private: // |fuchsia::ui::scenic::SessionListener| @@ -483,7 +499,7 @@ class GfxExternalViewEmbedderTest FAIL(); } - GfxSessionConnection CreateSessionConnection() { + std::shared_ptr CreateSessionConnection() { FML_CHECK(!fake_session_.is_bound()); FML_CHECK(!session_listener_.is_bound()); @@ -494,22 +510,22 @@ class GfxExternalViewEmbedderTest fake_session_.Bind(session_subloop_->dispatcher()); session_listener_.Bind(std::move(session_listener)); - return GfxSessionConnection( + return std::make_shared( GetCurrentTestName(), std::move(inspect_node), std::move(session), []() { FAIL(); }, [](auto...) {}, 1, fml::TimeDelta::Zero()); } async::TestLoop loop_; // Must come before FIDL bindings. - - inspect::Inspector inspector_; + std::unique_ptr session_subloop_; fidl::Binding session_listener_; - std::unique_ptr session_subloop_; - FakeSession fake_session_; - GfxSessionConnection session_connection_; + inspect::Inspector inspector_; - FakeSurfaceProducer fake_surface_producer_; + FakeSession fake_session_; + + std::shared_ptr session_connection_; + std::shared_ptr fake_surface_producer_; }; TEST_F(GfxExternalViewEmbedderTest, RootScene) { diff --git a/engine/src/flutter/shell/platform/fuchsia/flutter/vulkan_surface_producer.cc b/engine/src/flutter/shell/platform/fuchsia/flutter/vulkan_surface_producer.cc index b341956c9a..91225efc21 100644 --- a/engine/src/flutter/shell/platform/fuchsia/flutter/vulkan_surface_producer.cc +++ b/engine/src/flutter/shell/platform/fuchsia/flutter/vulkan_surface_producer.cc @@ -14,7 +14,6 @@ #include "flutter/fml/trace_event.h" #include "third_party/skia/include/gpu/GrBackendSemaphore.h" #include "third_party/skia/include/gpu/GrBackendSurface.h" -#include "third_party/skia/include/gpu/GrDirectContext.h" #include "third_party/skia/include/gpu/vk/GrVkBackendContext.h" #include "third_party/skia/include/gpu/vk/GrVkExtensions.h" #include "third_party/skia/include/gpu/vk/GrVkTypes.h" diff --git a/engine/src/flutter/shell/platform/fuchsia/flutter/vulkan_surface_producer.h b/engine/src/flutter/shell/platform/fuchsia/flutter/vulkan_surface_producer.h index 9933f6b781..3a87467d92 100644 --- a/engine/src/flutter/shell/platform/fuchsia/flutter/vulkan_surface_producer.h +++ b/engine/src/flutter/shell/platform/fuchsia/flutter/vulkan_surface_producer.h @@ -7,6 +7,8 @@ #include #include #include +#include +#include #include "flutter/fml/macros.h" #include "flutter/fml/memory/weak_ptr.h" @@ -14,8 +16,8 @@ #include "flutter/vulkan/vulkan_device.h" #include "flutter/vulkan/vulkan_proc_table.h" #include "flutter/vulkan/vulkan_provider.h" -#include "lib/ui/scenic/cpp/resources.h" -#include "lib/ui/scenic/cpp/session.h" +#include "third_party/skia/include/gpu/GrDirectContext.h" + #include "logging.h" #include "vulkan_surface.h" #include "vulkan_surface_pool.h" @@ -30,10 +32,12 @@ class VulkanSurfaceProducer final : public SurfaceProducer, bool IsValid() const { return valid_; } - GrDirectContext* gr_context() const { return context_.get(); } + // |SurfaceProducer| + GrDirectContext* gr_context() const override { return context_.get(); } + // |SurfaceProducer| std::unique_ptr ProduceOffscreenSurface( - const SkISize& size); + const SkISize& size) override; // |SurfaceProducer| std::unique_ptr ProduceSurface(