Skip to content

Commit

Permalink
fuchsia: Ensure full-screen input interceptor (#22687)
Browse files Browse the repository at this point in the history
  • Loading branch information
arbreng committed Nov 26, 2020
1 parent 5131aa4 commit 7b5f79f
Show file tree
Hide file tree
Showing 8 changed files with 99 additions and 126 deletions.
2 changes: 1 addition & 1 deletion flow/layers/container_layer.cc
Expand Up @@ -114,7 +114,7 @@ void ContainerLayer::UpdateSceneChildren(
if (child_layer_exists_below_) {
frame.emplace(
context, SkRRect::MakeRect(paint_bounds()), SK_ColorTRANSPARENT,
SkScalarRoundToInt(context->alphaf() * 255), "flutter::ContainerLayer");
SkScalarRoundToInt(context->alphaf() * 255), "flutter::Layer");
frame->AddPaintLayer(this);
}

Expand Down
8 changes: 2 additions & 6 deletions flow/layers/layer_tree.cc
Expand Up @@ -66,23 +66,19 @@ void LayerTree::UpdateScene(std::shared_ptr<SceneUpdateContext> context) {
TRACE_EVENT0("flutter", "LayerTree::UpdateScene");

// Reset for a new Scene.
context->Reset();

const float inv_dpr = 1.0f / device_pixel_ratio_;
SceneUpdateContext::Transform transform(context, inv_dpr, inv_dpr, 1.0f);
context->Reset(frame_size_, device_pixel_ratio_);

SceneUpdateContext::Frame frame(
context,
SkRRect::MakeRect(
SkRect::MakeWH(frame_size_.width(), frame_size_.height())),
SK_ColorTRANSPARENT, SK_AlphaOPAQUE, "flutter::LayerTree");
SK_ColorTRANSPARENT, SK_AlphaOPAQUE, "flutter::Layer");
if (root_layer_->needs_system_composite()) {
root_layer_->UpdateScene(context);
}
if (!root_layer_->is_empty()) {
frame.AddPaintLayer(root_layer_.get());
}
context->root_node().AddChild(transform.entity_node());
}
#endif

Expand Down
54 changes: 38 additions & 16 deletions flow/scene_update_context.cc
Expand Up @@ -76,10 +76,26 @@ SceneUpdateContext::SceneUpdateContext(std::string debug_label,
std::move(view_ref_pair.control_ref),
std::move(view_ref_pair.view_ref),
debug_label),
root_node_(session_.get()),
intercept_all_input_(intercept_all_input) {
root_view_.AddChild(root_node_);
root_node_.SetEventMask(fuchsia::ui::gfx::kMetricsEventMask);
metrics_node_(session.get()),
layer_tree_node_(session_.get()) {
layer_tree_node_.SetLabel("Flutter::LayerTree");
metrics_node_.SetLabel("Flutter::MetricsWatcher");
metrics_node_.SetEventMask(fuchsia::ui::gfx::kMetricsEventMask);
metrics_node_.AddChild(layer_tree_node_);
root_view_.AddChild(metrics_node_);

// Set up the input interceptor at the top of the scene, if applicable. It
// will capture all input, and any unwanted input will be reinjected into
// embedded views.
if (intercept_all_input) {
input_interceptor_node_.emplace(session_.get());
input_interceptor_node_->SetLabel("Flutter::InputInterceptor");
input_interceptor_node_->SetHitTestBehavior(
fuchsia::ui::gfx::HitTestBehavior::kDefault);
input_interceptor_node_->SetSemanticVisibility(false);

metrics_node_.AddChild(input_interceptor_node_.value());
}

session_.Present();
}
Expand All @@ -97,7 +113,8 @@ void SceneUpdateContext::EnableWireframe(bool enable) {
scenic::NewSetEnableDebugViewBoundsCmd(root_view_.id(), enable));
}

void SceneUpdateContext::Reset() {
void SceneUpdateContext::Reset(const SkISize& frame_size,
float device_pixel_ratio) {
paint_tasks_.clear();
top_entity_ = nullptr;
top_scale_x_ = 1.f;
Expand All @@ -106,9 +123,22 @@ void SceneUpdateContext::Reset() {
next_elevation_ = 0.f;
alpha_ = 1.f;

// Adjust scene scaling to match the device pixel ratio.
const float inv_dpr = 1.0f / device_pixel_ratio;
metrics_node_.SetScale(inv_dpr, inv_dpr, 1.0f);

// Set up the input interceptor at the top of the scene, if applicable.
if (input_interceptor_node_.has_value()) {
// TODO(fxb/): Don't hardcode elevation.
input_interceptor_node_->SetTranslation(frame_size.width() * 0.5f,
frame_size.height() * 0.5f, -100.f);
input_interceptor_node_->SetShape(scenic::Rectangle(
session_.get(), frame_size.width(), frame_size.height()));
}

// 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()));
// enqueue a detach op on the layer tree node.
layer_tree_node_.DetachChildren();
}

void SceneUpdateContext::CreateFrame(scenic::EntityNode& entity_node,
Expand Down Expand Up @@ -235,7 +265,7 @@ SceneUpdateContext::Entity::~Entity() {
if (previous_entity_) {
previous_entity_->embedder_node().AddChild(entity_node_);
} else {
context_->root_node_.AddChild(entity_node_);
context_->layer_tree_node_.AddChild(entity_node_);
}

FML_DCHECK(context_->top_entity_ == this);
Expand Down Expand Up @@ -326,14 +356,6 @@ SceneUpdateContext::Frame::Frame(std::shared_ptr<SceneUpdateContext> context,
// with opacity != 1. For now, clamp to a infinitesimally smaller value than
// 1, which does not cause visual problems in practice.
opacity_node_.SetOpacity(std::min(kOneMinusEpsilon, opacity_ / 255.0f));

if (context->intercept_all_input_) {
context->input_interceptor_.emplace(context->session_.get());
context->input_interceptor_->UpdateDimensions(
context->session_.get(), rrect.width(), rrect.height(),
-(local_elevation + kScenicZElevationBetweenLayers * 0.5f));
entity_node().AddChild(context->input_interceptor_->node());
}
}

SceneUpdateContext::Frame::~Frame() {
Expand Down
45 changes: 4 additions & 41 deletions flow/scene_update_context.h
Expand Up @@ -126,8 +126,6 @@ class SceneUpdateContext : public flutter::ExternalViewEmbedder {
bool intercept_all_input = false);
~SceneUpdateContext() = default;

scenic::ContainerNode& root_node() { return root_node_; }

// The cumulative alpha value based on all the parent OpacityLayers.
void set_alphaf(float alpha) { alpha_ = alpha; }
float alphaf() { return alpha_; }
Expand All @@ -139,7 +137,7 @@ class SceneUpdateContext : public flutter::ExternalViewEmbedder {
void EnableWireframe(bool enable);

// Reset state for a new frame.
void Reset();
void Reset(const SkISize& frame_size, float device_pixel_ratio);

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

private:
// Helper class for setting up an invisible rectangle to catch all input.
// Rejected input will then be re-injected into a suitable platform view
// controlled by this Engine instance.
class InputInterceptor {
public:
InputInterceptor(scenic::Session* session)
: opacity_node_(session), shape_node_(session) {
opacity_node_.SetLabel("Flutter::InputInterceptor");
opacity_node_.SetOpacity(0.f);

// Set the shape node to capture all input. Any unwanted input will be
// reinjected.
shape_node_.SetHitTestBehavior(
fuchsia::ui::gfx::HitTestBehavior::kDefault);
shape_node_.SetSemanticVisibility(false);

opacity_node_.AddChild(shape_node_);
}

void UpdateDimensions(scenic::Session* session,
float width,
float height,
float elevation) {
opacity_node_.SetTranslation(width * 0.5f, height * 0.5f, elevation);
shape_node_.SetShape(scenic::Rectangle(session, width, height));
}

const scenic::Node& node() { return opacity_node_; }

private:
scenic::OpacityNodeHACK opacity_node_;
scenic::ShapeNode shape_node_;
};

void CreateFrame(scenic::EntityNode& entity_node,
const SkRRect& rrect,
SkColor color,
Expand All @@ -222,7 +186,9 @@ class SceneUpdateContext : public flutter::ExternalViewEmbedder {
SessionWrapper& session_;

scenic::View root_view_;
scenic::EntityNode root_node_;
scenic::EntityNode metrics_node_;
scenic::EntityNode layer_tree_node_;
std::optional<scenic::ShapeNode> input_interceptor_node_;

std::vector<PaintTask> paint_tasks_;

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

std::optional<InputInterceptor> input_interceptor_;
bool intercept_all_input_ = false;

FML_DISALLOW_COPY_AND_ASSIGN(SceneUpdateContext);
};

Expand Down
2 changes: 2 additions & 0 deletions shell/platform/fuchsia/flutter/component.cc
Expand Up @@ -352,6 +352,8 @@ Application::Application(
std::string json_string;
if (dart_utils::ReadFileToString(kRunnerConfigPath, &json_string)) {
product_config_ = FlutterRunnerProductConfiguration(json_string);
FML_LOG(INFO) << "Successfully loaded runner configuration: "
<< json_string;
} else {
FML_LOG(WARNING) << "Failed to load runner configuration from "
<< kRunnerConfigPath << "; using default config values.";
Expand Down
Expand Up @@ -3,8 +3,10 @@
// found in the LICENSE file.

#include "flutter_runner_product_configuration.h"

#include <zircon/assert.h>

#include "flutter/fml/logging.h"
#include "rapidjson/document.h"

namespace flutter_runner {
Expand All @@ -14,8 +16,11 @@ FlutterRunnerProductConfiguration::FlutterRunnerProductConfiguration(
rapidjson::Document document;
document.Parse(json_string);

if (!document.IsObject())
if (!document.IsObject()) {
FML_LOG(ERROR) << "Failed to parse configuration; using defaults: "
<< json_string;
return;
}

// Parse out all values we're expecting.
if (document.HasMember("vsync_offset_in_us")) {
Expand Down
65 changes: 43 additions & 22 deletions shell/platform/fuchsia/flutter/fuchsia_external_view_embedder.cc
Expand Up @@ -21,6 +21,7 @@ namespace {
// Z-fighting.
constexpr float kScenicZElevationBetweenLayers = 0.0001f;
constexpr float kScenicZElevationForPlatformView = 100.f;
constexpr float kScenicElevationForInputInterceptor = 500.f;

} // namespace

Expand All @@ -39,18 +40,24 @@ FuchsiaExternalViewEmbedder::FuchsiaExternalViewEmbedder(
std::move(view_ref_pair.view_ref),
debug_label),
metrics_node_(session_.get()),
root_node_(session_.get()),
intercept_all_input_(intercept_all_input) {
root_view_.AddChild(metrics_node_);
metrics_node_.SetEventMask(fuchsia::ui::gfx::kMetricsEventMask);
layer_tree_node_(session_.get()) {
layer_tree_node_.SetLabel("Flutter::LayerTree");
metrics_node_.SetLabel("Flutter::MetricsWatcher");
metrics_node_.AddChild(root_node_);
root_node_.SetLabel("Flutter::LayerTree");
metrics_node_.SetEventMask(fuchsia::ui::gfx::kMetricsEventMask);
metrics_node_.AddChild(layer_tree_node_);
root_view_.AddChild(metrics_node_);

// Set up the input interceptor at the top of the scene, if applicable.
if (intercept_all_input_) {
input_interceptor_.emplace(session_.get());
metrics_node_.AddChild(input_interceptor_->node());
// Set up the input interceptor at the top of the scene, if applicable. It
// will capture all input, and any unwanted input will be reinjected into
// embedded views.
if (intercept_all_input) {
input_interceptor_node_.emplace(session_.get());
input_interceptor_node_->SetLabel("Flutter::InputInterceptor");
input_interceptor_node_->SetHitTestBehavior(
fuchsia::ui::gfx::HitTestBehavior::kDefault);
input_interceptor_node_->SetSemanticVisibility(false);

metrics_node_.AddChild(input_interceptor_node_.value());
}

session_.Present();
Expand Down Expand Up @@ -124,12 +131,28 @@ void FuchsiaExternalViewEmbedder::BeginFrame(
frame_composition_order_.push_back(kRootLayerId);

// Set up the input interceptor at the top of the scene, if applicable.
if (input_interceptor_.has_value()) {
// TODO: Don't hardcode elevation.
const float kMaximumElevation = -100.f;
input_interceptor_->UpdateDimensions(session_.get(), frame_size.width(),
frame_size.height(),
kMaximumElevation);
if (input_interceptor_node_.has_value()) {
const uint64_t rect_hash =
(static_cast<uint64_t>(frame_size_.width()) << 32) +
frame_size_.height();

// Create a new rect if needed for the interceptor.
auto found_rect = scenic_interceptor_rects_.find(rect_hash);
if (found_rect == scenic_interceptor_rects_.end()) {
auto [emplaced_rect, success] =
scenic_interceptor_rects_.emplace(std::make_pair(
rect_hash, scenic::Rectangle(session_.get(), frame_size_.width(),
frame_size_.height())));
FML_DCHECK(success);

found_rect = std::move(emplaced_rect);
}

// TODO(fxb/): Don't hardcode elevation.
input_interceptor_node_->SetTranslation(
frame_size.width() * 0.5f, frame_size.height() * 0.5f,
-kScenicElevationForInputInterceptor);
input_interceptor_node_->SetShape(found_rect->second);
}
}

Expand Down Expand Up @@ -179,7 +202,7 @@ void FuchsiaExternalViewEmbedder::SubmitFrame(

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

bool first_layer = true;
for (const auto& layer_id : frame_composition_order_) {
Expand Down Expand Up @@ -267,7 +290,7 @@ void FuchsiaExternalViewEmbedder::SubmitFrame(
}

// Attach the ScenicView to the main scene graph.
root_node_.AddChild(view_holder.opacity_node);
layer_tree_node_.AddChild(view_holder.opacity_node);

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

// Attach the ScenicLayer to the main scene graph.
root_node_.AddChild(scenic_layer.shape_node);
layer_tree_node_.AddChild(scenic_layer.shape_node);

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

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

// Clear images on all layers so they aren't cached unnecesarily.
for (auto& layer : scenic_layers_) {
layer.material.SetTexture(0);
}

input_interceptor_.reset();
}

} // namespace flutter_runner

0 comments on commit 7b5f79f

Please sign in to comment.