diff --git a/flow/layers/child_scene_layer.cc b/flow/layers/child_scene_layer.cc index 2a51590ff785c..795946e0855c9 100644 --- a/flow/layers/child_scene_layer.cc +++ b/flow/layers/child_scene_layer.cc @@ -4,6 +4,8 @@ #include "flutter/flow/layers/child_scene_layer.h" +#include "flutter/flow/view_holder.h" + namespace flutter { ChildSceneLayer::ChildSceneLayer(zx_koid_t layer_id, @@ -17,10 +19,12 @@ ChildSceneLayer::ChildSceneLayer(zx_koid_t layer_id, void ChildSceneLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) { TRACE_EVENT0("flutter", "ChildSceneLayer::Preroll"); + set_needs_system_composite(true); - context->child_scene_layer_exists_below = true; CheckForChildLayerBelow(context); + context->child_scene_layer_exists_below = true; + // An alpha "hole punch" is required if the frame behind us is not opaque. if (!context->is_opaque) { set_paint_bounds( @@ -45,7 +49,15 @@ void ChildSceneLayer::Paint(PaintContext& context) const { void ChildSceneLayer::UpdateScene(SceneUpdateContext& context) { TRACE_EVENT0("flutter", "ChildSceneLayer::UpdateScene"); FML_DCHECK(needs_system_composite()); - context.UpdateView(layer_id_, offset_, size_, hit_testable_); + + Layer::UpdateScene(context); + + auto* view_holder = ViewHolder::FromId(layer_id_); + FML_DCHECK(view_holder); + + view_holder->UpdateScene(context, offset_, size_, + SkScalarRoundToInt(context.alphaf() * 255), + hit_testable_); } } // namespace flutter diff --git a/flow/layers/container_layer.cc b/flow/layers/container_layer.cc index 825826b70835f..d8bf8ed13a1b4 100644 --- a/flow/layers/container_layer.cc +++ b/flow/layers/container_layer.cc @@ -4,8 +4,6 @@ #include "flutter/flow/layers/container_layer.h" -#include - namespace flutter { ContainerLayer::ContainerLayer() {} @@ -32,9 +30,6 @@ void ContainerLayer::PrerollChildren(PrerollContext* context, const SkMatrix& child_matrix, SkRect* child_paint_bounds) { #if defined(LEGACY_FUCHSIA_EMBEDDER) - // If there is embedded Fuchsia content in the scene (a ChildSceneLayer), - // Layers that appear above the embedded content will be turned into their own - // Scenic layers. child_layer_exists_below_ = context->child_scene_layer_exists_below; context->child_scene_layer_exists_below = false; #endif @@ -103,20 +98,63 @@ void ContainerLayer::UpdateScene(SceneUpdateContext& context) { } void ContainerLayer::UpdateSceneChildren(SceneUpdateContext& context) { + auto update_scene_layers = [&] { + // Paint all of the layers which need to be drawn into the container. + // These may be flattened down to a containing Scenic Frame. + for (auto& layer : layers_) { + if (layer->needs_system_composite()) { + layer->UpdateScene(context); + } + } + }; + FML_DCHECK(needs_system_composite()); - std::optional frame; + // If there is embedded Fuchsia content in the scene (a ChildSceneLayer), + // PhysicalShapeLayers that appear above the embedded content will be turned + // into their own Scenic layers. if (child_layer_exists_below_) { - frame.emplace( - context, SkRRect::MakeRect(paint_bounds()), SK_ColorTRANSPARENT, - SkScalarRoundToInt(context.alphaf() * 255), "flutter::ContainerLayer"); - frame->AddPaintLayer(this); - } - - for (auto& layer : layers_) { - if (layer->needs_system_composite()) { - layer->UpdateScene(context); + float global_scenic_elevation = + context.GetGlobalElevationForNextScenicLayer(); + float local_scenic_elevation = + global_scenic_elevation - context.scenic_elevation(); + float z_translation = -local_scenic_elevation; + + // Retained rendering: speedup by reusing a retained entity node if + // possible. When an entity node is reused, no paint layer is added to the + // frame so we won't call PhysicalShapeLayer::Paint. + LayerRasterCacheKey key(unique_id(), context.Matrix()); + if (context.HasRetainedNode(key)) { + TRACE_EVENT_INSTANT0("flutter", "retained layer cache hit"); + scenic::EntityNode* retained_node = context.GetRetainedNode(key); + FML_DCHECK(context.top_entity()); + FML_DCHECK(retained_node->session() == context.session()); + + // Re-adjust the elevation. + retained_node->SetTranslation(0.f, 0.f, z_translation); + + context.top_entity()->entity_node().AddChild(*retained_node); + return; } + + TRACE_EVENT_INSTANT0("flutter", "cache miss, creating"); + // If we can't find an existing retained surface, create one. + SceneUpdateContext::Frame frame( + context, SkRRect::MakeRect(paint_bounds()), SK_ColorTRANSPARENT, + SkScalarRoundToInt(context.alphaf() * 255), + "flutter::PhysicalShapeLayer", z_translation, this); + + frame.AddPaintLayer(this); + + // Node: UpdateSceneChildren needs to be called here so that |frame| is + // still in scope (and therefore alive) while UpdateSceneChildren is being + // called. + float scenic_elevation = context.scenic_elevation(); + context.set_scenic_elevation(scenic_elevation + local_scenic_elevation); + update_scene_layers(); + context.set_scenic_elevation(scenic_elevation); + } else { + update_scene_layers(); } } diff --git a/flow/layers/fuchsia_layer_unittests.cc b/flow/layers/fuchsia_layer_unittests.cc index fcc17c0ad06e4..b1e7d2be05500 100644 --- a/flow/layers/fuchsia_layer_unittests.cc +++ b/flow/layers/fuchsia_layer_unittests.cc @@ -238,17 +238,57 @@ class MockSession : public fuchsia::ui::scenic::testing::Session_TestBase { fuchsia::ui::scenic::SessionListenerPtr listener_; }; -class MockSessionWrapper : public flutter::SessionWrapper { +class MockSurfaceProducerSurface + : public SceneUpdateContext::SurfaceProducerSurface { public: - MockSessionWrapper(fuchsia::ui::scenic::SessionPtr session_ptr) - : session_(std::move(session_ptr)) {} - ~MockSessionWrapper() override = default; + MockSurfaceProducerSurface(scenic::Session* session, const SkISize& size) + : image_(session, 0, 0, {}), size_(size) {} - scenic::Session* get() override { return &session_; } - void Present() override { session_.Flush(); } + size_t AdvanceAndGetAge() override { return 0; } + + bool FlushSessionAcquireAndReleaseEvents() override { return false; } + + bool IsValid() const override { return false; } + + SkISize GetSize() const override { return size_; } + + void SignalWritesFinished( + const std::function& on_writes_committed) override {} + + scenic::Image* GetImage() override { return &image_; }; + + sk_sp GetSkiaSurface() const override { return nullptr; }; private: - scenic::Session session_; + scenic::Image image_; + SkISize size_; +}; + +class MockSurfaceProducer : public SceneUpdateContext::SurfaceProducer { + public: + MockSurfaceProducer(scenic::Session* session) : session_(session) {} + std::unique_ptr ProduceSurface( + const SkISize& size, + const LayerRasterCacheKey& layer_key, + std::unique_ptr entity_node) override { + return std::make_unique(session_, size); + } + + // Query a retained entity node (owned by a retained surface) for retained + // rendering. + bool HasRetainedNode(const LayerRasterCacheKey& key) const override { + return false; + } + + scenic::EntityNode* GetRetainedNode(const LayerRasterCacheKey& key) override { + return nullptr; + } + + void SubmitSurface(std::unique_ptr + surface) override {} + + private: + scenic::Session* session_; }; struct TestContext { @@ -257,11 +297,12 @@ struct TestContext { fml::RefPtr task_runner; // Session. - fidl::InterfaceRequest listener_request; MockSession mock_session; - std::unique_ptr mock_session_wrapper; + fidl::InterfaceRequest listener_request; + std::unique_ptr session; // SceneUpdateContext. + std::unique_ptr mock_surface_producer; std::unique_ptr scene_update_context; // PrerollContext. @@ -283,13 +324,15 @@ std::unique_ptr InitTest() { fuchsia::ui::scenic::SessionListenerPtr listener; context->listener_request = listener.NewRequest(); context->mock_session.Bind(session_ptr.NewRequest(), std::move(listener)); - context->mock_session_wrapper = - std::make_unique(std::move(session_ptr)); + context->session = std::make_unique(std::move(session_ptr)); // Init SceneUpdateContext. + context->mock_surface_producer = + std::make_unique(context->session.get()); context->scene_update_context = std::make_unique( - "fuchsia_layer_unittest", fuchsia::ui::views::ViewToken(), - scenic::ViewRefPair::New(), *(context->mock_session_wrapper)); + context->session.get(), context->mock_surface_producer.get()); + context->scene_update_context->set_metrics( + fidl::MakeOptional(fuchsia::ui::gfx::Metrics{1.f, 1.f, 1.f})); // Init PrerollContext. context->preroll_context = std::unique_ptr(new PrerollContext{ @@ -305,6 +348,7 @@ std::unique_ptr InitTest() { context->unused_texture_registry, // texture registry (not // supported) false, // checkerboard_offscreen_layers + 100.f, // maximum depth allowed for rendering 1.f // ratio between logical and physical }); @@ -558,7 +602,7 @@ TEST_F(FuchsiaLayerTest, DISABLED_PhysicalShapeLayersAndChildSceneLayers) { // against the list above. root->UpdateScene(*(test_context->scene_update_context)); - test_context->mock_session_wrapper->Present(); + test_context->session->Flush(); // Run loop until idle, so that the Session receives and processes // its method calls. @@ -740,7 +784,7 @@ TEST_F(FuchsiaLayerTest, DISABLED_OpacityAndTransformLayer) { // commands against the list above. root->UpdateScene(*(test_context->scene_update_context)); - test_context->mock_session_wrapper->Present(); + test_context->session->Flush(); // Run loop until idle, so that the Session receives and processes // its method calls. diff --git a/flow/layers/layer.cc b/flow/layers/layer.cc index a242f977cde29..97da04f7f54c8 100644 --- a/flow/layers/layer.cc +++ b/flow/layers/layer.cc @@ -58,9 +58,6 @@ Layer::AutoPrerollSaveLayerState::~AutoPrerollSaveLayerState() { #if defined(LEGACY_FUCHSIA_EMBEDDER) void Layer::CheckForChildLayerBelow(PrerollContext* context) { - // If there is embedded Fuchsia content in the scene (a ChildSceneLayer), - // PhysicalShapeLayers that appear above the embedded content will be turned - // into their own Scenic layers. child_layer_exists_below_ = context->child_scene_layer_exists_below; if (child_layer_exists_below_) { set_needs_system_composite(true); @@ -68,14 +65,42 @@ void Layer::CheckForChildLayerBelow(PrerollContext* context) { } void Layer::UpdateScene(SceneUpdateContext& context) { - FML_DCHECK(needs_system_composite()); - FML_DCHECK(child_layer_exists_below_); - - SceneUpdateContext::Frame frame( - context, SkRRect::MakeRect(paint_bounds()), SK_ColorTRANSPARENT, - SkScalarRoundToInt(context.alphaf() * 255), "flutter::Layer"); - - frame.AddPaintLayer(this); + // If there is embedded Fuchsia content in the scene (a ChildSceneLayer), + // PhysicalShapeLayers that appear above the embedded content will be turned + // into their own Scenic layers. + if (child_layer_exists_below_) { + float global_scenic_elevation = + context.GetGlobalElevationForNextScenicLayer(); + float local_scenic_elevation = + global_scenic_elevation - context.scenic_elevation(); + float z_translation = -local_scenic_elevation; + + // Retained rendering: speedup by reusing a retained entity node if + // possible. When an entity node is reused, no paint layer is added to the + // frame so we won't call PhysicalShapeLayer::Paint. + LayerRasterCacheKey key(unique_id(), context.Matrix()); + if (context.HasRetainedNode(key)) { + TRACE_EVENT_INSTANT0("flutter", "retained layer cache hit"); + scenic::EntityNode* retained_node = context.GetRetainedNode(key); + FML_DCHECK(context.top_entity()); + FML_DCHECK(retained_node->session() == context.session()); + + // Re-adjust the elevation. + retained_node->SetTranslation(0.f, 0.f, z_translation); + + context.top_entity()->entity_node().AddChild(*retained_node); + return; + } + + TRACE_EVENT_INSTANT0("flutter", "cache miss, creating"); + // If we can't find an existing retained surface, create one. + SceneUpdateContext::Frame frame( + context, SkRRect::MakeRect(paint_bounds()), SK_ColorTRANSPARENT, + SkScalarRoundToInt(context.alphaf() * 255), + "flutter::PhysicalShapeLayer", z_translation, this); + + frame.AddPaintLayer(this); + } } #endif diff --git a/flow/layers/layer.h b/flow/layers/layer.h index f9732760a7770..b22a322a73422 100644 --- a/flow/layers/layer.h +++ b/flow/layers/layer.h @@ -56,10 +56,14 @@ struct PrerollContext { const Stopwatch& ui_time; TextureRegistry& texture_registry; const bool checkerboard_offscreen_layers; - const float frame_device_pixel_ratio; + + // These allow us to make use of the scene metrics during Preroll. + float frame_physical_depth; + float frame_device_pixel_ratio; // These allow us to track properties like elevation, opacity, and the // prescence of a platform view during Preroll. + float total_elevation = 0.0f; bool has_platform_view = false; bool is_opaque = true; #if defined(LEGACY_FUCHSIA_EMBEDDER) @@ -124,7 +128,10 @@ class Layer { TextureRegistry& texture_registry; const RasterCache* raster_cache; const bool checkerboard_offscreen_layers; - const float frame_device_pixel_ratio; + + // These allow us to make use of the scene metrics during Paint. + float frame_physical_depth; + float frame_device_pixel_ratio; }; // Calls SkCanvas::saveLayer and restores the layer upon destruction. Also diff --git a/flow/layers/layer_tree.cc b/flow/layers/layer_tree.cc index 9b247b496b613..0f598ae6f153f 100644 --- a/flow/layers/layer_tree.cc +++ b/flow/layers/layer_tree.cc @@ -11,14 +11,15 @@ namespace flutter { -LayerTree::LayerTree(const SkISize& frame_size, float device_pixel_ratio) +LayerTree::LayerTree(const SkISize& frame_size, + float frame_physical_depth, + float frame_device_pixel_ratio) : frame_size_(frame_size), - device_pixel_ratio_(device_pixel_ratio), + frame_physical_depth_(frame_physical_depth), + frame_device_pixel_ratio_(frame_device_pixel_ratio), rasterizer_tracing_threshold_(0), checkerboard_raster_cache_images_(false), - checkerboard_offscreen_layers_(false) { - FML_CHECK(device_pixel_ratio_ != 0.0f); -} + checkerboard_offscreen_layers_(false) {} void LayerTree::RecordBuildTime(fml::TimePoint build_start, fml::TimePoint target_time) { @@ -53,21 +54,32 @@ bool LayerTree::Preroll(CompositorContext::ScopedFrame& frame, frame.context().ui_time(), frame.context().texture_registry(), checkerboard_offscreen_layers_, - device_pixel_ratio_}; + frame_physical_depth_, + frame_device_pixel_ratio_}; root_layer_->Preroll(&context, frame.root_surface_transformation()); return context.surface_needs_readback; } #if defined(LEGACY_FUCHSIA_EMBEDDER) -void LayerTree::UpdateScene(SceneUpdateContext& context) { +void LayerTree::UpdateScene(SceneUpdateContext& context, + scenic::ContainerNode& container) { TRACE_EVENT0("flutter", "LayerTree::UpdateScene"); - // Reset for a new Scene. - context.Reset(); + // Ensure the context is aware of the view metrics. + context.set_dimensions(frame_size_, frame_physical_depth_, + frame_device_pixel_ratio_); + + const auto& metrics = context.metrics(); + FML_DCHECK(metrics->scale_x > 0.0f); + FML_DCHECK(metrics->scale_y > 0.0f); + FML_DCHECK(metrics->scale_z > 0.0f); - const float inv_dpr = 1.0f / device_pixel_ratio_; - SceneUpdateContext::Transform transform(context, inv_dpr, inv_dpr, 1.0f); + SceneUpdateContext::Transform transform(context, // context + 1.0f / metrics->scale_x, // X + 1.0f / metrics->scale_y, // Y + 1.0f / metrics->scale_z // Z + ); SceneUpdateContext::Frame frame( context, @@ -80,7 +92,7 @@ void LayerTree::UpdateScene(SceneUpdateContext& context) { if (root_layer_->needs_painting()) { frame.AddPaintLayer(root_layer_.get()); } - context.root_node().AddChild(transform.entity_node()); + container.AddChild(transform.entity_node()); } #endif @@ -113,7 +125,8 @@ void LayerTree::Paint(CompositorContext::ScopedFrame& frame, frame.context().texture_registry(), ignore_raster_cache ? nullptr : &frame.context().raster_cache(), checkerboard_offscreen_layers_, - device_pixel_ratio_}; + frame_physical_depth_, + frame_device_pixel_ratio_}; if (root_layer_->needs_painting()) { root_layer_->Paint(context); @@ -138,18 +151,19 @@ sk_sp LayerTree::Flatten(const SkRect& bounds) { root_surface_transformation.reset(); PrerollContext preroll_context{ - nullptr, // raster_cache (don't consult the cache) - nullptr, // gr_context (used for the raster cache) - nullptr, // external view embedder - unused_stack, // mutator stack - nullptr, // SkColorSpace* dst_color_space - kGiantRect, // SkRect cull_rect - false, // layer reads from surface - unused_stopwatch, // frame time (dont care) - unused_stopwatch, // engine time (dont care) - unused_texture_registry, // texture registry (not supported) - false, // checkerboard_offscreen_layers - device_pixel_ratio_ // ratio between logical and physical + nullptr, // raster_cache (don't consult the cache) + nullptr, // gr_context (used for the raster cache) + nullptr, // external view embedder + unused_stack, // mutator stack + nullptr, // SkColorSpace* dst_color_space + kGiantRect, // SkRect cull_rect + false, // layer reads from surface + unused_stopwatch, // frame time (dont care) + unused_stopwatch, // engine time (dont care) + unused_texture_registry, // texture registry (not supported) + false, // checkerboard_offscreen_layers + frame_physical_depth_, // maximum depth allowed for rendering + frame_device_pixel_ratio_ // ratio between logical and physical }; SkISize canvas_size = canvas->getBaseLayerSize(); @@ -161,12 +175,13 @@ sk_sp LayerTree::Flatten(const SkRect& bounds) { canvas, // canvas nullptr, nullptr, - unused_stopwatch, // frame time (dont care) - unused_stopwatch, // engine time (dont care) - unused_texture_registry, // texture registry (not supported) - nullptr, // raster cache - false, // checkerboard offscreen layers - device_pixel_ratio_ // ratio between logical and physical + unused_stopwatch, // frame time (dont care) + unused_stopwatch, // engine time (dont care) + unused_texture_registry, // texture registry (not supported) + nullptr, // raster cache + false, // checkerboard offscreen layers + frame_physical_depth_, // maximum depth allowed for rendering + frame_device_pixel_ratio_ // ratio between logical and physical }; // Even if we don't have a root layer, we still need to create an empty diff --git a/flow/layers/layer_tree.h b/flow/layers/layer_tree.h index efee96071e6c6..733284afe65db 100644 --- a/flow/layers/layer_tree.h +++ b/flow/layers/layer_tree.h @@ -20,7 +20,9 @@ namespace flutter { class LayerTree { public: - LayerTree(const SkISize& frame_size, float device_pixel_ratio); + LayerTree(const SkISize& frame_size, + float frame_physical_depth, + float frame_device_pixel_ratio); // Perform a preroll pass on the tree and return information about // the tree that affects rendering this frame. @@ -33,7 +35,8 @@ class LayerTree { bool ignore_raster_cache = false); #if defined(LEGACY_FUCHSIA_EMBEDDER) - void UpdateScene(SceneUpdateContext& context); + void UpdateScene(SceneUpdateContext& context, + scenic::ContainerNode& container); #endif void Paint(CompositorContext::ScopedFrame& frame, @@ -48,7 +51,8 @@ class LayerTree { } const SkISize& frame_size() const { return frame_size_; } - float device_pixel_ratio() const { return device_pixel_ratio_; } + float frame_physical_depth() const { return frame_physical_depth_; } + float frame_device_pixel_ratio() const { return frame_device_pixel_ratio_; } void RecordBuildTime(fml::TimePoint build_start, fml::TimePoint target_time); fml::TimePoint build_start() const { return build_start_; } @@ -75,13 +79,16 @@ class LayerTree { checkerboard_offscreen_layers_ = checkerboard; } + double device_pixel_ratio() const { return frame_device_pixel_ratio_; } + private: std::shared_ptr root_layer_; fml::TimePoint build_start_; fml::TimePoint build_finish_; fml::TimePoint target_time_; SkISize frame_size_ = SkISize::MakeEmpty(); // Physical pixels. - const float device_pixel_ratio_; // Logical / Physical pixels ratio. + float frame_physical_depth_; + float frame_device_pixel_ratio_ = 1.0f; // Logical / Physical pixels ratio. uint32_t rasterizer_tracing_threshold_; bool checkerboard_raster_cache_images_; bool checkerboard_offscreen_layers_; diff --git a/flow/layers/layer_tree_unittests.cc b/flow/layers/layer_tree_unittests.cc index 7045497692afe..1215b72f78726 100644 --- a/flow/layers/layer_tree_unittests.cc +++ b/flow/layers/layer_tree_unittests.cc @@ -18,7 +18,7 @@ namespace testing { class LayerTreeTest : public CanvasTest { public: LayerTreeTest() - : layer_tree_(SkISize::Make(64, 64), 1.0f), + : layer_tree_(SkISize::Make(64, 64), 100.0f, 1.0f), compositor_context_(fml::kDefaultFrameBudget), root_transform_(SkMatrix::Translate(1.0f, 1.0f)), scoped_frame_(compositor_context_.AcquireFrame(nullptr, diff --git a/flow/layers/opacity_layer.h b/flow/layers/opacity_layer.h index ed5f0283ad356..73e508f854bc4 100644 --- a/flow/layers/opacity_layer.h +++ b/flow/layers/opacity_layer.h @@ -38,6 +38,7 @@ class OpacityLayer : public MergedContainerLayer { private: SkAlpha alpha_; SkPoint offset_; + SkRRect frameRRect_; FML_DISALLOW_COPY_AND_ASSIGN(OpacityLayer); }; diff --git a/flow/layers/physical_shape_layer.cc b/flow/layers/physical_shape_layer.cc index 7ba2b7cb734ea..4f87fb23605a0 100644 --- a/flow/layers/physical_shape_layer.cc +++ b/flow/layers/physical_shape_layer.cc @@ -21,7 +21,28 @@ PhysicalShapeLayer::PhysicalShapeLayer(SkColor color, shadow_color_(shadow_color), elevation_(elevation), path_(path), - clip_behavior_(clip_behavior) {} + isRect_(false), + clip_behavior_(clip_behavior) { + SkRect rect; + if (path.isRect(&rect)) { + isRect_ = true; + frameRRect_ = SkRRect::MakeRect(rect); + } else if (path.isRRect(&frameRRect_)) { + isRect_ = frameRRect_.isRect(); + } else if (path.isOval(&rect)) { + // isRRect returns false for ovals, so we need to explicitly check isOval + // as well. + frameRRect_ = SkRRect::MakeOval(rect); + } else { + // Scenic currently doesn't provide an easy way to create shapes from + // arbitrary paths. + // For shapes that cannot be represented as a rounded rectangle we + // default to use the bounding rectangle. + // TODO(amirh): fix this once we have a way to create a Scenic shape from + // an SkPath. + frameRRect_ = SkRRect::MakeRect(path.getBounds()); + } +} void PhysicalShapeLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) { @@ -29,9 +50,14 @@ void PhysicalShapeLayer::Preroll(PrerollContext* context, Layer::AutoPrerollSaveLayerState save = Layer::AutoPrerollSaveLayerState::Create(context, UsesSaveLayer()); + context->total_elevation += elevation_; + total_elevation_ = context->total_elevation; + SkRect child_paint_bounds; PrerollChildren(context, matrix, &child_paint_bounds); + context->total_elevation -= elevation_; + if (elevation_ == 0) { set_paint_bounds(path_.getBounds()); } else { diff --git a/flow/layers/physical_shape_layer.h b/flow/layers/physical_shape_layer.h index ce49af1a003ae..2c04368e9a81e 100644 --- a/flow/layers/physical_shape_layer.h +++ b/flow/layers/physical_shape_layer.h @@ -35,13 +35,16 @@ class PhysicalShapeLayer : public ContainerLayer { return clip_behavior_ == Clip::antiAliasWithSaveLayer; } - float elevation() const { return elevation_; } + float total_elevation() const { return total_elevation_; } private: SkColor color_; SkColor shadow_color_; float elevation_ = 0.0f; + float total_elevation_ = 0.0f; SkPath path_; + bool isRect_; + SkRRect frameRRect_; Clip clip_behavior_; }; diff --git a/flow/layers/physical_shape_layer_unittests.cc b/flow/layers/physical_shape_layer_unittests.cc index bb5d0acfad757..7ad0b4e5eddcb 100644 --- a/flow/layers/physical_shape_layer_unittests.cc +++ b/flow/layers/physical_shape_layer_unittests.cc @@ -131,7 +131,7 @@ TEST_F(PhysicalShapeLayerTest, ElevationSimple) { initial_elevation, 1.0f)); EXPECT_TRUE(layer->needs_painting()); EXPECT_FALSE(layer->needs_system_composite()); - EXPECT_EQ(layer->elevation(), initial_elevation); + EXPECT_EQ(layer->total_elevation(), initial_elevation); // The Fuchsia system compositor handles all elevated PhysicalShapeLayers and // their shadows , so we do not use the direct |Paint()| path there. @@ -162,6 +162,7 @@ TEST_F(PhysicalShapeLayerTest, ElevationComplex) { // | // layers[1] + 2.0f = 3.0f constexpr float initial_elevations[4] = {1.0f, 2.0f, 3.0f, 4.0f}; + constexpr float total_elevations[4] = {1.0f, 3.0f, 4.0f, 8.0f}; SkPath layer_path; layer_path.addRect(0, 0, 80, 80).close(); @@ -186,6 +187,7 @@ TEST_F(PhysicalShapeLayerTest, ElevationComplex) { 1.0f /* pixel_ratio */))); EXPECT_TRUE(layers[i]->needs_painting()); EXPECT_FALSE(layers[i]->needs_system_composite()); + EXPECT_EQ(layers[i]->total_elevation(), total_elevations[i]); } // The Fuchsia system compositor handles all elevated PhysicalShapeLayers and diff --git a/flow/layers/platform_view_layer.cc b/flow/layers/platform_view_layer.cc index 80514b5213e18..0bd6ee7f6dfab 100644 --- a/flow/layers/platform_view_layer.cc +++ b/flow/layers/platform_view_layer.cc @@ -48,9 +48,7 @@ void PlatformViewLayer::Paint(PaintContext& context) const { #if defined(LEGACY_FUCHSIA_EMBEDDER) void PlatformViewLayer::UpdateScene(SceneUpdateContext& context) { - TRACE_EVENT0("flutter", "PlatformViewLayer::UpdateScene"); - FML_DCHECK(needs_system_composite()); - context.UpdateView(view_id_, offset_, size_); + context.UpdateScene(view_id_, offset_, size_); } #endif diff --git a/flow/layers/transform_layer.cc b/flow/layers/transform_layer.cc index 8fe5dd32e1e85..d01c21950e498 100644 --- a/flow/layers/transform_layer.cc +++ b/flow/layers/transform_layer.cc @@ -4,8 +4,6 @@ #include "flutter/flow/layers/transform_layer.h" -#include - namespace flutter { TransformLayer::TransformLayer(const SkMatrix& transform) @@ -58,12 +56,12 @@ void TransformLayer::UpdateScene(SceneUpdateContext& context) { TRACE_EVENT0("flutter", "TransformLayer::UpdateScene"); FML_DCHECK(needs_system_composite()); - std::optional transform; if (!transform_.isIdentity()) { - transform.emplace(context, transform_); + SceneUpdateContext::Transform transform(context, transform_); + UpdateSceneChildren(context); + } else { + UpdateSceneChildren(context); } - - UpdateSceneChildren(context); } #endif diff --git a/flow/raster_cache.cc b/flow/raster_cache.cc index c39a7e7e22f7e..1e39e3701a679 100644 --- a/flow/raster_cache.cc +++ b/flow/raster_cache.cc @@ -169,6 +169,7 @@ std::unique_ptr RasterCache::RasterizeLayer( context->texture_registry, context->has_platform_view ? nullptr : context->raster_cache, context->checkerboard_offscreen_layers, + context->frame_physical_depth, context->frame_device_pixel_ratio}; if (layer->needs_painting()) { layer->Paint(paintContext); diff --git a/flow/scene_update_context.cc b/flow/scene_update_context.cc index 698a23ac96e26..b0628edf0c417 100644 --- a/flow/scene_update_context.cc +++ b/flow/scene_update_context.cc @@ -4,7 +4,6 @@ #include "flutter/flow/scene_update_context.h" -#include #include #include "flutter/flow/layers/layer.h" @@ -14,10 +13,10 @@ #include "include/core/SkColor.h" namespace flutter { -namespace { -void SetEntityNodeClipPlanes(scenic::EntityNode& entity_node, - const SkRect& bounds) { +// Helper function to generate clip planes for a scenic::EntityNode. +static void SetEntityNodeClipPlanes(scenic::EntityNode& entity_node, + const SkRect& bounds) { const float top = bounds.top(); const float bottom = bounds.bottom(); const float left = bounds.left(); @@ -54,69 +53,20 @@ void SetEntityNodeClipPlanes(scenic::EntityNode& entity_node, entity_node.SetClipPlanes(std::move(clip_planes)); } -void SetMaterialColor(scenic::Material& material, - SkColor color, - SkAlpha opacity) { - const SkAlpha color_alpha = static_cast( - ((float)SkColorGetA(color) * (float)opacity) / 255.0f); - material.SetColor(SkColorGetR(color), SkColorGetG(color), SkColorGetB(color), - color_alpha); -} - -} // namespace - -SceneUpdateContext::SceneUpdateContext(std::string debug_label, - fuchsia::ui::views::ViewToken view_token, - scenic::ViewRefPair view_ref_pair, - SessionWrapper& session) - : session_(session), - root_view_(session_.get(), - std::move(view_token), - std::move(view_ref_pair.control_ref), - std::move(view_ref_pair.view_ref), - debug_label), - root_node_(session_.get()) { - root_view_.AddChild(root_node_); - root_node_.SetEventMask(fuchsia::ui::gfx::kMetricsEventMask); - - session_.Present(); -} - -std::vector SceneUpdateContext::GetPaintTasks() { - std::vector frame_paint_tasks = std::move(paint_tasks_); - - paint_tasks_.clear(); - - return frame_paint_tasks; +SceneUpdateContext::SceneUpdateContext(scenic::Session* session, + SurfaceProducer* surface_producer) + : session_(session), surface_producer_(surface_producer) { + FML_DCHECK(surface_producer_ != nullptr); } -void SceneUpdateContext::EnableWireframe(bool enable) { - session_.get()->Enqueue( - scenic::NewSetEnableDebugViewBoundsCmd(root_view_.id(), enable)); -} - -void SceneUpdateContext::Reset() { - paint_tasks_.clear(); - top_entity_ = nullptr; - top_scale_x_ = 1.f; - top_scale_y_ = 1.f; - top_elevation_ = 0.f; - next_elevation_ = 0.f; - alpha_ = 1.f; - - // We are going to be sending down a fresh node hierarchy every frame. So just - // enqueue a detach op on the imported root node. - session_.get()->Enqueue(scenic::NewDetachChildrenCmd(root_node_.id())); -} - -void SceneUpdateContext::CreateFrame(scenic::EntityNode& entity_node, +void SceneUpdateContext::CreateFrame(scenic::EntityNode entity_node, const SkRRect& rrect, SkColor color, SkAlpha opacity, const SkRect& paint_bounds, - std::vector paint_layers) { - if (rrect.isEmpty()) - return; + std::vector paint_layers, + Layer* layer) { + FML_DCHECK(!rrect.isEmpty()); // Frames always clip their children. SkRect shape_bounds = rrect.getBounds(); @@ -124,8 +74,11 @@ void SceneUpdateContext::CreateFrame(scenic::EntityNode& entity_node, // and possibly for its texture. // TODO(SCN-137): Need to be able to express the radii as vectors. - scenic::ShapeNode shape_node(session_.get()); - scenic::Rectangle shape(session_.get(), rrect.width(), rrect.height()); + scenic::ShapeNode shape_node(session()); + scenic::Rectangle shape(session_, // session + rrect.width(), // width + rrect.height() // height + ); shape_node.SetShape(shape); shape_node.SetTranslation(shape_bounds.width() * 0.5f + shape_bounds.left(), shape_bounds.height() * 0.5f + shape_bounds.top(), @@ -135,49 +88,155 @@ void SceneUpdateContext::CreateFrame(scenic::EntityNode& entity_node, if (paint_bounds.isEmpty() || !paint_bounds.intersects(shape_bounds)) paint_layers.clear(); - scenic::Material material(session_.get()); + scenic::Material material(session()); shape_node.SetMaterial(material); entity_node.AddChild(shape_node); // Check whether a solid color will suffice. - if (paint_layers.empty() || shape_bounds.isEmpty()) { + if (paint_layers.empty()) { SetMaterialColor(material, color, opacity); } else { + // Apply current metrics and transformation scale factors. + const float scale_x = ScaleX(); + const float scale_y = ScaleY(); + + // Apply a texture to the whole shape. + SetMaterialTextureAndColor(material, color, opacity, scale_x, scale_y, + shape_bounds, std::move(paint_layers), layer, + std::move(entity_node)); + } +} + +void SceneUpdateContext::SetMaterialTextureAndColor( + scenic::Material& material, + SkColor color, + SkAlpha opacity, + SkScalar scale_x, + SkScalar scale_y, + const SkRect& paint_bounds, + std::vector paint_layers, + Layer* layer, + scenic::EntityNode entity_node) { + scenic::Image* image = GenerateImageIfNeeded( + color, scale_x, scale_y, paint_bounds, std::move(paint_layers), layer, + std::move(entity_node)); + + if (image != nullptr) { // The final shape's color is material_color * texture_color. The passed in // material color was already used as a background when generating the // texture, so set the model color to |SK_ColorWHITE| in order to allow // using the texture's color unmodified. SetMaterialColor(material, SK_ColorWHITE, opacity); + material.SetTexture(*image); + } else { + // No texture was needed, so apply a solid color to the whole shape. + SetMaterialColor(material, color, opacity); + } +} - // Enqueue a paint task for these layers, to apply a texture to the whole - // shape. - paint_tasks_.emplace_back(PaintTask{.paint_bounds = paint_bounds, - .scale_x = top_scale_x_, - .scale_y = top_scale_y_, - .background_color = color, - .material = std::move(material), - .layers = std::move(paint_layers)}); +void SceneUpdateContext::SetMaterialColor(scenic::Material& material, + SkColor color, + SkAlpha opacity) { + const SkAlpha color_alpha = static_cast( + ((float)SkColorGetA(color) * (float)opacity) / 255.0f); + material.SetColor(SkColorGetR(color), SkColorGetG(color), SkColorGetB(color), + color_alpha); +} + +scenic::Image* SceneUpdateContext::GenerateImageIfNeeded( + SkColor color, + SkScalar scale_x, + SkScalar scale_y, + const SkRect& paint_bounds, + std::vector paint_layers, + Layer* layer, + scenic::EntityNode entity_node) { + // Bail if there's nothing to paint. + if (paint_layers.empty()) + return nullptr; + + // Bail if the physical bounds are empty after rounding. + SkISize physical_size = SkISize::Make(paint_bounds.width() * scale_x, + paint_bounds.height() * scale_y); + if (physical_size.isEmpty()) + return nullptr; + + // Acquire a surface from the surface producer and register the paint tasks. + std::unique_ptr surface = + surface_producer_->ProduceSurface( + physical_size, + LayerRasterCacheKey( + // Root frame has a nullptr layer + layer ? layer->unique_id() : 0, Matrix()), + std::make_unique(std::move(entity_node))); + + if (!surface) { + FML_LOG(ERROR) << "Could not acquire a surface from the surface producer " + "of size: " + << physical_size.width() << "x" << physical_size.height(); + return nullptr; } + + auto image = surface->GetImage(); + + // Enqueue the paint task. + paint_tasks_.push_back({.surface = std::move(surface), + .left = paint_bounds.left(), + .top = paint_bounds.top(), + .scale_x = scale_x, + .scale_y = scale_y, + .background_color = color, + .layers = std::move(paint_layers)}); + return image; } -void SceneUpdateContext::UpdateView(int64_t view_id, - const SkPoint& offset, - const SkSize& size, - std::optional override_hit_testable) { +std::vector< + std::unique_ptr> +SceneUpdateContext::ExecutePaintTasks(CompositorContext::ScopedFrame& frame) { + TRACE_EVENT0("flutter", "SceneUpdateContext::ExecutePaintTasks"); + std::vector> surfaces_to_submit; + for (auto& task : paint_tasks_) { + FML_DCHECK(task.surface); + SkCanvas* canvas = task.surface->GetSkiaSurface()->getCanvas(); + Layer::PaintContext context = {canvas, + canvas, + frame.gr_context(), + nullptr, + frame.context().raster_time(), + frame.context().ui_time(), + frame.context().texture_registry(), + &frame.context().raster_cache(), + false, + frame_physical_depth_, + frame_device_pixel_ratio_}; + canvas->restoreToCount(1); + canvas->save(); + canvas->clear(task.background_color); + canvas->scale(task.scale_x, task.scale_y); + canvas->translate(-task.left, -task.top); + for (Layer* layer : task.layers) { + layer->Paint(context); + } + surfaces_to_submit.emplace_back(std::move(task.surface)); + } + paint_tasks_.clear(); + alpha_ = 1.f; + topmost_global_scenic_elevation_ = kScenicZElevationBetweenLayers; + scenic_elevation_ = 0.f; + return surfaces_to_submit; +} + +void SceneUpdateContext::UpdateScene(int64_t view_id, + const SkPoint& offset, + const SkSize& size) { auto* view_holder = ViewHolder::FromId(view_id); FML_DCHECK(view_holder); - if (size.width() > 0.f && size.height() > 0.f) { - view_holder->SetProperties(size.width(), size.height(), 0, 0, 0, 0, - view_holder->focusable()); - } - - bool hit_testable = override_hit_testable.has_value() - ? *override_hit_testable - : view_holder->hit_testable(); - view_holder->UpdateScene(session_.get(), top_entity_->embedder_node(), offset, - size, SkScalarRoundToInt(alphaf() * 255), - hit_testable); + view_holder->SetProperties(size.width(), size.height(), 0, 0, 0, 0, + view_holder->focusable()); + view_holder->UpdateScene(*this, offset, size, + SkScalarRoundToInt(alphaf() * 255), + view_holder->hit_testable()); } void SceneUpdateContext::CreateView(int64_t view_id, @@ -201,17 +260,13 @@ void SceneUpdateContext::DestroyView(int64_t view_id) { SceneUpdateContext::Entity::Entity(SceneUpdateContext& context) : context_(context), previous_entity_(context.top_entity_), - entity_node_(context.session_.get()) { + entity_node_(context.session()) { + if (previous_entity_) + previous_entity_->embedder_node().AddChild(entity_node_); context.top_entity_ = this; } SceneUpdateContext::Entity::~Entity() { - if (previous_entity_) { - previous_entity_->embedder_node().AddChild(entity_node_); - } else { - context_.root_node_.AddChild(entity_node_); - } - FML_DCHECK(context_.top_entity_ == this); context_.top_entity_ = previous_entity_; } @@ -274,20 +329,18 @@ SceneUpdateContext::Frame::Frame(SceneUpdateContext& context, const SkRRect& rrect, SkColor color, SkAlpha opacity, - std::string label) + std::string label, + float z_translation, + Layer* layer) : Entity(context), - previous_elevation_(context.top_elevation_), rrect_(rrect), color_(color), opacity_(opacity), - opacity_node_(context.session_.get()), - paint_bounds_(SkRect::MakeEmpty()) { + opacity_node_(context.session()), + paint_bounds_(SkRect::MakeEmpty()), + layer_(layer) { entity_node().SetLabel(label); - entity_node().SetTranslation(0.f, 0.f, - context.next_elevation_ - previous_elevation_); - context.top_elevation_ += kScenicZElevationBetweenLayers; - context.next_elevation_ += kScenicZElevationBetweenLayers; - + entity_node().SetTranslation(0.f, 0.f, z_translation); entity_node().AddChild(opacity_node_); // Scenic currently lacks an API to enable rendering of alpha channel; alpha // channels are only rendered if there is a OpacityNode higher in the tree @@ -297,11 +350,20 @@ SceneUpdateContext::Frame::Frame(SceneUpdateContext& context, } SceneUpdateContext::Frame::~Frame() { - // Add a part which represents the frame's geometry for clipping purposes - context().CreateFrame(entity_node(), rrect_, color_, opacity_, paint_bounds_, - std::move(paint_layers_)); + // We don't need a shape if the frame is zero size. + if (rrect_.isEmpty()) + return; + + // isEmpty should account for this, but we are adding these experimental + // checks to validate if this is the root cause for b/144933519. + if (std::isnan(rrect_.width()) || std::isnan(rrect_.height())) { + FML_LOG(ERROR) << "Invalid RoundedRectangle"; + return; + } - context().top_elevation_ = previous_elevation_; + // Add a part which represents the frame's geometry for clipping purposes + context().CreateFrame(std::move(entity_node()), rrect_, color_, opacity_, + paint_bounds_, std::move(paint_layers_), layer_); } void SceneUpdateContext::Frame::AddPaintLayer(Layer* layer) { diff --git a/flow/scene_update_context.h b/flow/scene_update_context.h index 9ca6c7dde4c05..3b46fb2247f3c 100644 --- a/flow/scene_update_context.h +++ b/flow/scene_update_context.h @@ -5,19 +5,18 @@ #ifndef FLUTTER_FLOW_SCENE_UPDATE_CONTEXT_H_ #define FLUTTER_FLOW_SCENE_UPDATE_CONTEXT_H_ -#include -#include -#include -#include - #include #include #include #include +#include "flutter/flow/compositor_context.h" #include "flutter/flow/embedded_views.h" +#include "flutter/flow/raster_cache_key.h" +#include "flutter/fml/compiler_specific.h" #include "flutter/fml/logging.h" #include "flutter/fml/macros.h" +#include "lib/ui/scenic/cpp/resources.h" #include "third_party/skia/include/core/SkRect.h" #include "third_party/skia/include/core/SkSurface.h" @@ -34,16 +33,50 @@ constexpr float kOneMinusEpsilon = 1 - FLT_EPSILON; // How much layers are separated in Scenic z elevation. constexpr float kScenicZElevationBetweenLayers = 10.f; -class SessionWrapper { +class SceneUpdateContext : public flutter::ExternalViewEmbedder { public: - virtual ~SessionWrapper() {} + class SurfaceProducerSurface { + public: + virtual ~SurfaceProducerSurface() = default; - virtual scenic::Session* get() = 0; - virtual void Present() = 0; -}; + virtual size_t AdvanceAndGetAge() = 0; + + virtual bool FlushSessionAcquireAndReleaseEvents() = 0; + + virtual bool IsValid() const = 0; + + virtual SkISize GetSize() const = 0; + + virtual void SignalWritesFinished( + const std::function& on_writes_committed) = 0; + + virtual scenic::Image* GetImage() = 0; + + virtual sk_sp GetSkiaSurface() const = 0; + }; + + class SurfaceProducer { + public: + virtual ~SurfaceProducer() = default; + + // The produced surface owns the entity_node and has a layer_key for + // retained rendering. The surface will only be retained if the layer_key + // has a non-null layer pointer (layer_key.id()). + virtual std::unique_ptr ProduceSurface( + const SkISize& size, + const LayerRasterCacheKey& layer_key, + std::unique_ptr entity_node) = 0; + + // Query a retained entity node (owned by a retained surface) for retained + // rendering. + virtual bool HasRetainedNode(const LayerRasterCacheKey& key) const = 0; + virtual scenic::EntityNode* GetRetainedNode( + const LayerRasterCacheKey& key) = 0; + + virtual void SubmitSurface( + std::unique_ptr surface) = 0; + }; -class SceneUpdateContext : public flutter::ExternalViewEmbedder { - public: class Entity { public: Entity(SceneUpdateContext& context); @@ -83,7 +116,9 @@ class SceneUpdateContext : public flutter::ExternalViewEmbedder { const SkRRect& rrect, SkColor color, SkAlpha opacity, - std::string label); + std::string label, + float z_translation = 0.0f, + Layer* layer = nullptr); virtual ~Frame(); scenic::ContainerNode& embedder_node() override { return opacity_node_; } @@ -91,8 +126,6 @@ class SceneUpdateContext : public flutter::ExternalViewEmbedder { void AddPaintLayer(Layer* layer); private: - const float previous_elevation_; - const SkRRect rrect_; SkColor const color_; SkAlpha const opacity_; @@ -100,6 +133,7 @@ class SceneUpdateContext : public flutter::ExternalViewEmbedder { scenic::OpacityNodeHACK opacity_node_; std::vector paint_layers_; SkRect paint_bounds_; + Layer* layer_; }; class Clip : public Entity { @@ -108,35 +142,68 @@ class SceneUpdateContext : public flutter::ExternalViewEmbedder { ~Clip() = default; }; - struct PaintTask { - SkRect paint_bounds; - SkScalar scale_x; - SkScalar scale_y; - SkColor background_color; - scenic::Material material; - std::vector layers; - }; - - SceneUpdateContext(std::string debug_label, - fuchsia::ui::views::ViewToken view_token, - scenic::ViewRefPair view_ref_pair, - SessionWrapper& session); + SceneUpdateContext(scenic::Session* session, + SurfaceProducer* surface_producer); ~SceneUpdateContext() = default; - scenic::ContainerNode& root_node() { return root_node_; } + scenic::Session* session() { return session_; } + + Entity* top_entity() { return top_entity_; } + + bool has_metrics() const { return !!metrics_; } + void set_metrics(fuchsia::ui::gfx::MetricsPtr metrics) { + metrics_ = std::move(metrics); + } + const fuchsia::ui::gfx::MetricsPtr& metrics() const { return metrics_; } + + void set_dimensions(const SkISize& frame_physical_size, + float frame_physical_depth, + float frame_device_pixel_ratio) { + frame_physical_size_ = frame_physical_size; + frame_physical_depth_ = frame_physical_depth; + frame_device_pixel_ratio_ = frame_device_pixel_ratio; + } + const SkISize& frame_size() const { return frame_physical_size_; } + float frame_physical_depth() const { return frame_physical_depth_; } + float frame_device_pixel_ratio() const { return frame_device_pixel_ratio_; } + + // TODO(chinmaygarde): This method must submit the surfaces as soon as paint + // tasks are done. However, given that there is no support currently for + // Vulkan semaphores, we need to submit all the surfaces after an explicit + // CPU wait. Once Vulkan semaphores are available, this method must return + // void and the implementation must submit surfaces on its own as soon as the + // specific canvas operations are done. + [[nodiscard]] std::vector> + ExecutePaintTasks(CompositorContext::ScopedFrame& frame); + + float ScaleX() const { return metrics_->scale_x * top_scale_x_; } + float ScaleY() const { return metrics_->scale_y * top_scale_y_; } + + // The transformation matrix of the current context. It's used to construct + // the LayerRasterCacheKey for a given layer. + SkMatrix Matrix() const { return SkMatrix::MakeScale(ScaleX(), ScaleY()); } + + bool HasRetainedNode(const LayerRasterCacheKey& key) const { + return surface_producer_->HasRetainedNode(key); + } + scenic::EntityNode* GetRetainedNode(const LayerRasterCacheKey& key) { + return surface_producer_->GetRetainedNode(key); + } // The cumulative alpha value based on all the parent OpacityLayers. void set_alphaf(float alpha) { alpha_ = alpha; } float alphaf() { return alpha_; } - // Returns all `PaintTask`s generated for the current frame. - std::vector GetPaintTasks(); + // The global scenic elevation at a given point in the traversal. + float scenic_elevation() { return scenic_elevation_; } - // Enable/disable wireframe rendering around the root view bounds. - void EnableWireframe(bool enable); + void set_scenic_elevation(float elevation) { scenic_elevation_ = elevation; } - // Reset state for a new frame. - void Reset(); + float GetGlobalElevationForNextScenicLayer() { + float elevation = topmost_global_scenic_elevation_; + topmost_global_scenic_elevation_ += kScenicZElevationBetweenLayers; + return elevation; + } // |ExternalViewEmbedder| SkCanvas* GetRootCanvas() override { return nullptr; } @@ -167,34 +234,73 @@ class SceneUpdateContext : public flutter::ExternalViewEmbedder { } void CreateView(int64_t view_id, bool hit_testable, bool focusable); + void DestroyView(int64_t view_id); - void UpdateView(int64_t view_id, - const SkPoint& offset, - const SkSize& size, - std::optional override_hit_testable = std::nullopt); + + void UpdateScene(int64_t view_id, const SkPoint& offset, const SkSize& size); private: - void CreateFrame(scenic::EntityNode& entity_node, + struct PaintTask { + std::unique_ptr surface; + SkScalar left; + SkScalar top; + SkScalar scale_x; + SkScalar scale_y; + SkColor background_color; + std::vector layers; + }; + + // Setup the entity_node as a frame that materialize all the paint_layers. In + // most cases, this creates a VulkanSurface (SurfaceProducerSurface) by + // calling SetShapeTextureOrColor and GenerageImageIfNeeded. Such surface will + // own the associated entity_node. If the layer pointer isn't nullptr, the + // surface (and thus the entity_node) will be retained for that layer to + // improve the performance. + void CreateFrame(scenic::EntityNode entity_node, const SkRRect& rrect, SkColor color, SkAlpha opacity, const SkRect& paint_bounds, - std::vector paint_layers); - - SessionWrapper& session_; - - scenic::View root_view_; - scenic::EntityNode root_node_; - - std::vector paint_tasks_; + std::vector paint_layers, + Layer* layer); + void SetMaterialTextureAndColor(scenic::Material& material, + SkColor color, + SkAlpha opacity, + SkScalar scale_x, + SkScalar scale_y, + const SkRect& paint_bounds, + std::vector paint_layers, + Layer* layer, + scenic::EntityNode entity_node); + void SetMaterialColor(scenic::Material& material, + SkColor color, + SkAlpha opacity); + scenic::Image* GenerateImageIfNeeded(SkColor color, + SkScalar scale_x, + SkScalar scale_y, + const SkRect& paint_bounds, + std::vector paint_layers, + Layer* layer, + scenic::EntityNode entity_node); Entity* top_entity_ = nullptr; float top_scale_x_ = 1.f; float top_scale_y_ = 1.f; - float top_elevation_ = 0.f; - float next_elevation_ = 0.f; - float alpha_ = 1.f; + scenic::Session* const session_; + SurfaceProducer* const surface_producer_; + + fuchsia::ui::gfx::MetricsPtr metrics_; + SkISize frame_physical_size_; + float frame_physical_depth_ = 0.0f; + float frame_device_pixel_ratio_ = + 1.0f; // Ratio between logical and physical pixels. + + float alpha_ = 1.0f; + float scenic_elevation_ = 0.f; + float topmost_global_scenic_elevation_ = kScenicZElevationBetweenLayers; + + std::vector paint_tasks_; FML_DISALLOW_COPY_AND_ASSIGN(SceneUpdateContext); }; diff --git a/flow/testing/layer_test.h b/flow/testing/layer_test.h index d2df8b404ca9b..c63057fca1942 100644 --- a/flow/testing/layer_test.h +++ b/flow/testing/layer_test.h @@ -47,9 +47,11 @@ class LayerTestBase : public CanvasTestBase { kGiantRect, /* cull_rect */ false, /* layer reads from surface */ raster_time_, ui_time_, texture_registry_, - false, /* checkerboard_offscreen_layers */ - 1.0f, /* frame_device_pixel_ratio */ - false, /* has_platform_view */ + false, /* checkerboard_offscreen_layers */ + 100.0f, /* frame_physical_depth */ + 1.0f, /* frame_device_pixel_ratio */ + 0.0f, /* total_elevation */ + false, /* has_platform_view */ }), paint_context_({ TestT::mock_canvas().internal_canvas(), /* internal_nodes_canvas */ @@ -59,6 +61,7 @@ class LayerTestBase : public CanvasTestBase { raster_time_, ui_time_, texture_registry_, nullptr, /* raster_cache */ false, /* checkerboard_offscreen_layers */ + 100.0f, /* frame_physical_depth */ 1.0f, /* frame_device_pixel_ratio */ }) { use_null_raster_cache(); diff --git a/flow/testing/mock_layer.cc b/flow/testing/mock_layer.cc index b32bdb23abc09..5fe1b98088af1 100644 --- a/flow/testing/mock_layer.cc +++ b/flow/testing/mock_layer.cc @@ -22,6 +22,7 @@ void MockLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) { parent_mutators_ = context->mutators_stack; parent_matrix_ = matrix; parent_cull_rect_ = context->cull_rect; + parent_elevation_ = context->total_elevation; parent_has_platform_view_ = context->has_platform_view; context->has_platform_view = fake_has_platform_view_; diff --git a/flow/testing/mock_layer.h b/flow/testing/mock_layer.h index b92583f581209..835c3ee9621cc 100644 --- a/flow/testing/mock_layer.h +++ b/flow/testing/mock_layer.h @@ -28,6 +28,7 @@ class MockLayer : public Layer { const MutatorsStack& parent_mutators() { return parent_mutators_; } const SkMatrix& parent_matrix() { return parent_matrix_; } const SkRect& parent_cull_rect() { return parent_cull_rect_; } + float parent_elevation() { return parent_elevation_; } bool parent_has_platform_view() { return parent_has_platform_view_; } private: @@ -36,6 +37,7 @@ class MockLayer : public Layer { SkRect parent_cull_rect_ = SkRect::MakeEmpty(); SkPath fake_paint_path_; SkPaint fake_paint_; + float parent_elevation_ = 0; bool parent_has_platform_view_ = false; bool fake_has_platform_view_ = false; bool fake_needs_system_composite_ = false; diff --git a/flow/testing/mock_layer_unittests.cc b/flow/testing/mock_layer_unittests.cc index ebb837ca8b8dd..0e6e37978c2e1 100644 --- a/flow/testing/mock_layer_unittests.cc +++ b/flow/testing/mock_layer_unittests.cc @@ -39,11 +39,13 @@ TEST_F(MockLayerTest, SimpleParams) { const SkMatrix start_matrix = SkMatrix::Translate(1.0f, 2.0f); const SkMatrix scale_matrix = SkMatrix::Scale(0.5f, 0.5f); const SkRect cull_rect = SkRect::MakeWH(5.0f, 5.0f); + const float parent_elevation = 5.0f; const bool parent_has_platform_view = true; auto layer = std::make_shared(path, paint); preroll_context()->mutators_stack.PushTransform(scale_matrix); preroll_context()->cull_rect = cull_rect; + preroll_context()->total_elevation = parent_elevation; preroll_context()->has_platform_view = parent_has_platform_view; layer->Preroll(preroll_context(), start_matrix); EXPECT_EQ(preroll_context()->has_platform_view, false); @@ -53,6 +55,7 @@ TEST_F(MockLayerTest, SimpleParams) { EXPECT_EQ(layer->parent_mutators(), std::vector{Mutator(scale_matrix)}); EXPECT_EQ(layer->parent_matrix(), start_matrix); EXPECT_EQ(layer->parent_cull_rect(), cull_rect); + EXPECT_EQ(layer->parent_elevation(), parent_elevation); EXPECT_EQ(layer->parent_has_platform_view(), parent_has_platform_view); layer->Paint(paint_context()); diff --git a/flow/view_holder.cc b/flow/view_holder.cc index c2011825c9474..7fd00500bb02c 100644 --- a/flow/view_holder.cc +++ b/flow/view_holder.cc @@ -4,8 +4,6 @@ #include "flutter/flow/view_holder.h" -#include - #include "flutter/fml/thread_local.h" namespace { @@ -100,17 +98,18 @@ ViewHolder::ViewHolder(fml::RefPtr ui_task_runner, FML_DCHECK(pending_view_holder_token_.value); } -void ViewHolder::UpdateScene(scenic::Session* session, - scenic::ContainerNode& container_node, +void ViewHolder::UpdateScene(SceneUpdateContext& context, const SkPoint& offset, const SkSize& size, SkAlpha opacity, bool hit_testable) { if (pending_view_holder_token_.value) { - entity_node_ = std::make_unique(session); - opacity_node_ = std::make_unique(session); + entity_node_ = std::make_unique(context.session()); + opacity_node_ = + std::make_unique(context.session()); view_holder_ = std::make_unique( - session, std::move(pending_view_holder_token_), "Flutter SceneHost"); + context.session(), std::move(pending_view_holder_token_), + "Flutter SceneHost"); opacity_node_->AddChild(*entity_node_); opacity_node_->SetLabel("flutter::ViewHolder"); entity_node_->Attach(*view_holder_); @@ -126,7 +125,7 @@ void ViewHolder::UpdateScene(scenic::Session* session, FML_DCHECK(opacity_node_); FML_DCHECK(view_holder_); - container_node.AddChild(*opacity_node_); + context.top_entity()->embedder_node().AddChild(*opacity_node_); opacity_node_->SetOpacity(opacity / 255.0f); entity_node_->SetTranslation(offset.x(), offset.y(), -0.1f); entity_node_->SetHitTestBehavior( diff --git a/flow/view_holder.h b/flow/view_holder.h index f25b205c7823c..bb8ff83d776ab 100644 --- a/flow/view_holder.h +++ b/flow/view_holder.h @@ -9,17 +9,17 @@ #include #include #include -#include #include +#include "third_party/skia/include/core/SkMatrix.h" +#include "third_party/skia/include/core/SkPoint.h" +#include "third_party/skia/include/core/SkSize.h" #include +#include "flutter/flow/scene_update_context.h" #include "flutter/fml/macros.h" #include "flutter/fml/memory/ref_counted.h" #include "flutter/fml/task_runner.h" -#include "third_party/skia/include/core/SkColor.h" -#include "third_party/skia/include/core/SkPoint.h" -#include "third_party/skia/include/core/SkSize.h" namespace flutter { @@ -54,8 +54,7 @@ class ViewHolder { // Creates or updates the contained ViewHolder resource using the specified // |SceneUpdateContext|. - void UpdateScene(scenic::Session* session, - scenic::ContainerNode& container_node, + void UpdateScene(SceneUpdateContext& context, const SkPoint& offset, const SkSize& size, SkAlpha opacity, diff --git a/lib/ui/compositing/scene.cc b/lib/ui/compositing/scene.cc index 96eac5885d083..d70033a346b09 100644 --- a/lib/ui/compositing/scene.cc +++ b/lib/ui/compositing/scene.cc @@ -50,6 +50,7 @@ Scene::Scene(std::shared_ptr rootLayer, layer_tree_ = std::make_unique( SkISize::Make(viewport_metrics.physical_width, viewport_metrics.physical_height), + static_cast(viewport_metrics.physical_depth), static_cast(viewport_metrics.device_pixel_ratio)); layer_tree_->set_root_layer(std::move(rootLayer)); layer_tree_->set_rasterizer_tracing_threshold(rasterizerTracingThreshold); diff --git a/lib/ui/hooks.dart b/lib/ui/hooks.dart index fa44deb654c04..c01095412a606 100644 --- a/lib/ui/hooks.dart +++ b/lib/ui/hooks.dart @@ -14,6 +14,7 @@ void _updateWindowMetrics( double devicePixelRatio, double width, double height, + double depth, double viewPaddingTop, double viewPaddingRight, double viewPaddingBottom, @@ -30,6 +31,7 @@ void _updateWindowMetrics( window .._devicePixelRatio = devicePixelRatio .._physicalSize = Size(width, height) + .._physicalDepth = depth .._viewPadding = WindowPadding._( top: viewPaddingTop, right: viewPaddingRight, diff --git a/lib/ui/window.dart b/lib/ui/window.dart index 82bc1d9ccd2fe..90c5603c1552f 100644 --- a/lib/ui/window.dart +++ b/lib/ui/window.dart @@ -649,6 +649,20 @@ class Window { Size get physicalSize => _physicalSize; Size _physicalSize = Size.zero; + /// The physical depth is the maximum elevation that the Window allows. + /// + /// Physical layers drawn at or above this elevation will have their elevation + /// clamped to this value. This can happen if the physical layer itself has + /// an elevation larger than available depth, or if some ancestor of the layer + /// causes it to have a cumulative elevation that is larger than the available + /// depth. + /// + /// The default value is [double.maxFinite], which is used for platforms that + /// do not specify a maximum elevation. This property is currently on expected + /// to be set to a non-default value on Fuchsia. + double get physicalDepth => _physicalDepth; + double _physicalDepth = double.maxFinite; + /// The number of physical pixels on each side of the display rectangle into /// which the application can render, but over which the operating system /// will likely place system UI, such as the keyboard, that fully obscures diff --git a/lib/ui/window/viewport_metrics.cc b/lib/ui/window/viewport_metrics.cc index f642bd116966f..329cea01c036a 100644 --- a/lib/ui/window/viewport_metrics.cc +++ b/lib/ui/window/viewport_metrics.cc @@ -8,8 +8,6 @@ namespace flutter { -ViewportMetrics::ViewportMetrics() = default; - ViewportMetrics::ViewportMetrics(double p_device_pixel_ratio, double p_physical_width, double p_physical_height, @@ -48,6 +46,40 @@ ViewportMetrics::ViewportMetrics(double p_device_pixel_ratio, FML_DCHECK(device_pixel_ratio > 0); } +ViewportMetrics::ViewportMetrics(double p_device_pixel_ratio, + double p_physical_width, + double p_physical_height, + double p_physical_depth, + double p_physical_padding_top, + double p_physical_padding_right, + double p_physical_padding_bottom, + double p_physical_padding_left, + double p_physical_view_inset_front, + double p_physical_view_inset_back, + double p_physical_view_inset_top, + double p_physical_view_inset_right, + double p_physical_view_inset_bottom, + double p_physical_view_inset_left) + : device_pixel_ratio(p_device_pixel_ratio), + physical_width(p_physical_width), + physical_height(p_physical_height), + physical_depth(p_physical_depth), + physical_padding_top(p_physical_padding_top), + physical_padding_right(p_physical_padding_right), + physical_padding_bottom(p_physical_padding_bottom), + physical_padding_left(p_physical_padding_left), + physical_view_inset_top(p_physical_view_inset_top), + physical_view_inset_right(p_physical_view_inset_right), + physical_view_inset_bottom(p_physical_view_inset_bottom), + physical_view_inset_left(p_physical_view_inset_left), + physical_view_inset_front(p_physical_view_inset_front), + physical_view_inset_back(p_physical_view_inset_back) { + // Ensure we don't have nonsensical dimensions. + FML_DCHECK(physical_width >= 0); + FML_DCHECK(physical_height >= 0); + FML_DCHECK(device_pixel_ratio > 0); +} + ViewportMetrics::ViewportMetrics(double p_device_pixel_ratio, double p_physical_width, double p_physical_height) diff --git a/lib/ui/window/viewport_metrics.h b/lib/ui/window/viewport_metrics.h index 01081e3f345f1..d4a7311ad95f3 100644 --- a/lib/ui/window/viewport_metrics.h +++ b/lib/ui/window/viewport_metrics.h @@ -5,10 +5,21 @@ #ifndef FLUTTER_LIB_UI_WINDOW_VIEWPORT_METRICS_H_ #define FLUTTER_LIB_UI_WINDOW_VIEWPORT_METRICS_H_ +#include + namespace flutter { +// This is the value of double.maxFinite from dart:core. +// Platforms that do not explicitly set a depth will use this value, which +// avoids the need to special case logic that wants to check the max depth on +// the Dart side. +static const double kUnsetDepth = 1.7976931348623157e+308; + struct ViewportMetrics { - ViewportMetrics(); + ViewportMetrics() = default; + ViewportMetrics(const ViewportMetrics& other) = default; + + // Create a 2D ViewportMetrics instance. ViewportMetrics(double p_device_pixel_ratio, double p_physical_width, double p_physical_height, @@ -25,6 +36,22 @@ struct ViewportMetrics { double p_physical_system_gesture_inset_bottom, double p_physical_system_gesture_inset_left); + // Create a ViewportMetrics instance that contains z information. + ViewportMetrics(double p_device_pixel_ratio, + double p_physical_width, + double p_physical_height, + double p_physical_depth, + double p_physical_padding_top, + double p_physical_padding_right, + double p_physical_padding_bottom, + double p_physical_padding_left, + double p_physical_view_inset_front, + double p_physical_view_inset_back, + double p_physical_view_inset_top, + double p_physical_view_inset_right, + double p_physical_view_inset_bottom, + double p_physical_view_inset_left); + // Create a ViewportMetrics instance that doesn't include depth, padding, or // insets. ViewportMetrics(double p_device_pixel_ratio, @@ -34,6 +61,7 @@ struct ViewportMetrics { double device_pixel_ratio = 1.0; double physical_width = 0; double physical_height = 0; + double physical_depth = kUnsetDepth; double physical_padding_top = 0; double physical_padding_right = 0; double physical_padding_bottom = 0; @@ -42,6 +70,8 @@ struct ViewportMetrics { double physical_view_inset_right = 0; double physical_view_inset_bottom = 0; double physical_view_inset_left = 0; + double physical_view_inset_front = kUnsetDepth; + double physical_view_inset_back = kUnsetDepth; double physical_system_gesture_inset_top = 0; double physical_system_gesture_inset_right = 0; double physical_system_gesture_inset_bottom = 0; @@ -51,6 +81,7 @@ struct ViewportMetrics { struct LogicalSize { double width = 0.0; double height = 0.0; + double depth = kUnsetDepth; }; struct LogicalInset { @@ -58,11 +89,14 @@ struct LogicalInset { double top = 0.0; double right = 0.0; double bottom = 0.0; + double front = kUnsetDepth; + double back = kUnsetDepth; }; struct LogicalMetrics { LogicalSize size; double scale = 1.0; + double scale_z = 1.0; LogicalInset padding; LogicalInset view_inset; }; diff --git a/lib/ui/window/window.cc b/lib/ui/window/window.cc index 1779998945554..d583682888313 100644 --- a/lib/ui/window/window.cc +++ b/lib/ui/window/window.cc @@ -49,6 +49,7 @@ void Window::UpdateWindowMetrics(const ViewportMetrics& metrics) { tonic::ToDart(metrics.device_pixel_ratio), tonic::ToDart(metrics.physical_width), tonic::ToDart(metrics.physical_height), + tonic::ToDart(metrics.physical_depth), tonic::ToDart(metrics.physical_padding_top), tonic::ToDart(metrics.physical_padding_right), tonic::ToDart(metrics.physical_padding_bottom), diff --git a/lib/web_ui/lib/src/engine/window.dart b/lib/web_ui/lib/src/engine/window.dart index 5f75265658c79..6463e6043502b 100644 --- a/lib/web_ui/lib/src/engine/window.dart +++ b/lib/web_ui/lib/src/engine/window.dart @@ -146,6 +146,9 @@ class EngineWindow extends ui.Window { /// Overrides the value of [physicalSize] in tests. ui.Size? webOnlyDebugPhysicalSizeOverride; + @override + double get physicalDepth => double.maxFinite; + /// Handles the browser history integration to allow users to use the back /// button, etc. final BrowserHistory _browserHistory = BrowserHistory(); diff --git a/lib/web_ui/lib/src/ui/window.dart b/lib/web_ui/lib/src/ui/window.dart index 135544bb299f1..cbe7465a69fc0 100644 --- a/lib/web_ui/lib/src/ui/window.dart +++ b/lib/web_ui/lib/src/ui/window.dart @@ -455,6 +455,19 @@ abstract class Window { /// observe when this value changes. Size get physicalSize; + /// The physical depth is the maximum elevation that the Window allows. + /// + /// Physical layers drawn at or above this elevation will have their elevation + /// clamped to this value. This can happen if the physical layer itself has + /// an elevation larger than available depth, or if some ancestor of the layer + /// causes it to have a cumulative elevation that is larger than the available + /// depth. + /// + /// The default value is [double.maxFinite], which is used for platforms that + /// do not specify a maximum elevation. This property is currently on expected + /// to be set to a non-default value on Fuchsia. + double get physicalDepth; + /// The number of physical pixels on each side of the display rectangle into /// which the application can render, but over which the operating system /// will likely place system UI, such as the keyboard, that fully obscures diff --git a/shell/common/engine.cc b/shell/common/engine.cc index dc309f72a8cf5..79f885dcbb7ce 100644 --- a/shell/common/engine.cc +++ b/shell/common/engine.cc @@ -276,6 +276,7 @@ void Engine::SetViewportMetrics(const ViewportMetrics& metrics) { bool dimensions_changed = viewport_metrics_.physical_height != metrics.physical_height || viewport_metrics_.physical_width != metrics.physical_width || + viewport_metrics_.physical_depth != metrics.physical_depth || viewport_metrics_.device_pixel_ratio != metrics.device_pixel_ratio; viewport_metrics_ = metrics; runtime_controller_->SetViewportMetrics(viewport_metrics_); @@ -461,7 +462,8 @@ void Engine::Render(std::unique_ptr layer_tree) { // Ensure frame dimensions are sane. if (layer_tree->frame_size().isEmpty() || - layer_tree->device_pixel_ratio() <= 0.0f) { + layer_tree->frame_physical_depth() <= 0.0f || + layer_tree->frame_device_pixel_ratio() <= 0.0f) { return; } diff --git a/shell/common/shell_test.cc b/shell/common/shell_test.cc index 6846884dc8dbd..075f4adb3769e 100644 --- a/shell/common/shell_test.cc +++ b/shell/common/shell_test.cc @@ -129,6 +129,7 @@ void ShellTest::PumpOneFrame(Shell* shell, auto layer_tree = std::make_unique( SkISize::Make(viewport_metrics.physical_width, viewport_metrics.physical_height), + static_cast(viewport_metrics.physical_depth), static_cast(viewport_metrics.device_pixel_ratio)); SkMatrix identity; identity.setIdentity(); diff --git a/shell/platform/fuchsia/flutter/BUILD.gn b/shell/platform/fuchsia/flutter/BUILD.gn index c8652cc9a0598..ba68d1bac6905 100644 --- a/shell/platform/fuchsia/flutter/BUILD.gn +++ b/shell/platform/fuchsia/flutter/BUILD.gn @@ -12,6 +12,7 @@ import("//flutter/tools/fuchsia/dart.gni") import("//flutter/tools/fuchsia/fuchsia_archive.gni") import("//flutter/tools/fuchsia/fuchsia_libs.gni") import("//flutter/vulkan/config.gni") +import("engine_flutter_runner.gni") # Fuchsia uses its own custom Surface implementation. shell_gpu_configuration("fuchsia_legacy_gpu_configuration") { @@ -21,158 +22,10 @@ shell_gpu_configuration("fuchsia_legacy_gpu_configuration") { enable_metal = false } -source_set("flutter_runner_sources") { - sources = [ - "accessibility_bridge.cc", - "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_intl.cc", - "fuchsia_intl.h", - "isolate_configurator.cc", - "isolate_configurator.h", - "logging.h", - "loop.cc", - "loop.h", - "platform_view.cc", - "platform_view.h", - "runner.cc", - "runner.h", - "session_connection.cc", - "session_connection.h", - "surface.cc", - "surface.h", - "task_observers.cc", - "task_observers.h", - "task_runner_adapter.cc", - "task_runner_adapter.h", - "thread.cc", - "thread.h", - "unique_fdio_ns.h", - "vsync_recorder.cc", - "vsync_recorder.h", - "vsync_waiter.cc", - "vsync_waiter.h", - "vulkan_surface.cc", - "vulkan_surface.h", - "vulkan_surface_pool.cc", - "vulkan_surface_pool.h", - "vulkan_surface_producer.cc", - "vulkan_surface_producer.h", - ] - - # The use of these dependencies is temporary and will be moved behind the - # embedder API. - flutter_public_deps = [ - "//flutter/flow:flow_fuchsia_legacy", - "//flutter/lib/ui:ui_fuchsia_legacy", - "//flutter/runtime:runtime_fuchsia_legacy", - "//flutter/shell/common:common_fuchsia_legacy", - ] - flutter_deps = [ - ":fuchsia_legacy_gpu_configuration", - "//flutter/assets", - "//flutter/common", - "//flutter/fml", - "//flutter/vulkan", - ] - - public_deps = [ - "$fuchsia_sdk_root/pkg:scenic_cpp", - "$fuchsia_sdk_root/pkg:sys_cpp", - "//flutter/shell/platform/fuchsia/runtime/dart/utils", - ] + flutter_public_deps - - deps = [ - "$fuchsia_sdk_root/fidl:fuchsia.accessibility.semantics", - "$fuchsia_sdk_root/fidl:fuchsia.fonts", - "$fuchsia_sdk_root/fidl:fuchsia.images", - "$fuchsia_sdk_root/fidl:fuchsia.intl", - "$fuchsia_sdk_root/fidl:fuchsia.io", - "$fuchsia_sdk_root/fidl:fuchsia.sys", - "$fuchsia_sdk_root/fidl:fuchsia.ui.app", - "$fuchsia_sdk_root/fidl:fuchsia.ui.scenic", - "$fuchsia_sdk_root/pkg:async-cpp", - "$fuchsia_sdk_root/pkg:async-default", - "$fuchsia_sdk_root/pkg:async-loop", - "$fuchsia_sdk_root/pkg:async-loop-cpp", - "$fuchsia_sdk_root/pkg:fdio", - "$fuchsia_sdk_root/pkg:fidl_cpp", - "$fuchsia_sdk_root/pkg:syslog", - "$fuchsia_sdk_root/pkg:trace", - "$fuchsia_sdk_root/pkg:trace-engine", - "$fuchsia_sdk_root/pkg:trace-provider-so", - "$fuchsia_sdk_root/pkg:vfs_cpp", - "$fuchsia_sdk_root/pkg:zx", - "//flutter/shell/platform/fuchsia/dart-pkg/fuchsia", - "//flutter/shell/platform/fuchsia/dart-pkg/zircon", - ] + flutter_deps -} - # Things that explicitly being excluded: # 1. Kernel snapshot framework mode. # 2. Profiler symbols. -# Builds a flutter_runner -# -# Parameters: -# -# output_name (required): -# The name of the resulting binary. -# -# extra_deps (required): -# Any additional dependencies. -# -# product (required): -# Whether to link against a Product mode Dart VM. -# -# extra_defines (optional): -# Any additional preprocessor defines. -template("flutter_runner") { - assert(defined(invoker.output_name), "flutter_runner must define output_name") - assert(defined(invoker.extra_deps), "flutter_runner must define extra_deps") - assert(defined(invoker.product), "flutter_runner must define product") - - invoker_output_name = invoker.output_name - extra_deps = invoker.extra_deps - - extra_defines = [] - if (defined(invoker.extra_defines)) { - extra_defines += invoker.extra_defines - } - - executable(target_name) { - output_name = invoker_output_name - - defines = extra_defines - - sources = [ "main.cc" ] - - deps = [ - ":flutter_runner_sources", - "$fuchsia_sdk_root/pkg:async-loop-cpp", - "$fuchsia_sdk_root/pkg:trace", - "$fuchsia_sdk_root/pkg:trace-provider-so", - ] + extra_deps - - # The flags below are needed so that Dart's CPU profiler can walk the - # C++ stack. - cflags = [ "-fno-omit-frame-pointer" ] - - if (!invoker.product) { - # This flag is needed so that the call to dladdr() in Dart's native symbol - # resolver can report good symbol information for the CPU profiler. - ldflags = [ "-rdynamic" ] - } - } -} - flutter_runner("jit") { output_name = "flutter_jit_runner" product = false @@ -414,36 +267,66 @@ executable("flutter_runner_unittests") { output_name = "flutter_runner_tests" sources = [ + "accessibility_bridge.cc", + "accessibility_bridge.h", "accessibility_bridge_unittest.cc", + "component.cc", + "component.h", "component_unittest.cc", "flutter_runner_fakes.h", + "flutter_runner_product_configuration.cc", + "flutter_runner_product_configuration.h", + "fuchsia_intl.cc", + "fuchsia_intl.h", "fuchsia_intl_unittest.cc", + "logging.h", + "loop.cc", + "loop.h", + "platform_view.cc", + "platform_view.h", "platform_view_unittest.cc", + "runner.cc", + "runner.h", "runner_unittest.cc", + "surface.cc", + "surface.h", + "task_observers.cc", + "task_observers.h", + "task_runner_adapter.cc", + "task_runner_adapter.h", "tests/flutter_runner_product_configuration_unittests.cc", "tests/vsync_recorder_unittests.cc", + "thread.cc", + "thread.h", + "vsync_recorder.cc", + "vsync_recorder.h", + "vsync_waiter.cc", + "vsync_waiter.h", "vsync_waiter_unittests.cc", ] # This is needed for //third_party/googletest for linking zircon symbols. libs = [ "//fuchsia/sdk/$host_os/arch/$target_cpu/sysroot/lib/libzircon.so" ] - # The use of these dependencies is temporary and will be moved behind the - # embedder API. - flutter_deps = [ + deps = [ + ":aot", + ":flutter_runner_fixtures", + "//build/fuchsia/fidl:fuchsia.accessibility.semantics", + "//build/fuchsia/pkg:async-default", + "//build/fuchsia/pkg:async-loop-cpp", + "//build/fuchsia/pkg:async-loop-default", + "//build/fuchsia/pkg:scenic_cpp", + "//build/fuchsia/pkg:sys_cpp_testing", + "//flutter/common", "//flutter/flow:flow_fuchsia_legacy", "//flutter/lib/ui:ui_fuchsia_legacy", + "//flutter/runtime:runtime_fuchsia_legacy", "//flutter/shell/common:common_fuchsia_legacy", + "//flutter/shell/platform/fuchsia/runtime/dart/utils", + "//flutter/testing", "//third_party/dart/runtime:libdart_jit", "//third_party/dart/runtime/platform:libdart_platform_jit", ] - - deps = [ - ":flutter_runner_fixtures", - ":flutter_runner_sources", - "//build/fuchsia/pkg:sys_cpp_testing", - "//flutter/testing", - ] + flutter_deps } executable("flutter_runner_tzdata_unittests") { @@ -451,24 +334,34 @@ executable("flutter_runner_tzdata_unittests") { output_name = "flutter_runner_tzdata_tests" - sources = [ "runner_tzdata_unittest.cc" ] + sources = [ + "runner.cc", + "runner.h", + "runner_tzdata_unittest.cc", + ] # This is needed for //third_party/googletest for linking zircon symbols. libs = [ "//fuchsia/sdk/$host_os/arch/$target_cpu/sysroot/lib/libzircon.so" ] - # The use of these dependencies is temporary and will be moved behind the - # embedder API. - flutter_deps = [ + deps = [ + ":aot", + ":flutter_runner_fixtures", + "//build/fuchsia/fidl:fuchsia.accessibility.semantics", + "//build/fuchsia/pkg:async-loop-cpp", + "//build/fuchsia/pkg:async-loop-default", + "//build/fuchsia/pkg:scenic_cpp", + "//build/fuchsia/pkg:sys_cpp_testing", + "//flutter/flow:flow_fuchsia_legacy", "//flutter/lib/ui:ui_fuchsia_legacy", + "//flutter/runtime:runtime_fuchsia_legacy", + "//flutter/shell/common:common_fuchsia_legacy", + "//flutter/shell/platform/fuchsia/runtime/dart/utils", + "//flutter/testing", "//third_party/dart/runtime:libdart_jit", "//third_party/dart/runtime/platform:libdart_platform_jit", + "//third_party/icu", + "//third_party/skia", ] - - deps = [ - ":flutter_runner_fixtures", - ":flutter_runner_sources", - "//flutter/testing", - ] + flutter_deps } executable("flutter_runner_scenic_unittests") { @@ -476,27 +369,84 @@ executable("flutter_runner_scenic_unittests") { output_name = "flutter_runner_scenic_tests" - sources = [ "tests/session_connection_unittests.cc" ] + sources = [ + "component.cc", + "component.h", + "compositor_context.cc", + "compositor_context.h", + "engine.cc", + "engine.h", + "fuchsia_intl.cc", + "fuchsia_intl.h", + "isolate_configurator.cc", + "isolate_configurator.h", + "logging.h", + "loop.cc", + "loop.h", + "platform_view.cc", + "platform_view.h", + "runner.cc", + "runner.h", + "session_connection.cc", + "session_connection.h", + "surface.cc", + "surface.h", + "task_observers.cc", + "task_observers.h", + "task_runner_adapter.cc", + "task_runner_adapter.h", + "tests/session_connection_unittests.cc", + "thread.cc", + "thread.h", + "unique_fdio_ns.h", + "vsync_recorder.cc", + "vsync_recorder.h", + "vsync_waiter.cc", + "vsync_waiter.h", + "vsync_waiter_unittests.cc", + "vulkan_surface.cc", + "vulkan_surface.h", + "vulkan_surface_pool.cc", + "vulkan_surface_pool.h", + "vulkan_surface_producer.cc", + "vulkan_surface_producer.h", + ] # This is needed for //third_party/googletest for linking zircon symbols. libs = [ "//fuchsia/sdk/$host_os/arch/$target_cpu/sysroot/lib/libzircon.so" ] - # The use of these dependencies is temporary and will be moved behind the - # embedder API. - flutter_deps = [ + deps = [ + ":flutter_runner_fixtures", + ":jit", + "$fuchsia_sdk_root/fidl:fuchsia.ui.policy", + "$fuchsia_sdk_root/pkg:trace-provider-so", + "//build/fuchsia/fidl:fuchsia.accessibility.semantics", + "//build/fuchsia/pkg:async-default", + "//build/fuchsia/pkg:async-loop-cpp", + "//build/fuchsia/pkg:async-loop-default", + "//build/fuchsia/pkg:scenic_cpp", + "//build/fuchsia/pkg:sys_cpp_testing", + "//flutter/common", + "//flutter/flow:flow_fuchsia_legacy", "//flutter/lib/ui:ui_fuchsia_legacy", + "//flutter/runtime:runtime_fuchsia_legacy", + "//flutter/shell/common:common_fuchsia_legacy", + "//flutter/shell/platform/fuchsia/dart-pkg/fuchsia", + "//flutter/shell/platform/fuchsia/dart-pkg/zircon", + "//flutter/shell/platform/fuchsia/runtime/dart/utils", + "//flutter/testing", + "//flutter/vulkan", "//third_party/dart/runtime:libdart_jit", "//third_party/dart/runtime/platform:libdart_platform_jit", + "//third_party/icu", + "//third_party/skia", ] - deps = [ - ":flutter_runner_fixtures", - ":flutter_runner_sources", - "$fuchsia_sdk_root/fidl:fuchsia.ui.policy", - "//flutter/testing", - ] + flutter_deps + public_deps = [ "//third_party/googletest:gtest" ] } +# When adding a new dep here, please also ensure the dep is added to +# testing/fuchsia/run_tests.sh and testing/fuchsia/test_fars fuchsia_archive("flutter_runner_tests") { testonly = true @@ -843,8 +793,6 @@ fuchsia_test_archive("ui_tests_next") { resources += vulkan_icds } -# When adding a new dep here, please also ensure the dep is added to -# testing/fuchsia/run_tests.sh and testing/fuchsia/test_fars group("tests") { testonly = true diff --git a/shell/platform/fuchsia/flutter/component.cc b/shell/platform/fuchsia/flutter/component.cc index 0106931820887..00cd9d318ae83 100644 --- a/shell/platform/fuchsia/flutter/component.cc +++ b/shell/platform/fuchsia/flutter/component.cc @@ -365,12 +365,6 @@ Application::Application( // Controls whether category "skia" trace events are enabled. settings_.trace_skia = true; - settings_.verbose_logging = true; - - settings_.advisory_script_uri = debug_label_; - - settings_.advisory_script_entrypoint = debug_label_; - settings_.icu_data_path = ""; settings_.assets_dir = application_assets_directory_.get(); diff --git a/shell/platform/fuchsia/flutter/compositor_context.cc b/shell/platform/fuchsia/flutter/compositor_context.cc index 599ebf77b0dd2..b0bbfc7ecbc28 100644 --- a/shell/platform/fuchsia/flutter/compositor_context.cc +++ b/shell/platform/fuchsia/flutter/compositor_context.cc @@ -4,8 +4,6 @@ #include "compositor_context.h" -#include - #include "flutter/flow/layers/layer_tree.h" #include "third_party/skia/include/gpu/GrDirectContext.h" @@ -13,38 +11,30 @@ namespace flutter_runner { class ScopedFrame final : public flutter::CompositorContext::ScopedFrame { public: - ScopedFrame(CompositorContext& context, - GrContext* gr_context, - SkCanvas* canvas, - flutter::ExternalViewEmbedder* view_embedder, + ScopedFrame(flutter::CompositorContext& context, const SkMatrix& root_surface_transformation, + flutter::ExternalViewEmbedder* view_embedder, bool instrumentation_enabled, - bool surface_supports_readback, - fml::RefPtr raster_thread_merger, - SessionConnection& session_connection, - VulkanSurfaceProducer& surface_producer, - flutter::SceneUpdateContext& scene_update_context) - : flutter::CompositorContext::ScopedFrame(context, - surface_producer.gr_context(), - canvas, - view_embedder, - root_surface_transformation, - instrumentation_enabled, - surface_supports_readback, - raster_thread_merger), - session_connection_(session_connection), - surface_producer_(surface_producer), - scene_update_context_(scene_update_context) {} + SessionConnection& session_connection) + : flutter::CompositorContext::ScopedFrame( + context, + session_connection.vulkan_surface_producer()->gr_context(), + nullptr, + view_embedder, + root_surface_transformation, + instrumentation_enabled, + true, + nullptr), + session_connection_(session_connection) {} private: SessionConnection& session_connection_; - VulkanSurfaceProducer& surface_producer_; - flutter::SceneUpdateContext& scene_update_context_; flutter::RasterStatus Raster(flutter::LayerTree& layer_tree, bool ignore_raster_cache) override { - std::vector frame_paint_tasks; - std::vector> frame_surfaces; + if (!session_connection_.has_metrics()) { + return flutter::RasterStatus::kSuccess; + } { // Preroll the Flutter layer tree. This allows Flutter to perform @@ -57,80 +47,15 @@ class ScopedFrame final : public flutter::CompositorContext::ScopedFrame { // Traverse the Flutter layer tree so that the necessary session ops to // represent the frame are enqueued in the underlying session. TRACE_EVENT0("flutter", "UpdateScene"); - layer_tree.UpdateScene(scene_update_context_); + layer_tree.UpdateScene(session_connection_.scene_update_context(), + session_connection_.root_node()); } { - // Flush all pending session ops: create surfaces and enqueue session - // Image ops for the frame's paint tasks, then Present. + // Flush all pending session ops. TRACE_EVENT0("flutter", "SessionPresent"); - frame_paint_tasks = scene_update_context_.GetPaintTasks(); - for (auto& task : frame_paint_tasks) { - SkISize physical_size = - SkISize::Make(layer_tree.device_pixel_ratio() * task.scale_x * - task.paint_bounds.width(), - layer_tree.device_pixel_ratio() * task.scale_y * - task.paint_bounds.height()); - if (physical_size.width() == 0 || physical_size.height() == 0) { - frame_surfaces.emplace_back(nullptr); - continue; - } - - std::unique_ptr surface = - surface_producer_.ProduceSurface(physical_size); - if (!surface) { - FML_LOG(ERROR) - << "Could not acquire a surface from the surface producer " - "of size: " - << physical_size.width() << "x" << physical_size.height(); - } else { - task.material.SetTexture(*(surface->GetImage())); - } - - frame_surfaces.emplace_back(std::move(surface)); - } - - session_connection_.Present(); - } - { - // Execute paint tasks in parallel with Scenic's side of the Present, then - // signal fences. - TRACE_EVENT0("flutter", "ExecutePaintTasks"); - size_t surface_index = 0; - for (auto& task : frame_paint_tasks) { - std::unique_ptr& task_surface = - frame_surfaces[surface_index++]; - if (!task_surface) { - continue; - } - - SkCanvas* canvas = task_surface->GetSkiaSurface()->getCanvas(); - flutter::Layer::PaintContext paint_context = { - canvas, - canvas, - gr_context(), - nullptr, - context().raster_time(), - context().ui_time(), - context().texture_registry(), - &context().raster_cache(), - false, - layer_tree.device_pixel_ratio()}; - canvas->restoreToCount(1); - canvas->save(); - canvas->clear(task.background_color); - canvas->scale(layer_tree.device_pixel_ratio() * task.scale_x, - layer_tree.device_pixel_ratio() * task.scale_y); - canvas->translate(-task.paint_bounds.left(), -task.paint_bounds.top()); - for (flutter::Layer* layer : task.layers) { - layer->Paint(paint_context); - } - } - - // Tell the surface producer that a present has occurred so it can perform - // book-keeping on buffer caches. - surface_producer_.OnSurfacesPresented(std::move(frame_surfaces)); + session_connection_.Present(this); } return flutter::RasterStatus::kSuccess; @@ -140,14 +65,51 @@ class ScopedFrame final : public flutter::CompositorContext::ScopedFrame { }; CompositorContext::CompositorContext( - SessionConnection& session_connection, - VulkanSurfaceProducer& surface_producer, - flutter::SceneUpdateContext& scene_update_context) - : session_connection_(session_connection), - surface_producer_(surface_producer), - scene_update_context_(scene_update_context) {} + std::string debug_label, + fuchsia::ui::views::ViewToken view_token, + scenic::ViewRefPair view_ref_pair, + fidl::InterfaceHandle session, + fml::closure session_error_callback, + zx_handle_t vsync_event_handle) + : debug_label_(std::move(debug_label)), + session_connection_( + debug_label_, + std::move(view_token), + std::move(view_ref_pair), + std::move(session), + session_error_callback, + [](auto) {}, + vsync_event_handle) {} + +void CompositorContext::OnSessionMetricsDidChange( + const fuchsia::ui::gfx::Metrics& metrics) { + session_connection_.set_metrics(metrics); +} -CompositorContext::~CompositorContext() = default; +void CompositorContext::OnSessionSizeChangeHint(float width_change_factor, + float height_change_factor) { + session_connection_.OnSessionSizeChangeHint(width_change_factor, + height_change_factor); +} + +void CompositorContext::OnWireframeEnabled(bool enabled) { + session_connection_.set_enable_wireframe(enabled); +} + +void CompositorContext::OnCreateView(int64_t view_id, + bool hit_testable, + bool focusable) { + session_connection_.scene_update_context().CreateView(view_id, hit_testable, + focusable); +} + +void CompositorContext::OnDestroyView(int64_t view_id) { + session_connection_.scene_update_context().DestroyView(view_id); +} + +CompositorContext::~CompositorContext() { + OnGrContextDestroyed(); +} std::unique_ptr CompositorContext::AcquireFrame( @@ -158,10 +120,16 @@ CompositorContext::AcquireFrame( bool instrumentation_enabled, bool surface_supports_readback, fml::RefPtr raster_thread_merger) { + // TODO: The AcquireFrame interface is too broad and must be refactored to get + // rid of the context and canvas arguments as those seem to be only used for + // colorspace correctness purposes on the mobile shells. return std::make_unique( - *this, gr_context, canvas, view_embedder, root_surface_transformation, - instrumentation_enabled, surface_supports_readback, raster_thread_merger, - session_connection_, surface_producer_, scene_update_context_); + *this, // + root_surface_transformation, // + view_embedder, + instrumentation_enabled, // + session_connection_ // + ); } } // namespace flutter_runner diff --git a/shell/platform/fuchsia/flutter/compositor_context.h b/shell/platform/fuchsia/flutter/compositor_context.h index 542e5d314fa71..6ad28785b119c 100644 --- a/shell/platform/fuchsia/flutter/compositor_context.h +++ b/shell/platform/fuchsia/flutter/compositor_context.h @@ -5,15 +5,15 @@ #ifndef FLUTTER_SHELL_PLATFORM_FUCHSIA_COMPOSITOR_CONTEXT_H_ #define FLUTTER_SHELL_PLATFORM_FUCHSIA_COMPOSITOR_CONTEXT_H_ -#include +#include +#include +#include +#include #include "flutter/flow/compositor_context.h" #include "flutter/flow/embedded_views.h" -#include "flutter/flow/scene_update_context.h" #include "flutter/fml/macros.h" - #include "session_connection.h" -#include "vulkan_surface_producer.h" namespace flutter_runner { @@ -21,16 +21,31 @@ namespace flutter_runner { // Fuchsia. class CompositorContext final : public flutter::CompositorContext { public: - CompositorContext(SessionConnection& session_connection, - VulkanSurfaceProducer& surface_producer, - flutter::SceneUpdateContext& scene_update_context); + CompositorContext(std::string debug_label, + fuchsia::ui::views::ViewToken view_token, + scenic::ViewRefPair view_ref_pair, + fidl::InterfaceHandle session, + fml::closure session_error_callback, + zx_handle_t vsync_event_handle); ~CompositorContext() override; + void OnSessionMetricsDidChange(const fuchsia::ui::gfx::Metrics& metrics); + void OnSessionSizeChangeHint(float width_change_factor, + float height_change_factor); + + void OnWireframeEnabled(bool enabled); + void OnCreateView(int64_t view_id, bool hit_testable, bool focusable); + void OnDestroyView(int64_t view_id); + + flutter::ExternalViewEmbedder* GetViewEmbedder() { + return &session_connection_.scene_update_context(); + } + private: - SessionConnection& session_connection_; - VulkanSurfaceProducer& surface_producer_; - flutter::SceneUpdateContext& scene_update_context_; + const std::string debug_label_; + scenic::ViewRefPair view_ref_pair_; + SessionConnection session_connection_; // |flutter::CompositorContext| std::unique_ptr AcquireFrame( diff --git a/shell/platform/fuchsia/flutter/engine.cc b/shell/platform/fuchsia/flutter/engine.cc index 82c66fc0100f5..7868a19671c1a 100644 --- a/shell/platform/fuchsia/flutter/engine.cc +++ b/shell/platform/fuchsia/flutter/engine.cc @@ -7,6 +7,9 @@ #include #include +#include + +#include "compositor_context.h" #include "flutter/common/task_runners.h" #include "flutter/fml/make_copyable.h" #include "flutter/fml/synchronization/waitable_event.h" @@ -14,21 +17,18 @@ #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 "../runtime/dart/utils/files.h" -#include "compositor_context.h" #include "flutter_runner_product_configuration.h" #include "fuchsia_intl.h" #include "platform_view.h" +#include "runtime/dart/utils/files.h" #include "task_runner_adapter.h" +#include "third_party/skia/include/ports/SkFontMgr_fuchsia.h" #include "thread.h" namespace flutter_runner { -namespace { -void UpdateNativeThreadLabelNames(const std::string& label, - const flutter::TaskRunners& runners) { +static void UpdateNativeThreadLabelNames(const std::string& label, + const flutter::TaskRunners& runners) { auto set_thread_name = [](fml::RefPtr runner, std::string prefix, std::string suffix) { if (!runner) { @@ -44,15 +44,13 @@ void UpdateNativeThreadLabelNames(const std::string& label, set_thread_name(runners.GetIOTaskRunner(), label, ".io"); } -fml::RefPtr MakeLocalizationPlatformMessage( +static fml::RefPtr MakeLocalizationPlatformMessage( const fuchsia::intl::Profile& intl_profile) { return fml::MakeRefCounted( "flutter/localization", MakeLocalizationPlatformMessageData(intl_profile), nullptr); } -} // namespace - Engine::Engine(Delegate& delegate, std::string thread_label, std::shared_ptr svc, @@ -66,70 +64,28 @@ Engine::Engine(Delegate& delegate, FlutterRunnerProductConfiguration product_config) : delegate_(delegate), thread_label_(std::move(thread_label)), + settings_(std::move(settings)), weak_factory_(this) { if (zx::event::create(0, &vsync_event_) != ZX_OK) { FML_DLOG(ERROR) << "Could not create the vsync event."; return; } - // Get the task runners from the managed threads. The current thread will be - // used as the "platform" thread. - const flutter::TaskRunners task_runners( - thread_label_, // Dart thread labels - CreateFMLTaskRunner(async_get_default_dispatcher()), // platform - CreateFMLTaskRunner(threads_[0].dispatcher()), // raster - CreateFMLTaskRunner(threads_[1].dispatcher()), // ui - CreateFMLTaskRunner(threads_[2].dispatcher()) // io - ); - UpdateNativeThreadLabelNames(thread_label_, task_runners); + // Launch the threads that will be used to run the shell. These threads will + // be joined in the destructor. + for (auto& thread : threads_) { + thread.reset(new Thread()); + } - // Connect to Scenic. + // Set up the session connection. auto scenic = svc->Connect(); fidl::InterfaceHandle session; fidl::InterfaceHandle session_listener; auto session_listener_request = session_listener.NewRequest(); scenic->CreateSession(session.NewRequest(), session_listener.Bind()); - // Make clones of the `ViewRef` before sending it down to Scenic. - fuchsia::ui::views::ViewRef platform_view_ref, isolate_view_ref; - view_ref_pair.view_ref.Clone(&platform_view_ref); - view_ref_pair.view_ref.Clone(&isolate_view_ref); - - // Session is terminated on the raster thread, but we must terminate ourselves - // on the platform thread. - // - // This handles the fidl error callback when the Session connection is - // broken. The SessionListener interface also has an OnError method, which is - // invoked on the platform thread (in PlatformView). - fml::closure session_error_callback = [dispatcher = - async_get_default_dispatcher(), - weak = weak_factory_.GetWeakPtr()]() { - async::PostTask(dispatcher, [weak]() { - if (weak) { - weak->Terminate(); - } - }); - }; - - // Set up the session connection and other Scenic helpers on the raster - // thread. - task_runners.GetRasterTaskRunner()->PostTask(fml::MakeCopyable( - [this, session = std::move(session), - session_error_callback = std::move(session_error_callback), - view_token = std::move(view_token), - view_ref_pair = std::move(view_ref_pair), - vsync_handle = vsync_event_.get()]() mutable { - session_connection_.emplace( - 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()); - })); - - // Grab the parent environment services. The platform view may want to - // access some of these services. + // Grab the parent environment services. The platform view may want to access + // some of these services. fuchsia::sys::EnvironmentPtr environment; svc->Connect(environment.NewRequest()); fidl::InterfaceHandle @@ -137,15 +93,23 @@ Engine::Engine(Delegate& delegate, environment->GetServices(parent_environment_service_provider.NewRequest()); environment.Unbind(); + // We need to manually schedule a frame when the session metrics change. + OnMetricsUpdate on_session_metrics_change_callback = std::bind( + &Engine::OnSessionMetricsDidChange, this, std::placeholders::_1); + + OnSizeChangeHint on_session_size_change_hint_callback = + std::bind(&Engine::OnSessionSizeChangeHint, this, std::placeholders::_1, + std::placeholders::_2); + OnEnableWireframe on_enable_wireframe_callback = std::bind( - &Engine::DebugWireframeSettingsChanged, this, std::placeholders::_1); + &Engine::OnDebugWireframeSettingsChanged, this, std::placeholders::_1); - OnCreateView on_create_view_callback = - std::bind(&Engine::CreateView, this, std::placeholders::_1, + flutter_runner::OnCreateView on_create_view_callback = + std::bind(&Engine::OnCreateView, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3); - OnDestroyView on_destroy_view_callback = - std::bind(&Engine::DestroyView, this, std::placeholders::_1); + flutter_runner::OnDestroyView on_destroy_view_callback = + std::bind(&Engine::OnDestroyView, this, std::placeholders::_1); OnGetViewEmbedder on_get_view_embedder_callback = std::bind(&Engine::GetViewEmbedder, this); @@ -163,6 +127,10 @@ Engine::Engine(Delegate& delegate, }); }; + fuchsia::ui::views::ViewRef platform_view_ref, isolate_view_ref; + view_ref_pair.view_ref.Clone(&platform_view_ref); + view_ref_pair.view_ref.Clone(&isolate_view_ref); + // Setup the callback that will instantiate the platform view. flutter::Shell::CreateCallback on_create_platform_view = fml::MakeCopyable( @@ -173,6 +141,10 @@ Engine::Engine(Delegate& delegate, session_listener_request = std::move(session_listener_request), on_session_listener_error_callback = std::move(on_session_listener_error_callback), + on_session_metrics_change_callback = + std::move(on_session_metrics_change_callback), + on_session_size_change_hint_callback = + std::move(on_session_size_change_hint_callback), on_enable_wireframe_callback = std::move(on_enable_wireframe_callback), on_create_view_callback = std::move(on_create_view_callback), @@ -190,6 +162,8 @@ Engine::Engine(Delegate& delegate, std::move(parent_environment_service_provider), // services std::move(session_listener_request), // session listener std::move(on_session_listener_error_callback), + std::move(on_session_metrics_change_callback), + std::move(on_session_size_change_hint_callback), std::move(on_enable_wireframe_callback), std::move(on_create_view_callback), std::move(on_destroy_view_callback), @@ -198,17 +172,53 @@ Engine::Engine(Delegate& delegate, product_config); }); + // Session can be terminated on the raster thread, but we must terminate + // ourselves on the platform thread. + // + // This handles the fidl error callback when the Session connection is + // broken. The SessionListener interface also has an OnError method, which is + // invoked on the platform thread (in PlatformView). + fml::closure on_session_error_callback = + [dispatcher = async_get_default_dispatcher(), + weak = weak_factory_.GetWeakPtr()]() { + async::PostTask(dispatcher, [weak]() { + if (weak) { + weak->Terminate(); + } + }); + }; + + // Get the task runners from the managed threads. The current thread will be + // used as the "platform" thread. + const flutter::TaskRunners task_runners( + thread_label_, // Dart thread labels + CreateFMLTaskRunner(async_get_default_dispatcher()), // platform + CreateFMLTaskRunner(threads_[0]->dispatcher()), // raster + CreateFMLTaskRunner(threads_[1]->dispatcher()), // ui + CreateFMLTaskRunner(threads_[2]->dispatcher()) // io + ); + // 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()); + fml::MakeCopyable([thread_label = thread_label_, // + view_token = std::move(view_token), // + view_ref_pair = std::move(view_ref_pair), // + session = std::move(session), // + on_session_error_callback, // + vsync_event = vsync_event_.get() // + ](flutter::Shell& shell) mutable { + std::unique_ptr compositor_context; + { + TRACE_DURATION("flutter", "CreateCompositorContext"); + compositor_context = + std::make_unique( + thread_label, // debug label + std::move(view_token), // scenic view we attach our tree to + std::move(view_ref_pair), // scenic view ref/view ref control + std::move(session), // scenic session + on_session_error_callback, // session did encounter error + vsync_event); // vsync event handle + } return std::make_unique( /*task_runners=*/shell.GetTaskRunners(), @@ -216,9 +226,18 @@ Engine::Engine(Delegate& delegate, /*is_gpu_disabled_sync_switch=*/shell.GetIsGpuDisabledSyncSwitch()); }); - settings.root_isolate_create_callback = + UpdateNativeThreadLabelNames(thread_label_, task_runners); + + settings_.verbose_logging = true; + + settings_.advisory_script_uri = thread_label_; + + settings_.advisory_script_entrypoint = thread_label_; + + settings_.root_isolate_create_callback = std::bind(&Engine::OnMainIsolateStart, this); - settings.root_isolate_shutdown_callback = + + settings_.root_isolate_shutdown_callback = std::bind([weak = weak_factory_.GetWeakPtr(), runner = task_runners.GetPlatformTaskRunner()]() { runner->PostTask([weak = std::move(weak)] { @@ -228,7 +247,7 @@ Engine::Engine(Delegate& delegate, }); }); - auto vm = flutter::DartVMRef::Create(settings); + auto vm = flutter::DartVMRef::Create(settings_); if (!isolate_snapshot) { isolate_snapshot = vm->GetVMData()->GetIsolateSnapshot(); @@ -237,13 +256,13 @@ Engine::Engine(Delegate& delegate, { TRACE_EVENT0("flutter", "CreateShell"); shell_ = flutter::Shell::Create( - std::move(task_runners), // host task runners - flutter::PlatformData(), // default window data - std::move(settings), // shell launch settings - std::move(isolate_snapshot), // isolate snapshot - std::move(on_create_platform_view), // platform view create callback - std::move(on_create_rasterizer), // rasterizer create callback - std::move(vm) // vm reference + task_runners, // host task runners + flutter::PlatformData(), // default window data + settings_, // shell launch settings + std::move(isolate_snapshot), // isolate snapshot + on_create_platform_view, // platform view create callback + on_create_rasterizer, // rasterizer create callback + std::move(vm) // vm reference ); } @@ -320,7 +339,7 @@ Engine::Engine(Delegate& delegate, // Launch the engine in the appropriate configuration. auto run_configuration = flutter::RunConfiguration::InferFromSettings( - shell_->GetSettings(), shell_->GetTaskRunners().GetIOTaskRunner()); + settings_, task_runners.GetIOTaskRunner()); auto on_run_failure = [weak = weak_factory_.GetWeakPtr()]() { // The engine could have been killed by the caller right after the @@ -357,11 +376,11 @@ Engine::Engine(Delegate& delegate, Engine::~Engine() { shell_.reset(); - for (auto& thread : threads_) { - thread.Quit(); + for (const auto& thread : threads_) { + thread->Quit(); } - for (auto& thread : threads_) { - thread.Join(); + for (const auto& thread : threads_) { + thread->Join(); } } @@ -466,41 +485,105 @@ void Engine::Terminate() { // collected this object. } -void Engine::DebugWireframeSettingsChanged(bool enabled) { - if (!shell_ || !scene_update_context_) { +void Engine::OnSessionMetricsDidChange( + const fuchsia::ui::gfx::Metrics& metrics) { + if (!shell_) { + return; + } + + shell_->GetTaskRunners().GetRasterTaskRunner()->PostTask( + [rasterizer = shell_->GetRasterizer(), metrics]() { + if (rasterizer) { + auto compositor_context = + reinterpret_cast( + rasterizer->compositor_context()); + + compositor_context->OnSessionMetricsDidChange(metrics); + } + }); +} + +void Engine::OnDebugWireframeSettingsChanged(bool enabled) { + if (!shell_) { return; } shell_->GetTaskRunners().GetRasterTaskRunner()->PostTask( - [this, enabled]() { scene_update_context_->EnableWireframe(enabled); }); + [rasterizer = shell_->GetRasterizer(), enabled]() { + if (rasterizer) { + auto compositor_context = + reinterpret_cast( + rasterizer->compositor_context()); + + compositor_context->OnWireframeEnabled(enabled); + } + }); } -void Engine::CreateView(int64_t view_id, bool hit_testable, bool focusable) { - if (!shell_ || !scene_update_context_) { +void Engine::OnCreateView(int64_t view_id, bool hit_testable, bool focusable) { + if (!shell_) { return; } shell_->GetTaskRunners().GetRasterTaskRunner()->PostTask( - [this, view_id, hit_testable, focusable]() { - scene_update_context_->CreateView(view_id, hit_testable, focusable); + [rasterizer = shell_->GetRasterizer(), view_id, hit_testable, + focusable]() { + if (rasterizer) { + auto compositor_context = + reinterpret_cast( + rasterizer->compositor_context()); + compositor_context->OnCreateView(view_id, hit_testable, focusable); + } }); } -void Engine::DestroyView(int64_t view_id) { - if (!shell_ || !scene_update_context_) { +void Engine::OnDestroyView(int64_t view_id) { + if (!shell_) { return; } shell_->GetTaskRunners().GetRasterTaskRunner()->PostTask( - [this, view_id]() { scene_update_context_->DestroyView(view_id); }); + [rasterizer = shell_->GetRasterizer(), view_id]() { + if (rasterizer) { + auto compositor_context = + reinterpret_cast( + rasterizer->compositor_context()); + compositor_context->OnDestroyView(view_id); + } + }); } flutter::ExternalViewEmbedder* Engine::GetViewEmbedder() { - if (!scene_update_context_) { - return nullptr; + // GetEmbedder should be called only after rasterizer is created. + FML_DCHECK(shell_); + FML_DCHECK(shell_->GetRasterizer()); + + auto rasterizer = shell_->GetRasterizer(); + auto compositor_context = + reinterpret_cast( + rasterizer->compositor_context()); + flutter::ExternalViewEmbedder* view_embedder = + compositor_context->GetViewEmbedder(); + return view_embedder; +} + +void Engine::OnSessionSizeChangeHint(float width_change_factor, + float height_change_factor) { + if (!shell_) { + return; } - return &scene_update_context_.value(); + shell_->GetTaskRunners().GetRasterTaskRunner()->PostTask( + [rasterizer = shell_->GetRasterizer(), width_change_factor, + height_change_factor]() { + if (rasterizer) { + auto compositor_context = reinterpret_cast( + rasterizer->compositor_context()); + + compositor_context->OnSessionSizeChangeHint(width_change_factor, + height_change_factor); + } + }); } #if !defined(DART_PRODUCT) diff --git a/shell/platform/fuchsia/flutter/engine.h b/shell/platform/fuchsia/flutter/engine.h index fdd7f68595426..5ed41394fd599 100644 --- a/shell/platform/fuchsia/flutter/engine.h +++ b/shell/platform/fuchsia/flutter/engine.h @@ -15,15 +15,11 @@ #include #include "flutter/flow/embedded_views.h" -#include "flutter/flow/scene_update_context.h" #include "flutter/fml/macros.h" #include "flutter/shell/common/shell.h" - #include "flutter_runner_product_configuration.h" #include "isolate_configurator.h" -#include "session_connection.h" #include "thread.h" -#include "vulkan_surface_producer.h" namespace flutter_runner { @@ -59,22 +55,15 @@ class Engine final { private: Delegate& delegate_; - const std::string thread_label_; - std::array threads_; - - std::optional session_connection_; - std::optional surface_producer_; - std::optional scene_update_context_; - + flutter::Settings settings_; + std::array, 3> threads_; std::unique_ptr isolate_configurator_; std::unique_ptr shell_; - - fuchsia::intl::PropertyProviderPtr intl_property_provider_; - zx::event vsync_event_; - fml::WeakPtrFactory weak_factory_; + // A stub for the FIDL protocol fuchsia.intl.PropertyProvider. + fuchsia::intl::PropertyProviderPtr intl_property_provider_; void OnMainIsolateStart(); @@ -82,9 +71,15 @@ class Engine final { void Terminate(); - void DebugWireframeSettingsChanged(bool enabled); - void CreateView(int64_t view_id, bool hit_testable, bool focusable); - void DestroyView(int64_t view_id); + void OnSessionMetricsDidChange(const fuchsia::ui::gfx::Metrics& metrics); + void OnSessionSizeChangeHint(float width_change_factor, + float height_change_factor); + + void OnDebugWireframeSettingsChanged(bool enabled); + + void OnCreateView(int64_t view_id, bool hit_testable, bool focusable); + + void OnDestroyView(int64_t view_id); flutter::ExternalViewEmbedder* GetViewEmbedder(); diff --git a/shell/platform/fuchsia/flutter/engine_flutter_runner.gni b/shell/platform/fuchsia/flutter/engine_flutter_runner.gni new file mode 100644 index 0000000000000..630575b0980c4 --- /dev/null +++ b/shell/platform/fuchsia/flutter/engine_flutter_runner.gni @@ -0,0 +1,150 @@ +# 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. + +assert(is_fuchsia) + +import("//build/fuchsia/sdk.gni") + +# Builds a flutter_runner +# +# Parameters: +# +# output_name (required): +# The name of the resulting binary. +# +# extra_deps (required): +# Any additional dependencies. +# +# product (required): +# Whether to link against a Product mode Dart VM. +# +# extra_defines (optional): +# Any additional preprocessor defines. +template("flutter_runner") { + assert(defined(invoker.output_name), "flutter_runner must define output_name") + assert(defined(invoker.extra_deps), "flutter_runner must define extra_deps") + assert(defined(invoker.product), "flutter_runner must define product") + + invoker_output_name = invoker.output_name + extra_deps = invoker.extra_deps + + extra_defines = [] + if (defined(invoker.extra_defines)) { + extra_defines += invoker.extra_defines + } + + executable(target_name) { + output_name = invoker_output_name + + defines = extra_defines + + libs = [] + + sources = [ + "accessibility_bridge.cc", + "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_intl.cc", + "fuchsia_intl.h", + "isolate_configurator.cc", + "isolate_configurator.h", + "logging.h", + "loop.cc", + "loop.h", + "main.cc", + "platform_view.cc", + "platform_view.h", + "runner.cc", + "runner.h", + "session_connection.cc", + "session_connection.h", + "surface.cc", + "surface.h", + "task_observers.cc", + "task_observers.h", + "task_runner_adapter.cc", + "task_runner_adapter.h", + "thread.cc", + "thread.h", + "unique_fdio_ns.h", + "vsync_recorder.cc", + "vsync_recorder.h", + "vsync_waiter.cc", + "vsync_waiter.h", + "vulkan_surface.cc", + "vulkan_surface.h", + "vulkan_surface_pool.cc", + "vulkan_surface_pool.h", + "vulkan_surface_producer.cc", + "vulkan_surface_producer.h", + ] + + # The use of these dependencies is temporary and will be moved behind the + # embedder API. + flutter_deps = [ + "../flutter:fuchsia_legacy_gpu_configuration", + "//flutter/assets", + "//flutter/common", + "//flutter/flow:flow_fuchsia_legacy", + "//flutter/fml", + "//flutter/lib/ui:ui_fuchsia_legacy", + "//flutter/runtime:runtime_fuchsia_legacy", + "//flutter/shell/common:common_fuchsia_legacy", + "//flutter/vulkan", + ] + + _fuchsia_platform = "//flutter/shell/platform/fuchsia" + + # TODO(kaushikiska) evaluate if all of these are needed. + fuchsia_deps = [ + "${_fuchsia_platform}/dart-pkg/fuchsia", + "${_fuchsia_platform}/dart-pkg/zircon", + "${_fuchsia_platform}/runtime/dart/utils", + ] + + deps = [ + "$fuchsia_sdk_root/fidl:fuchsia.accessibility.semantics", + "$fuchsia_sdk_root/fidl:fuchsia.fonts", + "$fuchsia_sdk_root/fidl:fuchsia.images", + "$fuchsia_sdk_root/fidl:fuchsia.intl", + "$fuchsia_sdk_root/fidl:fuchsia.io", + "$fuchsia_sdk_root/fidl:fuchsia.sys", + "$fuchsia_sdk_root/fidl:fuchsia.ui.app", + "$fuchsia_sdk_root/fidl:fuchsia.ui.scenic", + "$fuchsia_sdk_root/pkg:async-cpp", + "$fuchsia_sdk_root/pkg:async-default", + "$fuchsia_sdk_root/pkg:async-loop", + "$fuchsia_sdk_root/pkg:async-loop-cpp", + "$fuchsia_sdk_root/pkg:fdio", + "$fuchsia_sdk_root/pkg:fidl_cpp", + "$fuchsia_sdk_root/pkg:scenic_cpp", + "$fuchsia_sdk_root/pkg:sys_cpp", + "$fuchsia_sdk_root/pkg:syslog", + "$fuchsia_sdk_root/pkg:trace", + "$fuchsia_sdk_root/pkg:trace-engine", + "$fuchsia_sdk_root/pkg:trace-provider-so", + "$fuchsia_sdk_root/pkg:vfs_cpp", + "$fuchsia_sdk_root/pkg:zx", + "//third_party/skia", + "//flutter/third_party/tonic", + ] + fuchsia_deps + flutter_deps + extra_deps + + # The flags below are needed so that Dart's CPU profiler can walk the + # C++ stack. + cflags = [ "-fno-omit-frame-pointer" ] + + if (!invoker.product) { + # This flag is needed so that the call to dladdr() in Dart's native symbol + # resolver can report good symbol information for the CPU profiler. + ldflags = [ "-rdynamic" ] + } + } +} diff --git a/shell/platform/fuchsia/flutter/platform_view.cc b/shell/platform/fuchsia/flutter/platform_view.cc index 81441b47cd16f..df2b454336b1a 100644 --- a/shell/platform/fuchsia/flutter/platform_view.cc +++ b/shell/platform/fuchsia/flutter/platform_view.cc @@ -6,7 +6,6 @@ #include "platform_view.h" -#include #include #include "flutter/fml/logging.h" @@ -23,6 +22,41 @@ namespace flutter_runner { +namespace { + +inline fuchsia::ui::gfx::vec3 Add(const fuchsia::ui::gfx::vec3& a, + const fuchsia::ui::gfx::vec3& b) { + return {.x = a.x + b.x, .y = a.y + b.y, .z = a.z + b.z}; +} + +inline fuchsia::ui::gfx::vec3 Subtract(const fuchsia::ui::gfx::vec3& a, + const fuchsia::ui::gfx::vec3& b) { + return {.x = a.x - b.x, .y = a.y - b.y, .z = a.z - b.z}; +} + +inline fuchsia::ui::gfx::BoundingBox InsetBy( + const fuchsia::ui::gfx::BoundingBox& box, + const fuchsia::ui::gfx::vec3& inset_from_min, + const fuchsia::ui::gfx::vec3& inset_from_max) { + return {.min = Add(box.min, inset_from_min), + .max = Subtract(box.max, inset_from_max)}; +} + +inline fuchsia::ui::gfx::BoundingBox ViewPropertiesLayoutBox( + const fuchsia::ui::gfx::ViewProperties& view_properties) { + return InsetBy(view_properties.bounding_box, view_properties.inset_from_min, + view_properties.inset_from_max); +} + +inline fuchsia::ui::gfx::vec3 Max(const fuchsia::ui::gfx::vec3& v, + float min_val) { + return {.x = std::max(v.x, min_val), + .y = std::max(v.y, min_val), + .z = std::max(v.z, min_val)}; +} + +} // end namespace + static constexpr char kFlutterPlatformChannel[] = "flutter/platform"; static constexpr char kTextInputChannel[] = "flutter/textinput"; static constexpr char kKeyEventChannel[] = "flutter/keyevent"; @@ -55,6 +89,8 @@ PlatformView::PlatformView( fidl::InterfaceRequest session_listener_request, fit::closure session_listener_error_callback, + OnMetricsUpdate session_metrics_did_change_callback, + OnSizeChangeHint session_size_change_hint_callback, OnEnableWireframe wireframe_enabled_callback, OnCreateView on_create_view_callback, OnDestroyView on_destroy_view_callback, @@ -67,6 +103,8 @@ PlatformView::PlatformView( session_listener_binding_(this, std::move(session_listener_request)), session_listener_error_callback_( std::move(session_listener_error_callback)), + metrics_changed_callback_(std::move(session_metrics_did_change_callback)), + size_change_hint_callback_(std::move(session_size_change_hint_callback)), wireframe_enabled_callback_(std::move(wireframe_enabled_callback)), on_create_view_callback_(std::move(on_create_view_callback)), on_destroy_view_callback_(std::move(on_destroy_view_callback)), @@ -114,6 +152,64 @@ void PlatformView::RegisterPlatformMessageHandlers() { this, std::placeholders::_1); } +void PlatformView::OnPropertiesChanged( + const fuchsia::ui::gfx::ViewProperties& view_properties) { + fuchsia::ui::gfx::BoundingBox layout_box = + ViewPropertiesLayoutBox(view_properties); + + fuchsia::ui::gfx::vec3 logical_size = + Max(Subtract(layout_box.max, layout_box.min), 0.f); + + metrics_.size.width = logical_size.x; + metrics_.size.height = logical_size.y; + metrics_.size.depth = logical_size.z; + metrics_.padding.left = view_properties.inset_from_min.x; + metrics_.padding.top = view_properties.inset_from_min.y; + metrics_.padding.front = view_properties.inset_from_min.z; + metrics_.padding.right = view_properties.inset_from_max.x; + metrics_.padding.bottom = view_properties.inset_from_max.y; + metrics_.padding.back = view_properties.inset_from_max.z; + + FlushViewportMetrics(); +} + +// TODO(SCN-975): Re-enable. +// void PlatformView::ConnectSemanticsProvider( +// fuchsia::ui::viewsv1token::ViewToken token) { +// semantics_bridge_.SetupEnvironment( +// token.value, parent_environment_service_provider_.get()); +// } + +void PlatformView::UpdateViewportMetrics( + const fuchsia::ui::gfx::Metrics& metrics) { + metrics_.scale = metrics.scale_x; + metrics_.scale_z = metrics.scale_z; + + FlushViewportMetrics(); +} + +void PlatformView::FlushViewportMetrics() { + const auto scale = metrics_.scale; + const auto scale_z = metrics_.scale_z; + + SetViewportMetrics({ + scale, // device_pixel_ratio + metrics_.size.width * scale, // physical_width + metrics_.size.height * scale, // physical_height + metrics_.size.depth * scale_z, // physical_depth + metrics_.padding.top * scale, // physical_padding_top + metrics_.padding.right * scale, // physical_padding_right + metrics_.padding.bottom * scale, // physical_padding_bottom + metrics_.padding.left * scale, // physical_padding_left + metrics_.view_inset.front * scale_z, // physical_view_inset_front + metrics_.view_inset.back * scale_z, // physical_view_inset_back + metrics_.view_inset.top * scale, // physical_view_inset_top + metrics_.view_inset.right * scale, // physical_view_inset_right + metrics_.view_inset.bottom * scale, // physical_view_inset_bottom + metrics_.view_inset.left * scale // physical_view_inset_left + }); +} + // |fuchsia::ui::input::InputMethodEditorClient| void PlatformView::DidUpdateState( fuchsia::ui::input::TextInputState state, @@ -206,40 +302,27 @@ void PlatformView::OnScenicError(std::string error) { void PlatformView::OnScenicEvent( std::vector events) { TRACE_EVENT0("flutter", "PlatformView::OnScenicEvent"); - bool should_update_metrics = false; for (const auto& event : events) { switch (event.Which()) { case fuchsia::ui::scenic::Event::Tag::kGfx: switch (event.gfx().Which()) { case fuchsia::ui::gfx::Event::Tag::kMetrics: { - const fuchsia::ui::gfx::Metrics& metrics = - event.gfx().metrics().metrics; - const float new_view_pixel_ratio = metrics.scale_x; - - // Avoid metrics update when possible -- it is computationally - // expensive. - if (view_pixel_ratio_ != new_view_pixel_ratio) { - view_pixel_ratio_ = new_view_pixel_ratio; - should_update_metrics = true; + if (!fidl::Equals(event.gfx().metrics().metrics, scenic_metrics_)) { + scenic_metrics_ = std::move(event.gfx().metrics().metrics); + metrics_changed_callback_(scenic_metrics_); + UpdateViewportMetrics(scenic_metrics_); } break; } + case fuchsia::ui::gfx::Event::Tag::kSizeChangeHint: { + size_change_hint_callback_( + event.gfx().size_change_hint().width_change_factor, + event.gfx().size_change_hint().height_change_factor); + break; + } case fuchsia::ui::gfx::Event::Tag::kViewPropertiesChanged: { - const fuchsia::ui::gfx::BoundingBox& bounding_box = - event.gfx().view_properties_changed().properties.bounding_box; - const float new_view_width = - std::max(bounding_box.max.x - bounding_box.min.x, 0.0f); - const float new_view_height = - std::max(bounding_box.max.y - bounding_box.min.y, 0.0f); - - // Avoid metrics update when possible -- it is computationally - // expensive. - if (view_width_ != new_view_width || - view_height_ != new_view_width) { - view_width_ = new_view_width; - view_height_ = new_view_height; - should_update_metrics = true; - } + OnPropertiesChanged( + std::move(event.gfx().view_properties_changed().properties)); break; } case fuchsia::ui::gfx::Event::Tag::kViewConnected: @@ -289,26 +372,6 @@ void PlatformView::OnScenicEvent( } } } - - if (should_update_metrics) { - SetViewportMetrics({ - view_pixel_ratio_, // device_pixel_ratio - view_width_ * view_pixel_ratio_, // physical_width - view_height_ * view_pixel_ratio_, // physical_height - 0.0f, // physical_padding_top - 0.0f, // physical_padding_right - 0.0f, // physical_padding_bottom - 0.0f, // physical_padding_left - 0.0f, // physical_view_inset_top - 0.0f, // physical_view_inset_right - 0.0f, // physical_view_inset_bottom - 0.0f, // physical_view_inset_left - 0.0f, // p_physical_system_gesture_inset_top - 0.0f, // p_physical_system_gesture_inset_right - 0.0f, // p_physical_system_gesture_inset_bottom - 0.0f, // p_physical_system_gesture_inset_left - }); - } } void PlatformView::OnChildViewConnected(scenic::ResourceId view_holder_id) { @@ -388,9 +451,8 @@ bool PlatformView::OnHandlePointerEvent( pointer_data.change = GetChangeFromPointerEventPhase(pointer.phase); pointer_data.kind = GetKindFromPointerType(pointer.type); pointer_data.device = pointer.pointer_id; - // Pointer events are in logical pixels, so scale to physical. - pointer_data.physical_x = pointer.x * view_pixel_ratio_; - pointer_data.physical_y = pointer.y * view_pixel_ratio_; + pointer_data.physical_x = pointer.x * metrics_.scale; + pointer_data.physical_y = pointer.y * metrics_.scale; // Buttons are single bit values starting with kMousePrimaryButton = 1. pointer_data.buttons = static_cast(pointer.buttons); diff --git a/shell/platform/fuchsia/flutter/platform_view.h b/shell/platform/fuchsia/flutter/platform_view.h index b6dd8e1697206..45e1e1fb97732 100644 --- a/shell/platform/fuchsia/flutter/platform_view.h +++ b/shell/platform/fuchsia/flutter/platform_view.h @@ -5,6 +5,7 @@ #ifndef FLUTTER_SHELL_PLATFORM_FUCHSIA_PLATFORM_VIEW_H_ #define FLUTTER_SHELL_PLATFORM_FUCHSIA_PLATFORM_VIEW_H_ +#include #include #include #include @@ -13,6 +14,7 @@ #include #include "flutter/fml/macros.h" +#include "flutter/lib/ui/window/viewport_metrics.h" #include "flutter/shell/common/platform_view.h" #include "flutter/shell/platform/fuchsia/flutter/accessibility_bridge.h" #include "flutter_runner_product_configuration.h" @@ -22,6 +24,9 @@ namespace flutter_runner { +using OnMetricsUpdate = fit::function; +using OnSizeChangeHint = + fit::function; using OnEnableWireframe = fit::function; using OnCreateView = fit::function; using OnDestroyView = fit::function; @@ -48,15 +53,25 @@ class PlatformView final : public flutter::PlatformView, fidl::InterfaceRequest session_listener_request, fit::closure on_session_listener_error_callback, + OnMetricsUpdate session_metrics_did_change_callback, + OnSizeChangeHint session_size_change_hint_callback, OnEnableWireframe wireframe_enabled_callback, OnCreateView on_create_view_callback, OnDestroyView on_destroy_view_callback, OnGetViewEmbedder on_get_view_embedder_callback, zx_handle_t vsync_event_handle, FlutterRunnerProductConfiguration product_config); + PlatformView(flutter::PlatformView::Delegate& delegate, + std::string debug_label, + flutter::TaskRunners task_runners, + fidl::InterfaceHandle + parent_environment_service_provider, + zx_handle_t vsync_event_handle); ~PlatformView(); + void UpdateViewportMetrics(const fuchsia::ui::gfx::Metrics& metrics); + // |flutter::PlatformView| // |flutter_runner::AccessibilityBridge::Delegate| void SetSemanticsEnabled(bool enabled) override; @@ -77,6 +92,8 @@ class PlatformView final : public flutter::PlatformView, fidl::Binding session_listener_binding_; fit::closure session_listener_error_callback_; + OnMetricsUpdate metrics_changed_callback_; + OnSizeChangeHint size_change_hint_callback_; OnEnableWireframe wireframe_enabled_callback_; OnCreateView on_create_view_callback_; OnDestroyView on_destroy_view_callback_; @@ -88,7 +105,8 @@ class PlatformView final : public flutter::PlatformView, fuchsia::ui::input::ImeServicePtr text_sync_service_; fuchsia::sys::ServiceProviderPtr parent_environment_service_provider_; - + flutter::LogicalMetrics metrics_; + fuchsia::ui::gfx::Metrics scenic_metrics_; // last_text_state_ is the last state of the text input as reported by the IME // or initialized by Flutter. We set it to null if Flutter doesn't want any // input, since then there is no text input state at all. @@ -106,14 +124,16 @@ class PlatformView final : public flutter::PlatformView, std::set unregistered_channels_; 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(); + void FlushViewportMetrics(); + + // Called when the view's properties have changed. + void OnPropertiesChanged( + const fuchsia::ui::gfx::ViewProperties& view_properties); + // |fuchsia::ui::input::InputMethodEditorClient| void DidUpdateState( fuchsia::ui::input::TextInputState state, diff --git a/shell/platform/fuchsia/flutter/platform_view_unittest.cc b/shell/platform/fuchsia/flutter/platform_view_unittest.cc index 7a1929dd9bf3c..89a5658d8dd63 100644 --- a/shell/platform/fuchsia/flutter/platform_view_unittest.cc +++ b/shell/platform/fuchsia/flutter/platform_view_unittest.cc @@ -4,7 +4,7 @@ #include "flutter/shell/platform/fuchsia/flutter/platform_view.h" -#include +#include #include #include #include @@ -15,11 +15,11 @@ #include #include -#include "flutter/flow/embedded_views.h" +#include "flutter/flow/scene_update_context.h" #include "flutter/lib/ui/window/platform_message.h" #include "flutter/lib/ui/window/window.h" +#include "fuchsia/ui/views/cpp/fidl.h" #include "gtest/gtest.h" - #include "task_runner_adapter.h" namespace flutter_runner_test::flutter_runner_a11y_test { @@ -41,33 +41,6 @@ class PlatformViewTests : public testing::Test { FML_DISALLOW_COPY_AND_ASSIGN(PlatformViewTests); }; -class MockExternalViewEmbedder : public flutter::ExternalViewEmbedder { - public: - MockExternalViewEmbedder() = default; - ~MockExternalViewEmbedder() override = default; - - SkCanvas* GetRootCanvas() override { return nullptr; } - std::vector GetCurrentCanvases() override { - return std::vector(); - } - - void CancelFrame() override {} - void BeginFrame( - SkISize frame_size, - GrDirectContext* context, - double device_pixel_ratio, - fml::RefPtr raster_thread_merger) override {} - void SubmitFrame(GrDirectContext* context, - std::unique_ptr frame) override { - return; - } - - void PrerollCompositeEmbeddedView( - int view_id, - std::unique_ptr params) override {} - SkCanvas* CompositeEmbeddedView(int view_id) override { return nullptr; } -}; - class MockPlatformViewDelegate : public flutter::PlatformView::Delegate { public: // |flutter::PlatformView::Delegate| @@ -126,6 +99,30 @@ class MockPlatformViewDelegate : public flutter::PlatformView::Delegate { int32_t semantics_features_ = 0; }; +class MockSurfaceProducer + : public flutter::SceneUpdateContext::SurfaceProducer { + public: + std::unique_ptr + ProduceSurface(const SkISize& size, + const flutter::LayerRasterCacheKey& layer_key, + std::unique_ptr entity_node) override { + return nullptr; + } + + bool HasRetainedNode(const flutter::LayerRasterCacheKey& key) const override { + return false; + } + + scenic::EntityNode* GetRetainedNode( + const flutter::LayerRasterCacheKey& key) override { + return nullptr; + } + + void SubmitSurface( + std::unique_ptr + surface) override {} +}; + TEST_F(PlatformViewTests, ChangesAccessibilitySettings) { sys::testing::ServiceDirectoryProvider services_provider(dispatcher()); @@ -150,6 +147,8 @@ TEST_F(PlatformViewTests, ChangesAccessibilitySettings) { nullptr, // parent_environment_service_provider_handle nullptr, // session_listener_request nullptr, // on_session_listener_error_callback + nullptr, // session_metrics_did_change_callback + nullptr, // session_size_change_hint_callback nullptr, // on_enable_wireframe_callback, nullptr, // on_create_view_callback, nullptr, // on_destroy_view_callback, @@ -204,6 +203,8 @@ TEST_F(PlatformViewTests, EnableWireframeTest) { nullptr, // parent_environment_service_provider_handle nullptr, // session_listener_request nullptr, // on_session_listener_error_callback + nullptr, // session_metrics_did_change_callback + nullptr, // session_size_change_hint_callback EnableWireframeCallback, // on_enable_wireframe_callback, nullptr, // on_create_view_callback, nullptr, // on_destroy_view_callback, @@ -269,6 +270,8 @@ TEST_F(PlatformViewTests, CreateViewTest) { nullptr, // parent_environment_service_provider_handle nullptr, // session_listener_request nullptr, // on_session_listener_error_callback + nullptr, // session_metrics_did_change_callback + nullptr, // session_size_change_hint_callback nullptr, // on_enable_wireframe_callback, CreateViewCallback, // on_create_view_callback, nullptr, // on_destroy_view_callback, @@ -336,6 +339,8 @@ TEST_F(PlatformViewTests, DestroyViewTest) { nullptr, // parent_environment_service_provider_handle nullptr, // session_listener_request nullptr, // on_session_listener_error_callback + nullptr, // session_metrics_did_change_callback + nullptr, // session_size_change_hint_callback nullptr, // on_enable_wireframe_callback, nullptr, // on_create_view_callback, DestroyViewCallback, // on_destroy_view_callback, @@ -390,8 +395,11 @@ TEST_F(PlatformViewTests, GetViewEmbedderTest) { ); // Test get view embedder callback function. - MockExternalViewEmbedder view_embedder; - auto GetViewEmbedderCallback = [&view_embedder]() { return &view_embedder; }; + MockSurfaceProducer surfaceProducer; + flutter::SceneUpdateContext scene_update_context(nullptr, &surfaceProducer); + flutter::ExternalViewEmbedder* view_embedder = + reinterpret_cast(&scene_update_context); + auto GetViewEmbedderCallback = [view_embedder]() { return view_embedder; }; auto platform_view = flutter_runner::PlatformView( delegate, // delegate @@ -402,6 +410,8 @@ TEST_F(PlatformViewTests, GetViewEmbedderTest) { nullptr, // parent_environment_service_provider_handle nullptr, // session_listener_request nullptr, // on_session_listener_error_callback + nullptr, // session_metrics_did_change_callback + nullptr, // session_size_change_hint_callback nullptr, // on_enable_wireframe_callback, nullptr, // on_create_view_callback, nullptr, // on_destroy_view_callback, @@ -416,7 +426,7 @@ TEST_F(PlatformViewTests, GetViewEmbedderTest) { RunLoopUntilIdle(); - EXPECT_EQ(&view_embedder, delegate.get_view_embedder()); + EXPECT_EQ(view_embedder, delegate.get_view_embedder()); } } // namespace flutter_runner_test::flutter_runner_a11y_test diff --git a/shell/platform/fuchsia/flutter/session_connection.cc b/shell/platform/fuchsia/flutter/session_connection.cc index c87368a8993b9..133dc9a43b8be 100644 --- a/shell/platform/fuchsia/flutter/session_connection.cc +++ b/shell/platform/fuchsia/flutter/session_connection.cc @@ -5,8 +5,8 @@ #include "session_connection.h" #include "flutter/fml/make_copyable.h" -#include "flutter/fml/trace_event.h" - +#include "lib/fidl/cpp/optional.h" +#include "lib/ui/scenic/cpp/commands.h" #include "vsync_recorder.h" #include "vsync_waiter.h" @@ -14,11 +14,23 @@ namespace flutter_runner { SessionConnection::SessionConnection( std::string debug_label, + fuchsia::ui::views::ViewToken view_token, + scenic::ViewRefPair view_ref_pair, fidl::InterfaceHandle session, fml::closure session_error_callback, on_frame_presented_event on_frame_presented_callback, zx_handle_t vsync_event_handle) - : session_wrapper_(session.Bind(), nullptr), + : debug_label_(std::move(debug_label)), + session_wrapper_(session.Bind(), nullptr), + root_view_(&session_wrapper_, + std::move(view_token), + std::move(view_ref_pair.control_ref), + std::move(view_ref_pair.view_ref), + debug_label), + root_node_(&session_wrapper_), + surface_producer_( + std::make_unique(&session_wrapper_)), + scene_update_context_(&session_wrapper_, surface_producer_.get()), on_frame_presented_callback_(std::move(on_frame_presented_callback)), vsync_event_handle_(vsync_event_handle) { session_wrapper_.set_error_handler( @@ -51,7 +63,11 @@ SessionConnection::SessionConnection( } // callback ); - session_wrapper_.SetDebugName(debug_label); + session_wrapper_.SetDebugName(debug_label_); + + root_view_.AddChild(root_node_); + root_node_.SetEventMask(fuchsia::ui::gfx::kMetricsEventMask | + fuchsia::ui::gfx::kSizeChangeHintEventMask); // Get information to finish initialization and only then allow Present()s. session_wrapper_.RequestPresentationTimes( @@ -75,7 +91,8 @@ SessionConnection::SessionConnection( SessionConnection::~SessionConnection() = default; -void SessionConnection::Present() { +void SessionConnection::Present( + flutter::CompositorContext::ScopedFrame* frame) { TRACE_EVENT0("gfx", "SessionConnection::Present"); TRACE_FLOW_BEGIN("gfx", "SessionConnection::PresentSession", @@ -97,6 +114,21 @@ void SessionConnection::Present() { present_session_pending_ = true; ToggleSignal(vsync_event_handle_, false); } + + if (frame) { + // Execute paint tasks and signal fences. + auto surfaces_to_submit = scene_update_context_.ExecutePaintTasks(*frame); + + // Tell the surface producer that a present has occurred so it can perform + // book-keeping on buffer caches. + surface_producer_->OnSurfacesPresented(std::move(surfaces_to_submit)); + } +} + +void SessionConnection::OnSessionSizeChangeHint(float width_change_factor, + float height_change_factor) { + surface_producer_->OnSessionSizeChangeHint(width_change_factor, + height_change_factor); } fml::TimePoint SessionConnection::CalculateNextLatchPoint( @@ -128,6 +160,17 @@ fml::TimePoint SessionConnection::CalculateNextLatchPoint( return minimum_latch_point_to_target; } +void SessionConnection::set_enable_wireframe(bool enable) { + session_wrapper_.Enqueue( + scenic::NewSetEnableDebugViewBoundsCmd(root_view_.id(), enable)); +} + +void SessionConnection::EnqueueClearOps() { + // We are going to be sending down a fresh node hierarchy every frame. So just + // enqueue a detach op on the imported root node. + session_wrapper_.Enqueue(scenic::NewDetachChildrenCmd(root_node_.id())); +} + void SessionConnection::PresentSession() { TRACE_EVENT0("gfx", "SessionConnection::PresentSession"); @@ -189,6 +232,10 @@ void SessionConnection::PresentSession() { VsyncRecorder::GetInstance().UpdateNextPresentationInfo( std::move(info)); }); + + // Prepare for the next frame. These ops won't be processed till the next + // present. + EnqueueClearOps(); } void SessionConnection::ToggleSignal(zx_handle_t handle, bool set) { diff --git a/shell/platform/fuchsia/flutter/session_connection.h b/shell/platform/fuchsia/flutter/session_connection.h index 7630d15e77837..dcd55f19afbf4 100644 --- a/shell/platform/fuchsia/flutter/session_connection.h +++ b/shell/platform/fuchsia/flutter/session_connection.h @@ -5,14 +5,22 @@ #ifndef FLUTTER_SHELL_PLATFORM_FUCHSIA_SESSION_CONNECTION_H_ #define FLUTTER_SHELL_PLATFORM_FUCHSIA_SESSION_CONNECTION_H_ -#include +#include #include +#include #include +#include +#include +#include #include +#include +#include "flutter/flow/compositor_context.h" #include "flutter/flow/scene_update_context.h" #include "flutter/fml/closure.h" #include "flutter/fml/macros.h" +#include "flutter/fml/trace_event.h" +#include "vulkan_surface_producer.h" namespace flutter_runner { @@ -21,9 +29,11 @@ using on_frame_presented_event = // The component residing on the raster thread that is responsible for // maintaining the Scenic session connection and presenting node updates. -class SessionConnection final : public flutter::SessionWrapper { +class SessionConnection final { public: SessionConnection(std::string debug_label, + fuchsia::ui::views::ViewToken view_token, + scenic::ViewRefPair view_ref_pair, fidl::InterfaceHandle session, fml::closure session_error_callback, on_frame_presented_event on_frame_presented_callback, @@ -31,8 +41,36 @@ class SessionConnection final : public flutter::SessionWrapper { ~SessionConnection(); - scenic::Session* get() override { return &session_wrapper_; } - void Present() override; + bool has_metrics() const { return scene_update_context_.has_metrics(); } + + const fuchsia::ui::gfx::MetricsPtr& metrics() const { + return scene_update_context_.metrics(); + } + + void set_metrics(const fuchsia::ui::gfx::Metrics& metrics) { + fuchsia::ui::gfx::Metrics metrics_copy; + metrics.Clone(&metrics_copy); + scene_update_context_.set_metrics( + fidl::MakeOptional(std::move(metrics_copy))); + } + + void set_enable_wireframe(bool enable); + + flutter::SceneUpdateContext& scene_update_context() { + return scene_update_context_; + } + + scenic::ContainerNode& root_node() { return root_node_; } + scenic::View* root_view() { return &root_view_; } + + void Present(flutter::CompositorContext::ScopedFrame* frame); + + void OnSessionSizeChangeHint(float width_change_factor, + float height_change_factor); + + VulkanSurfaceProducer* vulkan_surface_producer() { + return surface_producer_.get(); + } static fml::TimePoint CalculateNextLatchPoint( fml::TimePoint present_requested_time, @@ -44,8 +82,14 @@ class SessionConnection final : public flutter::SessionWrapper { future_presentation_infos); private: + const std::string debug_label_; scenic::Session session_wrapper_; + scenic::View root_view_; + scenic::EntityNode root_node_; + + std::unique_ptr surface_producer_; + flutter::SceneUpdateContext scene_update_context_; on_frame_presented_event on_frame_presented_callback_; zx_handle_t vsync_event_handle_; @@ -78,6 +122,8 @@ class SessionConnection final : public flutter::SessionWrapper { bool present_session_pending_ = false; + void EnqueueClearOps(); + void PresentSession(); static void ToggleSignal(zx_handle_t handle, bool raise); diff --git a/shell/platform/fuchsia/flutter/tests/session_connection_unittests.cc b/shell/platform/fuchsia/flutter/tests/session_connection_unittests.cc index 5a5463266faa8..87efa11e95f2a 100644 --- a/shell/platform/fuchsia/flutter/tests/session_connection_unittests.cc +++ b/shell/platform/fuchsia/flutter/tests/session_connection_unittests.cc @@ -2,16 +2,18 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include -#include -#include +#include "gtest/gtest.h" + #include #include +#include + +#include +#include #include "flutter/shell/platform/fuchsia/flutter/logging.h" #include "flutter/shell/platform/fuchsia/flutter/runner.h" #include "flutter/shell/platform/fuchsia/flutter/session_connection.h" -#include "gtest/gtest.h" using namespace flutter_runner; @@ -28,8 +30,12 @@ class SessionConnectionTest : public ::testing::Test { loop_.StartThread("SessionConnectionTestThread", &fidl_thread_)); auto session_listener_request = session_listener_.NewRequest(); + auto [view_token, view_holder_token] = scenic::ViewTokenPair::New(); + view_token_ = std::move(view_token); scenic_->CreateSession(session_.NewRequest(), session_listener_.Bind()); + presenter_->PresentOrReplaceView(std::move(view_holder_token), nullptr); + FML_CHECK(zx::event::create(0, &vsync_event_) == ZX_OK); // Ensure Scenic has had time to wake up before the test logic begins. @@ -54,6 +60,7 @@ class SessionConnectionTest : public ::testing::Test { fidl::InterfaceHandle session_; fidl::InterfaceHandle session_listener_; + fuchsia::ui::views::ViewToken view_token_; zx::event vsync_event_; thrd_t fidl_thread_; }; @@ -69,11 +76,12 @@ TEST_F(SessionConnectionTest, SimplePresentTest) { }; flutter_runner::SessionConnection session_connection( - "debug label", std::move(session_), on_session_error_callback, + "debug label", std::move(view_token_), scenic::ViewRefPair::New(), + std::move(session_), on_session_error_callback, on_frame_presented_callback, vsync_event_.get()); for (int i = 0; i < 200; ++i) { - session_connection.Present(); + session_connection.Present(nullptr); std::this_thread::sleep_for(std::chrono::milliseconds(10)); } @@ -91,11 +99,12 @@ TEST_F(SessionConnectionTest, BatchedPresentTest) { }; flutter_runner::SessionConnection session_connection( - "debug label", std::move(session_), on_session_error_callback, + "debug label", std::move(view_token_), scenic::ViewRefPair::New(), + std::move(session_), on_session_error_callback, on_frame_presented_callback, vsync_event_.get()); for (int i = 0; i < 200; ++i) { - session_connection.Present(); + session_connection.Present(nullptr); if (i % 10 == 9) { std::this_thread::sleep_for(std::chrono::milliseconds(20)); } diff --git a/shell/platform/fuchsia/flutter/vulkan_surface.h b/shell/platform/fuchsia/flutter/vulkan_surface.h index bff2713a011c9..e6b7eb4e8943a 100644 --- a/shell/platform/fuchsia/flutter/vulkan_surface.h +++ b/shell/platform/fuchsia/flutter/vulkan_surface.h @@ -5,7 +5,6 @@ #pragma once #include -#include #include #include @@ -13,46 +12,17 @@ #include #include "flutter/flow/raster_cache_key.h" +#include "flutter/flow/scene_update_context.h" #include "flutter/fml/macros.h" #include "flutter/vulkan/vulkan_command_buffer.h" #include "flutter/vulkan/vulkan_handle.h" #include "flutter/vulkan/vulkan_proc_table.h" #include "flutter/vulkan/vulkan_provider.h" +#include "lib/ui/scenic/cpp/resources.h" #include "third_party/skia/include/core/SkSurface.h" namespace flutter_runner { -class SurfaceProducerSurface { - public: - virtual ~SurfaceProducerSurface() = default; - - virtual size_t AdvanceAndGetAge() = 0; - - virtual bool FlushSessionAcquireAndReleaseEvents() = 0; - - virtual bool IsValid() const = 0; - - virtual SkISize GetSize() const = 0; - - virtual void SignalWritesFinished( - const std::function& on_writes_committed) = 0; - - virtual scenic::Image* GetImage() = 0; - - virtual sk_sp GetSkiaSurface() const = 0; -}; - -class SurfaceProducer { - public: - virtual ~SurfaceProducer() = default; - - virtual std::unique_ptr ProduceSurface( - const SkISize& size) = 0; - - virtual void SubmitSurface( - std::unique_ptr surface) = 0; -}; - // A |VkImage| and its relevant metadata. struct VulkanImage { VulkanImage() = default; @@ -74,7 +44,8 @@ bool CreateVulkanImage(vulkan::VulkanProvider& vulkan_provider, const SkISize& size, VulkanImage* out_vulkan_image); -class VulkanSurface final : public SurfaceProducerSurface { +class VulkanSurface final + : public flutter::SceneUpdateContext::SurfaceProducerSurface { public: VulkanSurface(vulkan::VulkanProvider& vulkan_provider, sk_sp context, @@ -83,16 +54,16 @@ class VulkanSurface final : public SurfaceProducerSurface { ~VulkanSurface() override; - // |SurfaceProducerSurface| + // |flutter::SceneUpdateContext::SurfaceProducerSurface| size_t AdvanceAndGetAge() override; - // |SurfaceProducerSurface| + // |flutter::SceneUpdateContext::SurfaceProducerSurface| bool FlushSessionAcquireAndReleaseEvents() override; - // |SurfaceProducerSurface| + // |flutter::SceneUpdateContext::SurfaceProducerSurface| bool IsValid() const override; - // |SurfaceProducerSurface| + // |flutter::SceneUpdateContext::SurfaceProducerSurface| SkISize GetSize() const override; // Note: It is safe for the caller to collect the surface in the @@ -100,10 +71,10 @@ class VulkanSurface final : public SurfaceProducerSurface { void SignalWritesFinished( const std::function& on_writes_committed) override; - // |SurfaceProducerSurface| + // |flutter::SceneUpdateContext::SurfaceProducerSurface| scenic::Image* GetImage() override; - // |SurfaceProducerSurface| + // |flutter::SceneUpdateContext::SurfaceProducerSurface| sk_sp GetSkiaSurface() const override; const vulkan::VulkanHandle& GetVkImage() { @@ -148,6 +119,41 @@ class VulkanSurface final : public SurfaceProducerSurface { // if the swap was not successful. bool BindToImage(sk_sp context, VulkanImage vulkan_image); + // Flutter may retain a |VulkanSurface| for a |flutter::Layer| subtree to + // improve the performance. The |retained_key_| identifies which layer subtree + // this |VulkanSurface| is retained for. The key has two parts. One is the + // pointer to the root of that layer subtree: |retained_key_.id()|. Another is + // the transformation matrix: |retained_key_.matrix()|. We need the matrix + // part because a different matrix would invalidate the pixels (raster cache) + // in this |VulkanSurface|. + const flutter::LayerRasterCacheKey& GetRetainedKey() const { + return retained_key_; + } + + // For better safety in retained rendering, Flutter uses a retained + // |EntityNode| associated with the retained surface instead of using the + // retained surface directly. Hence Flutter can't modify the surface during + // retained rendering. However, the node itself is modifiable to be able + // to adjust its position. + scenic::EntityNode* GetRetainedNode() { + used_in_retained_rendering_ = true; + return retained_node_.get(); + } + + // Check whether the retained surface (and its associated |EntityNode|) is + // used in the current frame or not. If unused, the |VulkanSurfacePool| will + // try to recycle the surface. This flag is reset after each frame. + bool IsUsedInRetainedRendering() const { return used_in_retained_rendering_; } + void ResetIsUsedInRetainedRendering() { used_in_retained_rendering_ = false; } + + // Let this surface own the retained EntityNode associated with it (see + // |GetRetainedNode|), and set the retained key (see |GetRetainedKey|). + void SetRetainedInfo(const flutter::LayerRasterCacheKey& key, + std::unique_ptr node) { + retained_key_ = key; + retained_node_ = std::move(node); + } + private: static constexpr int kSizeHistorySize = 4; @@ -196,6 +202,11 @@ class VulkanSurface final : public SurfaceProducerSurface { size_t age_ = 0; bool valid_ = false; + flutter::LayerRasterCacheKey retained_key_ = {0, SkMatrix::Scale(1, 1)}; + std::unique_ptr retained_node_ = nullptr; + + std::atomic used_in_retained_rendering_ = {false}; + FML_DISALLOW_COPY_AND_ASSIGN(VulkanSurface); }; diff --git a/shell/platform/fuchsia/flutter/vulkan_surface_pool.cc b/shell/platform/fuchsia/flutter/vulkan_surface_pool.cc index 1b85a957898bd..cb40dd25ddbb3 100644 --- a/shell/platform/fuchsia/flutter/vulkan_surface_pool.cc +++ b/shell/platform/fuchsia/flutter/vulkan_surface_pool.cc @@ -112,7 +112,8 @@ std::unique_ptr VulkanSurfacePool::GetCachedOrCreateSurface( } void VulkanSurfacePool::SubmitSurface( - std::unique_ptr p_surface) { + std::unique_ptr + p_surface) { TRACE_EVENT0("flutter", "VulkanSurfacePool::SubmitSurface"); // This cast is safe because |VulkanSurface| is the only implementation of @@ -125,14 +126,43 @@ void VulkanSurfacePool::SubmitSurface( return; } - uintptr_t surface_key = reinterpret_cast(vulkan_surface.get()); - auto insert_iterator = pending_surfaces_.insert(std::make_pair( - surface_key, // key - std::move(vulkan_surface) // value - )); - if (insert_iterator.second) { - insert_iterator.first->second->SignalWritesFinished(std::bind( - &VulkanSurfacePool::RecyclePendingSurface, this, surface_key)); + const flutter::LayerRasterCacheKey& retained_key = + vulkan_surface->GetRetainedKey(); + + // TODO(https://bugs.fuchsia.dev/p/fuchsia/issues/detail?id=44141): Re-enable + // retained surfaces after we find out why textures are being prematurely + // recycled. + const bool kUseRetainedSurfaces = false; + if (kUseRetainedSurfaces && retained_key.id() != 0) { + // Add the surface to |retained_surfaces_| if its retained key has a valid + // layer id (|retained_key.id()|). + // + // We have to add the entry to |retained_surfaces_| map early when it's + // still pending (|is_pending| = true). Otherwise (if we add the surface + // later when |SignalRetainedReady| is called), Flutter would fail to find + // the retained node before the painting is done (which could take multiple + // frames). Flutter would then create a new |VulkanSurface| for the layer + // upon the failed lookup. The new |VulkanSurface| would invalidate this + // surface, and before the new |VulkanSurface| is done painting, another + // newer |VulkanSurface| is likely to be created to replace the new + // |VulkanSurface|. That would make the retained rendering much less useful + // in improving the performance. + auto insert_iterator = retained_surfaces_.insert(std::make_pair( + retained_key, RetainedSurface({true, std::move(vulkan_surface)}))); + if (insert_iterator.second) { + insert_iterator.first->second.vk_surface->SignalWritesFinished(std::bind( + &VulkanSurfacePool::SignalRetainedReady, this, retained_key)); + } + } else { + uintptr_t surface_key = reinterpret_cast(vulkan_surface.get()); + auto insert_iterator = pending_surfaces_.insert(std::make_pair( + surface_key, // key + std::move(vulkan_surface) // value + )); + if (insert_iterator.second) { + insert_iterator.first->second->SignalWritesFinished(std::bind( + &VulkanSurfacePool::RecyclePendingSurface, this, surface_key)); + } } } @@ -183,6 +213,25 @@ void VulkanSurfacePool::RecycleSurface(std::unique_ptr surface) { TraceStats(); } +void VulkanSurfacePool::RecycleRetainedSurface( + const flutter::LayerRasterCacheKey& key) { + auto it = retained_surfaces_.find(key); + if (it == retained_surfaces_.end()) { + return; + } + + // The surface should not be pending. + FML_DCHECK(!it->second.is_pending); + + auto surface_to_recycle = std::move(it->second.vk_surface); + retained_surfaces_.erase(it); + RecycleSurface(std::move(surface_to_recycle)); +} + +void VulkanSurfacePool::SignalRetainedReady(flutter::LayerRasterCacheKey key) { + retained_surfaces_[key].is_pending = false; +} + void VulkanSurfacePool::AgeAndCollectOldBuffers() { TRACE_EVENT0("flutter", "VulkanSurfacePool::AgeAndCollectOldBuffers"); @@ -219,6 +268,25 @@ void VulkanSurfacePool::AgeAndCollectOldBuffers() { } } + // Recycle retained surfaces that are not used and not pending in this frame. + // + // It's safe to recycle any retained surfaces that are not pending no matter + // whether they're used or not. Hence if there's memory pressure, feel free to + // recycle all retained surfaces that are not pending. + std::vector recycle_keys; + for (auto& [key, retained_surface] : retained_surfaces_) { + if (retained_surface.is_pending || + retained_surface.vk_surface->IsUsedInRetainedRendering()) { + // Reset the flag for the next frame + retained_surface.vk_surface->ResetIsUsedInRetainedRendering(); + } else { + recycle_keys.push_back(key); + } + } + for (auto& key : recycle_keys) { + RecycleRetainedSurface(key); + } + TraceStats(); } @@ -252,9 +320,15 @@ void VulkanSurfacePool::ShrinkToFit() { void VulkanSurfacePool::TraceStats() { // Resources held in cached buffers. size_t cached_surfaces_bytes = 0; + size_t retained_surfaces_bytes = 0; + for (const auto& surface : available_surfaces_) { cached_surfaces_bytes += surface->GetAllocationSize(); } + for (const auto& retained_entry : retained_surfaces_) { + retained_surfaces_bytes += + retained_entry.second.vk_surface->GetAllocationSize(); + } // Resources held by Skia. int skia_resources = 0; @@ -268,13 +342,13 @@ void VulkanSurfacePool::TraceStats() { "Created", trace_surfaces_created_, // "Reused", trace_surfaces_reused_, // "PendingInCompositor", pending_surfaces_.size(), // - "Retained", 0, // + "Retained", retained_surfaces_.size(), // "SkiaCacheResources", skia_resources // ); TRACE_COUNTER("flutter", "SurfacePoolBytes", 0u, // "CachedBytes", cached_surfaces_bytes, // - "RetainedBytes", 0, // + "RetainedBytes", retained_surfaces_bytes, // "SkiaCacheBytes", skia_bytes, // "SkiaCachePurgeable", skia_cache_purgeable // ); diff --git a/shell/platform/fuchsia/flutter/vulkan_surface_pool.h b/shell/platform/fuchsia/flutter/vulkan_surface_pool.h index 0667db88d6c0c..302be9bac7b8a 100644 --- a/shell/platform/fuchsia/flutter/vulkan_surface_pool.h +++ b/shell/platform/fuchsia/flutter/vulkan_surface_pool.h @@ -28,7 +28,9 @@ class VulkanSurfacePool final { std::unique_ptr AcquireSurface(const SkISize& size); - void SubmitSurface(std::unique_ptr surface); + void SubmitSurface( + std::unique_ptr + surface); void AgeAndCollectOldBuffers(); @@ -36,7 +38,26 @@ class VulkanSurfacePool final { // small as they can be. void ShrinkToFit(); + // For |VulkanSurfaceProducer::HasRetainedNode|. + bool HasRetainedNode(const flutter::LayerRasterCacheKey& key) const { + return retained_surfaces_.find(key) != retained_surfaces_.end(); + } + // For |VulkanSurfaceProducer::GetRetainedNode|. + scenic::EntityNode* GetRetainedNode(const flutter::LayerRasterCacheKey& key) { + FML_DCHECK(HasRetainedNode(key)); + return retained_surfaces_[key].vk_surface->GetRetainedNode(); + } + private: + // Struct for retained_surfaces_ map. + struct RetainedSurface { + // If |is_pending| is true, the |vk_surface| is still under painting + // (similar to those in |pending_surfaces_|) so we can't recycle the + // |vk_surface| yet. + bool is_pending; + std::unique_ptr vk_surface; + }; + vulkan::VulkanProvider& vulkan_provider_; sk_sp context_; scenic::Session* scenic_session_; @@ -44,6 +65,9 @@ class VulkanSurfacePool final { std::unordered_map> pending_surfaces_; + // Retained surfaces keyed by the layer that created and used the surface. + flutter::LayerRasterCacheKey::Map retained_surfaces_; + size_t trace_surfaces_created_ = 0; size_t trace_surfaces_reused_ = 0; @@ -55,6 +79,13 @@ class VulkanSurfacePool final { void RecyclePendingSurface(uintptr_t surface_key); + // Clear the |is_pending| flag of the retained surface. + void SignalRetainedReady(flutter::LayerRasterCacheKey key); + + // Remove the corresponding surface from |retained_surfaces| and recycle it. + // The surface must not be pending. + void RecycleRetainedSurface(const flutter::LayerRasterCacheKey& key); + void TraceStats(); FML_DISALLOW_COPY_AND_ASSIGN(VulkanSurfacePool); diff --git a/shell/platform/fuchsia/flutter/vulkan_surface_producer.cc b/shell/platform/fuchsia/flutter/vulkan_surface_producer.cc index 36eeadd85afa3..1e0d03df847a8 100644 --- a/shell/platform/fuchsia/flutter/vulkan_surface_producer.cc +++ b/shell/platform/fuchsia/flutter/vulkan_surface_producer.cc @@ -155,7 +155,9 @@ bool VulkanSurfaceProducer::Initialize(scenic::Session* scenic_session) { } void VulkanSurfaceProducer::OnSurfacesPresented( - std::vector> surfaces) { + std::vector< + std::unique_ptr> + surfaces) { TRACE_EVENT0("flutter", "VulkanSurfaceProducer::OnSurfacesPresented"); // Do a single flush for all canvases derived from the context. @@ -195,12 +197,11 @@ void VulkanSurfaceProducer::OnSurfacesPresented( } bool VulkanSurfaceProducer::TransitionSurfacesToExternal( - const std::vector>& surfaces) { + const std::vector< + std::unique_ptr>& + surfaces) { for (auto& surface : surfaces) { auto vk_surface = static_cast(surface.get()); - if (!vk_surface) { - continue; - } vulkan::VulkanCommandBuffer* command_buffer = vk_surface->GetCommandBuffer(logical_device_->GetCommandPool()); @@ -258,15 +259,21 @@ bool VulkanSurfaceProducer::TransitionSurfacesToExternal( return true; } -std::unique_ptr VulkanSurfaceProducer::ProduceSurface( - const SkISize& size) { +std::unique_ptr +VulkanSurfaceProducer::ProduceSurface( + const SkISize& size, + const flutter::LayerRasterCacheKey& layer_key, + std::unique_ptr entity_node) { FML_DCHECK(valid_); last_produce_time_ = async::Now(async_get_default_dispatcher()); - return surface_pool_->AcquireSurface(size); + auto surface = surface_pool_->AcquireSurface(size); + surface->SetRetainedInfo(layer_key, std::move(entity_node)); + return surface; } void VulkanSurfaceProducer::SubmitSurface( - std::unique_ptr surface) { + std::unique_ptr + surface) { FML_DCHECK(valid_ && surface != nullptr); surface_pool_->SubmitSurface(std::move(surface)); } diff --git a/shell/platform/fuchsia/flutter/vulkan_surface_producer.h b/shell/platform/fuchsia/flutter/vulkan_surface_producer.h index 2caaf64a36d7f..403ecd19bebbe 100644 --- a/shell/platform/fuchsia/flutter/vulkan_surface_producer.h +++ b/shell/platform/fuchsia/flutter/vulkan_surface_producer.h @@ -8,8 +8,8 @@ #include #include +#include "flutter/flow/scene_update_context.h" #include "flutter/fml/macros.h" -#include "flutter/fml/memory/weak_ptr.h" #include "flutter/vulkan/vulkan_application.h" #include "flutter/vulkan/vulkan_device.h" #include "flutter/vulkan/vulkan_proc_table.h" @@ -22,8 +22,9 @@ namespace flutter_runner { -class VulkanSurfaceProducer final : public SurfaceProducer, - public vulkan::VulkanProvider { +class VulkanSurfaceProducer final + : public flutter::SceneUpdateContext::SurfaceProducer, + public vulkan::VulkanProvider { public: VulkanSurfaceProducer(scenic::Session* scenic_session); @@ -31,15 +32,39 @@ class VulkanSurfaceProducer final : public SurfaceProducer, bool IsValid() const { return valid_; } - // |SurfaceProducer| - std::unique_ptr ProduceSurface( - const SkISize& size) override; + // |flutter::SceneUpdateContext::SurfaceProducer| + std::unique_ptr + ProduceSurface(const SkISize& size, + const flutter::LayerRasterCacheKey& layer_key, + std::unique_ptr entity_node) override; - // |SurfaceProducer| - void SubmitSurface(std::unique_ptr surface) override; + // |flutter::SceneUpdateContext::SurfaceProducer| + void SubmitSurface( + std::unique_ptr + surface) override; + + // |flutter::SceneUpdateContext::HasRetainedNode| + bool HasRetainedNode(const flutter::LayerRasterCacheKey& key) const override { + return surface_pool_->HasRetainedNode(key); + } + + // |flutter::SceneUpdateContext::GetRetainedNode| + scenic::EntityNode* GetRetainedNode( + const flutter::LayerRasterCacheKey& key) override { + return surface_pool_->GetRetainedNode(key); + } void OnSurfacesPresented( - std::vector> surfaces); + std::vector< + std::unique_ptr> + surfaces); + + void OnSessionSizeChangeHint(float width_change_factor, + float height_change_factor) { + FX_LOGF(INFO, LOG_TAG, + "VulkanSurfaceProducer:OnSessionSizeChangeHint %f, %f", + width_change_factor, height_change_factor); + } GrDirectContext* gr_context() { return context_.get(); } @@ -51,7 +76,9 @@ class VulkanSurfaceProducer final : public SurfaceProducer, } bool TransitionSurfacesToExternal( - const std::vector>& surfaces); + const std::vector< + std::unique_ptr>& + surfaces); // Note: the order here is very important. The proctable must be destroyed // last because it contains the function pointers for VkDestroyDevice and diff --git a/testing/dart/window_hooks_integration_test.dart b/testing/dart/window_hooks_integration_test.dart index 21eda23623664..186ca928a1c3e 100644 --- a/testing/dart/window_hooks_integration_test.dart +++ b/testing/dart/window_hooks_integration_test.dart @@ -46,6 +46,7 @@ void main() { double? oldDPR; Size? oldSize; + double? oldDepth; WindowPadding? oldPadding; WindowPadding? oldInsets; WindowPadding? oldSystemGestureInsets; @@ -53,6 +54,7 @@ void main() { void setUp() { oldDPR = window.devicePixelRatio; oldSize = window.physicalSize; + oldDepth = window.physicalDepth; oldPadding = window.viewPadding; oldInsets = window.viewInsets; oldSystemGestureInsets = window.systemGestureInsets; @@ -74,6 +76,7 @@ void main() { oldDPR!, // DPR oldSize!.width, // width oldSize!.height, // height + oldDepth!, // depth oldPadding!.top, // padding top oldPadding!.right, // padding right oldPadding!.bottom, // padding bottom @@ -158,6 +161,7 @@ void main() { 0.1234, // DPR 0.0, // width 0.0, // height + 0.0, // depth 0.0, // padding top 0.0, // padding right 0.0, // padding bottom @@ -374,6 +378,7 @@ void main() { 1.0, // DPR 800.0, // width 600.0, // height + 100.0, // depth 50.0, // padding top 0.0, // padding right 40.0, // padding bottom @@ -391,12 +396,14 @@ void main() { expectEquals(window.viewInsets.bottom, 0.0); expectEquals(window.viewPadding.bottom, 40.0); expectEquals(window.padding.bottom, 40.0); + expectEquals(window.physicalDepth, 100.0); expectEquals(window.systemGestureInsets.bottom, 0.0); _updateWindowMetrics( 1.0, // DPR 800.0, // width 600.0, // height + 100.0, // depth 50.0, // padding top 0.0, // padding right 40.0, // padding bottom @@ -414,6 +421,7 @@ void main() { expectEquals(window.viewInsets.bottom, 400.0); expectEquals(window.viewPadding.bottom, 40.0); expectEquals(window.padding.bottom, 0.0); + expectEquals(window.physicalDepth, 100.0); expectEquals(window.systemGestureInsets.bottom, 44.0); }); }