share platform view slicing logic across iOS and Android. (flutter/engine#54010)
This removes support for "unobstructed platform views" on iOS - instead prefering to use the Android strategy of minimizing overlay layers, as this is generally more performant.
This commit is contained in:
@@ -80,6 +80,7 @@
|
||||
../../../flutter/flow/surface_frame_unittests.cc
|
||||
../../../flutter/flow/testing
|
||||
../../../flutter/flow/texture_unittests.cc
|
||||
../../../flutter/flow/view_slicer_unittests.cc
|
||||
../../../flutter/flutter_frontend_server
|
||||
../../../flutter/fml/ascii_trie_unittests.cc
|
||||
../../../flutter/fml/backtrace_unittests.cc
|
||||
|
||||
@@ -41734,6 +41734,8 @@ ORIGIN: ../../../flutter/flow/surface.cc + ../../../flutter/LICENSE
|
||||
ORIGIN: ../../../flutter/flow/surface.h + ../../../flutter/LICENSE
|
||||
ORIGIN: ../../../flutter/flow/surface_frame.cc + ../../../flutter/LICENSE
|
||||
ORIGIN: ../../../flutter/flow/surface_frame.h + ../../../flutter/LICENSE
|
||||
ORIGIN: ../../../flutter/flow/view_slicer.cc + ../../../flutter/LICENSE
|
||||
ORIGIN: ../../../flutter/flow/view_slicer.h + ../../../flutter/LICENSE
|
||||
ORIGIN: ../../../flutter/flutter_vma/flutter_skia_vma.cc + ../../../flutter/LICENSE
|
||||
ORIGIN: ../../../flutter/flutter_vma/flutter_skia_vma.h + ../../../flutter/LICENSE
|
||||
ORIGIN: ../../../flutter/flutter_vma/flutter_vma.cc + ../../../flutter/LICENSE
|
||||
@@ -44616,6 +44618,8 @@ FILE: ../../../flutter/flow/surface.cc
|
||||
FILE: ../../../flutter/flow/surface.h
|
||||
FILE: ../../../flutter/flow/surface_frame.cc
|
||||
FILE: ../../../flutter/flow/surface_frame.h
|
||||
FILE: ../../../flutter/flow/view_slicer.cc
|
||||
FILE: ../../../flutter/flow/view_slicer.h
|
||||
FILE: ../../../flutter/flutter_vma/flutter_skia_vma.cc
|
||||
FILE: ../../../flutter/flutter_vma/flutter_skia_vma.h
|
||||
FILE: ../../../flutter/flutter_vma/flutter_vma.cc
|
||||
|
||||
@@ -88,6 +88,8 @@ source_set("flow") {
|
||||
"surface.h",
|
||||
"surface_frame.cc",
|
||||
"surface_frame.h",
|
||||
"view_slicer.cc",
|
||||
"view_slicer.h",
|
||||
]
|
||||
|
||||
public_configs = [ "//flutter:config" ]
|
||||
@@ -183,6 +185,7 @@ if (enable_unittests) {
|
||||
"testing/mock_layer_unittests.cc",
|
||||
"testing/mock_texture_unittests.cc",
|
||||
"texture_unittests.cc",
|
||||
"view_slicer_unittests.cc",
|
||||
]
|
||||
|
||||
deps = [
|
||||
|
||||
116
engine/src/flutter/flow/view_slicer.cc
Normal file
116
engine/src/flutter/flow/view_slicer.cc
Normal file
@@ -0,0 +1,116 @@
|
||||
// Copyright 2013 The Flutter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "flutter/flow/view_slicer.h"
|
||||
|
||||
#include <unordered_map>
|
||||
#include "flow/embedded_views.h"
|
||||
#include "fml/logging.h"
|
||||
|
||||
namespace flutter {
|
||||
|
||||
std::unordered_map<int64_t, SkRect> SliceViews(
|
||||
DlCanvas* background_canvas,
|
||||
const std::vector<int64_t>& composition_order,
|
||||
const std::unordered_map<int64_t, std::unique_ptr<EmbedderViewSlice>>&
|
||||
slices,
|
||||
const std::unordered_map<int64_t, SkRect>& view_rects) {
|
||||
std::unordered_map<int64_t, SkRect> overlay_layers;
|
||||
|
||||
auto current_frame_view_count = composition_order.size();
|
||||
|
||||
// Restore the clip context after exiting this method since it's changed
|
||||
// below.
|
||||
DlAutoCanvasRestore save(background_canvas, /*do_save=*/true);
|
||||
|
||||
for (size_t i = 0; i < current_frame_view_count; i++) {
|
||||
int64_t view_id = composition_order[i];
|
||||
EmbedderViewSlice* slice = slices.at(view_id).get();
|
||||
if (slice->canvas() == nullptr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
slice->end_recording();
|
||||
|
||||
SkRect full_joined_rect = SkRect::MakeEmpty();
|
||||
|
||||
// Determinate if Flutter UI intersects with any of the previous
|
||||
// platform views stacked by z position.
|
||||
//
|
||||
// This is done by querying the r-tree that holds the records for the
|
||||
// picture recorder corresponding to the flow layers added after a platform
|
||||
// view layer.
|
||||
for (int j = i; j >= 0; j--) {
|
||||
int64_t current_view_id = composition_order[j];
|
||||
auto maybe_rect = view_rects.find(current_view_id);
|
||||
FML_DCHECK(maybe_rect != view_rects.end());
|
||||
if (maybe_rect == view_rects.end()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
SkRect current_view_rect = maybe_rect->second;
|
||||
const SkIRect rounded_in_platform_view_rect = current_view_rect.roundIn();
|
||||
|
||||
// Each rect corresponds to a native view that renders Flutter UI.
|
||||
std::vector<SkIRect> intersection_rects =
|
||||
slice->region(current_view_rect).getRects();
|
||||
|
||||
// Ignore intersections of single width/height on the edge of the platform
|
||||
// view.
|
||||
// This is to address the following performance issue when interleaving
|
||||
// adjacent platform views and layers: Since we `roundOut` both platform
|
||||
// view rects and the layer rects, as long as the coordinate is
|
||||
// fractional, there will be an intersection of a single pixel width (or
|
||||
// height) after rounding out, even if they do not intersect before
|
||||
// rounding out. We have to round out both platform view rect and the
|
||||
// layer rect. Rounding in platform view rect will result in missing pixel
|
||||
// on the intersection edge. Rounding in layer rect will result in missing
|
||||
// pixel on the edge of the layer on top of the platform view.
|
||||
for (auto it = intersection_rects.begin(); it != intersection_rects.end();
|
||||
/*no-op*/) {
|
||||
// If intersection_rect does not intersect with the *rounded in*
|
||||
// platform view rect, then the intersection must be a single pixel
|
||||
// width (or height) on edge.
|
||||
if (!SkIRect::Intersects(*it, rounded_in_platform_view_rect)) {
|
||||
it = intersection_rects.erase(it);
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
// Limit the number of native views, so it doesn't grow forever.
|
||||
//
|
||||
// In this case, the rects are merged into a single one that is the union
|
||||
// of all the rects.
|
||||
SkRect partial_joined_rect = SkRect::MakeEmpty();
|
||||
for (const SkIRect& rect : intersection_rects) {
|
||||
partial_joined_rect.join(SkRect::Make(rect));
|
||||
}
|
||||
|
||||
// Get the intersection rect with the `current_view_rect`,
|
||||
partial_joined_rect.intersect(SkRect::Make(current_view_rect.roundOut()));
|
||||
|
||||
// Join the `partial_joined_rect` into `full_joined_rect` to get the rect
|
||||
// above the current `slice`
|
||||
full_joined_rect.join(partial_joined_rect);
|
||||
}
|
||||
|
||||
if (!full_joined_rect.isEmpty()) {
|
||||
overlay_layers.insert({view_id, full_joined_rect});
|
||||
|
||||
// Clip the background canvas, so it doesn't contain any of the pixels
|
||||
// drawn on the overlay layer.
|
||||
background_canvas->ClipRect(full_joined_rect,
|
||||
DlCanvas::ClipOp::kDifference);
|
||||
}
|
||||
slice->render_into(background_canvas);
|
||||
}
|
||||
|
||||
// Manually trigger the DlAutoCanvasRestore before we submit the frame
|
||||
save.Restore();
|
||||
|
||||
return overlay_layers;
|
||||
}
|
||||
|
||||
} // namespace flutter
|
||||
25
engine/src/flutter/flow/view_slicer.h
Normal file
25
engine/src/flutter/flow/view_slicer.h
Normal file
@@ -0,0 +1,25 @@
|
||||
// Copyright 2013 The Flutter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef FLUTTER_FLOW_VIEW_SLICER_H_
|
||||
#define FLUTTER_FLOW_VIEW_SLICER_H_
|
||||
|
||||
#include <unordered_map>
|
||||
#include "display_list/dl_canvas.h"
|
||||
#include "flow/embedded_views.h"
|
||||
|
||||
namespace flutter {
|
||||
|
||||
/// @brief Compute the required overlay layers and clip the view slices
|
||||
/// according to the size and position of the platform views.
|
||||
std::unordered_map<int64_t, SkRect> SliceViews(
|
||||
DlCanvas* background_canvas,
|
||||
const std::vector<int64_t>& composition_order,
|
||||
const std::unordered_map<int64_t, std::unique_ptr<EmbedderViewSlice>>&
|
||||
slices,
|
||||
const std::unordered_map<int64_t, SkRect>& view_rects);
|
||||
|
||||
} // namespace flutter
|
||||
|
||||
#endif // FLUTTER_FLOW_VIEW_SLICER_H_
|
||||
138
engine/src/flutter/flow/view_slicer_unittests.cc
Normal file
138
engine/src/flutter/flow/view_slicer_unittests.cc
Normal file
@@ -0,0 +1,138 @@
|
||||
// Copyright 2013 The Flutter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include <unordered_map>
|
||||
#include "display_list/dl_builder.h"
|
||||
#include "flow/embedded_views.h"
|
||||
#include "flutter/flow/view_slicer.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
namespace flutter {
|
||||
namespace testing {
|
||||
|
||||
namespace {
|
||||
void AddSliceOfSize(
|
||||
std::unordered_map<int64_t, std::unique_ptr<EmbedderViewSlice>>& slices,
|
||||
int64_t id,
|
||||
SkRect rect) {
|
||||
slices[id] = std::make_unique<DisplayListEmbedderViewSlice>(rect);
|
||||
DlPaint paint;
|
||||
paint.setColor(DlColor::kBlack());
|
||||
slices[id]->canvas()->DrawRect(rect, paint);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
TEST(ViewSlicerTest, CanSlicerNonOverlappingViews) {
|
||||
DisplayListBuilder builder(SkRect::MakeLTRB(0, 0, 100, 100));
|
||||
|
||||
std::vector<int64_t> composition_order = {1};
|
||||
std::unordered_map<int64_t, std::unique_ptr<EmbedderViewSlice>> slices;
|
||||
AddSliceOfSize(slices, 1, SkRect::MakeLTRB(99, 99, 100, 100));
|
||||
|
||||
std::unordered_map<int64_t, SkRect> view_rects = {
|
||||
{1, SkRect::MakeLTRB(50, 50, 60, 60)}};
|
||||
|
||||
auto computed_overlays =
|
||||
SliceViews(&builder, composition_order, slices, view_rects);
|
||||
|
||||
EXPECT_TRUE(computed_overlays.empty());
|
||||
}
|
||||
|
||||
TEST(ViewSlicerTest, IgnoresFractionalOverlaps) {
|
||||
DisplayListBuilder builder(SkRect::MakeLTRB(0, 0, 100, 100));
|
||||
|
||||
std::vector<int64_t> composition_order = {1};
|
||||
std::unordered_map<int64_t, std::unique_ptr<EmbedderViewSlice>> slices;
|
||||
AddSliceOfSize(slices, 1, SkRect::MakeLTRB(0, 0, 50.49, 50.49));
|
||||
|
||||
std::unordered_map<int64_t, SkRect> view_rects = {
|
||||
{1, SkRect::MakeLTRB(50.5, 50.5, 100, 100)}};
|
||||
|
||||
auto computed_overlays =
|
||||
SliceViews(&builder, composition_order, slices, view_rects);
|
||||
|
||||
EXPECT_TRUE(computed_overlays.empty());
|
||||
}
|
||||
|
||||
TEST(ViewSlicerTest, ComputesOverlapWith1PV) {
|
||||
DisplayListBuilder builder(SkRect::MakeLTRB(0, 0, 100, 100));
|
||||
|
||||
std::vector<int64_t> composition_order = {1};
|
||||
std::unordered_map<int64_t, std::unique_ptr<EmbedderViewSlice>> slices;
|
||||
AddSliceOfSize(slices, 1, SkRect::MakeLTRB(0, 0, 50, 50));
|
||||
|
||||
std::unordered_map<int64_t, SkRect> view_rects = {
|
||||
{1, SkRect::MakeLTRB(0, 0, 100, 100)}};
|
||||
|
||||
auto computed_overlays =
|
||||
SliceViews(&builder, composition_order, slices, view_rects);
|
||||
|
||||
EXPECT_EQ(computed_overlays.size(), 1u);
|
||||
auto overlay = computed_overlays.find(1);
|
||||
ASSERT_NE(overlay, computed_overlays.end());
|
||||
|
||||
EXPECT_EQ(overlay->second, SkRect::MakeLTRB(0, 0, 50, 50));
|
||||
}
|
||||
|
||||
TEST(ViewSlicerTest, ComputesOverlapWith2PV) {
|
||||
DisplayListBuilder builder(SkRect::MakeLTRB(0, 0, 100, 100));
|
||||
|
||||
std::vector<int64_t> composition_order = {1, 2};
|
||||
std::unordered_map<int64_t, std::unique_ptr<EmbedderViewSlice>> slices;
|
||||
AddSliceOfSize(slices, 1, SkRect::MakeLTRB(0, 0, 50, 50));
|
||||
AddSliceOfSize(slices, 2, SkRect::MakeLTRB(50, 50, 100, 100));
|
||||
|
||||
std::unordered_map<int64_t, SkRect> view_rects = {
|
||||
{1, SkRect::MakeLTRB(0, 0, 50, 50)}, //
|
||||
{2, SkRect::MakeLTRB(50, 50, 100, 100)}, //
|
||||
};
|
||||
|
||||
auto computed_overlays =
|
||||
SliceViews(&builder, composition_order, slices, view_rects);
|
||||
|
||||
EXPECT_EQ(computed_overlays.size(), 2u);
|
||||
|
||||
auto overlay = computed_overlays.find(1);
|
||||
ASSERT_NE(overlay, computed_overlays.end());
|
||||
|
||||
EXPECT_EQ(overlay->second, SkRect::MakeLTRB(0, 0, 50, 50));
|
||||
|
||||
overlay = computed_overlays.find(2);
|
||||
ASSERT_NE(overlay, computed_overlays.end());
|
||||
EXPECT_EQ(overlay->second, SkRect::MakeLTRB(50, 50, 100, 100));
|
||||
}
|
||||
|
||||
TEST(ViewSlicerTest, OverlappingTwoPVs) {
|
||||
DisplayListBuilder builder(SkRect::MakeLTRB(0, 0, 100, 100));
|
||||
|
||||
std::vector<int64_t> composition_order = {1, 2};
|
||||
std::unordered_map<int64_t, std::unique_ptr<EmbedderViewSlice>> slices;
|
||||
// This embeded view overlaps both platform views:
|
||||
//
|
||||
// [ A [ ]]
|
||||
// [_____[ C ]]
|
||||
// [ B [ ]]
|
||||
// [ ]
|
||||
AddSliceOfSize(slices, 1, SkRect::MakeLTRB(0, 0, 0, 0));
|
||||
AddSliceOfSize(slices, 2, SkRect::MakeLTRB(0, 0, 100, 100));
|
||||
|
||||
std::unordered_map<int64_t, SkRect> view_rects = {
|
||||
{1, SkRect::MakeLTRB(0, 0, 50, 50)}, //
|
||||
{2, SkRect::MakeLTRB(50, 50, 100, 100)}, //
|
||||
};
|
||||
|
||||
auto computed_overlays =
|
||||
SliceViews(&builder, composition_order, slices, view_rects);
|
||||
|
||||
EXPECT_EQ(computed_overlays.size(), 1u);
|
||||
|
||||
auto overlay = computed_overlays.find(2);
|
||||
ASSERT_NE(overlay, computed_overlays.end());
|
||||
|
||||
// We create a single overlay for both overlapping sections.
|
||||
EXPECT_EQ(overlay->second, SkRect::MakeLTRB(0, 0, 100, 100));
|
||||
}
|
||||
|
||||
} // namespace testing
|
||||
} // namespace flutter
|
||||
@@ -3,6 +3,7 @@
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "flutter/shell/platform/android/external_view_embedder/external_view_embedder.h"
|
||||
#include "flow/view_slicer.h"
|
||||
#include "flutter/common/constants.h"
|
||||
#include "flutter/fml/synchronization/waitable_event.h"
|
||||
#include "flutter/fml/trace_event.h"
|
||||
@@ -78,72 +79,17 @@ void AndroidExternalViewEmbedder::SubmitFlutterView(
|
||||
return;
|
||||
}
|
||||
|
||||
std::unordered_map<int64_t, SkRect> overlay_layers;
|
||||
DlCanvas* background_canvas = frame->Canvas();
|
||||
auto current_frame_view_count = composition_order_.size();
|
||||
|
||||
// Restore the clip context after exiting this method since it's changed
|
||||
// below.
|
||||
DlAutoCanvasRestore save(background_canvas, /*do_save=*/true);
|
||||
|
||||
for (size_t i = 0; i < current_frame_view_count; i++) {
|
||||
int64_t view_id = composition_order_[i];
|
||||
EmbedderViewSlice* slice = slices_.at(view_id).get();
|
||||
if (slice->canvas() == nullptr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
slice->end_recording();
|
||||
|
||||
SkRect full_joined_rect = SkRect::MakeEmpty();
|
||||
|
||||
// Determinate if Flutter UI intersects with any of the previous
|
||||
// platform views stacked by z position.
|
||||
//
|
||||
// This is done by querying the r-tree that holds the records for the
|
||||
// picture recorder corresponding to the flow layers added after a platform
|
||||
// view layer.
|
||||
for (ssize_t j = i; j >= 0; j--) {
|
||||
int64_t current_view_id = composition_order_[j];
|
||||
SkRect current_view_rect = GetViewRect(current_view_id);
|
||||
// The rect above the `current_view_rect`
|
||||
SkRect partial_joined_rect = SkRect::MakeEmpty();
|
||||
// Each rect corresponds to a native view that renders Flutter UI.
|
||||
std::vector<SkIRect> intersection_rects =
|
||||
slice->region(current_view_rect).getRects();
|
||||
|
||||
// Limit the number of native views, so it doesn't grow forever.
|
||||
//
|
||||
// In this case, the rects are merged into a single one that is the union
|
||||
// of all the rects.
|
||||
for (const SkIRect& rect : intersection_rects) {
|
||||
partial_joined_rect.join(SkRect::Make(rect));
|
||||
}
|
||||
// Get the intersection rect with the `current_view_rect`,
|
||||
partial_joined_rect.intersect(current_view_rect);
|
||||
// Join the `partial_joined_rect` into `full_joined_rect` to get the rect
|
||||
// above the current `slice`
|
||||
full_joined_rect.join(partial_joined_rect);
|
||||
}
|
||||
if (!full_joined_rect.isEmpty()) {
|
||||
// Subpixels in the platform may not align with the canvas subpixels.
|
||||
//
|
||||
// To workaround it, round the floating point bounds and make the rect
|
||||
// slightly larger.
|
||||
//
|
||||
// For example, {0.3, 0.5, 3.1, 4.7} becomes {0, 0, 4, 5}.
|
||||
full_joined_rect.set(full_joined_rect.roundOut());
|
||||
overlay_layers.insert({view_id, full_joined_rect});
|
||||
// Clip the background canvas, so it doesn't contain any of the pixels
|
||||
// drawn on the overlay layer.
|
||||
background_canvas->ClipRect(full_joined_rect,
|
||||
DlCanvas::ClipOp::kDifference);
|
||||
}
|
||||
slice->render_into(background_canvas);
|
||||
std::unordered_map<int64_t, SkRect> view_rects;
|
||||
for (auto platform_id : composition_order_) {
|
||||
view_rects[platform_id] = GetViewRect(platform_id);
|
||||
}
|
||||
|
||||
// Manually trigger the DlAutoCanvasRestore before we submit the frame
|
||||
save.Restore();
|
||||
std::unordered_map<int64_t, SkRect> overlay_layers =
|
||||
SliceViews(frame->Canvas(), //
|
||||
composition_order_, //
|
||||
slices_, //
|
||||
view_rects //
|
||||
);
|
||||
|
||||
// Submit the background canvas frame before switching the GL context to
|
||||
// the overlay surfaces.
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
#include <Metal/Metal.h>
|
||||
|
||||
#include "flutter/flow/view_slicer.h"
|
||||
#include "flutter/fml/platform/darwin/scoped_nsobject.h"
|
||||
#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterOverlayView.h"
|
||||
#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterView.h"
|
||||
@@ -681,101 +682,36 @@ bool FlutterPlatformViewsController::SubmitFrame(GrDirectContext* gr_context,
|
||||
|
||||
DlCanvas* background_canvas = frame->Canvas();
|
||||
|
||||
// Resolve all pending GPU operations before allocating a new surface.
|
||||
background_canvas->Flush();
|
||||
|
||||
// Clipping the background canvas before drawing the picture recorders requires
|
||||
// saving and restoring the clip context.
|
||||
DlAutoCanvasRestore save(background_canvas, /*do_save=*/true);
|
||||
|
||||
// Maps a platform view id to a vector of `FlutterPlatformViewLayer`.
|
||||
LayersMap platform_view_layers;
|
||||
|
||||
auto did_submit = true;
|
||||
auto num_platform_views = composition_order_.size();
|
||||
|
||||
// TODO(hellohuanlin) this double for-loop is expensive with wasted computations.
|
||||
// See: https://github.com/flutter/flutter/issues/145802
|
||||
for (size_t i = 0; i < num_platform_views; i++) {
|
||||
int64_t platform_view_id = composition_order_[i];
|
||||
EmbedderViewSlice* slice = slices_[platform_view_id].get();
|
||||
slice->end_recording();
|
||||
|
||||
// Check if the current picture contains overlays that intersect with the
|
||||
// current platform view or any of the previous platform views.
|
||||
for (size_t j = i + 1; j > 0; j--) {
|
||||
int64_t current_platform_view_id = composition_order_[j - 1];
|
||||
SkRect platform_view_rect = GetPlatformViewRect(current_platform_view_id);
|
||||
std::vector<SkIRect> intersection_rects = slice->region(platform_view_rect).getRects();
|
||||
const SkIRect rounded_in_platform_view_rect = platform_view_rect.roundIn();
|
||||
// Ignore intersections of single width/height on the edge of the platform view.
|
||||
// This is to address the following performance issue when interleaving adjacent
|
||||
// platform views and layers:
|
||||
// Since we `roundOut` both platform view rects and the layer rects, as long as
|
||||
// the coordinate is fractional, there will be an intersection of a single pixel width
|
||||
// (or height) after rounding out, even if they do not intersect before rounding out.
|
||||
// We have to round out both platform view rect and the layer rect.
|
||||
// Rounding in platform view rect will result in missing pixel on the intersection edge.
|
||||
// Rounding in layer rect will result in missing pixel on the edge of the layer on top
|
||||
// of the platform view.
|
||||
for (auto it = intersection_rects.begin(); it != intersection_rects.end(); /*no-op*/) {
|
||||
// If intersection_rect does not intersect with the *rounded in* platform
|
||||
// view rect, then the intersection must be a single pixel width (or height) on edge.
|
||||
if (!SkIRect::Intersects(*it, rounded_in_platform_view_rect)) {
|
||||
it = intersection_rects.erase(it);
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
auto allocation_size = intersection_rects.size();
|
||||
|
||||
// For testing purposes, the overlay id is used to find the overlay view.
|
||||
// This is the index of the layer for the current platform view.
|
||||
auto overlay_id = platform_view_layers[current_platform_view_id].size();
|
||||
|
||||
// If the max number of allocations per platform view is exceeded,
|
||||
// then join all the rects into a single one.
|
||||
//
|
||||
// TODO(egarciad): Consider making this configurable.
|
||||
// https://github.com/flutter/flutter/issues/52510
|
||||
if (allocation_size > kMaxLayerAllocations) {
|
||||
SkIRect joined_rect = SkIRect::MakeEmpty();
|
||||
for (const SkIRect& rect : intersection_rects) {
|
||||
joined_rect.join(rect);
|
||||
}
|
||||
// Replace the rects in the intersection rects list for a single rect that is
|
||||
// the union of all the rects in the list.
|
||||
intersection_rects.clear();
|
||||
intersection_rects.push_back(joined_rect);
|
||||
}
|
||||
for (SkIRect& joined_rect : intersection_rects) {
|
||||
// Get the intersection rect between the current rect
|
||||
// and the platform view rect.
|
||||
joined_rect.intersect(platform_view_rect.roundOut());
|
||||
// Clip the background canvas, so it doesn't contain any of the pixels drawn
|
||||
// on the overlay layer.
|
||||
background_canvas->ClipRect(SkRect::Make(joined_rect), DlCanvas::ClipOp::kDifference);
|
||||
// Get a new host layer.
|
||||
std::shared_ptr<FlutterPlatformViewLayer> layer =
|
||||
GetLayer(gr_context, //
|
||||
ios_context, //
|
||||
slice, //
|
||||
joined_rect, //
|
||||
current_platform_view_id, //
|
||||
overlay_id, //
|
||||
((FlutterView*)flutter_view_.get()).pixelFormat //
|
||||
);
|
||||
did_submit &= layer->did_submit_last_frame;
|
||||
platform_view_layers[current_platform_view_id].push_back(layer);
|
||||
overlay_id++;
|
||||
}
|
||||
}
|
||||
slice->render_into(background_canvas);
|
||||
std::unordered_map<int64_t, SkRect> view_rects;
|
||||
for (auto view_id : composition_order_) {
|
||||
view_rects[view_id] = GetPlatformViewRect(view_id);
|
||||
}
|
||||
|
||||
// Manually trigger the SkAutoCanvasRestore before we submit the frame
|
||||
save.Restore();
|
||||
std::unordered_map<int64_t, SkRect> overlay_layers =
|
||||
SliceViews(background_canvas, composition_order_, slices_, view_rects);
|
||||
|
||||
LayersMap platform_view_layers;
|
||||
auto did_submit = true;
|
||||
|
||||
int overlay_id = 0;
|
||||
for (int64_t view_id : composition_order_) {
|
||||
std::unordered_map<int64_t, SkRect>::const_iterator overlay = overlay_layers.find(view_id);
|
||||
if (overlay == overlay_layers.end()) {
|
||||
continue;
|
||||
}
|
||||
std::shared_ptr<FlutterPlatformViewLayer> layer =
|
||||
GetLayer(gr_context, //
|
||||
ios_context, //
|
||||
slices_[view_id].get(), //
|
||||
overlay->second, //
|
||||
view_id, //
|
||||
overlay_id, //
|
||||
((FlutterView*)flutter_view_.get()).pixelFormat //
|
||||
);
|
||||
did_submit &= layer->did_submit_last_frame;
|
||||
platform_view_layers[view_id].push_back(layer);
|
||||
overlay_id++;
|
||||
}
|
||||
|
||||
// If a layer was allocated in the previous frame, but it's not used in the current frame,
|
||||
// then it can be removed from the scene.
|
||||
@@ -833,7 +769,7 @@ std::shared_ptr<FlutterPlatformViewLayer> FlutterPlatformViewsController::GetLay
|
||||
GrDirectContext* gr_context,
|
||||
const std::shared_ptr<IOSContext>& ios_context,
|
||||
EmbedderViewSlice* slice,
|
||||
SkIRect rect,
|
||||
SkRect rect,
|
||||
int64_t view_id,
|
||||
int64_t overlay_id,
|
||||
MTLPixelFormat pixel_format) {
|
||||
@@ -868,7 +804,7 @@ std::shared_ptr<FlutterPlatformViewLayer> FlutterPlatformViewsController::GetLay
|
||||
DlCanvas* overlay_canvas = frame->Canvas();
|
||||
int restore_count = overlay_canvas->GetSaveCount();
|
||||
overlay_canvas->Save();
|
||||
overlay_canvas->ClipRect(SkRect::Make(rect));
|
||||
overlay_canvas->ClipRect(rect);
|
||||
overlay_canvas->Clear(DlColor::kTransparent());
|
||||
slice->render_into(overlay_canvas);
|
||||
overlay_canvas->RestoreToCount(restore_count);
|
||||
|
||||
@@ -288,8 +288,6 @@ class FlutterPlatformViewsController {
|
||||
void PushVisitedPlatformView(int64_t view_id) { visited_platform_views_.push_back(view_id); }
|
||||
|
||||
private:
|
||||
static const size_t kMaxLayerAllocations = 2;
|
||||
|
||||
using LayersMap = std::map<int64_t, std::vector<std::shared_ptr<FlutterPlatformViewLayer>>>;
|
||||
|
||||
void OnCreate(FlutterMethodCall* call, FlutterResult result) __attribute__((cf_audited_transfer));
|
||||
@@ -334,7 +332,7 @@ class FlutterPlatformViewsController {
|
||||
std::shared_ptr<FlutterPlatformViewLayer> GetLayer(GrDirectContext* gr_context,
|
||||
const std::shared_ptr<IOSContext>& ios_context,
|
||||
EmbedderViewSlice* slice,
|
||||
SkIRect rect,
|
||||
SkRect rect,
|
||||
int64_t view_id,
|
||||
int64_t overlay_id,
|
||||
MTLPixelFormat pixel_format);
|
||||
@@ -362,7 +360,7 @@ class FlutterPlatformViewsController {
|
||||
// operation until the next platform view or the end of the last leaf node in the layer tree.
|
||||
//
|
||||
// The Slices are deleted by the FlutterPlatformViewsController.reset().
|
||||
std::map<int64_t, std::unique_ptr<EmbedderViewSlice>> slices_;
|
||||
std::unordered_map<int64_t, std::unique_ptr<EmbedderViewSlice>> slices_;
|
||||
|
||||
fml::scoped_nsobject<FlutterMethodChannel> channel_;
|
||||
fml::scoped_nsobject<UIView> flutter_view_;
|
||||
|
||||
@@ -234,21 +234,14 @@ static const CGFloat kCompareAccuracy = 0.001;
|
||||
XCTAssertEqual(platform_view2.frame.size.width, 250);
|
||||
XCTAssertEqual(platform_view2.frame.size.height, 250);
|
||||
|
||||
XCUIElement* overlay1 = app.otherElements[@"platform_view[0].overlay[0]"];
|
||||
XCUIElement* overlay1 = app.otherElements[@"platform_view[1].overlay[0]"];
|
||||
XCTAssertTrue(overlay1.exists);
|
||||
XCTAssertEqual(overlay1.frame.origin.x, 25);
|
||||
XCTAssertEqual(overlay1.frame.origin.y, 300);
|
||||
XCTAssertEqual(overlay1.frame.origin.y, 0);
|
||||
XCTAssertEqual(overlay1.frame.size.width, 225);
|
||||
XCTAssertEqual(overlay1.frame.size.height, 200);
|
||||
XCTAssertEqual(overlay1.frame.size.height, 500);
|
||||
|
||||
XCUIElement* overlay2 = app.otherElements[@"platform_view[1].overlay[0]"];
|
||||
XCTAssertTrue(overlay2.exists);
|
||||
XCTAssertEqual(overlay2.frame.origin.x, 25);
|
||||
XCTAssertEqual(overlay2.frame.origin.y, 0);
|
||||
XCTAssertEqual(overlay2.frame.size.width, 225);
|
||||
XCTAssertEqual(overlay2.frame.size.height, 250);
|
||||
|
||||
XCUIElement* overlayView0 = app.otherElements[@"platform_view[0].overlay_view[0]"];
|
||||
XCUIElement* overlayView0 = app.otherElements[@"platform_view[1].overlay_view[0]"];
|
||||
XCTAssertTrue(overlayView0.exists);
|
||||
// Overlay should always be the same frame as the app.
|
||||
XCTAssertEqualWithAccuracy(overlayView0.frame.origin.x, app.frame.origin.x, kCompareAccuracy);
|
||||
@@ -256,15 +249,6 @@ static const CGFloat kCompareAccuracy = 0.001;
|
||||
XCTAssertEqualWithAccuracy(overlayView0.frame.size.width, app.frame.size.width, kCompareAccuracy);
|
||||
XCTAssertEqualWithAccuracy(overlayView0.frame.size.height, app.frame.size.height,
|
||||
kCompareAccuracy);
|
||||
|
||||
XCUIElement* overlayView1 = app.otherElements[@"platform_view[1].overlay_view[0]"];
|
||||
XCTAssertTrue(overlayView1.exists);
|
||||
// Overlay should always be the same frame as the app.
|
||||
XCTAssertEqualWithAccuracy(overlayView1.frame.origin.x, app.frame.origin.x, kCompareAccuracy);
|
||||
XCTAssertEqualWithAccuracy(overlayView1.frame.origin.y, app.frame.origin.x, kCompareAccuracy);
|
||||
XCTAssertEqualWithAccuracy(overlayView1.frame.size.width, app.frame.size.width, kCompareAccuracy);
|
||||
XCTAssertEqualWithAccuracy(overlayView1.frame.size.height, app.frame.size.height,
|
||||
kCompareAccuracy);
|
||||
}
|
||||
|
||||
// More then two overlays are merged into a single layer.
|
||||
|
||||
Reference in New Issue
Block a user