From 01646a0073169f0f29ab346833cc9c9233d0f1c6 Mon Sep 17 00:00:00 2001 From: Jim Graham Date: Fri, 10 May 2024 14:19:19 -0700 Subject: [PATCH] =?UTF-8?q?Revert=20"DisplayListBuilder=20internal=20reorg?= =?UTF-8?q?anization=20with=20better=20rendering=20op=20o=E2=80=A6"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 4bac4a6c5fd79b13acbef396ac8e36bff4d302d1. --- ci/licenses_golden/licenses_flutter | 10 +- display_list/BUILD.gn | 5 +- .../benchmarking/dl_complexity_unittests.cc | 2 +- display_list/display_list_unittests.cc | 99 +- display_list/dl_builder.cc | 959 +++++++----------- display_list/dl_builder.h | 321 +++--- display_list/dl_vertices.cc | 4 +- display_list/geometry/dl_geometry_types.h | 72 -- display_list/utils/dl_accumulation_rect.cc | 67 -- display_list/utils/dl_accumulation_rect.h | 54 - display_list/utils/dl_bounds_accumulator.cc | 134 +++ display_list/utils/dl_bounds_accumulator.h | 168 +++ display_list/utils/dl_matrix_clip_tracker.cc | 33 +- display_list/utils/dl_matrix_clip_tracker.h | 47 +- impeller/geometry/rect.h | 4 +- impeller/geometry/size.h | 4 +- 16 files changed, 909 insertions(+), 1074 deletions(-) delete mode 100644 display_list/geometry/dl_geometry_types.h delete mode 100644 display_list/utils/dl_accumulation_rect.cc delete mode 100644 display_list/utils/dl_accumulation_rect.h create mode 100644 display_list/utils/dl_bounds_accumulator.cc create mode 100644 display_list/utils/dl_bounds_accumulator.h diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index b8c15596edb2c..9c17b31321151 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -39927,7 +39927,6 @@ 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 @@ -39945,8 +39944,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_accumulation_rect.cc + ../../../flutter/LICENSE -ORIGIN: ../../../flutter/display_list/utils/dl_accumulation_rect.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_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 @@ -42809,7 +42808,6 @@ 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 @@ -42827,8 +42825,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_accumulation_rect.cc -FILE: ../../../flutter/display_list/utils/dl_accumulation_rect.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_comparable.h FILE: ../../../flutter/display_list/utils/dl_matrix_clip_tracker.cc FILE: ../../../flutter/display_list/utils/dl_matrix_clip_tracker.h diff --git a/display_list/BUILD.gn b/display_list/BUILD.gn index dd6aba8d3814b..2bbb1e545e4db 100644 --- a/display_list/BUILD.gn +++ b/display_list/BUILD.gn @@ -60,7 +60,6 @@ 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", @@ -78,8 +77,8 @@ source_set("display_list") { "skia/dl_sk_paint_dispatcher.cc", "skia/dl_sk_paint_dispatcher.h", "skia/dl_sk_types.h", - "utils/dl_accumulation_rect.cc", - "utils/dl_accumulation_rect.h", + "utils/dl_bounds_accumulator.cc", + "utils/dl_bounds_accumulator.h", "utils/dl_matrix_clip_tracker.cc", "utils/dl_matrix_clip_tracker.h", "utils/dl_receiver_utils.cc", diff --git a/display_list/benchmarking/dl_complexity_unittests.cc b/display_list/benchmarking/dl_complexity_unittests.cc index 511ba8b1f7972..3e86622a76591 100644 --- a/display_list/benchmarking/dl_complexity_unittests.cc +++ b/display_list/benchmarking/dl_complexity_unittests.cc @@ -102,7 +102,7 @@ TEST(DisplayListComplexity, StrokeWidth) { auto display_list_stroke_0 = builder_stroke_0.Build(); DisplayListBuilder builder_stroke_1; - builder_stroke_1.DrawLine(SkPoint::Make(0, 0), SkPoint::Make(100, 100), + builder_stroke_0.DrawLine(SkPoint::Make(0, 0), SkPoint::Make(100, 100), DlPaint().setStrokeWidth(1.0f)); auto display_list_stroke_1 = builder_stroke_1.Build(); diff --git a/display_list/display_list_unittests.cc b/display_list/display_list_unittests.cc index 20814eca265f4..a149e75acdd38 100644 --- a/display_list/display_list_unittests.cc +++ b/display_list/display_list_unittests.cc @@ -532,13 +532,13 @@ TEST_F(DisplayListTest, UnclippedSaveLayerContentAccountsForFilter) { builder.Restore(); auto display_list = builder.Build(); - EXPECT_EQ(display_list->op_count(), 6u); + ASSERT_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)); - EXPECT_EQ(display_list->bounds(), result_rect); + ASSERT_EQ(display_list->bounds(), result_rect); } TEST_F(DisplayListTest, ClippedSaveLayerContentAccountsForFilter) { @@ -565,72 +565,13 @@ TEST_F(DisplayListTest, ClippedSaveLayerContentAccountsForFilter) { builder.Restore(); auto display_list = builder.Build(); - EXPECT_EQ(display_list->op_count(), 6u); + ASSERT_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)); - 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(); + ASSERT_EQ(display_list->bounds(), result_rect); } TEST_F(DisplayListTest, SingleOpSizes) { @@ -1203,33 +1144,14 @@ TEST_F(DisplayListTest, SingleOpsMightSupportGroupOpacityBlendMode) { TEST_F(DisplayListTest, OverlappingOpsDoNotSupportGroupOpacity) { DisplayListBuilder builder; + DlOpReceiver& receiver = ToReceiver(builder); for (int i = 0; i < 10; i++) { - builder.DrawRect(SkRect::MakeXYWH(i * 10, 0, 30, 30), DlPaint()); + receiver.drawRect(SkRect::MakeXYWH(i * 10, 0, 30, 30)); } 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); @@ -1326,8 +1248,7 @@ class SaveLayerOptionsExpector : public virtual DlOpReceiver, void saveLayer(const SkRect& bounds, const SaveLayerOptions options, const DlImageFilter* backdrop) override { - EXPECT_EQ(options, expected_[save_layer_count_]) - << "index " << save_layer_count_; + EXPECT_EQ(options, expected_[save_layer_count_]); save_layer_count_++; } @@ -3937,7 +3858,7 @@ TEST_F(DisplayListTest, SaveContentDepthTest) { builder.Save(); // covers depth 1->9 { - builder.Translate(5, 5); // triggers deferred save at depth 1 + builder.Translate(5, 5); builder.DrawRect({10, 10, 20, 20}, DlPaint()); // depth 2 builder.DrawDisplayList(child, 1.0f); // depth 3 (content) + 4 (self) @@ -3947,12 +3868,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 6 + builder.Restore(); // layer is restored with depth 7 builder.DrawRect({16, 16, 26, 26}, DlPaint()); // depth 8 builder.DrawRect({18, 18, 28, 28}, DlPaint()); // depth 9 } - builder.Restore(); // save is restored with depth 9 + builder.Restore(); builder.DrawRect({16, 16, 26, 26}, DlPaint()); // depth 10 builder.DrawRect({18, 18, 28, 28}, DlPaint()); // depth 11 diff --git a/display_list/dl_builder.cc b/display_list/dl_builder.cc index c0edc341a0a3d..f417fa1bcb65e 100644 --- a/display_list/dl_builder.cc +++ b/display_list/dl_builder.cc @@ -9,7 +9,7 @@ #include "flutter/display_list/dl_op_flags.h" #include "flutter/display_list/dl_op_records.h" #include "flutter/display_list/effects/dl_color_source.h" -#include "flutter/display_list/utils/dl_accumulation_rect.h" +#include "flutter/display_list/utils/dl_bounds_accumulator.h" #include "fml/logging.h" #include "third_party/skia/include/core/SkScalar.h" @@ -65,7 +65,7 @@ void* DisplayListBuilder::Push(size_t pod, Args&&... args) { } sk_sp DisplayListBuilder::Build() { - while (save_stack_.size() > 1) { + while (layer_stack_.size() > 1) { restore(); } @@ -74,67 +74,42 @@ sk_sp DisplayListBuilder::Build() { size_t nested_bytes = nested_bytes_; int nested_count = nested_op_count_; uint32_t total_depth = depth_; - bool compatible = current_info().is_group_opacity_compatible(); + bool compatible = current_layer_->is_group_opacity_compatible(); bool is_safe = is_ui_thread_safe_; - bool affects_transparency = current_info().affects_transparent_layer; - - sk_sp rtree; - SkRect bounds; - if (rtree_data_.has_value()) { - auto& rects = rtree_data_->rects; - auto& indices = rtree_data_->indices; - rtree = sk_make_sp(rects.data(), rects.size(), indices.data(), - [](int id) { return id >= 0; }); - // RTree bounds may be tighter due to applying filter bounds - // adjustments to each op as we restore layers rather than to - // the entire layer bounds. - bounds = rtree->bounds(); - rtree_data_.reset(); - } else { - bounds = current_info().global_space_accumulator->bounds(); - } + bool affects_transparency = current_layer_->affects_transparent_layer(); + + sk_sp rtree = this->rtree(); + SkRect bounds = rtree ? rtree->bounds() : this->bounds(); used_ = allocated_ = render_op_count_ = op_index_ = 0; nested_bytes_ = nested_op_count_ = 0; depth_ = 0; is_ui_thread_safe_ = true; - current_opacity_compatibility_ = true; - render_op_depth_cost_ = 1u; + storage_.realloc(bytes); + layer_stack_.pop_back(); + layer_stack_.emplace_back(); + current_layer_ = &layer_stack_.back(); + tracker_.reset(); + layer_tracker_.reset(); current_ = DlPaint(); - save_stack_.pop_back(); - save_stack_.emplace_back(original_cull_rect_); - current_info().is_nop = original_cull_rect_.IsEmpty(); - if (rtree) { - rtree_data_.emplace(); - } else { - current_info().global_space_accumulator.reset(new AccumulationRect()); - } - - storage_.realloc(bytes); return sk_sp( new DisplayList(std::move(storage_), bytes, count, nested_bytes, nested_count, total_depth, bounds, compatible, is_safe, affects_transparency, std::move(rtree))); } -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); -} - DisplayListBuilder::DisplayListBuilder(const SkRect& cull_rect, bool prepare_rtree) - : original_cull_rect_(ProtectEmpty(cull_rect)) { - save_stack_.emplace_back(original_cull_rect_); - current_info().is_nop = original_cull_rect_.IsEmpty(); + : tracker_(cull_rect, SkMatrix::I()) { if (prepare_rtree) { - rtree_data_.emplace(); + accumulator_ = std::make_unique(); } else { - current_info().global_space_accumulator.reset(new AccumulationRect()); + accumulator_ = std::make_unique(); } + + layer_stack_.emplace_back(); + current_layer_ = &layer_stack_.back(); } DisplayListBuilder::~DisplayListBuilder() { @@ -145,7 +120,7 @@ DisplayListBuilder::~DisplayListBuilder() { } SkISize DisplayListBuilder::GetBaseLayerSize() const { - return ToSkISize(DlIRect::RoundOut(original_cull_rect_).GetSize()); + return tracker_.base_device_cull_rect().roundOut().size(); } SkImageInfo DisplayListBuilder::GetImageInfo() const { @@ -413,24 +388,193 @@ void DisplayListBuilder::SetAttributesFromPaint( } void DisplayListBuilder::checkForDeferredSave() { - if (current_info().has_deferred_save_op) { - size_t save_offset = used_; + if (current_layer_->has_deferred_save_op_) { + size_t save_offset_ = used_; Push(0); - current_info().save_offset = save_offset; - current_info().save_depth = depth_; - current_info().has_deferred_save_op = false; + current_layer_->save_offset_ = save_offset_; + current_layer_->start_depth_ = depth_; + current_layer_->has_deferred_save_op_ = false; } } void DisplayListBuilder::Save() { - bool was_nop = current_info().is_nop; - save_stack_.emplace_back(¤t_info()); - current_info().is_nop = was_nop; + layer_stack_.emplace_back(); + current_layer_ = &layer_stack_.back(); + + FML_DCHECK(layer_stack_.size() >= 2u); + // Note we can't use the previous value of current_layer_ because + // the emplace_back() may have moved the storage locations, so we + // recompute the location of the penultimate layer info here. + auto parent_layer = &layer_stack_.end()[-2]; + + current_layer_->has_deferred_save_op_ = true; + current_layer_->is_nop_ = parent_layer->is_nop_; + + if (parent_layer->layer_accumulator_) { + FML_DCHECK(layer_tracker_); + // If the previous layer was using an accumulator, we need to keep + // filling it with content bounds. We reuse the previous accumulator + // for this layer, but clone the associated transform so that new + // transform mutations are restricted to this save/restore context. + current_layer_->layer_accumulator_ = parent_layer->layer_accumulator_; + layer_tracker_->save(); + } else { + FML_DCHECK(!layer_tracker_); + } - FML_DCHECK(save_stack_.size() >= 2u); - FML_DCHECK(current_info().has_deferred_save_op); + tracker_.save(); + accumulator()->save(); } +void DisplayListBuilder::Restore() { + if (layer_stack_.size() <= 1) { + return; + } + + SaveOpBase* op = reinterpret_cast(storage_.get() + + current_layer_->save_offset()); + + if (!current_layer_->has_deferred_save_op_) { + op->restore_index = op_index_; + op->total_content_depth = depth_ - current_layer_->start_depth_; + Push(0); + if (current_layer_->is_save_layer()) { + // A saveLayer will usually do a final copy to the main buffer in + // addition to its content, but that is accounted for outside of + // the total content depth computed above. + depth_ += render_op_depth_cost_; + } + } + + std::shared_ptr filter = current_layer_->filter(); + { + // We should not pop the stack until we are done synching up the current + // and parent layers. + auto parent_layer = &layer_stack_.end()[-2]; + + if (current_layer_->is_save_layer()) { + // Layers are never deferred for now, we need to update the + // following code if we ever do saveLayer culling... + FML_DCHECK(!current_layer_->has_deferred_save_op_); + FML_DCHECK(current_layer_->layer_accumulator_); + + SkRect content_bounds = current_layer_->layer_accumulator_->bounds(); + + switch (op->type) { + case DisplayListOpType::kSaveLayer: + case DisplayListOpType::kSaveLayerBackdrop: { + SaveLayerOpBase* layer_op = reinterpret_cast(op); + if (op->options.bounds_from_caller()) { + if (!content_bounds.isEmpty() && + !layer_op->rect.contains(content_bounds)) { + op->options = op->options.with_content_is_clipped(); + content_bounds.intersect(layer_op->rect); + } + } + layer_op->rect = content_bounds; + break; + } + default: + FML_UNREACHABLE(); + } + + if (layer_tracker_->getSaveCount() > 1) { + layer_tracker_->restore(); + } else { + // If this was the last layer in the tracker, then there should + // be no parent saveLayer. + FML_DCHECK(!parent_layer->layer_accumulator_); + layer_tracker_.reset(); + } + + if (parent_layer->layer_accumulator_) { + SkRect bounds_for_parent = content_bounds; + if (filter) { + if (!filter->map_local_bounds(bounds_for_parent, bounds_for_parent)) { + parent_layer->set_unbounded(); + } + } + // The content_bounds were accumulated in the base coordinate system + // of the current layer, and have been adjusted there according to + // its image filter. + // The content bounds accumulation of the parent layer is relative + // to the parent's base coordinate system, so we need to adjust + // bounds_for_parent to that coordinate space. + FML_DCHECK(layer_tracker_); + layer_tracker_->mapRect(&bounds_for_parent); + parent_layer->layer_accumulator_->accumulate(bounds_for_parent); + } + + if (current_layer_->is_group_opacity_compatible()) { + // We are now going to go back and modify the matching saveLayer + // call to add the option indicating it can distribute an opacity + // value to its children. + // + // Note that this operation cannot and does not change the size + // or structure of the SaveLayerOp record. It only sets an option + // flag on an existing field. + // + // Note that these kinds of modification operations on data already + // in the DisplayList are only allowed *during* the build phase. + // Once built, the DisplayList records must remain read only to + // ensure consistency of rendering and |Equals()| behavior. + op->options = op->options.with_can_distribute_opacity(); + } + } else { + if (layer_tracker_) { + FML_DCHECK(layer_tracker_->getSaveCount() > 1); + layer_tracker_->restore(); + } + // For regular save() ops there was no protecting layer so we have to + // accumulate the inheritance properties into the enclosing layer. + if (current_layer_->cannot_inherit_opacity()) { + parent_layer->mark_incompatible(); + } else if (current_layer_->has_compatible_op()) { + parent_layer->add_compatible_op(); + } + } + } + + // Remember whether the outgoing layer was unbounded so we can adjust + // for it below after we apply the outgoing layer's filter to the bounds. + bool popped_was_unbounded = current_layer_->is_unbounded(); + + // parent_layer is no longer in scope, time to pop the layer. + layer_stack_.pop_back(); + tracker_.restore(); + current_layer_ = &layer_stack_.back(); + + // As we pop the accumulator, use the filter that was applied to the + // outgoing layer (saved above, if any) to adjust the bounds that + // were accumulated while that layer was active. + if (filter) { + const SkRect clip = tracker_.device_cull_rect(); + if (!accumulator()->restore( + [filter = filter, matrix = GetTransform()](const SkRect& input, + SkRect& output) { + SkIRect output_bounds; + bool ret = filter->map_device_bounds(input.roundOut(), matrix, + output_bounds); + output.set(output_bounds); + return ret; + }, + &clip)) { + popped_was_unbounded = true; + } + } else { + accumulator()->restore(); + } + + if (popped_was_unbounded) { + AccumulateUnbounded(); + } +} +void DisplayListBuilder::RestoreToCount(int restore_count) { + FML_DCHECK(restore_count <= GetSaveCount()); + while (restore_count < GetSaveCount() && GetSaveCount() > 1) { + restore(); + } +} void DisplayListBuilder::saveLayer(const SkRect& bounds, const SaveLayerOptions in_options, const DlImageFilter* backdrop) { @@ -440,122 +584,75 @@ void DisplayListBuilder::saveLayer(const SkRect& bounds, : kSaveLayerFlags; OpResult result = PaintResult(current_, flags); if (result == OpResult::kNoEffect) { - // If we can't render, whether because we were already in a no-render - // state from the parent or because our own attributes make us a nop, - // we can just simplify this whole layer to a regular save that has - // nop state. We need to have a SaveInfo for the eventual restore(), - // but no rendering ops should be accepted between now and then so - // it doesn't need any of the data associated with a layer SaveInfo. - Save(); - current_info().is_nop = true; + save(); + current_layer_->is_nop_ = true; return; } - // Snapshot these values before we do any work as we need the values - // from before the method was called, but some of the operations below - // might update them. - size_t save_offset = used_; - uint32_t save_depth = depth_; - - // A backdrop will affect up to the entire surface, bounded by the clip - bool will_be_unbounded = (backdrop != nullptr); - std::shared_ptr filter; + size_t save_layer_offset = used_; if (options.renders_with_attributes()) { + // The actual flood of the outer layer clip will occur after the + // (eventual) corresponding restore is called, but rather than + // remember this information in the LayerInfo until the restore + // method is processed, we just mark the unbounded state up front. + // Another reason to accumulate the clip here rather than in + // restore is so that this savelayer will be tagged in the rtree + // with its full bounds and the right op_index so that it doesn't + // get culled during rendering. if (!paint_nops_on_transparency()) { // We will fill the clip of the outer layer when we restore. - will_be_unbounded = true; + // Accumulate should always return true here because if the + // clip was empty then that would have been caught up above + // when we tested the PaintResult. + [[maybe_unused]] bool unclipped = AccumulateUnbounded(); + FML_DCHECK(unclipped); } - filter = current_.getImageFilter(); CheckLayerOpacityCompatibility(true); + layer_stack_.emplace_back(save_layer_offset, depth_); + layer_stack_.back().filter_ = current_.getImageFilter(); } else { CheckLayerOpacityCompatibility(false); + layer_stack_.emplace_back(save_layer_offset, depth_); } + current_layer_ = &layer_stack_.back(); + current_layer_->is_save_layer_ = true; - // The actual flood of the outer layer clip will occur after the - // (eventual) corresponding restore is called, but rather than - // remember this information in the LayerInfo until the restore - // method is processed, we just mark the unbounded state up front. - // Another reason to accumulate the clip here rather than in - // restore is so that this savelayer will be tagged in the rtree - // with its full bounds and the right op_index so that it doesn't - // get culled during rendering. - if (will_be_unbounded) { - // Accumulate should always return true here because if the - // clip was empty then that would have been caught up above - // when we tested the PaintResult. - [[maybe_unused]] bool unclipped = AccumulateUnbounded(); - FML_DCHECK(unclipped); - } + tracker_.save(); + accumulator()->save(); - // Accumulate information for the SaveInfo we are about to push onto the - // stack. - { - size_t rtree_index = - rtree_data_.has_value() ? rtree_data_->rects.size() : 0u; - - save_stack_.emplace_back(¤t_info(), filter, rtree_index); - current_info().is_nop = false; - FML_DCHECK(!current_info().has_deferred_save_op); - current_info().save_offset = save_offset; - current_info().save_depth = save_depth; - - if (filter && !rtree_data_.has_value()) { - // By default the new SaveInfo shares the global accumulation rect with - // the parent layer and will only have one if the rtree_data is not - // being accumulated. - // - // But, if we have a filter and we are not accumulating rtree data, - // then we'll need to adjust all of the bounds accumulated via this - // new layer by the filter so we need to use a separate global - // accumulation rect for this layer and adjust it during RestoreLayer() - // before accumulating it into the parent layer. - current_info().global_space_accumulator.reset(new AccumulationRect()); - } - - // If we inherit some culling bounds and we have a filter then we need - // to adjust them so that we cull for the correct input space for the - // output of the filter. - if (filter) { - SkRect outer_cull_rect = current_info().global_state.local_cull_rect(); - - SkIRect output_bounds = outer_cull_rect.roundOut(); - SkIRect input_bounds; - if (filter->get_input_device_bounds(output_bounds, SkMatrix::I(), - input_bounds)) { - current_info().global_state.resetLocalCullRect( - SkRect::Make(input_bounds)); - } else { - // Filter could not make any promises about the bounds it needs to - // fill the output space, so we use a maximal rect to accumulate - // the layer bounds. - current_info().global_state.resetDeviceCullRect(kMaxCullRect); - } - } - - // We always want to cull based on user provided bounds, though, as - // that is legacy behavior even if it doesn't always work precisely - // in a rotated or skewed coordinate system. - if (in_options.bounds_from_caller()) { - current_info().global_state.clipRect(bounds, ClipOp::kIntersect, false); - } + SkRect record_bounds; + if (in_options.bounds_from_caller()) { + options = options.with_bounds_from_caller(); + record_bounds = bounds; + } else { + FML_DCHECK(record_bounds.isEmpty()); } - - // Accumulate options to store in the SaveLayer op record. - { - SkRect record_bounds; + current_layer_->layer_accumulator_.reset(new RectBoundsAccumulator()); + if (layer_tracker_) { + layer_tracker_->save(); + layer_tracker_->setTransform(SkMatrix::I()); + } else { + SkRect cull_rect; if (in_options.bounds_from_caller()) { - options = options.with_bounds_from_caller(); - record_bounds = bounds; + cull_rect = bounds; } else { - FML_DCHECK(record_bounds.isEmpty()); + cull_rect = tracker_.local_cull_rect(); } + layer_tracker_.reset( + new DisplayListMatrixClipTracker(cull_rect, SkMatrix::I())); + } - if (backdrop) { - Push(0, options, record_bounds, backdrop); - } else { - Push(0, options, record_bounds); - } + if (backdrop) { + // A backdrop will affect up to the entire surface, bounded by the clip + // Accumulate should always return true here because if the + // clip was empty then that would have been caught up above + // when we tested the PaintResult. + [[maybe_unused]] bool unclipped = AccumulateUnbounded(); + FML_DCHECK(unclipped); + Push(0, options, record_bounds, backdrop); + } else { + Push(0, options, record_bounds); } if (options.renders_with_attributes()) { @@ -563,12 +660,32 @@ void DisplayListBuilder::saveLayer(const SkRect& bounds, // account because an individual primitive with an ImageFilter can apply // opacity on top of it. But, if the layer is applying the ImageFilter // then it cannot pass the opacity on. - if (!current_opacity_compatibility_ || filter) { + if (!current_opacity_compatibility_ || + current_.getImageFilter() != nullptr) { UpdateLayerOpacityCompatibility(false); } } - // REMIND: NEEDED? UpdateLayerResult(result); + + if (options.renders_with_attributes() && current_.getImageFilter()) { + // We use |resetCullRect| here because we will be accumulating bounds of + // primitives before applying the filter to those bounds. We might + // encounter a primitive whose bounds are clipped, but whose filtered + // bounds will not be clipped. If the individual rendering ops bounds + // are clipped, it will not contribute to the overall bounds which + // could lead to inaccurate (subset) bounds of the DisplayList. + // We need to reset the cull rect here to avoid this premature clipping. + // The filtered bounds will be clipped to the existing clip rect when + // this layer is restored. + // If bounds is null then the original cull_rect will be used. + tracker_.resetLocalCullRect(in_options.bounds_from_caller() ? &bounds + : nullptr); + } else if (in_options.bounds_from_caller()) { + // Even though Skia claims that the bounds are only a hint, they actually + // use them as the temporary layer bounds during rendering the layer, so + // we set them as if a clip operation were performed. + tracker_.clipRect(bounds, ClipOp::kIntersect, false); + } } void DisplayListBuilder::SaveLayer(const SkRect* bounds, const DlPaint* paint, @@ -589,300 +706,44 @@ void DisplayListBuilder::SaveLayer(const SkRect* bounds, saveLayer(temp_bounds, options, backdrop); } -void DisplayListBuilder::Restore() { - if (save_stack_.size() <= 1) { - return; - } - - { - // The current_info will have a lifetime that does not extend past the - // pop_back() method below. - auto& current_info = this->current_info(); - - if (!current_info.has_deferred_save_op) { - SaveOpBase* op = reinterpret_cast(storage_.get() + - current_info.save_offset); - FML_DCHECK(op->type == DisplayListOpType::kSave || - op->type == DisplayListOpType::kSaveLayer || - op->type == DisplayListOpType::kSaveLayerBackdrop); - - op->restore_index = op_index_; - op->total_content_depth = depth_ - current_info.save_depth; - - Push(0); - - if (current_info.is_save_layer) { - RestoreLayer(current_info, parent_info(), op); - } else { - // We only propagate these values through a regular save() - if (current_info.opacity_incompatible_op_detected) { - parent_info().opacity_incompatible_op_detected = true; - } - } - } else { - FML_DCHECK(!current_info.is_save_layer); - } - } - - save_stack_.pop_back(); -} - -void DisplayListBuilder::RestoreLayer(const SaveInfo& current_info, - SaveInfo& parent_info, - void* base_op) { - FML_DCHECK(save_stack_.size() > 1); - FML_DCHECK(!current_info.has_deferred_save_op); - - // A saveLayer will usually do a final copy to the main buffer in - // addition to its content, but that is accounted for outside of - // the total content depth computed above in Restore. - depth_ += render_op_depth_cost_; - - SkRect content_bounds = current_info.layer_local_accumulator->bounds(); - - SaveLayerOpBase* layer_op = reinterpret_cast(base_op); - FML_DCHECK(layer_op->type == DisplayListOpType::kSaveLayer || - layer_op->type == DisplayListOpType::kSaveLayerBackdrop); - - switch (layer_op->type) { - case DisplayListOpType::kSaveLayer: - case DisplayListOpType::kSaveLayerBackdrop: { - if (layer_op->options.bounds_from_caller()) { - if (!content_bounds.isEmpty() && - !layer_op->rect.contains(content_bounds)) { - layer_op->options = layer_op->options.with_content_is_clipped(); - content_bounds.intersect(layer_op->rect); - } - } - layer_op->rect = content_bounds; - break; - } - default: - FML_UNREACHABLE(); - } - - if (current_info.is_group_opacity_compatible()) { - // We are now going to go back and modify the matching saveLayer - // call to add the option indicating it can distribute an opacity - // value to its children. - layer_op->options = layer_op->options.with_can_distribute_opacity(); - } - - TransferLayerBounds(current_info, parent_info, content_bounds); -} - -// There are a few different conditions and corresponding operations to -// consider when transferring bounds from one layer to another. The current -// layer will have accumulated its bounds into 2 potential places: -// -// - Its own private layer local bounds, which were potentially clipped by -// the supplied bounds and passed here as the content_bounds. -// -// - Either the rtree rect list, or the global space accumulator, one or -// the other. -// -// If there is no filter then the private layer bounds are complete and -// they simply need to be passed along to the parent into its layer local -// accumulator. Also, if there was no filter then the existing bounds -// recorded in either the rtree rects or the layer's global space accumulator -// (shared with its parent) need no updating so no global space transfer -// has to occur. -// -// If there is a filter then the global content bounds will need to be -// adjusted in one of two ways (rtree vs non-rtree): -// -// - If we are accumulating rtree rects then each of the rects accumulated -// during this current layer will need to be updated by the filter in the -// global coordinate space in which they were accumulated. In this mode -// we should never have a global space accumulator on the layer. -// -// - Otherwise we were accumulating global bounds into our own private -// global space accumulator which need to be adjusted in the global space -// coordinate system by the filter. -// -// Finally, we will have to adjust the layer's content bounds by the filter -// and accumulate those into the parent layer's local bounds. -void DisplayListBuilder::TransferLayerBounds(const SaveInfo& current_info, - SaveInfo& parent_info, - const SkRect& content_bounds) { - auto& filter = current_info.filter; - - if (!filter) { - // One or the other of the rtree data or the global space accumulator - // must be non-null, and the other must be null. - FML_DCHECK(rtree_data_.has_value() != - static_cast(current_info.global_space_accumulator)); - - // The current and parent global space accumulators either must both be - // null, or they must both point to the same accumulator. - FML_DCHECK(current_info.global_space_accumulator.get() == - parent_info.global_space_accumulator.get()); - - // If we have no filter then the global bounds were already accumulated - // into the parent's global accumulator, but we need to update the local - // bounds of the parent for the results of the saveLayer call. - parent_info.AccumulateBoundsLocal(content_bounds); - return; - } - - bool parent_is_flooded = false; - SkRect bounds_for_parent = content_bounds; - - // First, let's adjust or transfer the global bounds. - - if (rtree_data_.has_value()) { - // Neither current or parent layer should have a global space accumulator - FML_DCHECK(!current_info.global_space_accumulator); - FML_DCHECK(!parent_info.global_space_accumulator); - - // The rtree rects were accumulated without the bounds modification of - // the filter applied to the layer so they may fail to trigger on a - // culled dispatch if their filter "fringes" are in the dispatch scope - // but their base rendering bounds are not. (Also, they will not - // contribute fully when we compute the overall bounds of this DL.) - // - // To make sure they are rendered in the culled dispatch situation, we - // revisit all of the RTree rects accumulated during the current layer - // (indicated by rtree_rects_start_index) and expand them by the filter. - - // Matrix and Clip are the global values from just before our saveLayer - // and should still be the current values present in the parent layer. - const SkRect clip = parent_info.global_state.device_cull_rect(); - const SkMatrix matrix = parent_info.global_state.matrix_3x3(); - - // Starting rect index was snapshotted to this layer's data during - // saveLayer. - auto rect_start_index = current_info.rtree_rects_start_index; - - if (AdjustRTreeRects(rtree_data_.value(), *filter, matrix, clip, - rect_start_index)) { - parent_is_flooded = true; - } - } else { - // Both current or parent layer should have a global space accumulator - FML_DCHECK(current_info.global_space_accumulator); - FML_DCHECK(parent_info.global_space_accumulator); - - // And they should not be the same accumulator - FML_DCHECK(current_info.global_space_accumulator.get() != - parent_info.global_space_accumulator.get()); - - SkRect global_bounds = current_info.global_space_accumulator->bounds(); - if (!global_bounds.isEmpty()) { - SkIRect global_ibounds = global_bounds.roundOut(); - if (!filter->map_device_bounds(global_ibounds, - parent_info.global_state.matrix_3x3(), - global_ibounds)) { - parent_is_flooded = true; - } else { - global_bounds.set(global_ibounds); - const SkRect clip = parent_info.global_state.device_cull_rect(); - if (global_bounds.intersect(clip)) { - parent_info.global_space_accumulator->accumulate(global_bounds); - } - } - } - } - - // Now we visit the layer bounds which are in the layer's local coordinate - // system must be accumulated into the parent layer's bounds while - // adjusting them by the layer's local coordinate system (handled by the - // Accumulate() methods). - - // A filter will happily adjust empty bounds to be non-empty, so we - // specifically avoid that case here. Also, if we are already planning - // to flood the parent due to any of the cases above, we don't need to - // run the filter on the content bounds only to discover the same - // condition. - if (!parent_is_flooded && !bounds_for_parent.isEmpty()) { - if (!filter->map_local_bounds(bounds_for_parent, bounds_for_parent)) { - parent_is_flooded = true; - } - } - - if (parent_is_flooded) { - // All of the above computations deferred the flooded parent status - // to here. We need to mark the parent as flooded in both its layer - // and global accumulators. Note that even though the rtree rects - // were expanded to the size of the clip above, this method will still - // add one more rect to the rtree with the op index of the restore - // command to prevent the saveLayer itself from being elided in the - // rare case that there are no rendering ops in it, or somehow none - // of them were chosen by the rtree search (unlikely). The saveLayer - // must be processed for the parent flood to happen. - AccumulateUnbounded(parent_info); - } else { - parent_info.AccumulateBoundsLocal(bounds_for_parent); - } -} - -bool DisplayListBuilder::AdjustRTreeRects(RTreeData& data, - const DlImageFilter& filter, - const SkMatrix& matrix, - const SkRect& clip, - size_t rect_start_index) { - auto& rects = data.rects; - auto& indices = data.indices; - FML_DCHECK(rects.size() == indices.size()); - int ret = false; - auto rect_keep = rect_start_index; - for (size_t i = rect_start_index; i < rects.size(); i++) { - SkRect bounds = rects[i]; - SkIRect ibounds; - if (filter.map_device_bounds(bounds.roundOut(), matrix, ibounds)) { - bounds.set(ibounds); - } else { - bounds = clip; - ret = true; - } - if (bounds.intersect(clip)) { - indices[rect_keep] = indices[i]; - rects[rect_keep] = bounds; - rect_keep++; - } - } - indices.resize(rect_keep); - rects.resize(rect_keep); - return ret; -} - -void DisplayListBuilder::RestoreToCount(int restore_count) { - FML_DCHECK(restore_count <= GetSaveCount()); - while (restore_count < GetSaveCount() && GetSaveCount() > 1) { - restore(); - } -} - void DisplayListBuilder::Translate(SkScalar tx, SkScalar ty) { if (std::isfinite(tx) && std::isfinite(ty) && (tx != 0.0 || ty != 0.0)) { checkForDeferredSave(); Push(0, tx, ty); - global_state().translate(tx, ty); - layer_local_state().translate(tx, ty); + tracker_.translate(tx, ty); + if (layer_tracker_) { + layer_tracker_->translate(tx, ty); + } } } void DisplayListBuilder::Scale(SkScalar sx, SkScalar sy) { if (std::isfinite(sx) && std::isfinite(sy) && (sx != 1.0 || sy != 1.0)) { checkForDeferredSave(); Push(0, sx, sy); - global_state().scale(sx, sy); - layer_local_state().scale(sx, sy); + tracker_.scale(sx, sy); + if (layer_tracker_) { + layer_tracker_->scale(sx, sy); + } } } void DisplayListBuilder::Rotate(SkScalar degrees) { if (SkScalarMod(degrees, 360.0) != 0.0) { checkForDeferredSave(); Push(0, degrees); - global_state().rotate(degrees); - layer_local_state().rotate(degrees); + tracker_.rotate(degrees); + if (layer_tracker_) { + layer_tracker_->rotate(degrees); + } } } void DisplayListBuilder::Skew(SkScalar sx, SkScalar sy) { if (std::isfinite(sx) && std::isfinite(sy) && (sx != 0.0 || sy != 0.0)) { checkForDeferredSave(); Push(0, sx, sy); - global_state().skew(sx, sy); - layer_local_state().skew(sx, sy); + tracker_.skew(sx, sy); + if (layer_tracker_) { + layer_tracker_->skew(sx, sy); + } } } @@ -903,10 +764,12 @@ void DisplayListBuilder::Transform2DAffine( Push(0, mxx, mxy, mxt, myx, myy, myt); - global_state().transform2DAffine(mxx, mxy, mxt, - myx, myy, myt); - layer_local_state().transform2DAffine(mxx, mxy, mxt, - myx, myy, myt); + tracker_.transform2DAffine(mxx, mxy, mxt, + myx, myy, myt); + if (layer_tracker_) { + layer_tracker_->transform2DAffine(mxx, mxy, mxt, + myx, myy, myt); + } } } } @@ -936,42 +799,44 @@ void DisplayListBuilder::TransformFullPerspective( myx, myy, myz, myt, mzx, mzy, mzz, mzt, mwx, mwy, mwz, mwt); - global_state().transformFullPerspective(mxx, mxy, mxz, mxt, - myx, myy, myz, myt, - mzx, mzy, mzz, mzt, - mwx, mwy, mwz, mwt); - layer_local_state().transformFullPerspective(mxx, mxy, mxz, mxt, - myx, myy, myz, myt, - mzx, mzy, mzz, mzt, - mwx, mwy, mwz, mwt); + tracker_.transformFullPerspective(mxx, mxy, mxz, mxt, + myx, myy, myz, myt, + mzx, mzy, mzz, mzt, + mwx, mwy, mwz, mwt); + if (layer_tracker_) { + layer_tracker_->transformFullPerspective(mxx, mxy, mxz, mxt, + myx, myy, myz, myt, + mzx, mzy, mzz, mzt, + mwx, mwy, mwz, mwt); + } } } // clang-format on void DisplayListBuilder::TransformReset() { checkForDeferredSave(); Push(0); - - // The matrices in layer_tracker_ and tracker_ are similar, but - // start at a different base transform. The tracker_ potentially - // has some number of transform operations on it that prefix the - // operations accumulated in layer_tracker_. So we can't set them both - // to identity in parallel as they would no longer maintain their - // relationship to each other. - // Instead we reinterpret this operation as transforming by the - // inverse of the current transform. Doing so to tracker_ sets it - // to identity so we can avoid the math there, but we must do the - // math the long way for layer_tracker_. This becomes: - // layer_tracker_.transform(tracker_.inverse()); - if (!layer_local_state().inverseTransform(global_state())) { - // If the inverse operation failed then that means that either - // the matrix above the current layer was singular, or the matrix - // became singular while we were accumulating the current layer. - // In either case, we should no longer be accumulating any - // contents so we set the layer tracking transform to a singular one. - layer_local_state().setTransform(SkMatrix::Scale(0.0f, 0.0f)); - } - - global_state().setIdentity(); + if (layer_tracker_) { + // The matrices in layer_tracker_ and tracker_ are similar, but + // start at a different base transform. The tracker_ potentially + // has some number of transform operations on it that prefix the + // operations accumulated in layer_tracker_. So we can't set them both + // to identity in parallel as they would no longer maintain their + // relationship to each other. + // Instead we reinterpret this operation as transforming by the + // inverse of the current transform. Doing so to tracker_ sets it + // to identity so we can avoid the math there, but we must do the + // math the long way for layer_tracker_. This becomes: + // layer_tracker_.transform(tracker_.inverse()); + if (!layer_tracker_->inverseTransform(tracker_)) { + // If the inverse operation failed then that means that either + // the matrix above the current layer was singular, or the matrix + // became singular while we were accumulating the current layer. + // In either case, we should no longer be accumulating any + // contents so we set the layer tracking transform to a singular one. + layer_tracker_->setTransform(SkMatrix::Scale(0.0f, 0.0f)); + } + } + tracker_.setIdentity(); } void DisplayListBuilder::Transform(const SkMatrix* matrix) { if (matrix != nullptr) { @@ -994,13 +859,11 @@ void DisplayListBuilder::ClipRect(const SkRect& rect, if (!rect.isFinite()) { return; } - global_state().clipRect(rect, clip_op, is_aa); - if (current_info().is_nop || - current_info().global_state.is_cull_rect_empty()) { - current_info().is_nop = true; + tracker_.clipRect(rect, clip_op, is_aa); + if (current_layer_->is_nop_ || tracker_.is_cull_rect_empty()) { + current_layer_->is_nop_ = true; return; } - layer_local_state().clipRect(rect, clip_op, is_aa); checkForDeferredSave(); switch (clip_op) { case ClipOp::kIntersect: @@ -1017,13 +880,11 @@ void DisplayListBuilder::ClipRRect(const SkRRect& rrect, if (rrect.isRect()) { clipRect(rrect.rect(), clip_op, is_aa); } else { - global_state().clipRRect(rrect, clip_op, is_aa); - if (current_info().is_nop || - current_info().global_state.is_cull_rect_empty()) { - current_info().is_nop = true; + tracker_.clipRRect(rrect, clip_op, is_aa); + if (current_layer_->is_nop_ || tracker_.is_cull_rect_empty()) { + current_layer_->is_nop_ = true; return; } - layer_local_state().clipRRect(rrect, clip_op, is_aa); checkForDeferredSave(); switch (clip_op) { case ClipOp::kIntersect: @@ -1055,13 +916,11 @@ void DisplayListBuilder::ClipPath(const SkPath& path, return; } } - global_state().clipPath(path, clip_op, is_aa); - if (current_info().is_nop || - current_info().global_state.is_cull_rect_empty()) { - current_info().is_nop = true; + tracker_.clipPath(path, clip_op, is_aa); + if (current_layer_->is_nop_ || tracker_.is_cull_rect_empty()) { + current_layer_->is_nop_ = true; return; } - layer_local_state().clipPath(path, clip_op, is_aa); checkForDeferredSave(); switch (clip_op) { case ClipOp::kIntersect: @@ -1074,7 +933,7 @@ void DisplayListBuilder::ClipPath(const SkPath& path, } bool DisplayListBuilder::QuickReject(const SkRect& bounds) const { - return global_state().content_culled(bounds); + return tracker_.content_culled(bounds); } void DisplayListBuilder::drawPaint() { @@ -1276,11 +1135,11 @@ void DisplayListBuilder::drawPoints(PointMode mode, FML_DCHECK(count < DlOpReceiver::kMaxDrawPointsCount); int bytes = count * sizeof(SkPoint); - AccumulationRect accumulator; + RectBoundsAccumulator ptBounds; for (size_t i = 0; i < count; i++) { - accumulator.accumulate(pts[i]); + ptBounds.accumulate(pts[i]); } - SkRect point_bounds = accumulator.bounds(); + SkRect point_bounds = ptBounds.bounds(); if (!AccumulateOpBounds(point_bounds, flags)) { return; } @@ -1306,12 +1165,7 @@ void DisplayListBuilder::drawPoints(PointMode mode, // distribution of group opacity without analyzing the mode and the // bounds of every sub-primitive. // See: https://fiddle.skia.org/c/228459001d2de8db117ce25ef5cedb0c - current_info().layer_local_accumulator->record_overlapping_bounds(); - // Even though we've eliminated the possibility of opacity peephole - // optimizations above, we still set the appropriate flags based on - // the rendering attributes in case we solve the overlapping points - // problem above. - CheckLayerOpacityCompatibility(); + UpdateLayerOpacityCompatibility(false); UpdateLayerResult(result); } void DisplayListBuilder::DrawPoints(PointMode mode, @@ -1335,15 +1189,6 @@ void DisplayListBuilder::drawVertices(const DlVertices* vertices, // cases. UpdateLayerOpacityCompatibility(false); UpdateLayerResult(result); - // Even though we already eliminated opacity peephole optimization - // due to the color issues identified above, drawVertices also fails - // based on the fact that the vertices are rendered independently - // so we cannot guarantee the non-overlapping condition. We record - // both conditions in case a solution is found to applying the - // colors above - both conditions must be analyzed sufficiently - // and implemented accordingly before drawVertices is compatible with - // opacity peephole optimizations. - current_info().layer_local_accumulator->record_overlapping_bounds(); } } void DisplayListBuilder::DrawVertices(const DlVertices* vertices, @@ -1467,31 +1312,18 @@ void DisplayListBuilder::drawAtlas(const sk_sp atlas, return; } SkPoint quad[4]; - AccumulationRect accumulator; + RectBoundsAccumulator atlasBounds; for (int i = 0; i < count; i++) { const SkRect& src = tex[i]; xform[i].toQuad(src.width(), src.height(), quad); for (int j = 0; j < 4; j++) { - accumulator.accumulate(quad[j]); + atlasBounds.accumulate(quad[j]); } } - if (accumulator.is_empty() || - !AccumulateOpBounds(accumulator.bounds(), flags)) { + if (atlasBounds.is_empty() || + !AccumulateOpBounds(atlasBounds.bounds(), flags)) { return; } - // Accumulating the bounds might not trip the overlap condition if the - // whole atlas operation is separated from other rendering calls, but - // since each atlas op is treated as an independent operation, we have - // to pass along our locally computed overlap condition for the individual - // atlas operations to the layer accumulator. - // Note that the above accumulation may falsely trigger the overlapping - // state as it is done quad corner by quad corner and an entire quad may - // be non-overlapping with the layer bounds, but as we add each point - // independently it might expand the bounds on one corner and then flag - // the condition when the next corner is added. - if (accumulator.overlap_detected()) { - current_info().layer_local_accumulator->record_overlapping_bounds(); - } int bytes = count * (sizeof(SkRSXform) + sizeof(SkRect)); void* data_ptr; @@ -1548,25 +1380,32 @@ void DisplayListBuilder::DrawDisplayList(const sk_sp display_list, SkScalar opacity) { if (!std::isfinite(opacity) || opacity <= SK_ScalarNearlyZero || display_list->op_count() == 0 || display_list->bounds().isEmpty() || - current_info().is_nop) { + current_layer_->is_nop_) { return; } const SkRect bounds = display_list->bounds(); bool accumulated; - sk_sp rtree; - if (!rtree_data_.has_value() || !(rtree = display_list->rtree())) { - accumulated = AccumulateOpBounds(bounds, kDrawDisplayListFlags); - } else { - std::list rects = - rtree->searchAndConsolidateRects(GetLocalClipBounds(), false); - accumulated = false; - for (const SkRect& rect : rects) { - // TODO (https://github.com/flutter/flutter/issues/114919): Attributes - // are not necessarily `kDrawDisplayListFlags`. - if (AccumulateOpBounds(rect, kDrawDisplayListFlags)) { - accumulated = true; + switch (accumulator()->type()) { + case BoundsAccumulatorType::kRect: + accumulated = AccumulateOpBounds(bounds, kDrawDisplayListFlags); + break; + case BoundsAccumulatorType::kRTree: + auto rtree = display_list->rtree(); + if (rtree) { + std::list rects = + rtree->searchAndConsolidateRects(GetLocalClipBounds(), false); + accumulated = false; + for (const SkRect& rect : rects) { + // TODO (https://github.com/flutter/flutter/issues/114919): Attributes + // are not necessarily `kDrawDisplayListFlags`. + if (AccumulateOpBounds(rect, kDrawDisplayListFlags)) { + accumulated = true; + } + } + } else { + accumulated = AccumulateOpBounds(bounds, kDrawDisplayListFlags); } - } + break; } if (!accumulated) { return; @@ -1769,21 +1608,17 @@ bool DisplayListBuilder::AdjustBoundsForPaint(SkRect& bounds, return true; } -bool DisplayListBuilder::AccumulateUnbounded(SaveInfo& layer) { - SkRect clip = layer.global_state.device_cull_rect(); +bool DisplayListBuilder::AccumulateUnbounded() { + SkRect clip = tracker_.device_cull_rect(); if (clip.isEmpty()) { return false; } - if (rtree_data_.has_value()) { - FML_DCHECK(!layer.global_space_accumulator); - rtree_data_->rects.push_back(clip); - rtree_data_->indices.push_back(op_index_); - } else { - FML_DCHECK(layer.global_space_accumulator); - layer.global_space_accumulator->accumulate(clip); + accumulator()->accumulate(clip, op_index_); + if (current_layer_->layer_accumulator_) { + FML_DCHECK(layer_tracker_); + current_layer_->layer_accumulator_->accumulate( + layer_tracker_->device_cull_rect()); } - clip = layer.layer_state.device_cull_rect(); - layer.layer_local_accumulator->accumulate(clip); return true; } @@ -1795,44 +1630,22 @@ bool DisplayListBuilder::AccumulateOpBounds(SkRect& bounds, return AccumulateUnbounded(); } } - -bool DisplayListBuilder::AccumulateBounds(const SkRect& bounds, - SaveInfo& layer, - int id) { - if (bounds.isEmpty()) { - return false; - } - SkRect device_bounds; - layer.global_state.mapRect(bounds, &device_bounds); - if (!device_bounds.intersect(layer.global_state.device_cull_rect())) { - return false; - } - if (rtree_data_.has_value()) { - FML_DCHECK(!layer.global_space_accumulator); - if (id >= 0) { - rtree_data_->rects.push_back(device_bounds); - rtree_data_->indices.push_back(id); +bool DisplayListBuilder::AccumulateBounds(SkRect& bounds) { + if (!bounds.isEmpty()) { + SkRect device_bounds; + tracker_.mapRect(bounds, &device_bounds); + if (device_bounds.intersect(tracker_.device_cull_rect())) { + accumulator()->accumulate(device_bounds, op_index_); + if (current_layer_->layer_accumulator_) { + FML_DCHECK(layer_tracker_); + SkRect layer_bounds; + layer_tracker_->mapRect(bounds, &layer_bounds); + current_layer_->layer_accumulator_->accumulate(layer_bounds); + } + return true; } - } else { - FML_DCHECK(layer.global_space_accumulator); - layer.global_space_accumulator->accumulate(device_bounds); } - SkRect layer_bounds; - layer.layer_state.mapRect(bounds, &layer_bounds); - layer.layer_local_accumulator->accumulate(layer_bounds); - return true; -} - -bool DisplayListBuilder::SaveInfo::AccumulateBoundsLocal(const SkRect& bounds) { - if (bounds.isEmpty()) { - return false; - } - SkRect local_bounds; - layer_state.mapRect(bounds, &local_bounds); - if (local_bounds.intersect(layer_state.device_cull_rect())) { - layer_local_accumulator->accumulate(local_bounds); - } - return true; + return false; } bool DisplayListBuilder::paint_nops_on_transparency() { @@ -1956,7 +1769,7 @@ DlColor DisplayListBuilder::GetEffectiveColor(const DlPaint& paint, DisplayListBuilder::OpResult DisplayListBuilder::PaintResult( const DlPaint& paint, DisplayListAttributeFlags flags) { - if (current_info().is_nop) { + if (current_layer_->is_nop_) { return OpResult::kNoEffect; } if (flags.applies_blend()) { diff --git a/display_list/dl_builder.h b/display_list/dl_builder.h index 54fdf6b2c1e17..64ccd65e216da 100644 --- a/display_list/dl_builder.h +++ b/display_list/dl_builder.h @@ -13,9 +13,8 @@ #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_accumulation_rect.h" +#include "flutter/display_list/utils/dl_bounds_accumulator.h" #include "flutter/display_list/utils/dl_comparable.h" #include "flutter/display_list/utils/dl_matrix_clip_tracker.h" #include "flutter/fml/macros.h" @@ -56,7 +55,7 @@ class DisplayListBuilder final : public virtual DlCanvas, // |DlCanvas| void Restore() override; // |DlCanvas| - int GetSaveCount() const override { return save_stack_.size(); } + int GetSaveCount() const override { return layer_stack_.size(); } // |DlCanvas| void RestoreToCount(int restore_count) override; @@ -105,13 +104,13 @@ class DisplayListBuilder final : public virtual DlCanvas, /// save stack. // |DlCanvas| SkM44 GetTransformFullPerspective() const override { - return global_state().matrix_4x4(); + return tracker_.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 global_state().matrix_3x3(); } + SkMatrix GetTransform() const override { return tracker_.matrix_3x3(); } // |DlCanvas| void ClipRect(const SkRect& rect, @@ -131,14 +130,14 @@ class DisplayListBuilder final : public virtual DlCanvas, /// be rendered. // |DlCanvas| SkRect GetDestinationClipBounds() const override { - return global_state().device_cull_rect(); + return tracker_.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 global_state().local_cull_rect(); + return tracker_.local_cull_rect(); } /// Return true iff the supplied bounds are easily shown to be outside @@ -505,174 +504,114 @@ class DisplayListBuilder final : public virtual DlCanvas, template void* Push(size_t extra, Args&&... args); - struct RTreeData { - std::vector rects; - std::vector indices; - }; + 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; + } - // 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& 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) {} + 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 !opacity_incompatible_op_detected && - !layer_local_accumulator->overlap_detected(); + return !cannot_inherit_opacity_; } - // 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 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 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 filter; - }; + 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; + } + } + } - const DlRect original_cull_rect_; - std::vector save_stack_; - std::optional rtree_data_; + // Records that the current layer contains an op that produces visible + // output on a transparent surface. + void add_visible_op() { + affects_transparent_layer_ = true; + } - DlPaint current_; + // The filter to apply to the layer bounds when it is restored + std::shared_ptr 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 filter_; + bool is_unbounded_ = false; + bool has_deferred_save_op_ = false; + bool is_nop_ = false; + bool affects_transparent_layer_ = false; + std::shared_ptr layer_accumulator_; + + friend class DisplayListBuilder; + }; - // 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); + std::vector layer_stack_; + SaveInfo* current_layer_; + DisplayListMatrixClipTracker tracker_; + std::unique_ptr layer_tracker_; + std::unique_ptr accumulator_; + BoundsAccumulator* accumulator() { return accumulator_.get(); } // This flag indicates whether or not the current rendering attributes // are compatible with rendering ops applying an inherited opacity. @@ -698,8 +637,10 @@ 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_info().opacity_incompatible_op_detected = true; + if (compatible) { + current_layer_->add_compatible_op(); + } else { + current_layer_->mark_incompatible(); } } @@ -742,6 +683,38 @@ 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 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 { @@ -760,7 +733,7 @@ class DisplayListBuilder final : public virtual DlCanvas, case OpResult::kPreservesTransparency: break; case OpResult::kAffectsAll: - current_info().affects_transparent_layer = true; + current_layer_->add_visible_op(); break; } } @@ -779,10 +752,7 @@ 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(SaveInfo& layer); - bool AccumulateUnbounded() { - return AccumulateUnbounded(current_info()); - } + bool AccumulateUnbounded(); // Records the bounds for an op after modifying them according to the // supplied attribute flags and transforming by the current matrix. @@ -799,10 +769,9 @@ class DisplayListBuilder final : public virtual DlCanvas, // Records the given bounds after transforming by the current matrix // and clipping against the current clip. - bool AccumulateBounds(const SkRect& bounds, SaveInfo& layer, int id); - bool AccumulateBounds(const SkRect& bounds) { - return AccumulateBounds(bounds, current_info(), op_index_); - } + bool AccumulateBounds(SkRect& bounds); + + DlPaint current_; }; } // namespace flutter diff --git a/display_list/dl_vertices.cc b/display_list/dl_vertices.cc index c338a02a97abc..cbbe6e1ea4ab8 100644 --- a/display_list/dl_vertices.cc +++ b/display_list/dl_vertices.cc @@ -4,7 +4,7 @@ #include "flutter/display_list/dl_vertices.h" -#include "flutter/display_list/utils/dl_accumulation_rect.h" +#include "flutter/display_list/utils/dl_bounds_accumulator.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) { - AccumulationRect accumulator; + RectBoundsAccumulator accumulator; for (int i = 0; i < count; i++) { accumulator.accumulate(points[i]); } diff --git a/display_list/geometry/dl_geometry_types.h b/display_list/geometry/dl_geometry_types.h deleted file mode 100644 index 34b431cde4c62..0000000000000 --- a/display_list/geometry/dl_geometry_types.h +++ /dev/null @@ -1,72 +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_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(&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(&rect); -} - -inline const SkISize& ToSkISize(const DlISize& size) { - return *reinterpret_cast(&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_ diff --git a/display_list/utils/dl_accumulation_rect.cc b/display_list/utils/dl_accumulation_rect.cc deleted file mode 100644 index cd882895d7fed..0000000000000 --- a/display_list/utils/dl_accumulation_rect.cc +++ /dev/null @@ -1,67 +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_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::infinity(); - min_y_ = std::numeric_limits::infinity(); - max_x_ = -std::numeric_limits::infinity(); - max_y_ = -std::numeric_limits::infinity(); - overlap_detected_ = false; -} - -} // namespace flutter diff --git a/display_list/utils/dl_accumulation_rect.h b/display_list/utils/dl_accumulation_rect.h deleted file mode 100644 index ca492c17e9d06..0000000000000 --- a/display_list/utils/dl_accumulation_rect.h +++ /dev/null @@ -1,54 +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_ACCUMULATION_RECT_H_ -#define FLUTTER_DISPLAY_LIST_UTILS_DL_ACCUMULATION_RECT_H_ - -#include - -#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_ diff --git a/display_list/utils/dl_bounds_accumulator.cc b/display_list/utils/dl_bounds_accumulator.cc new file mode 100644 index 0000000000000..34750ed4bd529 --- /dev/null +++ b/display_list/utils/dl_bounds_accumulator.cc @@ -0,0 +1,134 @@ +// 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 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::infinity(); + min_y_ = std::numeric_limits::infinity(); + max_x_ = -std::numeric_limits::infinity(); + max_y_ = -std::numeric_limits::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 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 RTreeBoundsAccumulator::rtree() const { + FML_DCHECK(saved_offsets_.empty()); + return sk_make_sp(rects_.data(), rects_.size(), rect_indices_.data(), + [](int id) { return id >= 0; }); +} + +} // namespace flutter diff --git a/display_list/utils/dl_bounds_accumulator.h b/display_list/utils/dl_bounds_accumulator.h new file mode 100644 index 0000000000000..db4ceda2698b6 --- /dev/null +++ b/display_list/utils/dl_bounds_accumulator.h @@ -0,0 +1,168 @@ +// 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 + +#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 map, + const SkRect* clip = nullptr) = 0; + + virtual SkRect bounds() const = 0; + + virtual sk_sp 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 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 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 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 map, + const SkRect* clip = nullptr) override; + + SkRect bounds() const override; + + sk_sp rtree() const override; + + BoundsAccumulatorType type() const override { + return BoundsAccumulatorType::kRTree; + } + + private: + std::vector rects_; + std::vector rect_indices_; + std::vector saved_offsets_; +}; + +} // namespace flutter + +#endif // FLUTTER_DISPLAY_LIST_UTILS_DL_BOUNDS_ACCUMULATOR_H_ diff --git a/display_list/utils/dl_matrix_clip_tracker.cc b/display_list/utils/dl_matrix_clip_tracker.cc index 9ed7f5d7ececa..6464889c0aafd 100644 --- a/display_list/utils/dl_matrix_clip_tracker.cc +++ b/display_list/utils/dl_matrix_clip_tracker.cc @@ -18,37 +18,24 @@ 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_(ProtectEmpty(cull_rect)), matrix_(matrix) {} - -DisplayListMatrixClipState::DisplayListMatrixClipState(const SkRect& cull_rect) - : cull_rect_(ProtectEmpty(cull_rect)), matrix_(DlMatrix()) {} + : cull_rect_(cull_rect), matrix_(matrix) {} DisplayListMatrixClipState::DisplayListMatrixClipState(const SkRect& cull_rect, const SkMatrix& matrix) - : cull_rect_(ProtectEmpty(cull_rect)), matrix_(ToDlMatrix(matrix)) {} + : cull_rect_(ToDlRect(cull_rect)), matrix_(ToDlMatrix(matrix)) {} DisplayListMatrixClipState::DisplayListMatrixClipState(const SkRect& cull_rect, const SkM44& matrix) - : cull_rect_(ProtectEmpty(cull_rect)), matrix_(ToDlMatrix(matrix)) {} + : cull_rect_(ToDlRect(cull_rect)), matrix_(ToDlMatrix(matrix)) {} DisplayListMatrixClipTracker::DisplayListMatrixClipTracker( const DlRect& cull_rect, const DlMatrix& matrix) { - saved_.emplace_back(cull_rect, 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); current_ = &saved_.back(); save(); // saved_[0] will always be the initial settings } @@ -56,7 +43,9 @@ DisplayListMatrixClipTracker::DisplayListMatrixClipTracker( DisplayListMatrixClipTracker::DisplayListMatrixClipTracker( const SkRect& cull_rect, const SkMatrix& matrix) { - saved_.emplace_back(cull_rect, 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); current_ = &saved_.back(); save(); // saved_[0] will always be the initial settings } @@ -64,7 +53,9 @@ DisplayListMatrixClipTracker::DisplayListMatrixClipTracker( DisplayListMatrixClipTracker::DisplayListMatrixClipTracker( const SkRect& cull_rect, const SkM44& m44) { - saved_.emplace_back(cull_rect, 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); current_ = &saved_.back(); save(); // saved_[0] will always be the initial settings } diff --git a/display_list/utils/dl_matrix_clip_tracker.h b/display_list/utils/dl_matrix_clip_tracker.h index 1c8f33992f6a2..286b395fb33eb 100644 --- a/display_list/utils/dl_matrix_clip_tracker.h +++ b/display_list/utils/dl_matrix_clip_tracker.h @@ -8,8 +8,9 @@ #include #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" @@ -23,11 +24,49 @@ 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(&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(&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: - explicit DisplayListMatrixClipState(const DlRect& cull_rect, - const DlMatrix& matrix = DlMatrix()); - explicit DisplayListMatrixClipState(const SkRect& cull_rect); + DisplayListMatrixClipState(const DlRect& cull_rect, const DlMatrix& matrix); DisplayListMatrixClipState(const SkRect& cull_rect, const SkMatrix& matrix); DisplayListMatrixClipState(const SkRect& cull_rect, const SkM44& matrix); DisplayListMatrixClipState(const DisplayListMatrixClipState& other) = default; diff --git a/impeller/geometry/rect.h b/impeller/geometry/rect.h index 919141c160ce1..9757eb9b7bd27 100644 --- a/impeller/geometry/rect.h +++ b/impeller/geometry/rect.h @@ -744,9 +744,7 @@ struct TRect { }; using Rect = TRect; -using IRect32 = TRect; -using IRect64 = TRect; -using IRect = IRect64; +using IRect = TRect; #undef ONLY_ON_FLOAT #undef ONLY_ON_FLOAT_M diff --git a/impeller/geometry/size.h b/impeller/geometry/size.h index 45554734a15f4..9841b63d32642 100644 --- a/impeller/geometry/size.h +++ b/impeller/geometry/size.h @@ -135,9 +135,7 @@ constexpr TSize operator/(U s, const TSize& p) { } using Size = TSize; -using ISize32 = TSize; -using ISize64 = TSize; -using ISize = ISize64; +using ISize = TSize; static_assert(sizeof(Size) == 2 * sizeof(Scalar));