Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit 1531372

Browse files
committed
[fuchsia][a11y] Set explicit hit regions per compositor layer on GFX.
1 parent 41930ec commit 1531372

File tree

3 files changed

+341
-107
lines changed

3 files changed

+341
-107
lines changed

shell/platform/fuchsia/flutter/gfx_external_view_embedder.cc

Lines changed: 87 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
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

shell/platform/fuchsia/flutter/gfx_external_view_embedder.h

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#include <vector>
1818

1919
#include "flutter/flow/embedded_views.h"
20+
#include "flutter/flow/rtree.h"
2021
#include "flutter/fml/logging.h"
2122
#include "flutter/fml/macros.h"
2223
#include "flutter/shell/common/canvas_spy.h"
@@ -138,18 +139,23 @@ class GfxExternalViewEmbedder final : public flutter::ExternalViewEmbedder {
138139

139140
struct EmbedderLayer {
140141
EmbedderLayer(const SkISize& frame_size,
141-
std::optional<flutter::EmbeddedViewParams> view_params)
142-
: embedded_view_params(std::move(view_params)),
142+
std::optional<flutter::EmbeddedViewParams> view_params,
143+
flutter::RTreeFactory rtree_factory)
144+
: rtree(rtree_factory.getInstance()),
145+
embedded_view_params(std::move(view_params)),
143146
recorder(std::make_unique<SkPictureRecorder>()),
144147
canvas_spy(std::make_unique<flutter::CanvasSpy>(
145-
recorder->beginRecording(frame_size.width(),
146-
frame_size.height()))),
147-
surface_size(frame_size) {}
148+
recorder->beginRecording(SkRect::Make(frame_size),
149+
&rtree_factory))),
150+
surface_size(frame_size),
151+
picture(nullptr) {}
148152

153+
sk_sp<flutter::RTree> rtree;
149154
std::optional<flutter::EmbeddedViewParams> embedded_view_params;
150155
std::unique_ptr<SkPictureRecorder> recorder;
151156
std::unique_ptr<flutter::CanvasSpy> canvas_spy;
152157
SkISize surface_size;
158+
sk_sp<SkPicture> picture;
153159
};
154160
using EmbedderLayerId = std::optional<uint32_t>;
155161
constexpr static EmbedderLayerId kRootLayerId = EmbedderLayerId{};
@@ -172,11 +178,23 @@ class GfxExternalViewEmbedder final : public flutter::ExternalViewEmbedder {
172178
bool pending_focusable = true;
173179
};
174180

175-
struct ScenicLayer {
181+
struct ScenicImage {
176182
scenic::ShapeNode shape_node;
177183
scenic::Material material;
178184
};
179185

186+
struct ScenicLayer {
187+
// Root of the subtree containing the scenic resources for this layer.
188+
scenic::EntityNode layer_node;
189+
190+
// Scenic resources used to render this layer's image.
191+
ScenicImage image;
192+
193+
// Scenic resources that specify which parts of this layer are responsive
194+
// to input.
195+
std::vector<scenic::ShapeNode> hit_regions;
196+
};
197+
180198
std::shared_ptr<GfxSessionConnection> session_;
181199
std::shared_ptr<SurfaceProducer> surface_producer_;
182200

0 commit comments

Comments
 (0)