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

Commit 147ac7d

Browse files
authored
Display list R-Tree culling (#38429)
* collect DL indices in RTree for clip culling * fix bounds in unit test and minor opt in Dispatch * normalize inline matrix objects and minor fixes to unit test * remove over-eager DCHECK and improve R-Tree comments * formatting * include vector for Windows * method rename and distribute child nodes more evenly * add R-Tree specific unit tests and debug checks * add comments about geometry to R-Tree unit tests and adjust spacing * licenses * licenses attempt 2 * fix potential overflow with uint32_t * aggressively const DisplayList fields and methods * add implementation comments per review feedback
1 parent 19de626 commit 147ac7d

15 files changed

+1295
-375
lines changed

ci/licenses_golden/excluded_files

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
../../../flutter/display_list/display_list_matrix_clip_tracker_unittests.cc
4040
../../../flutter/display_list/display_list_paint_unittests.cc
4141
../../../flutter/display_list/display_list_path_effect_unittests.cc
42+
../../../flutter/display_list/display_list_rtree_unittests.cc
4243
../../../flutter/display_list/display_list_unittests.cc
4344
../../../flutter/display_list/display_list_utils_unittests.cc
4445
../../../flutter/display_list/display_list_vertices_unittests.cc

display_list/BUILD.gn

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ if (enable_unittests) {
109109
"display_list_matrix_clip_tracker_unittests.cc",
110110
"display_list_paint_unittests.cc",
111111
"display_list_path_effect_unittests.cc",
112+
"display_list_rtree_unittests.cc",
112113
"display_list_unittests.cc",
113114
"display_list_utils_unittests.cc",
114115
"display_list_vertices_unittests.cc",

display_list/display_list.cc

Lines changed: 133 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -38,31 +38,144 @@ DisplayList::DisplayList(DisplayListStorage&& storage,
3838
op_count_(op_count),
3939
nested_byte_count_(nested_byte_count),
4040
nested_op_count_(nested_op_count),
41+
unique_id_(next_unique_id()),
4142
bounds_(bounds),
4243
can_apply_group_opacity_(can_apply_group_opacity),
43-
rtree_(std::move(rtree)) {
44+
rtree_(std::move(rtree)) {}
45+
46+
DisplayList::~DisplayList() {
47+
uint8_t* ptr = storage_.get();
48+
DisposeOps(ptr, ptr + byte_count_);
49+
}
50+
51+
uint32_t DisplayList::next_unique_id() {
4452
static std::atomic<uint32_t> next_id{1};
53+
uint32_t id;
4554
do {
46-
unique_id_ = next_id.fetch_add(+1, std::memory_order_relaxed);
47-
} while (unique_id_ == 0);
55+
id = next_id.fetch_add(+1, std::memory_order_relaxed);
56+
} while (id == 0);
57+
return id;
4858
}
4959

50-
DisplayList::~DisplayList() {
60+
class Culler {
61+
public:
62+
virtual bool init(DispatchContext& context) = 0;
63+
virtual void update(DispatchContext& context) = 0;
64+
};
65+
class NopCuller : public Culler {
66+
public:
67+
static NopCuller instance;
68+
69+
bool init(DispatchContext& context) override {
70+
// Setting next_render_index to 0 means that
71+
// all rendering ops will be at or after that
72+
// index so they will execute and all restore
73+
// indices will be after it as well so all
74+
// clip and transform operations will execute.
75+
context.next_render_index = 0;
76+
return true;
77+
}
78+
void update(DispatchContext& context) override {}
79+
};
80+
NopCuller NopCuller::instance = NopCuller();
81+
class VectorCuller : public Culler {
82+
public:
83+
VectorCuller(const DlRTree* rtree, const std::vector<int>& rect_indices)
84+
: rtree_(rtree), cur_(rect_indices.begin()), end_(rect_indices.end()) {}
85+
86+
bool init(DispatchContext& context) override {
87+
if (cur_ < end_) {
88+
context.next_render_index = rtree_->id(*cur_++);
89+
return true;
90+
} else {
91+
// Setting next_render_index to MAX_INT means that
92+
// all rendering ops will be "before" that index and
93+
// they will skip themselves and all clip and transform
94+
// ops will see that the next render index is not
95+
// before the next restore index (even if both are MAX_INT)
96+
// and so they will also not execute.
97+
// None of this really matters because returning false
98+
// here should cause the Dispatch operation to abort,
99+
// but this value is conceptually correct if that short
100+
// circuit optimization isn't used.
101+
context.next_render_index = std::numeric_limits<int>::max();
102+
return false;
103+
}
104+
}
105+
void update(DispatchContext& context) override {
106+
if (++context.cur_index > context.next_render_index) {
107+
while (cur_ < end_) {
108+
context.next_render_index = rtree_->id(*cur_++);
109+
if (context.next_render_index >= context.cur_index) {
110+
// It should be rare that we have duplicate indices
111+
// but if we do, then having a while loop is a cheap
112+
// insurance for those cases.
113+
// The main cause of duplicate indices is when a
114+
// DrawDisplayListOp was added to this DisplayList and
115+
// both are computing an R-Tree, in which case the
116+
// builder method will forward all of the child
117+
// DisplayList's rects to this R-Tree with the same
118+
// op_index.
119+
return;
120+
}
121+
}
122+
context.next_render_index = std::numeric_limits<int>::max();
123+
}
124+
}
125+
126+
private:
127+
const DlRTree* rtree_;
128+
std::vector<int>::const_iterator cur_;
129+
std::vector<int>::const_iterator end_;
130+
};
131+
132+
void DisplayList::Dispatch(Dispatcher& ctx) const {
51133
uint8_t* ptr = storage_.get();
52-
DisposeOps(ptr, ptr + byte_count_);
134+
Dispatch(ctx, ptr, ptr + byte_count_, NopCuller::instance);
135+
}
136+
void DisplayList::Dispatch(Dispatcher& ctx, const SkRect& cull_rect) const {
137+
if (cull_rect.isEmpty()) {
138+
return;
139+
}
140+
if (cull_rect.contains(bounds())) {
141+
Dispatch(ctx);
142+
return;
143+
}
144+
const DlRTree* rtree = this->rtree().get();
145+
FML_DCHECK(rtree != nullptr);
146+
if (rtree == nullptr) {
147+
FML_LOG(ERROR) << "dispatched with culling rect on DL with no rtree";
148+
Dispatch(ctx);
149+
return;
150+
}
151+
uint8_t* ptr = storage_.get();
152+
std::vector<int> rect_indices;
153+
rtree->search(cull_rect, &rect_indices);
154+
VectorCuller culler(rtree, rect_indices);
155+
Dispatch(ctx, ptr, ptr + byte_count_, culler);
53156
}
54157

55158
void DisplayList::Dispatch(Dispatcher& dispatcher,
56159
uint8_t* ptr,
57-
uint8_t* end) const {
160+
uint8_t* end,
161+
Culler& culler) const {
162+
DispatchContext context = {
163+
.dispatcher = dispatcher,
164+
.cur_index = 0,
165+
// next_render_index will be initialized by culler.init()
166+
.next_restore_index = std::numeric_limits<int>::max(),
167+
};
168+
if (!culler.init(context)) {
169+
return;
170+
}
58171
while (ptr < end) {
59172
auto op = reinterpret_cast<const DLOp*>(ptr);
60173
ptr += op->size;
61174
FML_DCHECK(ptr <= end);
62175
switch (op->type) {
63-
#define DL_OP_DISPATCH(name) \
64-
case DisplayListOpType::k##name: \
65-
static_cast<const name##Op*>(op)->dispatch(dispatcher); \
176+
#define DL_OP_DISPATCH(name) \
177+
case DisplayListOpType::k##name: \
178+
static_cast<const name##Op*>(op)->dispatch(context); \
66179
break;
67180

68181
FOR_EACH_DISPLAY_LIST_OP(DL_OP_DISPATCH)
@@ -73,6 +186,7 @@ void DisplayList::Dispatch(Dispatcher& dispatcher,
73186
FML_DCHECK(false);
74187
return;
75188
}
189+
culler.update(context);
76190
}
77191
}
78192

@@ -172,12 +286,20 @@ void DisplayList::RenderTo(DisplayListBuilder* builder,
172286
if (!builder) {
173287
return;
174288
}
175-
Dispatch(*builder);
289+
if (has_rtree()) {
290+
Dispatch(*builder, builder->getLocalClipBounds());
291+
} else {
292+
Dispatch(*builder);
293+
}
176294
}
177295

178296
void DisplayList::RenderTo(SkCanvas* canvas, SkScalar opacity) const {
179297
DisplayListCanvasDispatcher dispatcher(canvas, opacity);
180-
Dispatch(dispatcher);
298+
if (has_rtree()) {
299+
Dispatch(dispatcher, canvas->getLocalClipBounds());
300+
} else {
301+
Dispatch(dispatcher);
302+
}
181303
}
182304

183305
bool DisplayList::Equals(const DisplayList* other) const {

display_list/display_list.h

Lines changed: 23 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,8 @@ class DisplayListStorage {
235235
std::unique_ptr<uint8_t, FreeDeleter> ptr_;
236236
};
237237

238+
class Culler;
239+
238240
// The base class that contains a sequence of rendering operations
239241
// for dispatch to a Dispatcher. These objects must be instantiated
240242
// through an instance of DisplayListBuilder::build().
@@ -244,10 +246,8 @@ class DisplayList : public SkRefCnt {
244246

245247
~DisplayList();
246248

247-
void Dispatch(Dispatcher& ctx) const {
248-
uint8_t* ptr = storage_.get();
249-
Dispatch(ctx, ptr, ptr + byte_count_);
250-
}
249+
void Dispatch(Dispatcher& ctx) const;
250+
void Dispatch(Dispatcher& ctx, const SkRect& cull_rect) const;
251251

252252
void RenderTo(DisplayListBuilder* builder,
253253
SkScalar opacity = SK_Scalar1) const;
@@ -268,17 +268,18 @@ class DisplayList : public SkRefCnt {
268268

269269
uint32_t unique_id() const { return unique_id_; }
270270

271-
const SkRect& bounds() { return bounds_; }
271+
const SkRect& bounds() const { return bounds_; }
272272

273-
sk_sp<const DlRTree> rtree() { return rtree_; }
273+
bool has_rtree() const { return rtree_ != nullptr; }
274+
sk_sp<const DlRTree> rtree() const { return rtree_; }
274275

275276
bool Equals(const DisplayList* other) const;
276277
bool Equals(const DisplayList& other) const { return Equals(&other); }
277278
bool Equals(sk_sp<const DisplayList> other) const {
278279
return Equals(other.get());
279280
}
280281

281-
bool can_apply_group_opacity() { return can_apply_group_opacity_; }
282+
bool can_apply_group_opacity() const { return can_apply_group_opacity_; }
282283

283284
static void DisposeOps(uint8_t* ptr, uint8_t* end);
284285

@@ -292,20 +293,25 @@ class DisplayList : public SkRefCnt {
292293
bool can_apply_group_opacity,
293294
sk_sp<const DlRTree> rtree);
294295

295-
DisplayListStorage storage_;
296-
size_t byte_count_;
297-
unsigned int op_count_;
296+
static uint32_t next_unique_id();
297+
298+
const DisplayListStorage storage_;
299+
const size_t byte_count_;
300+
const unsigned int op_count_;
298301

299-
size_t nested_byte_count_;
300-
unsigned int nested_op_count_;
302+
const size_t nested_byte_count_;
303+
const unsigned int nested_op_count_;
301304

302-
uint32_t unique_id_;
303-
SkRect bounds_;
305+
const uint32_t unique_id_;
306+
const SkRect bounds_;
304307

305-
bool can_apply_group_opacity_;
306-
sk_sp<const DlRTree> rtree_;
308+
const bool can_apply_group_opacity_;
309+
const sk_sp<const DlRTree> rtree_;
307310

308-
void Dispatch(Dispatcher& ctx, uint8_t* ptr, uint8_t* end) const;
311+
void Dispatch(Dispatcher& ctx,
312+
uint8_t* ptr,
313+
uint8_t* end,
314+
Culler& culler) const;
309315

310316
friend class DisplayListBuilder;
311317
};

0 commit comments

Comments
 (0)