Skip to content

Commit 7b5f79f

Browse files
authored
fuchsia: Ensure full-screen input interceptor (flutter#22687)
1 parent 5131aa4 commit 7b5f79f

File tree

8 files changed

+99
-126
lines changed

8 files changed

+99
-126
lines changed

flow/layers/container_layer.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ void ContainerLayer::UpdateSceneChildren(
114114
if (child_layer_exists_below_) {
115115
frame.emplace(
116116
context, SkRRect::MakeRect(paint_bounds()), SK_ColorTRANSPARENT,
117-
SkScalarRoundToInt(context->alphaf() * 255), "flutter::ContainerLayer");
117+
SkScalarRoundToInt(context->alphaf() * 255), "flutter::Layer");
118118
frame->AddPaintLayer(this);
119119
}
120120

flow/layers/layer_tree.cc

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -66,23 +66,19 @@ void LayerTree::UpdateScene(std::shared_ptr<SceneUpdateContext> context) {
6666
TRACE_EVENT0("flutter", "LayerTree::UpdateScene");
6767

6868
// Reset for a new Scene.
69-
context->Reset();
70-
71-
const float inv_dpr = 1.0f / device_pixel_ratio_;
72-
SceneUpdateContext::Transform transform(context, inv_dpr, inv_dpr, 1.0f);
69+
context->Reset(frame_size_, device_pixel_ratio_);
7370

7471
SceneUpdateContext::Frame frame(
7572
context,
7673
SkRRect::MakeRect(
7774
SkRect::MakeWH(frame_size_.width(), frame_size_.height())),
78-
SK_ColorTRANSPARENT, SK_AlphaOPAQUE, "flutter::LayerTree");
75+
SK_ColorTRANSPARENT, SK_AlphaOPAQUE, "flutter::Layer");
7976
if (root_layer_->needs_system_composite()) {
8077
root_layer_->UpdateScene(context);
8178
}
8279
if (!root_layer_->is_empty()) {
8380
frame.AddPaintLayer(root_layer_.get());
8481
}
85-
context->root_node().AddChild(transform.entity_node());
8682
}
8783
#endif
8884

flow/scene_update_context.cc

Lines changed: 38 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -76,10 +76,26 @@ SceneUpdateContext::SceneUpdateContext(std::string debug_label,
7676
std::move(view_ref_pair.control_ref),
7777
std::move(view_ref_pair.view_ref),
7878
debug_label),
79-
root_node_(session_.get()),
80-
intercept_all_input_(intercept_all_input) {
81-
root_view_.AddChild(root_node_);
82-
root_node_.SetEventMask(fuchsia::ui::gfx::kMetricsEventMask);
79+
metrics_node_(session.get()),
80+
layer_tree_node_(session_.get()) {
81+
layer_tree_node_.SetLabel("Flutter::LayerTree");
82+
metrics_node_.SetLabel("Flutter::MetricsWatcher");
83+
metrics_node_.SetEventMask(fuchsia::ui::gfx::kMetricsEventMask);
84+
metrics_node_.AddChild(layer_tree_node_);
85+
root_view_.AddChild(metrics_node_);
86+
87+
// Set up the input interceptor at the top of the scene, if applicable. It
88+
// will capture all input, and any unwanted input will be reinjected into
89+
// embedded views.
90+
if (intercept_all_input) {
91+
input_interceptor_node_.emplace(session_.get());
92+
input_interceptor_node_->SetLabel("Flutter::InputInterceptor");
93+
input_interceptor_node_->SetHitTestBehavior(
94+
fuchsia::ui::gfx::HitTestBehavior::kDefault);
95+
input_interceptor_node_->SetSemanticVisibility(false);
96+
97+
metrics_node_.AddChild(input_interceptor_node_.value());
98+
}
8399

84100
session_.Present();
85101
}
@@ -97,7 +113,8 @@ void SceneUpdateContext::EnableWireframe(bool enable) {
97113
scenic::NewSetEnableDebugViewBoundsCmd(root_view_.id(), enable));
98114
}
99115

100-
void SceneUpdateContext::Reset() {
116+
void SceneUpdateContext::Reset(const SkISize& frame_size,
117+
float device_pixel_ratio) {
101118
paint_tasks_.clear();
102119
top_entity_ = nullptr;
103120
top_scale_x_ = 1.f;
@@ -106,9 +123,22 @@ void SceneUpdateContext::Reset() {
106123
next_elevation_ = 0.f;
107124
alpha_ = 1.f;
108125

126+
// Adjust scene scaling to match the device pixel ratio.
127+
const float inv_dpr = 1.0f / device_pixel_ratio;
128+
metrics_node_.SetScale(inv_dpr, inv_dpr, 1.0f);
129+
130+
// Set up the input interceptor at the top of the scene, if applicable.
131+
if (input_interceptor_node_.has_value()) {
132+
// TODO(fxb/): Don't hardcode elevation.
133+
input_interceptor_node_->SetTranslation(frame_size.width() * 0.5f,
134+
frame_size.height() * 0.5f, -100.f);
135+
input_interceptor_node_->SetShape(scenic::Rectangle(
136+
session_.get(), frame_size.width(), frame_size.height()));
137+
}
138+
109139
// We are going to be sending down a fresh node hierarchy every frame. So just
110-
// enqueue a detach op on the imported root node.
111-
session_.get()->Enqueue(scenic::NewDetachChildrenCmd(root_node_.id()));
140+
// enqueue a detach op on the layer tree node.
141+
layer_tree_node_.DetachChildren();
112142
}
113143

114144
void SceneUpdateContext::CreateFrame(scenic::EntityNode& entity_node,
@@ -235,7 +265,7 @@ SceneUpdateContext::Entity::~Entity() {
235265
if (previous_entity_) {
236266
previous_entity_->embedder_node().AddChild(entity_node_);
237267
} else {
238-
context_->root_node_.AddChild(entity_node_);
268+
context_->layer_tree_node_.AddChild(entity_node_);
239269
}
240270

241271
FML_DCHECK(context_->top_entity_ == this);
@@ -326,14 +356,6 @@ SceneUpdateContext::Frame::Frame(std::shared_ptr<SceneUpdateContext> context,
326356
// with opacity != 1. For now, clamp to a infinitesimally smaller value than
327357
// 1, which does not cause visual problems in practice.
328358
opacity_node_.SetOpacity(std::min(kOneMinusEpsilon, opacity_ / 255.0f));
329-
330-
if (context->intercept_all_input_) {
331-
context->input_interceptor_.emplace(context->session_.get());
332-
context->input_interceptor_->UpdateDimensions(
333-
context->session_.get(), rrect.width(), rrect.height(),
334-
-(local_elevation + kScenicZElevationBetweenLayers * 0.5f));
335-
entity_node().AddChild(context->input_interceptor_->node());
336-
}
337359
}
338360

339361
SceneUpdateContext::Frame::~Frame() {

flow/scene_update_context.h

Lines changed: 4 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -126,8 +126,6 @@ class SceneUpdateContext : public flutter::ExternalViewEmbedder {
126126
bool intercept_all_input = false);
127127
~SceneUpdateContext() = default;
128128

129-
scenic::ContainerNode& root_node() { return root_node_; }
130-
131129
// The cumulative alpha value based on all the parent OpacityLayers.
132130
void set_alphaf(float alpha) { alpha_ = alpha; }
133131
float alphaf() { return alpha_; }
@@ -139,7 +137,7 @@ class SceneUpdateContext : public flutter::ExternalViewEmbedder {
139137
void EnableWireframe(bool enable);
140138

141139
// Reset state for a new frame.
142-
void Reset();
140+
void Reset(const SkISize& frame_size, float device_pixel_ratio);
143141

144142
// |ExternalViewEmbedder|
145143
SkCanvas* GetRootCanvas() override { return nullptr; }
@@ -178,40 +176,6 @@ class SceneUpdateContext : public flutter::ExternalViewEmbedder {
178176
std::optional<bool> override_hit_testable = std::nullopt);
179177

180178
private:
181-
// Helper class for setting up an invisible rectangle to catch all input.
182-
// Rejected input will then be re-injected into a suitable platform view
183-
// controlled by this Engine instance.
184-
class InputInterceptor {
185-
public:
186-
InputInterceptor(scenic::Session* session)
187-
: opacity_node_(session), shape_node_(session) {
188-
opacity_node_.SetLabel("Flutter::InputInterceptor");
189-
opacity_node_.SetOpacity(0.f);
190-
191-
// Set the shape node to capture all input. Any unwanted input will be
192-
// reinjected.
193-
shape_node_.SetHitTestBehavior(
194-
fuchsia::ui::gfx::HitTestBehavior::kDefault);
195-
shape_node_.SetSemanticVisibility(false);
196-
197-
opacity_node_.AddChild(shape_node_);
198-
}
199-
200-
void UpdateDimensions(scenic::Session* session,
201-
float width,
202-
float height,
203-
float elevation) {
204-
opacity_node_.SetTranslation(width * 0.5f, height * 0.5f, elevation);
205-
shape_node_.SetShape(scenic::Rectangle(session, width, height));
206-
}
207-
208-
const scenic::Node& node() { return opacity_node_; }
209-
210-
private:
211-
scenic::OpacityNodeHACK opacity_node_;
212-
scenic::ShapeNode shape_node_;
213-
};
214-
215179
void CreateFrame(scenic::EntityNode& entity_node,
216180
const SkRRect& rrect,
217181
SkColor color,
@@ -222,7 +186,9 @@ class SceneUpdateContext : public flutter::ExternalViewEmbedder {
222186
SessionWrapper& session_;
223187

224188
scenic::View root_view_;
225-
scenic::EntityNode root_node_;
189+
scenic::EntityNode metrics_node_;
190+
scenic::EntityNode layer_tree_node_;
191+
std::optional<scenic::ShapeNode> input_interceptor_node_;
226192

227193
std::vector<PaintTask> paint_tasks_;
228194

@@ -234,9 +200,6 @@ class SceneUpdateContext : public flutter::ExternalViewEmbedder {
234200
float next_elevation_ = 0.f;
235201
float alpha_ = 1.f;
236202

237-
std::optional<InputInterceptor> input_interceptor_;
238-
bool intercept_all_input_ = false;
239-
240203
FML_DISALLOW_COPY_AND_ASSIGN(SceneUpdateContext);
241204
};
242205

shell/platform/fuchsia/flutter/component.cc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -352,6 +352,8 @@ Application::Application(
352352
std::string json_string;
353353
if (dart_utils::ReadFileToString(kRunnerConfigPath, &json_string)) {
354354
product_config_ = FlutterRunnerProductConfiguration(json_string);
355+
FML_LOG(INFO) << "Successfully loaded runner configuration: "
356+
<< json_string;
355357
} else {
356358
FML_LOG(WARNING) << "Failed to load runner configuration from "
357359
<< kRunnerConfigPath << "; using default config values.";

shell/platform/fuchsia/flutter/flutter_runner_product_configuration.cc

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,10 @@
33
// found in the LICENSE file.
44

55
#include "flutter_runner_product_configuration.h"
6+
67
#include <zircon/assert.h>
78

9+
#include "flutter/fml/logging.h"
810
#include "rapidjson/document.h"
911

1012
namespace flutter_runner {
@@ -14,8 +16,11 @@ FlutterRunnerProductConfiguration::FlutterRunnerProductConfiguration(
1416
rapidjson::Document document;
1517
document.Parse(json_string);
1618

17-
if (!document.IsObject())
19+
if (!document.IsObject()) {
20+
FML_LOG(ERROR) << "Failed to parse configuration; using defaults: "
21+
<< json_string;
1822
return;
23+
}
1924

2025
// Parse out all values we're expecting.
2126
if (document.HasMember("vsync_offset_in_us")) {

shell/platform/fuchsia/flutter/fuchsia_external_view_embedder.cc

Lines changed: 43 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ namespace {
2121
// Z-fighting.
2222
constexpr float kScenicZElevationBetweenLayers = 0.0001f;
2323
constexpr float kScenicZElevationForPlatformView = 100.f;
24+
constexpr float kScenicElevationForInputInterceptor = 500.f;
2425

2526
} // namespace
2627

@@ -39,18 +40,24 @@ FuchsiaExternalViewEmbedder::FuchsiaExternalViewEmbedder(
3940
std::move(view_ref_pair.view_ref),
4041
debug_label),
4142
metrics_node_(session_.get()),
42-
root_node_(session_.get()),
43-
intercept_all_input_(intercept_all_input) {
44-
root_view_.AddChild(metrics_node_);
45-
metrics_node_.SetEventMask(fuchsia::ui::gfx::kMetricsEventMask);
43+
layer_tree_node_(session_.get()) {
44+
layer_tree_node_.SetLabel("Flutter::LayerTree");
4645
metrics_node_.SetLabel("Flutter::MetricsWatcher");
47-
metrics_node_.AddChild(root_node_);
48-
root_node_.SetLabel("Flutter::LayerTree");
46+
metrics_node_.SetEventMask(fuchsia::ui::gfx::kMetricsEventMask);
47+
metrics_node_.AddChild(layer_tree_node_);
48+
root_view_.AddChild(metrics_node_);
4949

50-
// Set up the input interceptor at the top of the scene, if applicable.
51-
if (intercept_all_input_) {
52-
input_interceptor_.emplace(session_.get());
53-
metrics_node_.AddChild(input_interceptor_->node());
50+
// Set up the input interceptor at the top of the scene, if applicable. It
51+
// will capture all input, and any unwanted input will be reinjected into
52+
// embedded views.
53+
if (intercept_all_input) {
54+
input_interceptor_node_.emplace(session_.get());
55+
input_interceptor_node_->SetLabel("Flutter::InputInterceptor");
56+
input_interceptor_node_->SetHitTestBehavior(
57+
fuchsia::ui::gfx::HitTestBehavior::kDefault);
58+
input_interceptor_node_->SetSemanticVisibility(false);
59+
60+
metrics_node_.AddChild(input_interceptor_node_.value());
5461
}
5562

5663
session_.Present();
@@ -124,12 +131,28 @@ void FuchsiaExternalViewEmbedder::BeginFrame(
124131
frame_composition_order_.push_back(kRootLayerId);
125132

126133
// Set up the input interceptor at the top of the scene, if applicable.
127-
if (input_interceptor_.has_value()) {
128-
// TODO: Don't hardcode elevation.
129-
const float kMaximumElevation = -100.f;
130-
input_interceptor_->UpdateDimensions(session_.get(), frame_size.width(),
131-
frame_size.height(),
132-
kMaximumElevation);
134+
if (input_interceptor_node_.has_value()) {
135+
const uint64_t rect_hash =
136+
(static_cast<uint64_t>(frame_size_.width()) << 32) +
137+
frame_size_.height();
138+
139+
// Create a new rect if needed for the interceptor.
140+
auto found_rect = scenic_interceptor_rects_.find(rect_hash);
141+
if (found_rect == scenic_interceptor_rects_.end()) {
142+
auto [emplaced_rect, success] =
143+
scenic_interceptor_rects_.emplace(std::make_pair(
144+
rect_hash, scenic::Rectangle(session_.get(), frame_size_.width(),
145+
frame_size_.height())));
146+
FML_DCHECK(success);
147+
148+
found_rect = std::move(emplaced_rect);
149+
}
150+
151+
// TODO(fxb/): Don't hardcode elevation.
152+
input_interceptor_node_->SetTranslation(
153+
frame_size.width() * 0.5f, frame_size.height() * 0.5f,
154+
-kScenicElevationForInputInterceptor);
155+
input_interceptor_node_->SetShape(found_rect->second);
133156
}
134157
}
135158

@@ -179,7 +202,7 @@ void FuchsiaExternalViewEmbedder::SubmitFrame(
179202

180203
// First re-scale everything according to the DPR.
181204
const float inv_dpr = 1.0f / frame_dpr_;
182-
root_node_.SetScale(inv_dpr, inv_dpr, 1.0f);
205+
layer_tree_node_.SetScale(inv_dpr, inv_dpr, 1.0f);
183206

184207
bool first_layer = true;
185208
for (const auto& layer_id : frame_composition_order_) {
@@ -267,7 +290,7 @@ void FuchsiaExternalViewEmbedder::SubmitFrame(
267290
}
268291

269292
// Attach the ScenicView to the main scene graph.
270-
root_node_.AddChild(view_holder.opacity_node);
293+
layer_tree_node_.AddChild(view_holder.opacity_node);
271294

272295
// Account for the ScenicView's height when positioning the next layer.
273296
embedded_views_height += kScenicZElevationForPlatformView;
@@ -355,7 +378,7 @@ void FuchsiaExternalViewEmbedder::SubmitFrame(
355378
first_layer = false;
356379

357380
// Attach the ScenicLayer to the main scene graph.
358-
root_node_.AddChild(scenic_layer.shape_node);
381+
layer_tree_node_.AddChild(scenic_layer.shape_node);
359382

360383
// Account for the ScenicLayer's height when positioning the next layer.
361384
scenic_layer_index++;
@@ -461,14 +484,12 @@ void FuchsiaExternalViewEmbedder::Reset() {
461484
frame_dpr_ = 1.f;
462485

463486
// Detach the root node to prepare for the next frame.
464-
session_.get()->Enqueue(scenic::NewDetachChildrenCmd(root_node_.id()));
487+
layer_tree_node_.DetachChildren();
465488

466489
// Clear images on all layers so they aren't cached unnecesarily.
467490
for (auto& layer : scenic_layers_) {
468491
layer.material.SetTexture(0);
469492
}
470-
471-
input_interceptor_.reset();
472493
}
473494

474495
} // namespace flutter_runner

0 commit comments

Comments
 (0)