1010
1111#include < algorithm> // For std::clamp
1212
13+ #include " flutter/fml/logging.h"
1314#include " flutter/fml/trace_event.h"
1415#include " third_party/skia/include/core/SkPicture.h"
1516#include " third_party/skia/include/core/SkSurface.h"
@@ -169,8 +170,9 @@ void GfxExternalViewEmbedder::PrerollCompositeEmbeddedView(
169170 zx_handle_t handle = static_cast <zx_handle_t >(view_id);
170171 FML_CHECK (frame_layers_.count (handle) == 0 );
171172
172- frame_layers_.emplace (std::make_pair (EmbedderLayerId{handle},
173- EmbedderLayer (frame_size_, *params)));
173+ frame_layers_.emplace (std::make_pair (
174+ EmbedderLayerId{handle},
175+ EmbedderLayer (frame_size_, *params, flutter::RTreeFactory ())));
174176 frame_composition_order_.push_back (handle);
175177}
176178
@@ -200,8 +202,9 @@ void GfxExternalViewEmbedder::BeginFrame(
200202 frame_dpr_ = device_pixel_ratio;
201203
202204 // Create the root layer.
203- frame_layers_.emplace (
204- std::make_pair (kRootLayerId , EmbedderLayer (frame_size, std::nullopt )));
205+ frame_layers_.emplace (std::make_pair (
206+ kRootLayerId ,
207+ EmbedderLayer (frame_size, std::nullopt , flutter::RTreeFactory ())));
205208 frame_composition_order_.push_back (kRootLayerId );
206209
207210 // Set up the input interceptor at the top of the scene, if applicable.
@@ -271,6 +274,19 @@ void GfxExternalViewEmbedder::SubmitFrame(
271274 }
272275 }
273276
277+ // Finish recording SkPictures.
278+ {
279+ TRACE_EVENT0 (" flutter" , " FinishRecordingPictures" );
280+
281+ for (const auto & surface_index : frame_surface_indices) {
282+ const auto & layer = frame_layers_.find (surface_index.first );
283+ FML_CHECK (layer != frame_layers_.end ());
284+ layer->second .picture =
285+ layer->second .recorder ->finishRecordingAsPicture ();
286+ FML_CHECK (layer->second .picture != nullptr );
287+ }
288+ }
289+
274290 // Submit layers and platform views to Scenic in composition order.
275291 {
276292 TRACE_EVENT0 (" flutter" , " SubmitLayers" );
@@ -437,10 +453,18 @@ void GfxExternalViewEmbedder::SubmitFrame(
437453 FML_CHECK (scenic_layer_index <= scenic_layers_.size ());
438454 if (scenic_layer_index == scenic_layers_.size ()) {
439455 ScenicLayer new_layer{
440- .shape_node = scenic::ShapeNode (session_->get ()),
441- .material = scenic::Material (session_->get ()),
456+ .layer_node = scenic::EntityNode (session_->get ()),
457+ .image =
458+ ScenicImage{
459+ .shape_node = scenic::ShapeNode (session_->get ()),
460+ .material = scenic::Material (session_->get ()),
461+ },
462+ // We'll set hit regions later.
463+ .hit_regions = {},
442464 };
443- new_layer.shape_node .SetMaterial (new_layer.material );
465+ new_layer.layer_node .SetLabel (" Flutter::Layer" );
466+ new_layer.layer_node .AddChild (new_layer.image .shape_node );
467+ new_layer.image .shape_node .SetMaterial (new_layer.image .material );
444468 scenic_layers_.emplace_back (std::move (new_layer));
445469 }
446470
@@ -491,25 +515,50 @@ void GfxExternalViewEmbedder::SubmitFrame(
491515 embedded_views_height;
492516 auto & scenic_layer = scenic_layers_[scenic_layer_index];
493517 auto & scenic_rect = found_rects->second [rect_index];
494- scenic_layer.shape_node .SetLabel (" Flutter::Layer" );
495- scenic_layer.shape_node .SetShape (scenic_rect);
496- scenic_layer.shape_node .SetTranslation (
518+ auto & image = scenic_layer.image ;
519+ image.shape_node .SetLabel (" Flutter::Layer::Image" );
520+ image.shape_node .SetShape (scenic_rect);
521+ image.shape_node .SetTranslation (
497522 layer->second .surface_size .width () * 0 .5f ,
498523 layer->second .surface_size .height () * 0 .5f , -layer_elevation);
499- scenic_layer.material .SetColor (SK_AlphaOPAQUE, SK_AlphaOPAQUE,
500- SK_AlphaOPAQUE, layer_opacity);
501- scenic_layer.material .SetTexture (surface_for_layer->GetImageId ());
502-
503- // Only the first (i.e. the bottom-most) layer should receive input.
504- // TODO: Workaround for invisible overlays stealing input. Remove when
505- // the underlying bug is fixed.
506- const fuchsia::ui::gfx::HitTestBehavior layer_hit_test_behavior =
507- first_layer ? fuchsia::ui::gfx::HitTestBehavior::kDefault
508- : fuchsia::ui::gfx::HitTestBehavior::kSuppress ;
509- scenic_layer.shape_node .SetHitTestBehavior (layer_hit_test_behavior);
524+ image.material .SetColor (SK_AlphaOPAQUE, SK_AlphaOPAQUE, SK_AlphaOPAQUE,
525+ layer_opacity);
526+ image.material .SetTexture (surface_for_layer->GetImageId ());
527+
528+ // We'll set hit regions expliclty on a separate ShapeNode, so the image
529+ // itself should be unhittable and semantically invisible.
530+ image.shape_node .SetHitTestBehavior (
531+ fuchsia::ui::gfx::HitTestBehavior::kSuppress );
532+ image.shape_node .SetSemanticVisibility (false );
510533
511534 // Attach the ScenicLayer to the main scene graph.
512- layer_tree_node_.AddChild (scenic_layer.shape_node );
535+ layer_tree_node_.AddChild (scenic_layer.layer_node );
536+
537+ // Compute the set of non-overlapping set of bounding boxes for the
538+ // painted content in this layer.
539+ {
540+ FML_CHECK (layer->second .rtree );
541+ std::list<SkRect> intersection_rects =
542+ layer->second .rtree ->searchNonOverlappingDrawnRects (
543+ SkRect::Make (layer->second .surface_size ));
544+
545+ // SkRect joined_rect = SkRect::MakeEmpty();
546+ for (const SkRect& rect : intersection_rects) {
547+ auto paint_bounds =
548+ scenic::Rectangle (session_->get (), rect.width (), rect.height ());
549+ auto hit_region = scenic::ShapeNode (session_->get ());
550+ hit_region.SetLabel (" Flutter::Layer::HitRegion" );
551+ hit_region.SetShape (paint_bounds);
552+ hit_region.SetTranslation (rect.centerX (), rect.centerY (),
553+ -layer_elevation);
554+ hit_region.SetHitTestBehavior (
555+ fuchsia::ui::gfx::HitTestBehavior::kDefault );
556+ hit_region.SetSemanticVisibility (true );
557+
558+ scenic_layer.layer_node .AddChild (hit_region);
559+ scenic_layer.hit_regions .push_back (std::move (hit_region));
560+ }
561+ }
513562 }
514563
515564 // Reset for the next pass:
@@ -527,7 +576,11 @@ void GfxExternalViewEmbedder::SubmitFrame(
527576 session_->Present ();
528577 }
529578
530- // Render the recorded SkPictures into the surfaces.
579+ // Flush pending skia operations.
580+ // NOTE: This operation MUST occur AFTER the `Present() ` call above. We
581+ // pipeline the Skia rendering work with scenic IPC, and scenic will delay
582+ // internally until Skia is finished. So, doing this work before calling
583+ // `Present()` would adversely affect performance.
531584 {
532585 TRACE_EVENT0 (" flutter" , " RasterizeSurfaces" );
533586
@@ -548,13 +601,10 @@ void GfxExternalViewEmbedder::SubmitFrame(
548601
549602 const auto & layer = frame_layers_.find (surface_index.first );
550603 FML_CHECK (layer != frame_layers_.end ());
551- sk_sp<SkPicture> picture =
552- layer->second .recorder ->finishRecordingAsPicture ();
553- FML_CHECK (picture != nullptr );
554604
555605 canvas->setMatrix (SkMatrix::I ());
556606 canvas->clear (SK_ColorTRANSPARENT);
557- canvas->drawPicture (picture);
607+ canvas->drawPicture (layer-> second . picture );
558608 canvas->flush ();
559609 }
560610 }
@@ -636,7 +686,16 @@ void GfxExternalViewEmbedder::Reset() {
636686
637687 // Clear images on all layers so they aren't cached unnecessarily.
638688 for (auto & layer : scenic_layers_) {
639- layer.material .SetTexture (0 );
689+ layer.image .material .SetTexture (0 );
690+
691+ // Detach hit regions; otherwise, they may persist across frames
692+ // incorrectly.
693+ for (auto & hit_region : layer.hit_regions ) {
694+ hit_region.Detach ();
695+ }
696+
697+ // Remove cached hit regions so that we don't recreate stale ones.
698+ layer.hit_regions .clear ();
640699 }
641700}
642701
0 commit comments