Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit fb703b1

Browse files
authored
add non-rendering operation culling to DisplayListBuilder (#41463)
This optimization avoids recording unnecessary render operations that will not affect the output and also eliminates the need for "draw detection" mechanisms like `DlOpSpy` and `CanvasSpy` by remembering if any non-transparent operations were included. The `DlOpSpy` unit tests were updated to check if the results from that object match the new `DisplayList::affects_transparent_surface()` method. Fixes flutter/flutter#125338 In addition, this change will unblock some other Issues: - flutter/flutter#125318 - flutter/flutter#125403
1 parent 2f59ede commit fb703b1

20 files changed

+870
-306
lines changed

display_list/benchmarking/dl_complexity_unittests.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -423,7 +423,7 @@ TEST(DisplayListComplexity, DrawAtlas) {
423423
std::vector<SkRSXform> xforms;
424424
for (int i = 0; i < 10; i++) {
425425
rects.push_back(SkRect::MakeXYWH(0, 0, 10, 10));
426-
xforms.push_back(SkRSXform::Make(0, 0, 0, 0));
426+
xforms.push_back(SkRSXform::Make(1, 0, 0, 0));
427427
}
428428

429429
DisplayListBuilder builder;

display_list/display_list.cc

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@ DisplayList::DisplayList()
2222
unique_id_(0),
2323
bounds_({0, 0, 0, 0}),
2424
can_apply_group_opacity_(true),
25-
is_ui_thread_safe_(true) {}
25+
is_ui_thread_safe_(true),
26+
affects_transparent_surface_(false) {}
2627

2728
DisplayList::DisplayList(DisplayListStorage&& storage,
2829
size_t byte_count,
@@ -32,6 +33,7 @@ DisplayList::DisplayList(DisplayListStorage&& storage,
3233
const SkRect& bounds,
3334
bool can_apply_group_opacity,
3435
bool is_ui_thread_safe,
36+
bool affects_transparent_surface,
3537
sk_sp<const DlRTree> rtree)
3638
: storage_(std::move(storage)),
3739
byte_count_(byte_count),
@@ -42,6 +44,7 @@ DisplayList::DisplayList(DisplayListStorage&& storage,
4244
bounds_(bounds),
4345
can_apply_group_opacity_(can_apply_group_opacity),
4446
is_ui_thread_safe_(is_ui_thread_safe),
47+
affects_transparent_surface_(affects_transparent_surface),
4548
rtree_(std::move(rtree)) {}
4649

4750
DisplayList::~DisplayList() {

display_list/display_list.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,9 @@ class DisplayList : public SkRefCnt {
263263
}
264264

265265
bool can_apply_group_opacity() const { return can_apply_group_opacity_; }
266+
bool affects_transparent_surface() const {
267+
return affects_transparent_surface_;
268+
}
266269
bool isUIThreadSafe() const { return is_ui_thread_safe_; }
267270

268271
private:
@@ -274,6 +277,7 @@ class DisplayList : public SkRefCnt {
274277
const SkRect& bounds,
275278
bool can_apply_group_opacity,
276279
bool is_ui_thread_safe,
280+
bool affects_transparent_surface,
277281
sk_sp<const DlRTree> rtree);
278282

279283
static uint32_t next_unique_id();
@@ -292,6 +296,8 @@ class DisplayList : public SkRefCnt {
292296

293297
const bool can_apply_group_opacity_;
294298
const bool is_ui_thread_safe_;
299+
const bool affects_transparent_surface_;
300+
295301
const sk_sp<const DlRTree> rtree_;
296302

297303
void Dispatch(DlOpReceiver& ctx,

display_list/display_list_unittests.cc

Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#include "flutter/testing/testing.h"
2323

2424
#include "third_party/skia/include/core/SkPictureRecorder.h"
25+
#include "third_party/skia/include/core/SkRSXform.h"
2526
#include "third_party/skia/include/core/SkSurface.h"
2627

2728
namespace flutter {
@@ -2636,5 +2637,164 @@ TEST_F(DisplayListTest, DrawSaveDrawCannotInheritOpacity) {
26362637
ASSERT_FALSE(display_list->can_apply_group_opacity());
26372638
}
26382639

2640+
TEST_F(DisplayListTest, NopOperationsOmittedFromRecords) {
2641+
auto run_tests = [](const std::string& name,
2642+
void init(DisplayListBuilder & builder, DlPaint & paint),
2643+
uint32_t expected_op_count = 0u) {
2644+
auto run_one_test =
2645+
[init](const std::string& name,
2646+
void build(DisplayListBuilder & builder, DlPaint & paint),
2647+
uint32_t expected_op_count = 0u) {
2648+
DisplayListBuilder builder;
2649+
DlPaint paint;
2650+
init(builder, paint);
2651+
build(builder, paint);
2652+
auto list = builder.Build();
2653+
if (list->op_count() != expected_op_count) {
2654+
FML_LOG(ERROR) << *list;
2655+
}
2656+
ASSERT_EQ(list->op_count(), expected_op_count) << name;
2657+
ASSERT_TRUE(list->bounds().isEmpty()) << name;
2658+
};
2659+
run_one_test(
2660+
name + " DrawColor",
2661+
[](DisplayListBuilder& builder, DlPaint& paint) {
2662+
builder.DrawColor(paint.getColor(), paint.getBlendMode());
2663+
},
2664+
expected_op_count);
2665+
run_one_test(
2666+
name + " DrawPaint",
2667+
[](DisplayListBuilder& builder, DlPaint& paint) {
2668+
builder.DrawPaint(paint);
2669+
},
2670+
expected_op_count);
2671+
run_one_test(
2672+
name + " DrawRect",
2673+
[](DisplayListBuilder& builder, DlPaint& paint) {
2674+
builder.DrawRect({10, 10, 20, 20}, paint);
2675+
},
2676+
expected_op_count);
2677+
run_one_test(
2678+
name + " Other Draw Ops",
2679+
[](DisplayListBuilder& builder, DlPaint& paint) {
2680+
builder.DrawLine({10, 10}, {20, 20}, paint);
2681+
builder.DrawOval({10, 10, 20, 20}, paint);
2682+
builder.DrawCircle({50, 50}, 20, paint);
2683+
builder.DrawRRect(SkRRect::MakeRectXY({10, 10, 20, 20}, 5, 5), paint);
2684+
builder.DrawDRRect(SkRRect::MakeRectXY({5, 5, 100, 100}, 5, 5),
2685+
SkRRect::MakeRectXY({10, 10, 20, 20}, 5, 5),
2686+
paint);
2687+
builder.DrawPath(kTestPath1, paint);
2688+
builder.DrawArc({10, 10, 20, 20}, 45, 90, true, paint);
2689+
SkPoint pts[] = {{10, 10}, {20, 20}};
2690+
builder.DrawPoints(PointMode::kLines, 2, pts, paint);
2691+
builder.DrawVertices(TestVertices1, DlBlendMode::kSrcOver, paint);
2692+
builder.DrawImage(TestImage1, {10, 10}, DlImageSampling::kLinear,
2693+
&paint);
2694+
builder.DrawImageRect(TestImage1, SkRect{0.0f, 0.0f, 10.0f, 10.0f},
2695+
SkRect{10.0f, 10.0f, 25.0f, 25.0f},
2696+
DlImageSampling::kLinear, &paint);
2697+
builder.DrawImageNine(TestImage1, {10, 10, 20, 20},
2698+
{10, 10, 100, 100}, DlFilterMode::kLinear,
2699+
&paint);
2700+
SkRSXform xforms[] = {{1, 0, 10, 10}, {0, 1, 10, 10}};
2701+
SkRect rects[] = {{10, 10, 20, 20}, {10, 20, 30, 20}};
2702+
builder.DrawAtlas(TestImage1, xforms, rects, nullptr, 2,
2703+
DlBlendMode::kSrcOver, DlImageSampling::kLinear,
2704+
nullptr, &paint);
2705+
builder.DrawTextBlob(TestBlob1, 10, 10, paint);
2706+
2707+
// Dst mode eliminates most rendering ops except for
2708+
// the following two, so we'll prune those manually...
2709+
if (paint.getBlendMode() != DlBlendMode::kDst) {
2710+
builder.DrawDisplayList(TestDisplayList1, paint.getOpacity());
2711+
builder.DrawShadow(kTestPath1, paint.getColor(), 1, true, 1);
2712+
}
2713+
},
2714+
expected_op_count);
2715+
run_one_test(
2716+
name + " SaveLayer",
2717+
[](DisplayListBuilder& builder, DlPaint& paint) {
2718+
builder.SaveLayer(nullptr, &paint, nullptr);
2719+
builder.DrawRect({10, 10, 20, 20}, DlPaint());
2720+
builder.Restore();
2721+
},
2722+
expected_op_count);
2723+
run_one_test(
2724+
name + " inside Save",
2725+
[](DisplayListBuilder& builder, DlPaint& paint) {
2726+
builder.Save();
2727+
builder.DrawRect({10, 10, 20, 20}, paint);
2728+
builder.Restore();
2729+
},
2730+
expected_op_count);
2731+
};
2732+
run_tests("transparent color", //
2733+
[](DisplayListBuilder& builder, DlPaint& paint) {
2734+
paint.setColor(DlColor::kTransparent());
2735+
});
2736+
run_tests("0 alpha", //
2737+
[](DisplayListBuilder& builder, DlPaint& paint) {
2738+
// The transparent test above already tested transparent
2739+
// black (all 0s), we set White color here so we can test
2740+
// the case of all 1s with a 0 alpha
2741+
paint.setColor(DlColor::kWhite());
2742+
paint.setAlpha(0);
2743+
});
2744+
run_tests("BlendMode::kDst", //
2745+
[](DisplayListBuilder& builder, DlPaint& paint) {
2746+
paint.setBlendMode(DlBlendMode::kDst);
2747+
});
2748+
run_tests("Empty rect clip", //
2749+
[](DisplayListBuilder& builder, DlPaint& paint) {
2750+
builder.ClipRect(SkRect::MakeEmpty(), ClipOp::kIntersect, false);
2751+
});
2752+
run_tests("Empty rrect clip", //
2753+
[](DisplayListBuilder& builder, DlPaint& paint) {
2754+
builder.ClipRRect(SkRRect::MakeEmpty(), ClipOp::kIntersect,
2755+
false);
2756+
});
2757+
run_tests("Empty path clip", //
2758+
[](DisplayListBuilder& builder, DlPaint& paint) {
2759+
builder.ClipPath(SkPath(), ClipOp::kIntersect, false);
2760+
});
2761+
run_tests("Transparent SaveLayer", //
2762+
[](DisplayListBuilder& builder, DlPaint& paint) {
2763+
DlPaint save_paint;
2764+
save_paint.setColor(DlColor::kTransparent());
2765+
builder.SaveLayer(nullptr, &save_paint);
2766+
});
2767+
run_tests("0 alpha SaveLayer", //
2768+
[](DisplayListBuilder& builder, DlPaint& paint) {
2769+
DlPaint save_paint;
2770+
// The transparent test above already tested transparent
2771+
// black (all 0s), we set White color here so we can test
2772+
// the case of all 1s with a 0 alpha
2773+
save_paint.setColor(DlColor::kWhite());
2774+
save_paint.setAlpha(0);
2775+
builder.SaveLayer(nullptr, &save_paint);
2776+
});
2777+
run_tests("Dst blended SaveLayer", //
2778+
[](DisplayListBuilder& builder, DlPaint& paint) {
2779+
DlPaint save_paint;
2780+
save_paint.setBlendMode(DlBlendMode::kDst);
2781+
builder.SaveLayer(nullptr, &save_paint);
2782+
});
2783+
run_tests(
2784+
"Nop inside SaveLayer",
2785+
[](DisplayListBuilder& builder, DlPaint& paint) {
2786+
builder.SaveLayer(nullptr, nullptr);
2787+
paint.setBlendMode(DlBlendMode::kDst);
2788+
},
2789+
2u);
2790+
run_tests("DrawImage inside Culled SaveLayer", //
2791+
[](DisplayListBuilder& builder, DlPaint& paint) {
2792+
DlPaint save_paint;
2793+
save_paint.setColor(DlColor::kTransparent());
2794+
builder.SaveLayer(nullptr, &save_paint);
2795+
builder.DrawImage(TestImage1, {10, 10}, DlImageSampling::kLinear);
2796+
});
2797+
}
2798+
26392799
} // namespace testing
26402800
} // namespace flutter

0 commit comments

Comments
 (0)