diff --git a/flow/layers/container_layer.cc b/flow/layers/container_layer.cc index 5bbbb438c9d79..ee031531fb073 100644 --- a/flow/layers/container_layer.cc +++ b/flow/layers/container_layer.cc @@ -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); } diff --git a/flow/layers/layer_tree.cc b/flow/layers/layer_tree.cc index 8169e8bf6ba2e..eb58bc1c4aeaf 100644 --- a/flow/layers/layer_tree.cc +++ b/flow/layers/layer_tree.cc @@ -66,23 +66,19 @@ void LayerTree::UpdateScene(std::shared_ptr 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 diff --git a/flow/scene_update_context.cc b/flow/scene_update_context.cc index d96e959cb300a..cfa8ad841b2a1 100644 --- a/flow/scene_update_context.cc +++ b/flow/scene_update_context.cc @@ -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(); } @@ -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; @@ -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, @@ -228,7 +258,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); @@ -319,14 +349,6 @@ SceneUpdateContext::Frame::Frame(std::shared_ptr 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() { diff --git a/flow/scene_update_context.h b/flow/scene_update_context.h index 23edb35a1c1e9..dac82771867d5 100644 --- a/flow/scene_update_context.h +++ b/flow/scene_update_context.h @@ -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_; } @@ -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; } @@ -178,40 +176,6 @@ class SceneUpdateContext : public flutter::ExternalViewEmbedder { std::optional 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, @@ -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 input_interceptor_node_; std::vector paint_tasks_; @@ -234,9 +200,6 @@ class SceneUpdateContext : public flutter::ExternalViewEmbedder { float next_elevation_ = 0.f; float alpha_ = 1.f; - std::optional input_interceptor_; - bool intercept_all_input_ = false; - FML_DISALLOW_COPY_AND_ASSIGN(SceneUpdateContext); }; diff --git a/shell/platform/fuchsia/flutter/component.cc b/shell/platform/fuchsia/flutter/component.cc index 8544031f162f9..2d3c5c65f5d6a 100644 --- a/shell/platform/fuchsia/flutter/component.cc +++ b/shell/platform/fuchsia/flutter/component.cc @@ -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."; diff --git a/shell/platform/fuchsia/flutter/flutter_runner_product_configuration.cc b/shell/platform/fuchsia/flutter/flutter_runner_product_configuration.cc index f05bb5addac22..e3e774aec63db 100644 --- a/shell/platform/fuchsia/flutter/flutter_runner_product_configuration.cc +++ b/shell/platform/fuchsia/flutter/flutter_runner_product_configuration.cc @@ -3,8 +3,10 @@ // found in the LICENSE file. #include "flutter_runner_product_configuration.h" + #include +#include "flutter/fml/logging.h" #include "rapidjson/document.h" namespace flutter_runner { @@ -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")) { diff --git a/shell/platform/fuchsia/flutter/fuchsia_external_view_embedder.cc b/shell/platform/fuchsia/flutter/fuchsia_external_view_embedder.cc index 987db137595e8..bbcbf1fe7ebdb 100644 --- a/shell/platform/fuchsia/flutter/fuchsia_external_view_embedder.cc +++ b/shell/platform/fuchsia/flutter/fuchsia_external_view_embedder.cc @@ -21,6 +21,7 @@ namespace { // Z-fighting. constexpr float kScenicZElevationBetweenLayers = 0.0001f; constexpr float kScenicZElevationForPlatformView = 100.f; +constexpr float kScenicElevationForInputInterceptor = 500.f; } // namespace @@ -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(); @@ -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(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); } } @@ -178,7 +201,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_) { @@ -266,7 +289,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; @@ -354,7 +377,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++; @@ -460,14 +483,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 diff --git a/shell/platform/fuchsia/flutter/fuchsia_external_view_embedder.h b/shell/platform/fuchsia/flutter/fuchsia_external_view_embedder.h index c2aa386e220e3..cc2ba55f9dadf 100644 --- a/shell/platform/fuchsia/flutter/fuchsia_external_view_embedder.h +++ b/shell/platform/fuchsia/flutter/fuchsia_external_view_embedder.h @@ -132,40 +132,6 @@ class FuchsiaExternalViewEmbedder final : public flutter::ExternalViewEmbedder { scenic::Material material; }; - // 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.5f); - - // 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_; - }; - using EmbedderLayerId = std::optional; constexpr static EmbedderLayerId kRootLayerId = EmbedderLayerId{}; @@ -174,21 +140,19 @@ class FuchsiaExternalViewEmbedder final : public flutter::ExternalViewEmbedder { scenic::View root_view_; scenic::EntityNode metrics_node_; - scenic::EntityNode root_node_; + scenic::EntityNode layer_tree_node_; + std::optional input_interceptor_node_; + std::unordered_map scenic_interceptor_rects_; std::unordered_map> scenic_rects_; std::unordered_map scenic_views_; std::vector scenic_layers_; - std::optional input_interceptor_; - std::unordered_map frame_layers_; std::vector frame_composition_order_; SkISize frame_size_ = SkISize::Make(0, 0); float frame_dpr_ = 1.f; - bool intercept_all_input_ = false; - FML_DISALLOW_COPY_AND_ASSIGN(FuchsiaExternalViewEmbedder); };