void GfxExternalViewEmbedder::SubmitFrame()

in shell/platform/fuchsia/flutter/gfx_external_view_embedder.cc [239:571]


void GfxExternalViewEmbedder::SubmitFrame(
    GrDirectContext* context,
    std::unique_ptr<flutter::SurfaceFrame> frame) {
  TRACE_EVENT0("flutter", "GfxExternalViewEmbedder::SubmitFrame");
  std::vector<std::unique_ptr<SurfaceProducerSurface>> frame_surfaces;
  std::unordered_map<EmbedderLayerId, size_t> frame_surface_indices;

  // Create surfaces for the frame and associate them with layer IDs.
  {
    TRACE_EVENT0("flutter", "CreateSurfaces");

    for (const auto& layer : frame_layers_) {
      if (!layer.second.canvas_spy->DidDrawIntoCanvas()) {
        continue;
      }

      auto surface =
          surface_producer_->ProduceSurface(layer.second.surface_size);
      if (!surface) {
        const std::string layer_id_str =
            layer.first.has_value() ? std::to_string(layer.first.value())
                                    : "Background";
        FML_LOG(ERROR) << "Failed to create surface for layer " << layer_id_str
                       << "; size (" << layer.second.surface_size.width()
                       << ", " << layer.second.surface_size.height() << ")";
        FML_DCHECK(false);
        continue;
      }

      frame_surface_indices.emplace(
          std::make_pair(layer.first, frame_surfaces.size()));
      frame_surfaces.emplace_back(std::move(surface));
    }
  }

  // Submit layers and platform views to Scenic in composition order.
  {
    TRACE_EVENT0("flutter", "SubmitLayers");

    std::unordered_map<uint64_t, size_t> scenic_rect_indices;
    size_t scenic_layer_index = 0;
    float embedded_views_height = 0.0f;

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

    bool first_layer = true;
    for (const auto& layer_id : frame_composition_order_) {
      const auto& layer = frame_layers_.find(layer_id);
      FML_CHECK(layer != frame_layers_.end());

      // Draw the PlatformView associated with each layer first.
      if (layer_id.has_value()) {
        FML_CHECK(layer->second.embedded_view_params.has_value());
        auto& view_params = layer->second.embedded_view_params.value();

        // Get the ScenicView structure corresponding to the platform view.
        auto found = scenic_views_.find(layer_id.value());
        FML_CHECK(found != scenic_views_.end());
        auto& view_holder = found->second;

        // Compute mutators, size, and elevation for the platform view.
        const ViewMutators view_mutators =
            ParseMutatorStack(view_params.mutatorsStack());
        const SkSize view_size = view_params.sizePoints();
        const float view_elevation =
            kScenicZElevationBetweenLayers * scenic_layer_index +
            embedded_views_height;
        FML_CHECK(view_mutators.total_transform ==
                  view_params.transformMatrix());

        // Set clips for the platform view.
        if (view_mutators.clips != view_holder.mutators.clips) {
          // Expand the clip_nodes array to fit any new nodes.
          while (view_holder.clip_nodes.size() < view_mutators.clips.size()) {
            view_holder.clip_nodes.emplace_back(
                scenic::EntityNode(session_->get()));
          }
          FML_CHECK(view_holder.clip_nodes.size() >=
                    view_mutators.clips.size());

          // Adjust and re-parent all clip rects.
          for (auto& clip_node : view_holder.clip_nodes) {
            clip_node.DetachChildren();
          }
          for (size_t c = 0; c < view_mutators.clips.size(); c++) {
            const SkMatrix& clip_transform = view_mutators.clips[c].transform;
            const SkRect& clip_rect = view_mutators.clips[c].rect;

            view_holder.clip_nodes[c].SetTranslation(
                clip_transform.getTranslateX(), clip_transform.getTranslateY(),
                0.f);
            view_holder.clip_nodes[c].SetScale(clip_transform.getScaleX(),
                                               clip_transform.getScaleY(), 1.f);
            view_holder.clip_nodes[c].SetClipPlanes(
                ClipPlanesFromRect(clip_rect));

            if (c != (view_mutators.clips.size() - 1)) {
              view_holder.clip_nodes[c].AddChild(view_holder.clip_nodes[c + 1]);
            } else {
              view_holder.clip_nodes[c].AddChild(view_holder.opacity_node);
            }
          }

          view_holder.mutators.clips = view_mutators.clips;
        }

        // Set transform and elevation for the platform view.
        if (view_mutators.transform != view_holder.mutators.transform ||
            view_elevation != view_holder.elevation) {
          view_holder.transform_node.SetTranslation(
              view_mutators.transform.getTranslateX(),
              view_mutators.transform.getTranslateY(), -view_elevation);
          view_holder.transform_node.SetScale(
              view_mutators.transform.getScaleX(),
              view_mutators.transform.getScaleY(), 1.f);

          view_holder.mutators.transform = view_mutators.transform;
          view_holder.elevation = view_elevation;
        }

        // Set HitTestBehavior for the platform view.
        if (view_holder.pending_hit_testable != view_holder.hit_testable) {
          view_holder.transform_node.SetHitTestBehavior(
              view_holder.pending_hit_testable
                  ? fuchsia::ui::gfx::HitTestBehavior::kDefault
                  : fuchsia::ui::gfx::HitTestBehavior::kSuppress);

          view_holder.hit_testable = view_holder.pending_hit_testable;
        }

        // Set opacity for the platform view.
        if (view_mutators.opacity != view_holder.mutators.opacity) {
          view_holder.opacity_node.SetOpacity(view_mutators.opacity);

          view_holder.mutators.opacity = view_mutators.opacity;
        }

        // Set size, occlusion hint, and focusable.
        if (view_size != view_holder.size ||
            view_holder.pending_occlusion_hint != view_holder.occlusion_hint ||
            view_holder.pending_focusable != view_holder.focusable) {
          view_holder.view_holder.SetViewProperties({
              .bounding_box =
                  {
                      .min = {.x = 0.f, .y = 0.f, .z = -1000.f},
                      .max = {.x = view_size.fWidth,
                              .y = view_size.fHeight,
                              .z = 0.f},
                  },
              .inset_from_min = {.x = view_holder.pending_occlusion_hint.fLeft,
                                 .y = view_holder.pending_occlusion_hint.fTop,
                                 .z = 0.f},
              .inset_from_max = {.x = view_holder.pending_occlusion_hint.fRight,
                                 .y =
                                     view_holder.pending_occlusion_hint.fBottom,
                                 .z = 0.f},
              .focus_change = view_holder.pending_focusable,
          });

          view_holder.size = view_size;
          view_holder.occlusion_hint = view_holder.pending_occlusion_hint;
          view_holder.focusable = view_holder.pending_focusable;
        }

        // Attach the ScenicView to the main scene graph.
        if (view_holder.mutators.clips.empty()) {
          layer_tree_node_.AddChild(view_holder.opacity_node);
        } else {
          layer_tree_node_.AddChild(view_holder.clip_nodes[0]);
        }

        // Account for the ScenicView's height when positioning the next layer.
        embedded_views_height += kScenicZElevationForPlatformView;
      }

      // Acquire the surface associated with the layer.
      SurfaceProducerSurface* surface_for_layer = nullptr;
      if (layer->second.canvas_spy->DidDrawIntoCanvas()) {
        const auto& surface_index = frame_surface_indices.find(layer_id);
        if (surface_index != frame_surface_indices.end()) {
          FML_CHECK(surface_index->second < frame_surfaces.size());
          surface_for_layer = frame_surfaces[surface_index->second].get();
          FML_CHECK(surface_for_layer != nullptr);
        } else {
          const std::string layer_id_str =
              layer_id.has_value() ? std::to_string(layer_id.value())
                                   : "Background";
          FML_LOG(ERROR) << "Missing surface for layer " << layer_id_str
                         << "; skipping scene graph add of layer.";
          FML_DCHECK(false);
        }
      }

      // Draw the layer if we acquired a surface for it successfully.
      if (surface_for_layer != nullptr) {
        // Create a new layer if needed for the surface.
        FML_CHECK(scenic_layer_index <= scenic_layers_.size());
        if (scenic_layer_index == scenic_layers_.size()) {
          ScenicLayer new_layer{
              .shape_node = scenic::ShapeNode(session_->get()),
              .material = scenic::Material(session_->get()),
          };
          new_layer.shape_node.SetMaterial(new_layer.material);
          scenic_layers_.emplace_back(std::move(new_layer));
        }

        // Compute a hash and index for the rect.
        const uint64_t rect_hash =
            (static_cast<uint64_t>(layer->second.surface_size.width()) << 32) +
            layer->second.surface_size.height();
        size_t rect_index = 0;
        auto found_index = scenic_rect_indices.find(rect_hash);
        if (found_index == scenic_rect_indices.end()) {
          scenic_rect_indices.emplace(std::make_pair(rect_hash, 0));
        } else {
          rect_index = found_index->second + 1;
          scenic_rect_indices[rect_hash] = rect_index;
        }

        // Create a new rect if needed for the surface.
        auto found_rects = scenic_rects_.find(rect_hash);
        if (found_rects == scenic_rects_.end()) {
          auto [emplaced_rects, success] = scenic_rects_.emplace(
              std::make_pair(rect_hash, std::vector<scenic::Rectangle>()));
          FML_CHECK(success);

          found_rects = std::move(emplaced_rects);
        }
        FML_CHECK(rect_index <= found_rects->second.size());
        if (rect_index == found_rects->second.size()) {
          found_rects->second.emplace_back(scenic::Rectangle(
              session_->get(), layer->second.surface_size.width(),
              layer->second.surface_size.height()));
        }

        // Set layer shape and texture.
        // Scenic currently lacks an API to enable rendering of alpha channel;
        // Flutter Embedder also lacks an API to detect if a layer has alpha or
        // not. Alpha channels are only rendered if there is a OpacityNode
        // higher in the tree with opacity != 1. For now, assume any layer
        // beyond the first has alpha and clamp to a infinitesimally smaller
        // value than 1.  The first layer retains an opacity of 1 to avoid
        // blending with anything underneath.
        //
        // This does not cause visual problems in practice, but probably has
        // performance implications.
        const SkAlpha layer_opacity =
            first_layer ? kBackgroundLayerOpacity : kOverlayLayerOpacity;
        const float layer_elevation =
            kScenicZElevationBetweenLayers * scenic_layer_index +
            embedded_views_height;
        auto& scenic_layer = scenic_layers_[scenic_layer_index];
        auto& scenic_rect = found_rects->second[rect_index];
        scenic_layer.shape_node.SetLabel("Flutter::Layer");
        scenic_layer.shape_node.SetShape(scenic_rect);
        scenic_layer.shape_node.SetTranslation(
            layer->second.surface_size.width() * 0.5f,
            layer->second.surface_size.height() * 0.5f, -layer_elevation);
        scenic_layer.material.SetColor(SK_AlphaOPAQUE, SK_AlphaOPAQUE,
                                       SK_AlphaOPAQUE, layer_opacity);
        scenic_layer.material.SetTexture(surface_for_layer->GetImageId());

        // Only the first (i.e. the bottom-most) layer should receive input.
        // TODO: Workaround for invisible overlays stealing input. Remove when
        // the underlying bug is fixed.
        const fuchsia::ui::gfx::HitTestBehavior layer_hit_test_behavior =
            first_layer ? fuchsia::ui::gfx::HitTestBehavior::kDefault
                        : fuchsia::ui::gfx::HitTestBehavior::kSuppress;
        scenic_layer.shape_node.SetHitTestBehavior(layer_hit_test_behavior);

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

      // Reset for the next pass:
      //  +The next layer will not be the first layer.
      //  +Account for the current layer's height when positioning the next.
      first_layer = false;
      scenic_layer_index++;
    }
  }

  // Present the session to Scenic, along with surface acquire/release fencess.
  {
    TRACE_EVENT0("flutter", "SessionPresent");

    session_->Present();
  }

  // Render the recorded SkPictures into the surfaces.
  {
    TRACE_EVENT0("flutter", "RasterizeSurfaces");

    for (const auto& surface_index : frame_surface_indices) {
      TRACE_EVENT0("flutter", "RasterizeSurface");

      FML_CHECK(surface_index.second < frame_surfaces.size());
      SurfaceProducerSurface* surface =
          frame_surfaces[surface_index.second].get();
      FML_CHECK(surface != nullptr);

      sk_sp<SkSurface> sk_surface = surface->GetSkiaSurface();
      FML_CHECK(sk_surface != nullptr);
      FML_CHECK(SkISize::Make(sk_surface->width(), sk_surface->height()) ==
                frame_size_);
      SkCanvas* canvas = sk_surface->getCanvas();
      FML_CHECK(canvas != nullptr);

      const auto& layer = frame_layers_.find(surface_index.first);
      FML_CHECK(layer != frame_layers_.end());
      sk_sp<SkPicture> picture =
          layer->second.recorder->finishRecordingAsPicture();
      FML_CHECK(picture != nullptr);

      canvas->setMatrix(SkMatrix::I());
      canvas->clear(SK_ColorTRANSPARENT);
      canvas->drawPicture(picture);
      canvas->flush();
    }
  }

  // Flush deferred Skia work and inform Scenic that render targets are ready.
  {
    TRACE_EVENT0("flutter", "PresentSurfaces");

    surface_producer_->SubmitSurfaces(std::move(frame_surfaces));
  }

  // Submit the underlying render-backend-specific frame for processing.
  frame->Submit();
}