Flutter Engine
The Flutter Engine
display_list.cc
Go to the documentation of this file.
1// Copyright 2013 The Flutter Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include <type_traits>
6
7#include "flutter/display_list/display_list.h"
8#include "flutter/display_list/dl_op_records.h"
9#include "flutter/fml/trace_event.h"
10
11namespace flutter {
12
15 kNoAttributes.with_renders_with_attributes();
16
18 : byte_count_(0),
19 op_count_(0),
20 nested_byte_count_(0),
21 nested_op_count_(0),
22 total_depth_(0),
23 unique_id_(0),
24 bounds_({0, 0, 0, 0}),
25 can_apply_group_opacity_(true),
26 is_ui_thread_safe_(true),
27 modifies_transparent_black_(false),
28 root_has_backdrop_filter_(false),
29 max_root_blend_mode_(DlBlendMode::kClear) {}
30
31DisplayList::DisplayList(DisplayListStorage&& storage,
32 size_t byte_count,
33 uint32_t op_count,
34 size_t nested_byte_count,
35 uint32_t nested_op_count,
36 uint32_t total_depth,
37 const SkRect& bounds,
38 bool can_apply_group_opacity,
39 bool is_ui_thread_safe,
40 bool modifies_transparent_black,
41 DlBlendMode max_root_blend_mode,
42 bool root_has_backdrop_filter,
44 : storage_(std::move(storage)),
45 byte_count_(byte_count),
46 op_count_(op_count),
47 nested_byte_count_(nested_byte_count),
48 nested_op_count_(nested_op_count),
49 total_depth_(total_depth),
50 unique_id_(next_unique_id()),
51 bounds_(bounds),
52 can_apply_group_opacity_(can_apply_group_opacity),
53 is_ui_thread_safe_(is_ui_thread_safe),
54 modifies_transparent_black_(modifies_transparent_black),
55 root_has_backdrop_filter_(root_has_backdrop_filter),
56 max_root_blend_mode_(max_root_blend_mode),
57 rtree_(std::move(rtree)) {}
58
60 const uint8_t* ptr = storage_.get();
61 DisposeOps(ptr, ptr + byte_count_);
62}
63
64uint32_t DisplayList::next_unique_id() {
65 static std::atomic<uint32_t> next_id{1};
66 uint32_t id;
67 do {
68 id = next_id.fetch_add(+1, std::memory_order_relaxed);
69 } while (id == 0);
70 return id;
71}
72
73class Culler {
74 public:
75 virtual ~Culler() = default;
76 virtual bool init(DispatchContext& context) = 0;
77 virtual void update(DispatchContext& context) = 0;
78};
79class NopCuller final : public Culler {
80 public:
82
83 ~NopCuller() = default;
84
85 bool init(DispatchContext& context) override {
86 // Setting next_render_index to 0 means that
87 // all rendering ops will be at or after that
88 // index so they will execute and all restore
89 // indices will be after it as well so all
90 // clip and transform operations will execute.
91 context.next_render_index = 0;
92 return true;
93 }
94 void update(DispatchContext& context) override {}
95};
96NopCuller NopCuller::instance = NopCuller();
97class VectorCuller final : public Culler {
98 public:
99 VectorCuller(const DlRTree* rtree, const std::vector<int>& rect_indices)
100 : rtree_(rtree), cur_(rect_indices.begin()), end_(rect_indices.end()) {}
101
102 ~VectorCuller() = default;
103
104 bool init(DispatchContext& context) override {
105 if (cur_ < end_) {
106 context.next_render_index = rtree_->id(*cur_++);
107 return true;
108 } else {
109 // Setting next_render_index to MAX_INT means that
110 // all rendering ops will be "before" that index and
111 // they will skip themselves and all clip and transform
112 // ops will see that the next render index is not
113 // before the next restore index (even if both are MAX_INT)
114 // and so they will also not execute.
115 // None of this really matters because returning false
116 // here should cause the Dispatch operation to abort,
117 // but this value is conceptually correct if that short
118 // circuit optimization isn't used.
120 return false;
121 }
122 }
123 void update(DispatchContext& context) override {
124 if (++context.cur_index > context.next_render_index) {
125 while (cur_ < end_) {
126 context.next_render_index = rtree_->id(*cur_++);
127 if (context.next_render_index >= context.cur_index) {
128 // It should be rare that we have duplicate indices
129 // but if we do, then having a while loop is a cheap
130 // insurance for those cases.
131 // The main cause of duplicate indices is when a
132 // DrawDisplayListOp was added to this DisplayList and
133 // both are computing an R-Tree, in which case the
134 // builder method will forward all of the child
135 // DisplayList's rects to this R-Tree with the same
136 // op_index.
137 return;
138 }
139 }
141 }
142 }
143
144 private:
145 const DlRTree* rtree_;
146 std::vector<int>::const_iterator cur_;
147 std::vector<int>::const_iterator end_;
148};
149
150void DisplayList::Dispatch(DlOpReceiver& receiver) const {
151 const uint8_t* ptr = storage_.get();
152 Dispatch(receiver, ptr, ptr + byte_count_, NopCuller::instance);
153}
154
156 const SkIRect& cull_rect) const {
157 Dispatch(receiver, SkRect::Make(cull_rect));
158}
159
161 const SkRect& cull_rect) const {
162 if (cull_rect.isEmpty()) {
163 return;
164 }
165 if (!has_rtree() || cull_rect.contains(bounds())) {
166 Dispatch(receiver);
167 return;
168 }
169 const DlRTree* rtree = this->rtree().get();
170 FML_DCHECK(rtree != nullptr);
171 const uint8_t* ptr = storage_.get();
172 std::vector<int> rect_indices;
173 rtree->search(cull_rect, &rect_indices);
174 VectorCuller culler(rtree, rect_indices);
175 Dispatch(receiver, ptr, ptr + byte_count_, culler);
176}
177
179 const uint8_t* ptr,
180 const uint8_t* end,
181 Culler& culler) const {
182 DispatchContext context = {
183 .receiver = receiver,
184 .cur_index = 0,
185 // next_render_index will be initialized by culler.init()
186 .next_restore_index = std::numeric_limits<int>::max(),
187 };
188 if (!culler.init(context)) {
189 return;
190 }
191 while (ptr < end) {
192 auto op = reinterpret_cast<const DLOp*>(ptr);
193 ptr += op->size;
194 FML_DCHECK(ptr <= end);
195 switch (op->type) {
196#define DL_OP_DISPATCH(name) \
197 case DisplayListOpType::k##name: \
198 static_cast<const name##Op*>(op)->dispatch(context); \
199 break;
200
202#ifdef IMPELLER_ENABLE_3D
203 DL_OP_DISPATCH(SetSceneColorSource)
204#endif // IMPELLER_ENABLE_3D
205
206#undef DL_OP_DISPATCH
207
208 default:
209 FML_DCHECK(false);
210 return;
211 }
212 culler.update(context);
213 }
214}
215
216void DisplayList::DisposeOps(const uint8_t* ptr, const uint8_t* end) {
217 while (ptr < end) {
218 auto op = reinterpret_cast<const DLOp*>(ptr);
219 ptr += op->size;
220 FML_DCHECK(ptr <= end);
221 switch (op->type) {
222#define DL_OP_DISPOSE(name) \
223 case DisplayListOpType::k##name: \
224 if (!std::is_trivially_destructible_v<name##Op>) { \
225 static_cast<const name##Op*>(op)->~name##Op(); \
226 } \
227 break;
228
230#ifdef IMPELLER_ENABLE_3D
231 DL_OP_DISPOSE(SetSceneColorSource)
232#endif // IMPELLER_ENABLE_3D
233
234#undef DL_OP_DISPOSE
235
236 default:
237 FML_DCHECK(false);
238 return;
239 }
240 }
241}
242
243static bool CompareOps(const uint8_t* ptrA,
244 const uint8_t* endA,
245 const uint8_t* ptrB,
246 const uint8_t* endB) {
247 // These conditions are checked by the caller...
248 FML_DCHECK((endA - ptrA) == (endB - ptrB));
249 FML_DCHECK(ptrA != ptrB);
250 const uint8_t* bulk_start_a = ptrA;
251 const uint8_t* bulk_start_b = ptrB;
252 while (ptrA < endA && ptrB < endB) {
253 auto opA = reinterpret_cast<const DLOp*>(ptrA);
254 auto opB = reinterpret_cast<const DLOp*>(ptrB);
255 if (opA->type != opB->type || opA->size != opB->size) {
256 return false;
257 }
258 ptrA += opA->size;
259 ptrB += opB->size;
260 FML_DCHECK(ptrA <= endA);
261 FML_DCHECK(ptrB <= endB);
263 switch (opA->type) {
264#define DL_OP_EQUALS(name) \
265 case DisplayListOpType::k##name: \
266 result = static_cast<const name##Op*>(opA)->equals( \
267 static_cast<const name##Op*>(opB)); \
268 break;
269
271#ifdef IMPELLER_ENABLE_3D
272 DL_OP_EQUALS(SetSceneColorSource)
273#endif // IMPELLER_ENABLE_3D
274
275#undef DL_OP_EQUALS
276
277 default:
278 FML_DCHECK(false);
279 return false;
280 }
281 switch (result) {
283 return false;
285 break;
287 // Check if we have a backlog of bytes to bulk compare and then
288 // reset the bulk compare pointers to the address following this op
289 auto bulk_bytes = reinterpret_cast<const uint8_t*>(opA) - bulk_start_a;
290 if (bulk_bytes > 0) {
291 if (memcmp(bulk_start_a, bulk_start_b, bulk_bytes) != 0) {
292 return false;
293 }
294 }
295 bulk_start_a = ptrA;
296 bulk_start_b = ptrB;
297 break;
298 }
299 }
300 if (ptrA != endA || ptrB != endB) {
301 return false;
302 }
303 if (bulk_start_a < ptrA) {
304 // Perform a final bulk compare if we have remaining bytes waiting
305 if (memcmp(bulk_start_a, bulk_start_b, ptrA - bulk_start_a) != 0) {
306 return false;
307 }
308 }
309 return true;
310}
311
312bool DisplayList::Equals(const DisplayList* other) const {
313 if (this == other) {
314 return true;
315 }
316 if (byte_count_ != other->byte_count_ || op_count_ != other->op_count_) {
317 return false;
318 }
319 const uint8_t* ptr = storage_.get();
320 const uint8_t* o_ptr = other->storage_.get();
321 if (ptr == o_ptr) {
322 return true;
323 }
324 return CompareOps(ptr, ptr + byte_count_, o_ptr, o_ptr + other->byte_count_);
325}
326
327} // namespace flutter
static uint32_t next_id()
Definition: SkTextBlob.cpp:146
virtual void update(DispatchContext &context)=0
virtual bool init(DispatchContext &context)=0
virtual ~Culler()=default
const SkRect & bounds() const
Definition: display_list.h:294
bool Equals(const DisplayList *other) const
bool has_rtree() const
Definition: display_list.h:296
void Dispatch(DlOpReceiver &ctx) const
sk_sp< const DlRTree > rtree() const
Definition: display_list.h:297
Internal API for rendering recorded display lists to backends.
int id(int result_index) const
Definition: dl_rtree.h:85
~NopCuller()=default
void update(DispatchContext &context) override
Definition: display_list.cc:94
bool init(DispatchContext &context) override
Definition: display_list.cc:85
static NopCuller instance
Definition: display_list.cc:81
static const SaveLayerOptions kNoAttributes
Definition: display_list.h:155
SaveLayerOptions with_renders_with_attributes() const
Definition: display_list.h:170
static const SaveLayerOptions kWithAttributes
Definition: display_list.h:154
bool init(DispatchContext &context) override
void update(DispatchContext &context) override
VectorCuller(const DlRTree *rtree, const std::vector< int > &rect_indices)
Definition: display_list.cc:99
#define DL_OP_EQUALS(name)
#define DL_OP_DISPOSE(name)
#define DL_OP_DISPATCH(name)
#define FOR_EACH_DISPLAY_LIST_OP(V)
Definition: display_list.h:58
static const char * begin(const StringSlice &s)
Definition: editor.cpp:252
glong glong end
GAsyncResult * result
#define FML_DCHECK(condition)
Definition: logging.h:103
static float max(float r, float g, float b)
Definition: hsl.cpp:49
Optional< SkRect > bounds
Definition: SkRecords.h:189
static bool CompareOps(const uint8_t *ptrA, const uint8_t *endA, const uint8_t *ptrB, const uint8_t *endB)
DisplayListCompare
Definition: dl_op_records.h:72
Definition: ref_ptr.h:256
flutter::SaveLayerOptions SaveLayerOptions
Definition: SkRect.h:32
static SkRect Make(const SkISize &size)
Definition: SkRect.h:669
bool contains(SkScalar x, SkScalar y) const
Definition: extension.cpp:19
bool isEmpty() const
Definition: SkRect.h:693
DlOpReceiver & receiver
Definition: dl_op_records.h:43
const uintptr_t id