mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
DisplayListBuilder internal reorganization with better rendering op overlap detection (flutter/engine#52646)
This commit makes some long-needed internal improvements to the way that the DisplayListBuilder manages its per-save/saveLayer data. The information for layer bounds and matrix/clips is now maintained in the layer info structure itself rather than shared across a number of stack structures which required careful alignment of the 3 different stacks and made it more difficult to compare and update adjacent layers during save and restore operations. The new code stores all information for a layer within a single structure and the save and restore operations can be more clear about which information they are getting or setting in the current and parent layers. In addition, the layer bounds accumulations were updated to have a more flexible algorithm for detecting overlap of rendering operations for the opacity peephole optimization. Previously, more than one rendering op on a layer would prevent opacity peephole optimizations, but now the condition will be recognized until the first rendering op that overlaps the bounds of the previous rendering operations. This will help for some potentially common cases of 2 non-overlapping ops or even a list of rendering operations laid out in a row.
This commit is contained in:
parent
f1ac521788
commit
13d64bc737
@ -39927,6 +39927,7 @@ ORIGIN: ../../../flutter/display_list/effects/dl_path_effect.cc + ../../../flutt
|
||||
ORIGIN: ../../../flutter/display_list/effects/dl_path_effect.h + ../../../flutter/LICENSE
|
||||
ORIGIN: ../../../flutter/display_list/effects/dl_runtime_effect.cc + ../../../flutter/LICENSE
|
||||
ORIGIN: ../../../flutter/display_list/effects/dl_runtime_effect.h + ../../../flutter/LICENSE
|
||||
ORIGIN: ../../../flutter/display_list/geometry/dl_geometry_types.h + ../../../flutter/LICENSE
|
||||
ORIGIN: ../../../flutter/display_list/geometry/dl_region.cc + ../../../flutter/LICENSE
|
||||
ORIGIN: ../../../flutter/display_list/geometry/dl_region.h + ../../../flutter/LICENSE
|
||||
ORIGIN: ../../../flutter/display_list/geometry/dl_rtree.cc + ../../../flutter/LICENSE
|
||||
@ -39944,8 +39945,8 @@ ORIGIN: ../../../flutter/display_list/skia/dl_sk_dispatcher.h + ../../../flutter
|
||||
ORIGIN: ../../../flutter/display_list/skia/dl_sk_paint_dispatcher.cc + ../../../flutter/LICENSE
|
||||
ORIGIN: ../../../flutter/display_list/skia/dl_sk_paint_dispatcher.h + ../../../flutter/LICENSE
|
||||
ORIGIN: ../../../flutter/display_list/skia/dl_sk_types.h + ../../../flutter/LICENSE
|
||||
ORIGIN: ../../../flutter/display_list/utils/dl_bounds_accumulator.cc + ../../../flutter/LICENSE
|
||||
ORIGIN: ../../../flutter/display_list/utils/dl_bounds_accumulator.h + ../../../flutter/LICENSE
|
||||
ORIGIN: ../../../flutter/display_list/utils/dl_accumulation_rect.cc + ../../../flutter/LICENSE
|
||||
ORIGIN: ../../../flutter/display_list/utils/dl_accumulation_rect.h + ../../../flutter/LICENSE
|
||||
ORIGIN: ../../../flutter/display_list/utils/dl_comparable.h + ../../../flutter/LICENSE
|
||||
ORIGIN: ../../../flutter/display_list/utils/dl_matrix_clip_tracker.cc + ../../../flutter/LICENSE
|
||||
ORIGIN: ../../../flutter/display_list/utils/dl_matrix_clip_tracker.h + ../../../flutter/LICENSE
|
||||
@ -42804,6 +42805,7 @@ FILE: ../../../flutter/display_list/effects/dl_path_effect.cc
|
||||
FILE: ../../../flutter/display_list/effects/dl_path_effect.h
|
||||
FILE: ../../../flutter/display_list/effects/dl_runtime_effect.cc
|
||||
FILE: ../../../flutter/display_list/effects/dl_runtime_effect.h
|
||||
FILE: ../../../flutter/display_list/geometry/dl_geometry_types.h
|
||||
FILE: ../../../flutter/display_list/geometry/dl_region.cc
|
||||
FILE: ../../../flutter/display_list/geometry/dl_region.h
|
||||
FILE: ../../../flutter/display_list/geometry/dl_rtree.cc
|
||||
@ -42821,8 +42823,8 @@ FILE: ../../../flutter/display_list/skia/dl_sk_dispatcher.h
|
||||
FILE: ../../../flutter/display_list/skia/dl_sk_paint_dispatcher.cc
|
||||
FILE: ../../../flutter/display_list/skia/dl_sk_paint_dispatcher.h
|
||||
FILE: ../../../flutter/display_list/skia/dl_sk_types.h
|
||||
FILE: ../../../flutter/display_list/utils/dl_bounds_accumulator.cc
|
||||
FILE: ../../../flutter/display_list/utils/dl_bounds_accumulator.h
|
||||
FILE: ../../../flutter/display_list/utils/dl_accumulation_rect.cc
|
||||
FILE: ../../../flutter/display_list/utils/dl_accumulation_rect.h
|
||||
FILE: ../../../flutter/display_list/utils/dl_comparable.h
|
||||
FILE: ../../../flutter/display_list/utils/dl_matrix_clip_tracker.cc
|
||||
FILE: ../../../flutter/display_list/utils/dl_matrix_clip_tracker.h
|
||||
|
||||
@ -60,6 +60,7 @@ source_set("display_list") {
|
||||
"effects/dl_path_effect.h",
|
||||
"effects/dl_runtime_effect.cc",
|
||||
"effects/dl_runtime_effect.h",
|
||||
"geometry/dl_geometry_types.h",
|
||||
"geometry/dl_region.cc",
|
||||
"geometry/dl_region.h",
|
||||
"geometry/dl_rtree.cc",
|
||||
@ -77,8 +78,8 @@ source_set("display_list") {
|
||||
"skia/dl_sk_paint_dispatcher.cc",
|
||||
"skia/dl_sk_paint_dispatcher.h",
|
||||
"skia/dl_sk_types.h",
|
||||
"utils/dl_bounds_accumulator.cc",
|
||||
"utils/dl_bounds_accumulator.h",
|
||||
"utils/dl_accumulation_rect.cc",
|
||||
"utils/dl_accumulation_rect.h",
|
||||
"utils/dl_matrix_clip_tracker.cc",
|
||||
"utils/dl_matrix_clip_tracker.h",
|
||||
"utils/dl_receiver_utils.cc",
|
||||
|
||||
@ -102,7 +102,7 @@ TEST(DisplayListComplexity, StrokeWidth) {
|
||||
auto display_list_stroke_0 = builder_stroke_0.Build();
|
||||
|
||||
DisplayListBuilder builder_stroke_1;
|
||||
builder_stroke_0.DrawLine(SkPoint::Make(0, 0), SkPoint::Make(100, 100),
|
||||
builder_stroke_1.DrawLine(SkPoint::Make(0, 0), SkPoint::Make(100, 100),
|
||||
DlPaint().setStrokeWidth(1.0f));
|
||||
auto display_list_stroke_1 = builder_stroke_1.Build();
|
||||
|
||||
|
||||
@ -532,13 +532,13 @@ TEST_F(DisplayListTest, UnclippedSaveLayerContentAccountsForFilter) {
|
||||
builder.Restore();
|
||||
auto display_list = builder.Build();
|
||||
|
||||
ASSERT_EQ(display_list->op_count(), 6u);
|
||||
EXPECT_EQ(display_list->op_count(), 6u);
|
||||
EXPECT_EQ(display_list->total_depth(), 2u);
|
||||
|
||||
SkRect result_rect = draw_rect.makeOutset(30.0f, 30.0f);
|
||||
ASSERT_TRUE(result_rect.intersect(clip_rect));
|
||||
ASSERT_EQ(result_rect, SkRect::MakeLTRB(100.0f, 110.0f, 131.0f, 190.0f));
|
||||
ASSERT_EQ(display_list->bounds(), result_rect);
|
||||
EXPECT_EQ(display_list->bounds(), result_rect);
|
||||
}
|
||||
|
||||
TEST_F(DisplayListTest, ClippedSaveLayerContentAccountsForFilter) {
|
||||
@ -565,13 +565,72 @@ TEST_F(DisplayListTest, ClippedSaveLayerContentAccountsForFilter) {
|
||||
builder.Restore();
|
||||
auto display_list = builder.Build();
|
||||
|
||||
ASSERT_EQ(display_list->op_count(), 6u);
|
||||
EXPECT_EQ(display_list->op_count(), 6u);
|
||||
EXPECT_EQ(display_list->total_depth(), 2u);
|
||||
|
||||
SkRect result_rect = draw_rect.makeOutset(30.0f, 30.0f);
|
||||
ASSERT_TRUE(result_rect.intersect(clip_rect));
|
||||
ASSERT_EQ(result_rect, SkRect::MakeLTRB(100.0f, 110.0f, 129.0f, 190.0f));
|
||||
ASSERT_EQ(display_list->bounds(), result_rect);
|
||||
EXPECT_EQ(display_list->bounds(), result_rect);
|
||||
}
|
||||
|
||||
TEST_F(DisplayListTest, OOBSaveLayerContentCulledWithBlurFilter) {
|
||||
SkRect cull_rect = SkRect::MakeLTRB(100.0f, 100.0f, 200.0f, 200.0f);
|
||||
SkRect draw_rect = SkRect::MakeLTRB(25.0f, 25.0f, 99.0f, 75.0f);
|
||||
auto filter = DlBlurImageFilter::Make(10.0f, 10.0f, DlTileMode::kDecal);
|
||||
DlPaint layer_paint = DlPaint().setImageFilter(filter);
|
||||
|
||||
// We want a draw rect that is outside the layer bounds even though its
|
||||
// filtered output might be inside. The drawn rect should be culled by
|
||||
// the expectations of the layer bounds even though it is close enough
|
||||
// to be visible due to filtering.
|
||||
ASSERT_FALSE(cull_rect.intersects(draw_rect));
|
||||
SkRect mapped_rect;
|
||||
ASSERT_TRUE(filter->map_local_bounds(draw_rect, mapped_rect));
|
||||
ASSERT_TRUE(mapped_rect.intersects(cull_rect));
|
||||
|
||||
DisplayListBuilder builder;
|
||||
builder.SaveLayer(&cull_rect, &layer_paint);
|
||||
{ //
|
||||
builder.DrawRect(draw_rect, DlPaint());
|
||||
}
|
||||
builder.Restore();
|
||||
auto display_list = builder.Build();
|
||||
|
||||
EXPECT_EQ(display_list->op_count(), 2u);
|
||||
EXPECT_EQ(display_list->total_depth(), 1u);
|
||||
|
||||
EXPECT_TRUE(display_list->bounds().isEmpty()) << display_list->bounds();
|
||||
}
|
||||
|
||||
TEST_F(DisplayListTest, OOBSaveLayerContentCulledWithMatrixFilter) {
|
||||
SkRect cull_rect = SkRect::MakeLTRB(100.0f, 100.0f, 200.0f, 200.0f);
|
||||
SkRect draw_rect = SkRect::MakeLTRB(25.0f, 125.0f, 75.0f, 175.0f);
|
||||
auto filter = DlMatrixImageFilter::Make(SkMatrix::Translate(100.0f, 0.0f),
|
||||
DlImageSampling::kLinear);
|
||||
DlPaint layer_paint = DlPaint().setImageFilter(filter);
|
||||
|
||||
// We want a draw rect that is outside the layer bounds even though its
|
||||
// filtered output might be inside. The drawn rect should be culled by
|
||||
// the expectations of the layer bounds even though it is close enough
|
||||
// to be visible due to filtering.
|
||||
ASSERT_FALSE(cull_rect.intersects(draw_rect));
|
||||
SkRect mapped_rect;
|
||||
ASSERT_TRUE(filter->map_local_bounds(draw_rect, mapped_rect));
|
||||
ASSERT_TRUE(mapped_rect.intersects(cull_rect));
|
||||
|
||||
DisplayListBuilder builder;
|
||||
builder.SaveLayer(&cull_rect, &layer_paint);
|
||||
{ //
|
||||
builder.DrawRect(draw_rect, DlPaint());
|
||||
}
|
||||
builder.Restore();
|
||||
auto display_list = builder.Build();
|
||||
|
||||
EXPECT_EQ(display_list->op_count(), 2u);
|
||||
EXPECT_EQ(display_list->total_depth(), 1u);
|
||||
|
||||
EXPECT_TRUE(display_list->bounds().isEmpty()) << display_list->bounds();
|
||||
}
|
||||
|
||||
TEST_F(DisplayListTest, SingleOpSizes) {
|
||||
@ -1144,14 +1203,33 @@ TEST_F(DisplayListTest, SingleOpsMightSupportGroupOpacityBlendMode) {
|
||||
|
||||
TEST_F(DisplayListTest, OverlappingOpsDoNotSupportGroupOpacity) {
|
||||
DisplayListBuilder builder;
|
||||
DlOpReceiver& receiver = ToReceiver(builder);
|
||||
for (int i = 0; i < 10; i++) {
|
||||
receiver.drawRect(SkRect::MakeXYWH(i * 10, 0, 30, 30));
|
||||
builder.DrawRect(SkRect::MakeXYWH(i * 10, 0, 30, 30), DlPaint());
|
||||
}
|
||||
auto display_list = builder.Build();
|
||||
EXPECT_FALSE(display_list->can_apply_group_opacity());
|
||||
}
|
||||
|
||||
TEST_F(DisplayListTest, LineOfNonOverlappingOpsSupportGroupOpacity) {
|
||||
DisplayListBuilder builder;
|
||||
for (int i = 0; i < 10; i++) {
|
||||
builder.DrawRect(SkRect::MakeXYWH(i * 30, 0, 30, 30), DlPaint());
|
||||
}
|
||||
auto display_list = builder.Build();
|
||||
EXPECT_TRUE(display_list->can_apply_group_opacity());
|
||||
}
|
||||
|
||||
TEST_F(DisplayListTest, CrossOfNonOverlappingOpsSupportGroupOpacity) {
|
||||
DisplayListBuilder builder;
|
||||
builder.DrawRect(SkRect::MakeLTRB(200, 200, 300, 300), DlPaint()); // center
|
||||
builder.DrawRect(SkRect::MakeLTRB(100, 200, 200, 300), DlPaint()); // left
|
||||
builder.DrawRect(SkRect::MakeLTRB(200, 100, 300, 200), DlPaint()); // above
|
||||
builder.DrawRect(SkRect::MakeLTRB(300, 200, 400, 300), DlPaint()); // right
|
||||
builder.DrawRect(SkRect::MakeLTRB(200, 300, 300, 400), DlPaint()); // below
|
||||
auto display_list = builder.Build();
|
||||
EXPECT_TRUE(display_list->can_apply_group_opacity());
|
||||
}
|
||||
|
||||
TEST_F(DisplayListTest, SaveLayerFalseSupportsGroupOpacityOverlappingChidren) {
|
||||
DisplayListBuilder builder;
|
||||
DlOpReceiver& receiver = ToReceiver(builder);
|
||||
@ -1248,7 +1326,8 @@ class SaveLayerOptionsExpector : public virtual DlOpReceiver,
|
||||
void saveLayer(const SkRect& bounds,
|
||||
const SaveLayerOptions options,
|
||||
const DlImageFilter* backdrop) override {
|
||||
EXPECT_EQ(options, expected_[save_layer_count_]);
|
||||
EXPECT_EQ(options, expected_[save_layer_count_])
|
||||
<< "index " << save_layer_count_;
|
||||
save_layer_count_++;
|
||||
}
|
||||
|
||||
@ -3858,7 +3937,7 @@ TEST_F(DisplayListTest, SaveContentDepthTest) {
|
||||
|
||||
builder.Save(); // covers depth 1->9
|
||||
{
|
||||
builder.Translate(5, 5);
|
||||
builder.Translate(5, 5); // triggers deferred save at depth 1
|
||||
builder.DrawRect({10, 10, 20, 20}, DlPaint()); // depth 2
|
||||
|
||||
builder.DrawDisplayList(child, 1.0f); // depth 3 (content) + 4 (self)
|
||||
@ -3868,12 +3947,12 @@ TEST_F(DisplayListTest, SaveContentDepthTest) {
|
||||
builder.DrawRect({12, 12, 22, 22}, DlPaint()); // depth 5
|
||||
builder.DrawRect({14, 14, 24, 24}, DlPaint()); // depth 6
|
||||
}
|
||||
builder.Restore(); // layer is restored with depth 7
|
||||
builder.Restore(); // layer is restored with depth 6
|
||||
|
||||
builder.DrawRect({16, 16, 26, 26}, DlPaint()); // depth 8
|
||||
builder.DrawRect({18, 18, 28, 28}, DlPaint()); // depth 9
|
||||
}
|
||||
builder.Restore();
|
||||
builder.Restore(); // save is restored with depth 9
|
||||
|
||||
builder.DrawRect({16, 16, 26, 26}, DlPaint()); // depth 10
|
||||
builder.DrawRect({18, 18, 28, 28}, DlPaint()); // depth 11
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -13,8 +13,9 @@
|
||||
#include "flutter/display_list/dl_paint.h"
|
||||
#include "flutter/display_list/dl_sampling_options.h"
|
||||
#include "flutter/display_list/effects/dl_path_effect.h"
|
||||
#include "flutter/display_list/geometry/dl_geometry_types.h"
|
||||
#include "flutter/display_list/image/dl_image.h"
|
||||
#include "flutter/display_list/utils/dl_bounds_accumulator.h"
|
||||
#include "flutter/display_list/utils/dl_accumulation_rect.h"
|
||||
#include "flutter/display_list/utils/dl_comparable.h"
|
||||
#include "flutter/display_list/utils/dl_matrix_clip_tracker.h"
|
||||
#include "flutter/fml/macros.h"
|
||||
@ -55,7 +56,7 @@ class DisplayListBuilder final : public virtual DlCanvas,
|
||||
// |DlCanvas|
|
||||
void Restore() override;
|
||||
// |DlCanvas|
|
||||
int GetSaveCount() const override { return layer_stack_.size(); }
|
||||
int GetSaveCount() const override { return save_stack_.size(); }
|
||||
// |DlCanvas|
|
||||
void RestoreToCount(int restore_count) override;
|
||||
|
||||
@ -104,13 +105,13 @@ class DisplayListBuilder final : public virtual DlCanvas,
|
||||
/// save stack.
|
||||
// |DlCanvas|
|
||||
SkM44 GetTransformFullPerspective() const override {
|
||||
return tracker_.matrix_4x4();
|
||||
return global_state().matrix_4x4();
|
||||
}
|
||||
/// Returns the 3x3 partial perspective transform representing all transform
|
||||
/// operations executed so far in this DisplayList within the enclosing
|
||||
/// save stack.
|
||||
// |DlCanvas|
|
||||
SkMatrix GetTransform() const override { return tracker_.matrix_3x3(); }
|
||||
SkMatrix GetTransform() const override { return global_state().matrix_3x3(); }
|
||||
|
||||
// |DlCanvas|
|
||||
void ClipRect(const SkRect& rect,
|
||||
@ -130,14 +131,14 @@ class DisplayListBuilder final : public virtual DlCanvas,
|
||||
/// be rendered.
|
||||
// |DlCanvas|
|
||||
SkRect GetDestinationClipBounds() const override {
|
||||
return tracker_.device_cull_rect();
|
||||
return global_state().device_cull_rect();
|
||||
}
|
||||
/// Conservative estimate of the bounds of all outstanding clip operations
|
||||
/// transformed into the local coordinate space in which currently
|
||||
/// recorded rendering operations are interpreted.
|
||||
// |DlCanvas|
|
||||
SkRect GetLocalClipBounds() const override {
|
||||
return tracker_.local_cull_rect();
|
||||
return global_state().local_cull_rect();
|
||||
}
|
||||
|
||||
/// Return true iff the supplied bounds are easily shown to be outside
|
||||
@ -504,114 +505,174 @@ class DisplayListBuilder final : public virtual DlCanvas,
|
||||
template <typename T, typename... Args>
|
||||
void* Push(size_t extra, Args&&... args);
|
||||
|
||||
void intersect(const SkRect& rect);
|
||||
|
||||
// kInvalidSigma is used to indicate that no MaskBlur is currently set.
|
||||
static constexpr SkScalar kInvalidSigma = 0.0;
|
||||
static bool mask_sigma_valid(SkScalar sigma) {
|
||||
return std::isfinite(sigma) && sigma > 0.0;
|
||||
}
|
||||
|
||||
class SaveInfo {
|
||||
public:
|
||||
explicit SaveInfo(size_t save_offset = 0, uint32_t start_depth = 0)
|
||||
: save_offset_(save_offset), start_depth_(start_depth) {}
|
||||
|
||||
// The offset into the memory buffer where the save DLOp record
|
||||
// for this save() call is placed. This may be needed if the
|
||||
// eventual restore() call has discovered important information about
|
||||
// the records inside the saveLayer that may impact how the saveLayer
|
||||
// is handled (e.g., |cannot_inherit_opacity| == false).
|
||||
// This offset is only valid if |has_layer| is true.
|
||||
size_t save_offset() const { return save_offset_; }
|
||||
|
||||
bool is_save_layer() const { return is_save_layer_; }
|
||||
bool cannot_inherit_opacity() const { return cannot_inherit_opacity_; }
|
||||
bool has_compatible_op() const { return has_compatible_op_; }
|
||||
bool affects_transparent_layer() const {
|
||||
return affects_transparent_layer_;
|
||||
}
|
||||
|
||||
bool is_group_opacity_compatible() const {
|
||||
return !cannot_inherit_opacity_;
|
||||
}
|
||||
|
||||
void mark_incompatible() { cannot_inherit_opacity_ = true; }
|
||||
|
||||
// For now this only allows a single compatible op to mark the
|
||||
// layer as being compatible with group opacity. If we start
|
||||
// computing bounds of ops in the Builder methods then we
|
||||
// can upgrade this to checking for overlapping ops.
|
||||
// See https://github.com/flutter/flutter/issues/93899
|
||||
void add_compatible_op() {
|
||||
if (!cannot_inherit_opacity_) {
|
||||
if (has_compatible_op_) {
|
||||
cannot_inherit_opacity_ = true;
|
||||
} else {
|
||||
has_compatible_op_ = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Records that the current layer contains an op that produces visible
|
||||
// output on a transparent surface.
|
||||
void add_visible_op() {
|
||||
affects_transparent_layer_ = true;
|
||||
}
|
||||
|
||||
// The filter to apply to the layer bounds when it is restored
|
||||
std::shared_ptr<const DlImageFilter> filter() { return filter_; }
|
||||
|
||||
// is_unbounded should be set to true if we ever encounter an operation
|
||||
// on a layer that either is unrestricted (|drawColor| or |drawPaint|)
|
||||
// or cannot compute its bounds (some effects and filters) and there
|
||||
// was no outstanding clip op at the time.
|
||||
// When the layer is restored, the outer layer may then process this
|
||||
// unbounded state by accumulating its own clip or transferring the
|
||||
// unbounded state to its own outer layer.
|
||||
// Typically the DisplayList will have been constructed with a cull
|
||||
// rect which will act as a default clip for the outermost layer and
|
||||
// the unbounded state of all sub layers will eventually be caught by
|
||||
// that cull rect so that the overall unbounded state of the entire
|
||||
// DisplayList will never be true.
|
||||
//
|
||||
// For historical consistency it is worth noting that SkPicture used
|
||||
// to treat these same conditions as a Nop (they accumulate the
|
||||
// SkPicture cull rect, but if no cull rect was specified then it is
|
||||
// an empty Rect and so has no effect on the bounds).
|
||||
//
|
||||
// Flutter is unlikely to ever run into this as the Dart mechanisms
|
||||
// all supply a non-null cull rect for all Dart Picture objects,
|
||||
// even if that cull rect is kGiantRect.
|
||||
void set_unbounded() { is_unbounded_ = true; }
|
||||
|
||||
// |is_unbounded| should be called after |getLayerBounds| in case
|
||||
// a problem was found during the computation of those bounds,
|
||||
// the layer will have one last chance to flag an unbounded state.
|
||||
bool is_unbounded() const { return is_unbounded_; }
|
||||
|
||||
private:
|
||||
size_t save_offset_;
|
||||
uint32_t start_depth_;
|
||||
bool is_save_layer_ = false;
|
||||
bool cannot_inherit_opacity_ = false;
|
||||
bool has_compatible_op_ = false;
|
||||
std::shared_ptr<const DlImageFilter> filter_;
|
||||
bool is_unbounded_ = false;
|
||||
bool has_deferred_save_op_ = false;
|
||||
bool is_nop_ = false;
|
||||
bool affects_transparent_layer_ = false;
|
||||
std::shared_ptr<BoundsAccumulator> layer_accumulator_;
|
||||
|
||||
friend class DisplayListBuilder;
|
||||
struct RTreeData {
|
||||
std::vector<SkRect> rects;
|
||||
std::vector<int> indices;
|
||||
};
|
||||
|
||||
std::vector<SaveInfo> layer_stack_;
|
||||
SaveInfo* current_layer_;
|
||||
DisplayListMatrixClipTracker tracker_;
|
||||
std::unique_ptr<DisplayListMatrixClipTracker> layer_tracker_;
|
||||
std::unique_ptr<BoundsAccumulator> accumulator_;
|
||||
BoundsAccumulator* accumulator() { return accumulator_.get(); }
|
||||
// The SaveInfo class stores internal data for both Save and SaveLayer calls
|
||||
class SaveInfo {
|
||||
public:
|
||||
// For vector reallocation calls to copy vector data
|
||||
SaveInfo(const SaveInfo& copy) = default;
|
||||
SaveInfo(SaveInfo&& copy) = default;
|
||||
|
||||
// For constructor (root layer) initialization
|
||||
explicit SaveInfo(const DlRect& cull_rect)
|
||||
: is_root_layer(true),
|
||||
is_save_layer(true),
|
||||
global_state(cull_rect),
|
||||
layer_state(cull_rect),
|
||||
layer_local_accumulator(new AccumulationRect()) {}
|
||||
|
||||
// For regular save calls:
|
||||
// Passing a pointer to the parent_info so as to distinguish this
|
||||
// call from the copy constructor used above in vector reallocations
|
||||
explicit SaveInfo(const SaveInfo* parent_info)
|
||||
: is_root_layer(false),
|
||||
is_save_layer(false),
|
||||
has_deferred_save_op(true),
|
||||
global_state(parent_info->global_state),
|
||||
layer_state(parent_info->layer_state),
|
||||
global_space_accumulator(parent_info->global_space_accumulator),
|
||||
layer_local_accumulator(parent_info->layer_local_accumulator) {}
|
||||
|
||||
// For saveLayer calls:
|
||||
explicit SaveInfo(const SaveInfo* parent_info,
|
||||
const std::shared_ptr<const DlImageFilter>& filter,
|
||||
int rtree_rect_index)
|
||||
: is_root_layer(false),
|
||||
is_save_layer(true),
|
||||
rtree_rects_start_index(rtree_rect_index),
|
||||
global_state(parent_info->global_state),
|
||||
layer_state(parent_info->global_state.local_cull_rect()),
|
||||
global_space_accumulator(parent_info->global_space_accumulator),
|
||||
layer_local_accumulator(new AccumulationRect()),
|
||||
filter(filter) {}
|
||||
|
||||
bool is_group_opacity_compatible() const {
|
||||
return !opacity_incompatible_op_detected &&
|
||||
!layer_local_accumulator->overlap_detected();
|
||||
}
|
||||
|
||||
// Records the given bounds after transforming by the global and
|
||||
// layer matrices.
|
||||
bool AccumulateBoundsLocal(const SkRect& bounds);
|
||||
|
||||
// Simply transfers the local bounds to the parent
|
||||
void TransferBoundsToParent(const SaveInfo& parent);
|
||||
|
||||
const bool is_root_layer;
|
||||
const bool is_save_layer;
|
||||
|
||||
bool has_deferred_save_op = false;
|
||||
bool is_nop = false;
|
||||
bool opacity_incompatible_op_detected = false;
|
||||
bool is_unbounded = false;
|
||||
bool affects_transparent_layer = false;
|
||||
|
||||
// The offset into the buffer where the associated save op is recorded
|
||||
// (which is not necessarily the same as when the Save() method is called)
|
||||
size_t save_offset = 0;
|
||||
// The depth when the save call is recorded, used to compute the total
|
||||
// depth of its content when the associated restore is called.
|
||||
uint32_t save_depth = 0;
|
||||
|
||||
// The index of the rtree rects when the saveLayer was called, used
|
||||
// only in the case that the saveLayer has a filter so that the
|
||||
// accumulated rects can be updated in the corresponding restore call.
|
||||
const size_t rtree_rects_start_index = 0;
|
||||
|
||||
// The transform and clip accumulated since the root of the DisplayList
|
||||
DisplayListMatrixClipState global_state;
|
||||
|
||||
// The transform and clip accumulated since the most recent saveLayer,
|
||||
// used to compute and update its bounds when the restore is called.
|
||||
DisplayListMatrixClipState layer_state;
|
||||
|
||||
// Not every layer needs its own accumulator(s). In particular, the
|
||||
// global accumulator is only used if we are not construting an rtree.
|
||||
// Regular save calls will share both accumulators with their parent.
|
||||
// Additionally, a saveLayer will separate its global accumulator from
|
||||
// its parent (if not constructing an rtree) when it has a filter which
|
||||
// requires it to post-adjust the bounds accumulated while recording
|
||||
// its content. Finally, every saveLayer has its own local accumulator.
|
||||
//
|
||||
// All accumulations could occur in the local layer space, and then be
|
||||
// transformed and accumulated into the parent as each layer is restored,
|
||||
// but that technique would compound the bounds errors that happen when
|
||||
// a list of transforms is performed serially on a rectangle (mainly
|
||||
// when multiple rotation or skew transforms are involved).
|
||||
|
||||
// The bounds accumulator for the entire DisplayList, relative to its root
|
||||
std::shared_ptr<AccumulationRect> global_space_accumulator;
|
||||
|
||||
// The bounds accumulator to set/verify the bounds of the most recently
|
||||
// invoked saveLayer call, relative to the root of that saveLayer
|
||||
std::shared_ptr<AccumulationRect> layer_local_accumulator;
|
||||
|
||||
// The filter that will be applied to the contents of the saveLayer
|
||||
// when it is restored into the parent layer.
|
||||
const std::shared_ptr<const DlImageFilter> filter;
|
||||
};
|
||||
|
||||
const DlRect original_cull_rect_;
|
||||
std::vector<SaveInfo> save_stack_;
|
||||
std::optional<RTreeData> rtree_data_;
|
||||
|
||||
DlPaint current_;
|
||||
|
||||
// Returns a reference to the SaveInfo structure at the top of the current
|
||||
// save_stack state. Note that the clip and matrix state can be accessed
|
||||
// more directly through global_state() and layer_state().
|
||||
SaveInfo& current_info() { return save_stack_.back(); }
|
||||
const SaveInfo& current_info() const { return save_stack_.back(); }
|
||||
|
||||
// Returns a reference to the SaveInfo structure just below the top
|
||||
// of the current save_stack state.
|
||||
SaveInfo& parent_info() { return *std::prev(save_stack_.end(), 2); }
|
||||
const SaveInfo& parent_info() const {
|
||||
return *std::prev(save_stack_.end(), 2);
|
||||
}
|
||||
|
||||
// Returns a reference to the matrix and clip state for the entire
|
||||
// DisplayList. The initial transform of this state is identity and
|
||||
// the initial cull_rect is the root original_cull_rect supplied
|
||||
// in the constructor. It is a summary of all transform and clip
|
||||
// calls that have happened since the DisplayList was created
|
||||
// (and have not yet been removed by a restore() call).
|
||||
DisplayListMatrixClipState& global_state() {
|
||||
return current_info().global_state;
|
||||
}
|
||||
const DisplayListMatrixClipState& global_state() const {
|
||||
return current_info().global_state;
|
||||
}
|
||||
|
||||
// Returns a reference to the matrix and clip state relative to the
|
||||
// current layer, whether that is defined by the most recent saveLayer
|
||||
// call, or by the initial root state of the entire DisplayList for
|
||||
// calls not surrounded by a saveLayer/restore pair. It is a summary
|
||||
// of only those transform and clip calls that have happened since
|
||||
// the creation of the DisplayList or since the most recent saveLayer
|
||||
// (and have not yet been removed by a restore() call).
|
||||
DisplayListMatrixClipState& layer_local_state() {
|
||||
return current_info().layer_state;
|
||||
}
|
||||
const DisplayListMatrixClipState& layer_local_state() const {
|
||||
return current_info().layer_state;
|
||||
}
|
||||
|
||||
void RestoreLayer(const SaveInfo& current_info,
|
||||
SaveInfo& parent_info,
|
||||
void* base_op);
|
||||
void TransferLayerBounds(const SaveInfo& current_info,
|
||||
SaveInfo& parent_info,
|
||||
const SkRect& content_bounds);
|
||||
bool AdjustRTreeRects(RTreeData& data,
|
||||
const DlImageFilter& filter,
|
||||
const SkMatrix& matrix,
|
||||
const SkRect& clip,
|
||||
size_t rect_index);
|
||||
|
||||
// This flag indicates whether or not the current rendering attributes
|
||||
// are compatible with rendering ops applying an inherited opacity.
|
||||
@ -637,10 +698,8 @@ class DisplayListBuilder final : public virtual DlCanvas,
|
||||
// Update the opacity compatibility flags of the current layer for an op
|
||||
// that has determined its compatibility as indicated by |compatible|.
|
||||
void UpdateLayerOpacityCompatibility(bool compatible) {
|
||||
if (compatible) {
|
||||
current_layer_->add_compatible_op();
|
||||
} else {
|
||||
current_layer_->mark_incompatible();
|
||||
if (!compatible) {
|
||||
current_info().opacity_incompatible_op_detected = true;
|
||||
}
|
||||
}
|
||||
|
||||
@ -683,38 +742,6 @@ class DisplayListBuilder final : public virtual DlCanvas,
|
||||
void onSetPathEffect(const DlPathEffect* effect);
|
||||
void onSetMaskFilter(const DlMaskFilter* filter);
|
||||
|
||||
// The DisplayList had an unbounded call with no cull rect or clip
|
||||
// to contain it. Should only be called after the stream is fully
|
||||
// built.
|
||||
// Unbounded operations are calls like |drawColor| which are defined
|
||||
// to flood the entire surface, or calls that relied on a rendering
|
||||
// attribute which is unable to compute bounds (should be rare).
|
||||
// In those cases the bounds will represent only the accumulation
|
||||
// of the bounded calls and this flag will be set to indicate that
|
||||
// condition.
|
||||
bool is_unbounded() const {
|
||||
FML_DCHECK(layer_stack_.size() == 1);
|
||||
return layer_stack_.front().is_unbounded();
|
||||
}
|
||||
|
||||
SkRect bounds() const {
|
||||
FML_DCHECK(layer_stack_.size() == 1);
|
||||
if (is_unbounded()) {
|
||||
FML_LOG(INFO) << "returning partial bounds for unbounded DisplayList";
|
||||
}
|
||||
|
||||
return accumulator_->bounds();
|
||||
}
|
||||
|
||||
sk_sp<DlRTree> rtree() {
|
||||
FML_DCHECK(layer_stack_.size() == 1);
|
||||
if (is_unbounded()) {
|
||||
FML_LOG(INFO) << "returning partial rtree for unbounded DisplayList";
|
||||
}
|
||||
|
||||
return accumulator_->rtree();
|
||||
}
|
||||
|
||||
static DisplayListAttributeFlags FlagsForPointMode(PointMode mode);
|
||||
|
||||
enum class OpResult {
|
||||
@ -733,7 +760,7 @@ class DisplayListBuilder final : public virtual DlCanvas,
|
||||
case OpResult::kPreservesTransparency:
|
||||
break;
|
||||
case OpResult::kAffectsAll:
|
||||
current_layer_->add_visible_op();
|
||||
current_info().affects_transparent_layer = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -752,7 +779,10 @@ class DisplayListBuilder final : public virtual DlCanvas,
|
||||
|
||||
// Records the fact that we encountered an op that either could not
|
||||
// estimate its bounds or that fills all of the destination space.
|
||||
bool AccumulateUnbounded();
|
||||
bool AccumulateUnbounded(SaveInfo& layer);
|
||||
bool AccumulateUnbounded() {
|
||||
return AccumulateUnbounded(current_info());
|
||||
}
|
||||
|
||||
// Records the bounds for an op after modifying them according to the
|
||||
// supplied attribute flags and transforming by the current matrix.
|
||||
@ -769,9 +799,10 @@ class DisplayListBuilder final : public virtual DlCanvas,
|
||||
|
||||
// Records the given bounds after transforming by the current matrix
|
||||
// and clipping against the current clip.
|
||||
bool AccumulateBounds(SkRect& bounds);
|
||||
|
||||
DlPaint current_;
|
||||
bool AccumulateBounds(const SkRect& bounds, SaveInfo& layer, int id);
|
||||
bool AccumulateBounds(const SkRect& bounds) {
|
||||
return AccumulateBounds(bounds, current_info(), op_index_);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace flutter
|
||||
|
||||
@ -4,7 +4,7 @@
|
||||
|
||||
#include "flutter/display_list/dl_vertices.h"
|
||||
|
||||
#include "flutter/display_list/utils/dl_bounds_accumulator.h"
|
||||
#include "flutter/display_list/utils/dl_accumulation_rect.h"
|
||||
#include "flutter/fml/logging.h"
|
||||
|
||||
namespace flutter {
|
||||
@ -86,7 +86,7 @@ size_t DlVertices::size() const {
|
||||
}
|
||||
|
||||
static SkRect compute_bounds(const SkPoint* points, int count) {
|
||||
RectBoundsAccumulator accumulator;
|
||||
AccumulationRect accumulator;
|
||||
for (int i = 0; i < count; i++) {
|
||||
accumulator.accumulate(points[i]);
|
||||
}
|
||||
|
||||
72
engine/src/flutter/display_list/geometry/dl_geometry_types.h
Normal file
72
engine/src/flutter/display_list/geometry/dl_geometry_types.h
Normal file
@ -0,0 +1,72 @@
|
||||
// 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_DISPLAY_LIST_GEOMETRY_DL_GEOMETRY_TYPES_H_
|
||||
#define FLUTTER_DISPLAY_LIST_GEOMETRY_DL_GEOMETRY_TYPES_H_
|
||||
|
||||
#include "flutter/impeller/geometry/matrix.h"
|
||||
#include "flutter/impeller/geometry/rect.h"
|
||||
#include "flutter/impeller/geometry/scalar.h"
|
||||
|
||||
#include "flutter/third_party/skia/include/core/SkM44.h"
|
||||
#include "flutter/third_party/skia/include/core/SkMatrix.h"
|
||||
#include "flutter/third_party/skia/include/core/SkRect.h"
|
||||
#include "flutter/third_party/skia/include/core/SkSize.h"
|
||||
|
||||
namespace flutter {
|
||||
|
||||
using DlScalar = impeller::Scalar;
|
||||
using DlDegrees = impeller::Degrees;
|
||||
using DlRadians = impeller::Radians;
|
||||
|
||||
using DlISize = impeller::ISize32;
|
||||
using DlSize = impeller::Size;
|
||||
using DlRect = impeller::Rect;
|
||||
using DlIRect = impeller::IRect32;
|
||||
using DlMatrix = impeller::Matrix;
|
||||
|
||||
static_assert(sizeof(SkRect) == sizeof(DlRect));
|
||||
|
||||
inline const DlRect& ToDlRect(const SkRect& rect) {
|
||||
return *reinterpret_cast<const DlRect*>(&rect);
|
||||
}
|
||||
|
||||
inline constexpr DlMatrix ToDlMatrix(const SkMatrix& matrix) {
|
||||
// clang-format off
|
||||
return DlMatrix::MakeColumn(
|
||||
matrix[SkMatrix::kMScaleX], matrix[SkMatrix::kMSkewY], 0.0f, matrix[SkMatrix::kMPersp0],
|
||||
matrix[SkMatrix::kMSkewX], matrix[SkMatrix::kMScaleY], 0.0f, matrix[SkMatrix::kMPersp1],
|
||||
0.0f, 0.0f, 1.0f, 0.0f,
|
||||
matrix[SkMatrix::kMTransX], matrix[SkMatrix::kMTransY], 0.0f, matrix[SkMatrix::kMPersp2]
|
||||
);
|
||||
// clang-format on
|
||||
}
|
||||
|
||||
inline constexpr DlMatrix ToDlMatrix(const SkM44& matrix) {
|
||||
DlMatrix dl_matrix;
|
||||
matrix.getColMajor(dl_matrix.m);
|
||||
return dl_matrix;
|
||||
}
|
||||
|
||||
inline const SkRect& ToSkRect(const DlRect& rect) {
|
||||
return *reinterpret_cast<const SkRect*>(&rect);
|
||||
}
|
||||
|
||||
inline const SkISize& ToSkISize(const DlISize& size) {
|
||||
return *reinterpret_cast<const SkISize*>(&size);
|
||||
}
|
||||
|
||||
inline constexpr SkMatrix ToSkMatrix(const DlMatrix& matrix) {
|
||||
return SkMatrix::MakeAll(matrix.m[0], matrix.m[4], matrix.m[12], //
|
||||
matrix.m[1], matrix.m[5], matrix.m[13], //
|
||||
matrix.m[3], matrix.m[7], matrix.m[15]);
|
||||
}
|
||||
|
||||
inline constexpr SkM44 ToSkM44(const DlMatrix& matrix) {
|
||||
return SkM44::ColMajor(matrix.m);
|
||||
}
|
||||
|
||||
} // namespace flutter
|
||||
|
||||
#endif // FLUTTER_DISPLAY_LIST_GEOMETRY_DL_GEOMETRY_TYPES_H_
|
||||
@ -0,0 +1,67 @@
|
||||
// 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/display_list/utils/dl_accumulation_rect.h"
|
||||
|
||||
namespace flutter {
|
||||
|
||||
void AccumulationRect::accumulate(SkScalar x, SkScalar y) {
|
||||
if (!std::isfinite(x) || !std::isfinite(y)) {
|
||||
return;
|
||||
}
|
||||
if (x >= min_x_ && x < max_x_ && y >= min_y_ && y < max_y_) {
|
||||
record_overlapping_bounds();
|
||||
return;
|
||||
}
|
||||
if (min_x_ > x) {
|
||||
min_x_ = x;
|
||||
}
|
||||
if (min_y_ > y) {
|
||||
min_y_ = y;
|
||||
}
|
||||
if (max_x_ < x) {
|
||||
max_x_ = x;
|
||||
}
|
||||
if (max_y_ < y) {
|
||||
max_y_ = y;
|
||||
}
|
||||
}
|
||||
|
||||
void AccumulationRect::accumulate(SkRect r) {
|
||||
if (r.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
if (r.fLeft < max_x_ && r.fRight > min_x_ && //
|
||||
r.fTop < max_y_ && r.fBottom > min_y_) {
|
||||
record_overlapping_bounds();
|
||||
}
|
||||
if (min_x_ > r.fLeft) {
|
||||
min_x_ = r.fLeft;
|
||||
}
|
||||
if (min_y_ > r.fTop) {
|
||||
min_y_ = r.fTop;
|
||||
}
|
||||
if (max_x_ < r.fRight) {
|
||||
max_x_ = r.fRight;
|
||||
}
|
||||
if (max_y_ < r.fBottom) {
|
||||
max_y_ = r.fBottom;
|
||||
}
|
||||
}
|
||||
|
||||
SkRect AccumulationRect::bounds() const {
|
||||
return (max_x_ >= min_x_ && max_y_ >= min_y_)
|
||||
? SkRect::MakeLTRB(min_x_, min_y_, max_x_, max_y_)
|
||||
: SkRect::MakeEmpty();
|
||||
}
|
||||
|
||||
void AccumulationRect::reset() {
|
||||
min_x_ = std::numeric_limits<SkScalar>::infinity();
|
||||
min_y_ = std::numeric_limits<SkScalar>::infinity();
|
||||
max_x_ = -std::numeric_limits<SkScalar>::infinity();
|
||||
max_y_ = -std::numeric_limits<SkScalar>::infinity();
|
||||
overlap_detected_ = false;
|
||||
}
|
||||
|
||||
} // namespace flutter
|
||||
54
engine/src/flutter/display_list/utils/dl_accumulation_rect.h
Normal file
54
engine/src/flutter/display_list/utils/dl_accumulation_rect.h
Normal file
@ -0,0 +1,54 @@
|
||||
// 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_DISPLAY_LIST_UTILS_DL_ACCUMULATION_RECT_H_
|
||||
#define FLUTTER_DISPLAY_LIST_UTILS_DL_ACCUMULATION_RECT_H_
|
||||
|
||||
#include <functional>
|
||||
|
||||
#include "flutter/display_list/geometry/dl_geometry_types.h"
|
||||
#include "flutter/display_list/geometry/dl_rtree.h"
|
||||
#include "flutter/fml/logging.h"
|
||||
|
||||
namespace flutter {
|
||||
|
||||
// Utility class to collect bounds from a bunch of rectangles and points
|
||||
// while also noting if there might be any overlap between any of the data
|
||||
// point/rects. Note that the overlap protection is not sophisticated,
|
||||
// simply noting if the new data intersects with the already accumulated
|
||||
// bounds. This can successfully detect non-overlap of a linear sequence
|
||||
// of non-overlapping objects, or even a cross of non-overlapping objects
|
||||
// as long as they are built out from the center in the right order. True
|
||||
// detection of non-overlapping objects would require much more time and/or
|
||||
// space.
|
||||
class AccumulationRect {
|
||||
public:
|
||||
AccumulationRect() { reset(); }
|
||||
|
||||
void accumulate(SkScalar x, SkScalar y);
|
||||
void accumulate(SkPoint p) { accumulate(p.fX, p.fY); }
|
||||
void accumulate(SkRect r);
|
||||
void accumulate(DlRect r) { accumulate(ToSkRect(r)); }
|
||||
|
||||
bool is_empty() const { return min_x_ >= max_x_ || min_y_ >= max_y_; }
|
||||
bool is_not_empty() const { return min_x_ < max_x_ && min_y_ < max_y_; }
|
||||
|
||||
SkRect bounds() const;
|
||||
|
||||
void reset();
|
||||
|
||||
bool overlap_detected() const { return overlap_detected_; }
|
||||
void record_overlapping_bounds() { overlap_detected_ = true; }
|
||||
|
||||
private:
|
||||
DlScalar min_x_;
|
||||
DlScalar min_y_;
|
||||
DlScalar max_x_;
|
||||
DlScalar max_y_;
|
||||
bool overlap_detected_;
|
||||
};
|
||||
|
||||
} // namespace flutter
|
||||
|
||||
#endif // FLUTTER_DISPLAY_LIST_UTILS_DL_ACCUMULATION_RECT_H_
|
||||
@ -1,134 +0,0 @@
|
||||
// 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/display_list/utils/dl_bounds_accumulator.h"
|
||||
|
||||
namespace flutter {
|
||||
|
||||
void RectBoundsAccumulator::accumulate(const SkRect& r, int index) {
|
||||
if (r.fLeft < r.fRight && r.fTop < r.fBottom) {
|
||||
rect_.accumulate(r.fLeft, r.fTop);
|
||||
rect_.accumulate(r.fRight, r.fBottom);
|
||||
}
|
||||
}
|
||||
|
||||
void RectBoundsAccumulator::save() {
|
||||
saved_rects_.emplace_back(rect_);
|
||||
rect_ = AccumulationRect();
|
||||
}
|
||||
void RectBoundsAccumulator::restore() {
|
||||
if (!saved_rects_.empty()) {
|
||||
SkRect layer_bounds = rect_.bounds();
|
||||
pop_and_accumulate(layer_bounds, nullptr);
|
||||
}
|
||||
}
|
||||
bool RectBoundsAccumulator::restore(
|
||||
std::function<bool(const SkRect&, SkRect&)> mapper,
|
||||
const SkRect* clip) {
|
||||
bool success = true;
|
||||
if (!saved_rects_.empty()) {
|
||||
SkRect layer_bounds = rect_.bounds();
|
||||
success = mapper(layer_bounds, layer_bounds);
|
||||
pop_and_accumulate(layer_bounds, clip);
|
||||
}
|
||||
return success;
|
||||
}
|
||||
void RectBoundsAccumulator::pop_and_accumulate(SkRect& layer_bounds,
|
||||
const SkRect* clip) {
|
||||
FML_DCHECK(!saved_rects_.empty());
|
||||
|
||||
rect_ = saved_rects_.back();
|
||||
saved_rects_.pop_back();
|
||||
|
||||
if (clip == nullptr || layer_bounds.intersect(*clip)) {
|
||||
accumulate(layer_bounds, -1);
|
||||
}
|
||||
}
|
||||
|
||||
RectBoundsAccumulator::AccumulationRect::AccumulationRect() {
|
||||
min_x_ = std::numeric_limits<SkScalar>::infinity();
|
||||
min_y_ = std::numeric_limits<SkScalar>::infinity();
|
||||
max_x_ = -std::numeric_limits<SkScalar>::infinity();
|
||||
max_y_ = -std::numeric_limits<SkScalar>::infinity();
|
||||
}
|
||||
void RectBoundsAccumulator::AccumulationRect::accumulate(SkScalar x,
|
||||
SkScalar y) {
|
||||
if (min_x_ > x) {
|
||||
min_x_ = x;
|
||||
}
|
||||
if (min_y_ > y) {
|
||||
min_y_ = y;
|
||||
}
|
||||
if (max_x_ < x) {
|
||||
max_x_ = x;
|
||||
}
|
||||
if (max_y_ < y) {
|
||||
max_y_ = y;
|
||||
}
|
||||
}
|
||||
SkRect RectBoundsAccumulator::AccumulationRect::bounds() const {
|
||||
return (max_x_ >= min_x_ && max_y_ >= min_y_)
|
||||
? SkRect::MakeLTRB(min_x_, min_y_, max_x_, max_y_)
|
||||
: SkRect::MakeEmpty();
|
||||
}
|
||||
|
||||
void RTreeBoundsAccumulator::accumulate(const SkRect& r, int index) {
|
||||
if (r.fLeft < r.fRight && r.fTop < r.fBottom) {
|
||||
rects_.push_back(r);
|
||||
rect_indices_.push_back(index);
|
||||
}
|
||||
}
|
||||
void RTreeBoundsAccumulator::save() {
|
||||
saved_offsets_.push_back(rects_.size());
|
||||
}
|
||||
void RTreeBoundsAccumulator::restore() {
|
||||
if (saved_offsets_.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
saved_offsets_.pop_back();
|
||||
}
|
||||
bool RTreeBoundsAccumulator::restore(
|
||||
std::function<bool(const SkRect& original, SkRect& modified)> map,
|
||||
const SkRect* clip) {
|
||||
if (saved_offsets_.empty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t previous_size = saved_offsets_.back();
|
||||
saved_offsets_.pop_back();
|
||||
|
||||
bool success = true;
|
||||
for (size_t i = previous_size; i < rects_.size(); i++) {
|
||||
SkRect original = rects_[i];
|
||||
if (!map(original, original)) {
|
||||
success = false;
|
||||
}
|
||||
if (clip == nullptr || original.intersect(*clip)) {
|
||||
rect_indices_[previous_size] = rect_indices_[i];
|
||||
rects_[previous_size] = original;
|
||||
previous_size++;
|
||||
}
|
||||
}
|
||||
rects_.resize(previous_size);
|
||||
rect_indices_.resize(previous_size);
|
||||
return success;
|
||||
}
|
||||
|
||||
SkRect RTreeBoundsAccumulator::bounds() const {
|
||||
FML_DCHECK(saved_offsets_.empty());
|
||||
RectBoundsAccumulator accumulator;
|
||||
for (auto& rect : rects_) {
|
||||
accumulator.accumulate(rect, 0);
|
||||
}
|
||||
return accumulator.bounds();
|
||||
}
|
||||
|
||||
sk_sp<DlRTree> RTreeBoundsAccumulator::rtree() const {
|
||||
FML_DCHECK(saved_offsets_.empty());
|
||||
return sk_make_sp<DlRTree>(rects_.data(), rects_.size(), rect_indices_.data(),
|
||||
[](int id) { return id >= 0; });
|
||||
}
|
||||
|
||||
} // namespace flutter
|
||||
@ -1,168 +0,0 @@
|
||||
// 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_DISPLAY_LIST_UTILS_DL_BOUNDS_ACCUMULATOR_H_
|
||||
#define FLUTTER_DISPLAY_LIST_UTILS_DL_BOUNDS_ACCUMULATOR_H_
|
||||
|
||||
#include <functional>
|
||||
|
||||
#include "flutter/display_list/geometry/dl_rtree.h"
|
||||
#include "flutter/fml/logging.h"
|
||||
|
||||
// This file contains various utility classes to ease implementing
|
||||
// a Flutter DisplayList DlOpReceiver, including:
|
||||
//
|
||||
// IgnoreAttributeDispatchHelper:
|
||||
// IgnoreClipDispatchHelper:
|
||||
// IgnoreTransformDispatchHelper
|
||||
// Empty overrides of all of the associated methods of DlOpReceiver
|
||||
// for receivers that only track some of the rendering operations
|
||||
|
||||
namespace flutter {
|
||||
|
||||
enum class BoundsAccumulatorType {
|
||||
kRect,
|
||||
kRTree,
|
||||
};
|
||||
|
||||
class BoundsAccumulator {
|
||||
public:
|
||||
/// function definition for modifying the bounds of a rectangle
|
||||
/// during a restore operation. The function is used primarily
|
||||
/// to account for the bounds impact of an ImageFilter on a
|
||||
/// saveLayer on a per-rect basis. The implementation may apply
|
||||
/// this function at whatever granularity it can manage easily
|
||||
/// (for example, a Rect accumulator might apply it to the entire
|
||||
/// local bounds being restored, whereas an RTree accumulator might
|
||||
/// apply it individually to each element in the local RTree).
|
||||
///
|
||||
/// The function will do a best faith attempt at determining the
|
||||
/// modified bounds and store the results in the supplied |dest|
|
||||
/// rectangle and return true. If the function is unable to
|
||||
/// accurately determine the modifed bounds, it will set the
|
||||
/// |dest| rectangle to a copy of the input bounds (or a best
|
||||
/// guess) and return false to indicate that the bounds should not
|
||||
/// be trusted.
|
||||
typedef bool BoundsModifier(const SkRect& original, SkRect* dest);
|
||||
|
||||
virtual ~BoundsAccumulator() = default;
|
||||
|
||||
virtual void accumulate(const SkRect& r, int index = 0) = 0;
|
||||
|
||||
/// Save aside the rects/bounds currently being accumulated and start
|
||||
/// accumulating a new set of rects/bounds. When restore is called,
|
||||
/// some additional modifications may be applied to these new bounds
|
||||
/// before they are accumulated back into the surrounding bounds.
|
||||
virtual void save() = 0;
|
||||
|
||||
/// Restore to the previous accumulation and incorporate the bounds of
|
||||
/// the primitives that were recorded since the last save (if needed).
|
||||
virtual void restore() = 0;
|
||||
|
||||
/// Restore the previous set of accumulation rects/bounds and accumulate
|
||||
/// the current rects/bounds that were accumulated since the most recent
|
||||
/// call to |save| into them with modifications specified by the |map|
|
||||
/// parameter and clipping to the clip parameter if it is not null.
|
||||
///
|
||||
/// The indicated map function is applied to the various rects and bounds
|
||||
/// that have been accumulated in this save/restore cycle before they
|
||||
/// are then accumulated into the previous accumulations. The granularity
|
||||
/// of the application of the map function to the rectangles that were
|
||||
/// accumulated during the save period is left up to the implementation.
|
||||
///
|
||||
/// This method will return true if the map function returned true on
|
||||
/// every single invocation. A false return value means that the
|
||||
/// bounds accumulated during this restore may not be trusted (as
|
||||
/// determined by the map function).
|
||||
///
|
||||
/// If there are no saved accumulations to restore to, this method will
|
||||
/// NOP ignoring the map function and the optional clip entirely.
|
||||
virtual bool restore(
|
||||
std::function<bool(const SkRect& original, SkRect& modified)> map,
|
||||
const SkRect* clip = nullptr) = 0;
|
||||
|
||||
virtual SkRect bounds() const = 0;
|
||||
|
||||
virtual sk_sp<DlRTree> rtree() const = 0;
|
||||
|
||||
virtual BoundsAccumulatorType type() const = 0;
|
||||
};
|
||||
|
||||
class RectBoundsAccumulator final : public virtual BoundsAccumulator {
|
||||
public:
|
||||
void accumulate(SkScalar x, SkScalar y) { rect_.accumulate(x, y); }
|
||||
void accumulate(const SkPoint& p) { rect_.accumulate(p.fX, p.fY); }
|
||||
void accumulate(const SkRect& r, int index) override;
|
||||
|
||||
bool is_empty() const { return rect_.is_empty(); }
|
||||
bool is_not_empty() const { return rect_.is_not_empty(); }
|
||||
|
||||
void save() override;
|
||||
void restore() override;
|
||||
bool restore(std::function<bool(const SkRect&, SkRect&)> mapper,
|
||||
const SkRect* clip) override;
|
||||
|
||||
SkRect bounds() const override {
|
||||
FML_DCHECK(saved_rects_.empty());
|
||||
return rect_.bounds();
|
||||
}
|
||||
|
||||
BoundsAccumulatorType type() const override {
|
||||
return BoundsAccumulatorType::kRect;
|
||||
}
|
||||
|
||||
sk_sp<DlRTree> rtree() const override { return nullptr; }
|
||||
|
||||
private:
|
||||
class AccumulationRect {
|
||||
public:
|
||||
AccumulationRect();
|
||||
|
||||
void accumulate(SkScalar x, SkScalar y);
|
||||
|
||||
bool is_empty() const { return min_x_ >= max_x_ || min_y_ >= max_y_; }
|
||||
bool is_not_empty() const { return min_x_ < max_x_ && min_y_ < max_y_; }
|
||||
|
||||
SkRect bounds() const;
|
||||
|
||||
private:
|
||||
SkScalar min_x_;
|
||||
SkScalar min_y_;
|
||||
SkScalar max_x_;
|
||||
SkScalar max_y_;
|
||||
};
|
||||
|
||||
void pop_and_accumulate(SkRect& layer_bounds, const SkRect* clip);
|
||||
|
||||
AccumulationRect rect_;
|
||||
std::vector<AccumulationRect> saved_rects_;
|
||||
};
|
||||
|
||||
class RTreeBoundsAccumulator final : public virtual BoundsAccumulator {
|
||||
public:
|
||||
void accumulate(const SkRect& r, int index) override;
|
||||
void save() override;
|
||||
void restore() override;
|
||||
|
||||
bool restore(
|
||||
std::function<bool(const SkRect& original, SkRect& modified)> map,
|
||||
const SkRect* clip = nullptr) override;
|
||||
|
||||
SkRect bounds() const override;
|
||||
|
||||
sk_sp<DlRTree> rtree() const override;
|
||||
|
||||
BoundsAccumulatorType type() const override {
|
||||
return BoundsAccumulatorType::kRTree;
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<SkRect> rects_;
|
||||
std::vector<int> rect_indices_;
|
||||
std::vector<size_t> saved_offsets_;
|
||||
};
|
||||
|
||||
} // namespace flutter
|
||||
|
||||
#endif // FLUTTER_DISPLAY_LIST_UTILS_DL_BOUNDS_ACCUMULATOR_H_
|
||||
@ -18,24 +18,37 @@ bool DisplayListMatrixClipTracker::is_3x3(const SkM44& m) {
|
||||
// clang-format on
|
||||
}
|
||||
|
||||
static constexpr DlRect kEmpty = DlRect();
|
||||
|
||||
static const DlRect& ProtectEmpty(const SkRect& rect) {
|
||||
// isEmpty protects us against NaN while we normalize any empty cull rects
|
||||
return rect.isEmpty() ? kEmpty : ToDlRect(rect);
|
||||
}
|
||||
|
||||
static const DlRect& ProtectEmpty(const DlRect& rect) {
|
||||
// isEmpty protects us against NaN while we normalize any empty cull rects
|
||||
return rect.IsEmpty() ? kEmpty : rect;
|
||||
}
|
||||
|
||||
DisplayListMatrixClipState::DisplayListMatrixClipState(const DlRect& cull_rect,
|
||||
const DlMatrix& matrix)
|
||||
: cull_rect_(cull_rect), matrix_(matrix) {}
|
||||
: cull_rect_(ProtectEmpty(cull_rect)), matrix_(matrix) {}
|
||||
|
||||
DisplayListMatrixClipState::DisplayListMatrixClipState(const SkRect& cull_rect)
|
||||
: cull_rect_(ProtectEmpty(cull_rect)), matrix_(DlMatrix()) {}
|
||||
|
||||
DisplayListMatrixClipState::DisplayListMatrixClipState(const SkRect& cull_rect,
|
||||
const SkMatrix& matrix)
|
||||
: cull_rect_(ToDlRect(cull_rect)), matrix_(ToDlMatrix(matrix)) {}
|
||||
: cull_rect_(ProtectEmpty(cull_rect)), matrix_(ToDlMatrix(matrix)) {}
|
||||
|
||||
DisplayListMatrixClipState::DisplayListMatrixClipState(const SkRect& cull_rect,
|
||||
const SkM44& matrix)
|
||||
: cull_rect_(ToDlRect(cull_rect)), matrix_(ToDlMatrix(matrix)) {}
|
||||
: cull_rect_(ProtectEmpty(cull_rect)), matrix_(ToDlMatrix(matrix)) {}
|
||||
|
||||
DisplayListMatrixClipTracker::DisplayListMatrixClipTracker(
|
||||
const DlRect& cull_rect,
|
||||
const DlMatrix& matrix) {
|
||||
// isEmpty protects us against NaN as we normalize any empty cull rects
|
||||
DlRect cull = cull_rect.IsEmpty() ? DlRect() : cull_rect;
|
||||
saved_.emplace_back(cull, matrix);
|
||||
saved_.emplace_back(cull_rect, matrix);
|
||||
current_ = &saved_.back();
|
||||
save(); // saved_[0] will always be the initial settings
|
||||
}
|
||||
@ -43,9 +56,7 @@ DisplayListMatrixClipTracker::DisplayListMatrixClipTracker(
|
||||
DisplayListMatrixClipTracker::DisplayListMatrixClipTracker(
|
||||
const SkRect& cull_rect,
|
||||
const SkMatrix& matrix) {
|
||||
// isEmpty protects us against NaN as we normalize any empty cull rects
|
||||
SkRect cull = cull_rect.isEmpty() ? SkRect::MakeEmpty() : cull_rect;
|
||||
saved_.emplace_back(cull, matrix);
|
||||
saved_.emplace_back(cull_rect, matrix);
|
||||
current_ = &saved_.back();
|
||||
save(); // saved_[0] will always be the initial settings
|
||||
}
|
||||
@ -53,9 +64,7 @@ DisplayListMatrixClipTracker::DisplayListMatrixClipTracker(
|
||||
DisplayListMatrixClipTracker::DisplayListMatrixClipTracker(
|
||||
const SkRect& cull_rect,
|
||||
const SkM44& m44) {
|
||||
// isEmpty protects us against NaN as we normalize any empty cull rects
|
||||
SkRect cull = cull_rect.isEmpty() ? SkRect::MakeEmpty() : cull_rect;
|
||||
saved_.emplace_back(cull, m44);
|
||||
saved_.emplace_back(cull_rect, m44);
|
||||
current_ = &saved_.back();
|
||||
save(); // saved_[0] will always be the initial settings
|
||||
}
|
||||
|
||||
@ -8,9 +8,8 @@
|
||||
#include <vector>
|
||||
|
||||
#include "flutter/display_list/dl_canvas.h"
|
||||
#include "flutter/display_list/geometry/dl_geometry_types.h"
|
||||
#include "flutter/fml/logging.h"
|
||||
#include "flutter/impeller/geometry/matrix.h"
|
||||
#include "flutter/impeller/geometry/rect.h"
|
||||
|
||||
#include "third_party/skia/include/core/SkM44.h"
|
||||
#include "third_party/skia/include/core/SkMatrix.h"
|
||||
@ -24,49 +23,11 @@ namespace flutter {
|
||||
class DisplayListMatrixClipState {
|
||||
private:
|
||||
using ClipOp = DlCanvas::ClipOp;
|
||||
using DlRect = impeller::Rect;
|
||||
using DlMatrix = impeller::Matrix;
|
||||
using DlDegrees = impeller::Degrees;
|
||||
|
||||
static_assert(sizeof(SkRect) == sizeof(DlRect));
|
||||
|
||||
static const DlRect& ToDlRect(const SkRect& rect) {
|
||||
return *reinterpret_cast<const DlRect*>(&rect);
|
||||
}
|
||||
|
||||
static constexpr DlMatrix ToDlMatrix(const SkMatrix& matrix) {
|
||||
// clang-format off
|
||||
return DlMatrix::MakeColumn(
|
||||
matrix[SkMatrix::kMScaleX], matrix[SkMatrix::kMSkewY], 0.0f, matrix[SkMatrix::kMPersp0],
|
||||
matrix[SkMatrix::kMSkewX], matrix[SkMatrix::kMScaleY], 0.0f, matrix[SkMatrix::kMPersp1],
|
||||
0.0f, 0.0f, 1.0f, 0.0f,
|
||||
matrix[SkMatrix::kMTransX], matrix[SkMatrix::kMTransY], 0.0f, matrix[SkMatrix::kMPersp2]
|
||||
);
|
||||
// clang-format on
|
||||
}
|
||||
|
||||
static constexpr DlMatrix ToDlMatrix(const SkM44& matrix) {
|
||||
DlMatrix dl_matrix;
|
||||
matrix.getColMajor(dl_matrix.m);
|
||||
return dl_matrix;
|
||||
}
|
||||
|
||||
static const SkRect& ToSkRect(const DlRect& rect) {
|
||||
return *reinterpret_cast<const SkRect*>(&rect);
|
||||
}
|
||||
|
||||
static constexpr SkMatrix ToSkMatrix(const DlMatrix& matrix) {
|
||||
return SkMatrix::MakeAll(matrix.m[0], matrix.m[4], matrix.m[12],
|
||||
matrix.m[1], matrix.m[5], matrix.m[13],
|
||||
matrix.m[3], matrix.m[7], matrix.m[15]);
|
||||
}
|
||||
|
||||
static constexpr SkM44 ToSkM44(const DlMatrix& matrix) {
|
||||
return SkM44::ColMajor(matrix.m);
|
||||
}
|
||||
|
||||
public:
|
||||
DisplayListMatrixClipState(const DlRect& cull_rect, const DlMatrix& matrix);
|
||||
explicit DisplayListMatrixClipState(const DlRect& cull_rect,
|
||||
const DlMatrix& matrix = DlMatrix());
|
||||
explicit DisplayListMatrixClipState(const SkRect& cull_rect);
|
||||
DisplayListMatrixClipState(const SkRect& cull_rect, const SkMatrix& matrix);
|
||||
DisplayListMatrixClipState(const SkRect& cull_rect, const SkM44& matrix);
|
||||
DisplayListMatrixClipState(const DisplayListMatrixClipState& other) = default;
|
||||
|
||||
@ -744,7 +744,9 @@ struct TRect {
|
||||
};
|
||||
|
||||
using Rect = TRect<Scalar>;
|
||||
using IRect = TRect<int64_t>;
|
||||
using IRect32 = TRect<int32_t>;
|
||||
using IRect64 = TRect<int64_t>;
|
||||
using IRect = IRect64;
|
||||
|
||||
#undef ONLY_ON_FLOAT
|
||||
#undef ONLY_ON_FLOAT_M
|
||||
|
||||
@ -135,7 +135,9 @@ constexpr TSize<T> operator/(U s, const TSize<T>& p) {
|
||||
}
|
||||
|
||||
using Size = TSize<Scalar>;
|
||||
using ISize = TSize<int64_t>;
|
||||
using ISize32 = TSize<int32_t>;
|
||||
using ISize64 = TSize<int64_t>;
|
||||
using ISize = ISize64;
|
||||
|
||||
static_assert(sizeof(Size) == 2 * sizeof(Scalar));
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user