| 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 | #ifndef FLUTTER_DISPLAY_LIST_DISPLAY_LIST_H_ |
| 6 | #define FLUTTER_DISPLAY_LIST_DISPLAY_LIST_H_ |
| 7 | |
| 8 | #include <memory> |
| 9 | #include <optional> |
| 10 | |
| 11 | #include "flutter/display_list/dl_sampling_options.h" |
| 12 | #include "flutter/display_list/geometry/dl_rtree.h" |
| 13 | #include "flutter/fml/logging.h" |
| 14 | |
| 15 | // The Flutter DisplayList mechanism encapsulates a persistent sequence of |
| 16 | // rendering operations. |
| 17 | // |
| 18 | // This file contains the definitions for: |
| 19 | // DisplayList: the base class that holds the information about the |
| 20 | // sequence of operations and can dispatch them to a DlOpReceiver |
| 21 | // DlOpReceiver: a pure virtual interface which can be implemented to field |
| 22 | // the requests for purposes such as sending them to an SkCanvas |
| 23 | // or detecting various rendering optimization scenarios |
| 24 | // DisplayListBuilder: a class for constructing a DisplayList from DlCanvas |
| 25 | // method calls and which can act as a DlOpReceiver as well |
| 26 | // |
| 27 | // Other files include various class definitions for dealing with display |
| 28 | // lists, such as: |
| 29 | // skia/dl_sk_*.h: classes to interact between SkCanvas and DisplayList |
| 30 | // (SkCanvas->DisplayList adapter and vice versa) |
| 31 | // |
| 32 | // display_list_utils.h: various utility classes to ease implementing |
| 33 | // a DlOpReceiver, including NOP implementations of |
| 34 | // the attribute, clip, and transform methods, |
| 35 | // classes to track attributes, clips, and transforms |
| 36 | // and a class to compute the bounds of a DisplayList |
| 37 | // Any class implementing DlOpReceiver can inherit from |
| 38 | // these utility classes to simplify its creation |
| 39 | // |
| 40 | // The Flutter DisplayList mechanism is used in a similar manner to the Skia |
| 41 | // SkPicture mechanism. |
| 42 | // |
| 43 | // A DisplayList must be created using a DisplayListBuilder using its stateless |
| 44 | // methods inherited from DlCanvas. |
| 45 | // |
| 46 | // A DisplayList can be read back by implementing the DlOpReceiver virtual |
| 47 | // methods (with help from some of the classes in the utils file) and |
| 48 | // passing an instance to the Dispatch() method, or it can be rendered |
| 49 | // to Skia using a DlSkCanvasDispatcher. |
| 50 | // |
| 51 | // The mechanism is inspired by the SkLiteDL class that is not directly |
| 52 | // supported by Skia, but has been recommended as a basis for custom |
| 53 | // display lists for a number of their customers. |
| 54 | |
| 55 | namespace flutter { |
| 56 | |
| 57 | #define FOR_EACH_DISPLAY_LIST_OP(V) \ |
| 58 | V(SetAntiAlias) \ |
| 59 | V(SetDither) \ |
| 60 | V(SetInvertColors) \ |
| 61 | \ |
| 62 | V(SetStrokeCap) \ |
| 63 | V(SetStrokeJoin) \ |
| 64 | \ |
| 65 | V(SetStyle) \ |
| 66 | V(SetStrokeWidth) \ |
| 67 | V(SetStrokeMiter) \ |
| 68 | \ |
| 69 | V(SetColor) \ |
| 70 | V(SetBlendMode) \ |
| 71 | \ |
| 72 | V(SetPodPathEffect) \ |
| 73 | V(ClearPathEffect) \ |
| 74 | \ |
| 75 | V(ClearColorFilter) \ |
| 76 | V(SetPodColorFilter) \ |
| 77 | \ |
| 78 | V(ClearColorSource) \ |
| 79 | V(SetPodColorSource) \ |
| 80 | V(SetImageColorSource) \ |
| 81 | V(SetRuntimeEffectColorSource) \ |
| 82 | \ |
| 83 | V(ClearImageFilter) \ |
| 84 | V(SetPodImageFilter) \ |
| 85 | V(SetSharedImageFilter) \ |
| 86 | \ |
| 87 | V(ClearMaskFilter) \ |
| 88 | V(SetPodMaskFilter) \ |
| 89 | \ |
| 90 | V(Save) \ |
| 91 | V(SaveLayer) \ |
| 92 | V(SaveLayerBounds) \ |
| 93 | V(SaveLayerBackdrop) \ |
| 94 | V(SaveLayerBackdropBounds) \ |
| 95 | V(Restore) \ |
| 96 | \ |
| 97 | V(Translate) \ |
| 98 | V(Scale) \ |
| 99 | V(Rotate) \ |
| 100 | V(Skew) \ |
| 101 | V(Transform2DAffine) \ |
| 102 | V(TransformFullPerspective) \ |
| 103 | V(TransformReset) \ |
| 104 | \ |
| 105 | V(ClipIntersectRect) \ |
| 106 | V(ClipIntersectRRect) \ |
| 107 | V(ClipIntersectPath) \ |
| 108 | V(ClipDifferenceRect) \ |
| 109 | V(ClipDifferenceRRect) \ |
| 110 | V(ClipDifferencePath) \ |
| 111 | \ |
| 112 | V(DrawPaint) \ |
| 113 | V(DrawColor) \ |
| 114 | \ |
| 115 | V(DrawLine) \ |
| 116 | V(DrawRect) \ |
| 117 | V(DrawOval) \ |
| 118 | V(DrawCircle) \ |
| 119 | V(DrawRRect) \ |
| 120 | V(DrawDRRect) \ |
| 121 | V(DrawArc) \ |
| 122 | V(DrawPath) \ |
| 123 | \ |
| 124 | V(DrawPoints) \ |
| 125 | V(DrawLines) \ |
| 126 | V(DrawPolygon) \ |
| 127 | V(DrawVertices) \ |
| 128 | \ |
| 129 | V(DrawImage) \ |
| 130 | V(DrawImageWithAttr) \ |
| 131 | V(DrawImageRect) \ |
| 132 | V(DrawImageNine) \ |
| 133 | V(DrawImageNineWithAttr) \ |
| 134 | V(DrawAtlas) \ |
| 135 | V(DrawAtlasCulled) \ |
| 136 | \ |
| 137 | V(DrawDisplayList) \ |
| 138 | V(DrawTextBlob) \ |
| 139 | \ |
| 140 | V(DrawShadow) \ |
| 141 | V(DrawShadowTransparentOccluder) |
| 142 | |
| 143 | #define DL_OP_TO_ENUM_VALUE(name) k##name, |
| 144 | enum class DisplayListOpType { |
| 145 | FOR_EACH_DISPLAY_LIST_OP(DL_OP_TO_ENUM_VALUE) |
| 146 | #ifdef IMPELLER_ENABLE_3D |
| 147 | DL_OP_TO_ENUM_VALUE(SetSceneColorSource) |
| 148 | #endif // IMPELLER_ENABLE_3D |
| 149 | }; |
| 150 | #undef DL_OP_TO_ENUM_VALUE |
| 151 | |
| 152 | class DlOpReceiver; |
| 153 | class DisplayListBuilder; |
| 154 | |
| 155 | class SaveLayerOptions { |
| 156 | public: |
| 157 | static const SaveLayerOptions kWithAttributes; |
| 158 | static const SaveLayerOptions kNoAttributes; |
| 159 | |
| 160 | SaveLayerOptions() : flags_(0) {} |
| 161 | SaveLayerOptions(const SaveLayerOptions& options) : flags_(options.flags_) {} |
| 162 | SaveLayerOptions(const SaveLayerOptions* options) : flags_(options->flags_) {} |
| 163 | |
| 164 | SaveLayerOptions without_optimizations() const { |
| 165 | SaveLayerOptions options; |
| 166 | options.fRendersWithAttributes = fRendersWithAttributes; |
| 167 | return options; |
| 168 | } |
| 169 | |
| 170 | bool renders_with_attributes() const { return fRendersWithAttributes; } |
| 171 | SaveLayerOptions with_renders_with_attributes() const { |
| 172 | SaveLayerOptions options(this); |
| 173 | options.fRendersWithAttributes = true; |
| 174 | return options; |
| 175 | } |
| 176 | |
| 177 | bool can_distribute_opacity() const { return fCanDistributeOpacity; } |
| 178 | SaveLayerOptions with_can_distribute_opacity() const { |
| 179 | SaveLayerOptions options(this); |
| 180 | options.fCanDistributeOpacity = true; |
| 181 | return options; |
| 182 | } |
| 183 | |
| 184 | SaveLayerOptions& operator=(const SaveLayerOptions& other) { |
| 185 | flags_ = other.flags_; |
| 186 | return *this; |
| 187 | } |
| 188 | bool operator==(const SaveLayerOptions& other) const { |
| 189 | return flags_ == other.flags_; |
| 190 | } |
| 191 | bool operator!=(const SaveLayerOptions& other) const { |
| 192 | return flags_ != other.flags_; |
| 193 | } |
| 194 | |
| 195 | private: |
| 196 | union { |
| 197 | struct { |
| 198 | unsigned fRendersWithAttributes : 1; |
| 199 | unsigned fCanDistributeOpacity : 1; |
| 200 | }; |
| 201 | uint32_t flags_; |
| 202 | }; |
| 203 | }; |
| 204 | |
| 205 | // Manages a buffer allocated with malloc. |
| 206 | class DisplayListStorage { |
| 207 | public: |
| 208 | DisplayListStorage() = default; |
| 209 | DisplayListStorage(DisplayListStorage&&) = default; |
| 210 | |
| 211 | uint8_t* get() const { return ptr_.get(); } |
| 212 | |
| 213 | void realloc(size_t count) { |
| 214 | ptr_.reset(p: static_cast<uint8_t*>(std::realloc(ptr: ptr_.release(), size: count))); |
| 215 | FML_CHECK(ptr_); |
| 216 | } |
| 217 | |
| 218 | private: |
| 219 | struct FreeDeleter { |
| 220 | void operator()(uint8_t* p) { std::free(ptr: p); } |
| 221 | }; |
| 222 | std::unique_ptr<uint8_t, FreeDeleter> ptr_; |
| 223 | }; |
| 224 | |
| 225 | class Culler; |
| 226 | |
| 227 | // The base class that contains a sequence of rendering operations |
| 228 | // for dispatch to a DlOpReceiver. These objects must be instantiated |
| 229 | // through an instance of DisplayListBuilder::build(). |
| 230 | class DisplayList : public SkRefCnt { |
| 231 | public: |
| 232 | DisplayList(); |
| 233 | |
| 234 | ~DisplayList(); |
| 235 | |
| 236 | void Dispatch(DlOpReceiver& ctx) const; |
| 237 | void Dispatch(DlOpReceiver& ctx, const SkRect& cull_rect) const; |
| 238 | void Dispatch(DlOpReceiver& ctx, const SkIRect& cull_rect) const; |
| 239 | |
| 240 | // From historical behavior, SkPicture always included nested bytes, |
| 241 | // but nested ops are only included if requested. The defaults used |
| 242 | // here for these accessors follow that pattern. |
| 243 | size_t bytes(bool nested = true) const { |
| 244 | return sizeof(DisplayList) + byte_count_ + |
| 245 | (nested ? nested_byte_count_ : 0); |
| 246 | } |
| 247 | |
| 248 | unsigned int op_count(bool nested = false) const { |
| 249 | return op_count_ + (nested ? nested_op_count_ : 0); |
| 250 | } |
| 251 | |
| 252 | uint32_t unique_id() const { return unique_id_; } |
| 253 | |
| 254 | const SkRect& bounds() const { return bounds_; } |
| 255 | |
| 256 | bool has_rtree() const { return rtree_ != nullptr; } |
| 257 | sk_sp<const DlRTree> rtree() const { return rtree_; } |
| 258 | |
| 259 | bool Equals(const DisplayList* other) const; |
| 260 | bool Equals(const DisplayList& other) const { return Equals(other: &other); } |
| 261 | bool Equals(const sk_sp<const DisplayList>& other) const { |
| 262 | return Equals(other: other.get()); |
| 263 | } |
| 264 | |
| 265 | bool can_apply_group_opacity() const { return can_apply_group_opacity_; } |
| 266 | bool isUIThreadSafe() const { return is_ui_thread_safe_; } |
| 267 | |
| 268 | /// @brief Indicates if there are any rendering operations in this |
| 269 | /// DisplayList that will modify a surface of transparent black |
| 270 | /// pixels. |
| 271 | /// |
| 272 | /// This condition can be used to determine whether to create a cleared |
| 273 | /// surface, render a DisplayList into it, and then composite the |
| 274 | /// result into a scene. It is not uncommon for code in the engine to |
| 275 | /// come across such degenerate DisplayList objects when slicing up a |
| 276 | /// frame between platform views. |
| 277 | bool modifies_transparent_black() const { |
| 278 | return modifies_transparent_black_; |
| 279 | } |
| 280 | |
| 281 | private: |
| 282 | DisplayList(DisplayListStorage&& ptr, |
| 283 | size_t byte_count, |
| 284 | unsigned int op_count, |
| 285 | size_t nested_byte_count, |
| 286 | unsigned int nested_op_count, |
| 287 | const SkRect& bounds, |
| 288 | bool can_apply_group_opacity, |
| 289 | bool is_ui_thread_safe, |
| 290 | bool modifies_transparent_black, |
| 291 | sk_sp<const DlRTree> rtree); |
| 292 | |
| 293 | static uint32_t next_unique_id(); |
| 294 | |
| 295 | static void DisposeOps(uint8_t* ptr, uint8_t* end); |
| 296 | |
| 297 | const DisplayListStorage storage_; |
| 298 | const size_t byte_count_; |
| 299 | const unsigned int op_count_; |
| 300 | |
| 301 | const size_t nested_byte_count_; |
| 302 | const unsigned int nested_op_count_; |
| 303 | |
| 304 | const uint32_t unique_id_; |
| 305 | const SkRect bounds_; |
| 306 | |
| 307 | const bool can_apply_group_opacity_; |
| 308 | const bool is_ui_thread_safe_; |
| 309 | const bool modifies_transparent_black_; |
| 310 | |
| 311 | const sk_sp<const DlRTree> rtree_; |
| 312 | |
| 313 | void Dispatch(DlOpReceiver& ctx, |
| 314 | uint8_t* ptr, |
| 315 | uint8_t* end, |
| 316 | Culler& culler) const; |
| 317 | |
| 318 | friend class DisplayListBuilder; |
| 319 | }; |
| 320 | |
| 321 | } // namespace flutter |
| 322 | |
| 323 | #endif // FLUTTER_DISPLAY_LIST_DISPLAY_LIST_H_ |
| 324 | |