| // 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 <type_traits> | 
 |  | 
 | #include "flutter/display_list/display_list.h" | 
 | #include "flutter/display_list/dl_op_records.h" | 
 | #include "flutter/fml/trace_event.h" | 
 |  | 
 | namespace flutter { | 
 |  | 
 | const SaveLayerOptions SaveLayerOptions::kNoAttributes = SaveLayerOptions(); | 
 | const SaveLayerOptions SaveLayerOptions::kWithAttributes = | 
 |     kNoAttributes.with_renders_with_attributes(); | 
 |  | 
 | DisplayList::DisplayList() | 
 |     : byte_count_(0), | 
 |       op_count_(0), | 
 |       nested_byte_count_(0), | 
 |       nested_op_count_(0), | 
 |       total_depth_(0), | 
 |       unique_id_(0), | 
 |       bounds_({0, 0, 0, 0}), | 
 |       can_apply_group_opacity_(true), | 
 |       is_ui_thread_safe_(true), | 
 |       modifies_transparent_black_(false), | 
 |       root_has_backdrop_filter_(false), | 
 |       root_is_unbounded_(false), | 
 |       max_root_blend_mode_(DlBlendMode::kClear) {} | 
 |  | 
 | // Eventually we should rework DisplayListBuilder to compute these and | 
 | // deliver the vector alongside the storage. | 
 | static std::vector<size_t> MakeOffsets(const DisplayListStorage& storage, | 
 |                                        size_t byte_count) { | 
 |   std::vector<size_t> offsets; | 
 |   const uint8_t* start = storage.get(); | 
 |   const uint8_t* end = start + byte_count; | 
 |   const uint8_t* ptr = start; | 
 |   while (ptr < end) { | 
 |     offsets.push_back(ptr - start); | 
 |     ptr += reinterpret_cast<const DLOp*>(ptr)->size; | 
 |   } | 
 |   return offsets; | 
 | } | 
 |  | 
 | DisplayList::DisplayList(DisplayListStorage&& storage, | 
 |                          size_t byte_count, | 
 |                          uint32_t op_count, | 
 |                          size_t nested_byte_count, | 
 |                          uint32_t nested_op_count, | 
 |                          uint32_t total_depth, | 
 |                          const SkRect& bounds, | 
 |                          bool can_apply_group_opacity, | 
 |                          bool is_ui_thread_safe, | 
 |                          bool modifies_transparent_black, | 
 |                          DlBlendMode max_root_blend_mode, | 
 |                          bool root_has_backdrop_filter, | 
 |                          bool root_is_unbounded, | 
 |                          sk_sp<const DlRTree> rtree) | 
 |     : storage_(std::move(storage)), | 
 |       offsets_(MakeOffsets(storage_, byte_count)), | 
 |       byte_count_(byte_count), | 
 |       op_count_(op_count), | 
 |       nested_byte_count_(nested_byte_count), | 
 |       nested_op_count_(nested_op_count), | 
 |       total_depth_(total_depth), | 
 |       unique_id_(next_unique_id()), | 
 |       bounds_(bounds), | 
 |       can_apply_group_opacity_(can_apply_group_opacity), | 
 |       is_ui_thread_safe_(is_ui_thread_safe), | 
 |       modifies_transparent_black_(modifies_transparent_black), | 
 |       root_has_backdrop_filter_(root_has_backdrop_filter), | 
 |       root_is_unbounded_(root_is_unbounded), | 
 |       max_root_blend_mode_(max_root_blend_mode), | 
 |       rtree_(std::move(rtree)) {} | 
 |  | 
 | DisplayList::~DisplayList() { | 
 |   const uint8_t* ptr = storage_.get(); | 
 |   DisposeOps(ptr, ptr + byte_count_); | 
 | } | 
 |  | 
 | uint32_t DisplayList::next_unique_id() { | 
 |   static std::atomic<uint32_t> next_id{1}; | 
 |   uint32_t id; | 
 |   do { | 
 |     id = next_id.fetch_add(+1, std::memory_order_relaxed); | 
 |   } while (id == 0); | 
 |   return id; | 
 | } | 
 |  | 
 | struct SaveInfo { | 
 |   SaveInfo(DlIndex previous_restore_index, bool save_was_needed) | 
 |       : previous_restore_index(previous_restore_index), | 
 |         save_was_needed(save_was_needed) {} | 
 |  | 
 |   DlIndex previous_restore_index; | 
 |   bool save_was_needed; | 
 | }; | 
 |  | 
 | void DisplayList::RTreeResultsToIndexVector( | 
 |     std::vector<DlIndex>& indices, | 
 |     const std::vector<int>& rtree_results) const { | 
 |   FML_DCHECK(rtree_); | 
 |   auto cur_rect = rtree_results.begin(); | 
 |   auto end_rect = rtree_results.end(); | 
 |   if (cur_rect >= end_rect) { | 
 |     return; | 
 |   } | 
 |   DlIndex next_render_index = rtree_->id(*cur_rect++); | 
 |   DlIndex next_restore_index = std::numeric_limits<DlIndex>::max(); | 
 |   std::vector<SaveInfo> save_infos; | 
 |   for (DlIndex index = 0u; index < offsets_.size(); index++) { | 
 |     while (index > next_render_index) { | 
 |       if (cur_rect < end_rect) { | 
 |         next_render_index = rtree_->id(*cur_rect++); | 
 |       } else { | 
 |         // Nothing left to render. | 
 |         // Nothing left to do, but match our restores from the stack. | 
 |         while (!save_infos.empty()) { | 
 |           SaveInfo& info = save_infos.back(); | 
 |           // stack top boolean tells us whether the local variable | 
 |           // next_restore_index should be executed. The local variable | 
 |           // then gets reset to the value stored in the stack top | 
 |           if (info.save_was_needed) { | 
 |             FML_DCHECK(next_restore_index < offsets_.size()); | 
 |             indices.push_back(next_restore_index); | 
 |           } | 
 |           next_restore_index = info.previous_restore_index; | 
 |           save_infos.pop_back(); | 
 |         } | 
 |         return; | 
 |       } | 
 |     } | 
 |     const uint8_t* ptr = storage_.get() + offsets_[index]; | 
 |     const DLOp* op = reinterpret_cast<const DLOp*>(ptr); | 
 |     switch (GetOpCategory(op->type)) { | 
 |       case DisplayListOpCategory::kAttribute: | 
 |         // Attributes are always needed | 
 |         indices.push_back(index); | 
 |         break; | 
 |  | 
 |       case DisplayListOpCategory::kTransform: | 
 |       case DisplayListOpCategory::kClip: | 
 |         if (next_render_index < next_restore_index) { | 
 |           indices.push_back(index); | 
 |         } | 
 |         break; | 
 |  | 
 |       case DisplayListOpCategory::kRendering: | 
 |       case DisplayListOpCategory::kSubDisplayList: | 
 |         if (index == next_render_index) { | 
 |           indices.push_back(index); | 
 |         } | 
 |         break; | 
 |  | 
 |       case DisplayListOpCategory::kSave: | 
 |       case DisplayListOpCategory::kSaveLayer: { | 
 |         bool needed = (next_render_index < next_restore_index); | 
 |         save_infos.emplace_back(next_restore_index, needed); | 
 |         switch (op->type) { | 
 |           case DisplayListOpType::kSave: | 
 |           case DisplayListOpType::kSaveLayer: | 
 |           case DisplayListOpType::kSaveLayerBackdrop: | 
 |             next_restore_index = | 
 |                 static_cast<const SaveOpBase*>(op)->restore_index; | 
 |             break; | 
 |           default: | 
 |             FML_UNREACHABLE(); | 
 |         } | 
 |         if (needed) { | 
 |           indices.push_back(index); | 
 |         } | 
 |         break; | 
 |       } | 
 |  | 
 |       case DisplayListOpCategory::kRestore: { | 
 |         FML_DCHECK(!save_infos.empty()); | 
 |         FML_DCHECK(index == next_restore_index); | 
 |         SaveInfo& info = save_infos.back(); | 
 |         next_restore_index = info.previous_restore_index; | 
 |         if (info.save_was_needed) { | 
 |           indices.push_back(index); | 
 |         } | 
 |         save_infos.pop_back(); | 
 |         break; | 
 |       } | 
 |  | 
 |       case DisplayListOpCategory::kInvalidCategory: | 
 |         FML_UNREACHABLE(); | 
 |     } | 
 |   } | 
 | } | 
 |  | 
 | void DisplayList::Dispatch(DlOpReceiver& receiver) const { | 
 |   const uint8_t* base = storage_.get(); | 
 |   for (size_t offset : offsets_) { | 
 |     DispatchOneOp(receiver, base + offset); | 
 |   } | 
 | } | 
 |  | 
 | void DisplayList::Dispatch(DlOpReceiver& receiver, | 
 |                            const SkIRect& cull_rect) const { | 
 |   Dispatch(receiver, SkRect::Make(cull_rect)); | 
 | } | 
 |  | 
 | void DisplayList::Dispatch(DlOpReceiver& receiver, | 
 |                            const SkRect& cull_rect) const { | 
 |   if (cull_rect.isEmpty()) { | 
 |     return; | 
 |   } | 
 |   if (!has_rtree() || cull_rect.contains(bounds())) { | 
 |     Dispatch(receiver); | 
 |   } else { | 
 |     auto op_indices = GetCulledIndices(cull_rect); | 
 |     const uint8_t* base = storage_.get(); | 
 |     for (DlIndex index : op_indices) { | 
 |       DispatchOneOp(receiver, base + offsets_[index]); | 
 |     } | 
 |   } | 
 | } | 
 |  | 
 | void DisplayList::DispatchOneOp(DlOpReceiver& receiver, | 
 |                                 const uint8_t* ptr) const { | 
 |   auto op = reinterpret_cast<const DLOp*>(ptr); | 
 |   switch (op->type) { | 
 | #define DL_OP_DISPATCH(name)                              \ | 
 |   case DisplayListOpType::k##name:                        \ | 
 |     static_cast<const name##Op*>(op)->dispatch(receiver); \ | 
 |     break; | 
 |  | 
 |     FOR_EACH_DISPLAY_LIST_OP(DL_OP_DISPATCH) | 
 |  | 
 | #undef DL_OP_DISPATCH | 
 |  | 
 |     case DisplayListOpType::kInvalidOp: | 
 |     default: | 
 |       FML_DCHECK(false) << "Unrecognized op type: " | 
 |                         << static_cast<int>(op->type); | 
 |   } | 
 | } | 
 |  | 
 | void DisplayList::DisposeOps(const uint8_t* ptr, const uint8_t* end) { | 
 |   while (ptr < end) { | 
 |     auto op = reinterpret_cast<const DLOp*>(ptr); | 
 |     ptr += op->size; | 
 |     FML_DCHECK(ptr <= end); | 
 |     switch (op->type) { | 
 | #define DL_OP_DISPOSE(name)                            \ | 
 |   case DisplayListOpType::k##name:                     \ | 
 |     if (!std::is_trivially_destructible_v<name##Op>) { \ | 
 |       static_cast<const name##Op*>(op)->~name##Op();   \ | 
 |     }                                                  \ | 
 |     break; | 
 |  | 
 |       FOR_EACH_DISPLAY_LIST_OP(DL_OP_DISPOSE) | 
 |  | 
 | #undef DL_OP_DISPOSE | 
 |  | 
 |       default: | 
 |         FML_UNREACHABLE(); | 
 |     } | 
 |   } | 
 | } | 
 |  | 
 | DisplayListOpCategory DisplayList::GetOpCategory(DlIndex index) const { | 
 |   return GetOpCategory(GetOpType(index)); | 
 | } | 
 |  | 
 | DisplayListOpCategory DisplayList::GetOpCategory(DisplayListOpType type) { | 
 |   switch (type) { | 
 |     case DisplayListOpType::kSetAntiAlias: | 
 |     case DisplayListOpType::kSetInvertColors: | 
 |     case DisplayListOpType::kSetStrokeCap: | 
 |     case DisplayListOpType::kSetStrokeJoin: | 
 |     case DisplayListOpType::kSetStyle: | 
 |     case DisplayListOpType::kSetStrokeWidth: | 
 |     case DisplayListOpType::kSetStrokeMiter: | 
 |     case DisplayListOpType::kSetColor: | 
 |     case DisplayListOpType::kSetBlendMode: | 
 |     case DisplayListOpType::kClearColorFilter: | 
 |     case DisplayListOpType::kSetPodColorFilter: | 
 |     case DisplayListOpType::kClearColorSource: | 
 |     case DisplayListOpType::kSetPodColorSource: | 
 |     case DisplayListOpType::kSetImageColorSource: | 
 |     case DisplayListOpType::kSetRuntimeEffectColorSource: | 
 |     case DisplayListOpType::kClearImageFilter: | 
 |     case DisplayListOpType::kSetPodImageFilter: | 
 |     case DisplayListOpType::kSetSharedImageFilter: | 
 |     case DisplayListOpType::kClearMaskFilter: | 
 |     case DisplayListOpType::kSetPodMaskFilter: | 
 |       return DisplayListOpCategory::kAttribute; | 
 |  | 
 |     case DisplayListOpType::kSave: | 
 |       return DisplayListOpCategory::kSave; | 
 |     case DisplayListOpType::kSaveLayer: | 
 |     case DisplayListOpType::kSaveLayerBackdrop: | 
 |       return DisplayListOpCategory::kSaveLayer; | 
 |     case DisplayListOpType::kRestore: | 
 |       return DisplayListOpCategory::kRestore; | 
 |  | 
 |     case DisplayListOpType::kTranslate: | 
 |     case DisplayListOpType::kScale: | 
 |     case DisplayListOpType::kRotate: | 
 |     case DisplayListOpType::kSkew: | 
 |     case DisplayListOpType::kTransform2DAffine: | 
 |     case DisplayListOpType::kTransformFullPerspective: | 
 |     case DisplayListOpType::kTransformReset: | 
 |       return DisplayListOpCategory::kTransform; | 
 |  | 
 |     case DisplayListOpType::kClipIntersectRect: | 
 |     case DisplayListOpType::kClipIntersectOval: | 
 |     case DisplayListOpType::kClipIntersectRRect: | 
 |     case DisplayListOpType::kClipIntersectPath: | 
 |     case DisplayListOpType::kClipDifferenceRect: | 
 |     case DisplayListOpType::kClipDifferenceOval: | 
 |     case DisplayListOpType::kClipDifferenceRRect: | 
 |     case DisplayListOpType::kClipDifferencePath: | 
 |       return DisplayListOpCategory::kClip; | 
 |  | 
 |     case DisplayListOpType::kDrawPaint: | 
 |     case DisplayListOpType::kDrawColor: | 
 |     case DisplayListOpType::kDrawLine: | 
 |     case DisplayListOpType::kDrawDashedLine: | 
 |     case DisplayListOpType::kDrawRect: | 
 |     case DisplayListOpType::kDrawOval: | 
 |     case DisplayListOpType::kDrawCircle: | 
 |     case DisplayListOpType::kDrawRRect: | 
 |     case DisplayListOpType::kDrawDRRect: | 
 |     case DisplayListOpType::kDrawArc: | 
 |     case DisplayListOpType::kDrawPath: | 
 |     case DisplayListOpType::kDrawPoints: | 
 |     case DisplayListOpType::kDrawLines: | 
 |     case DisplayListOpType::kDrawPolygon: | 
 |     case DisplayListOpType::kDrawVertices: | 
 |     case DisplayListOpType::kDrawImage: | 
 |     case DisplayListOpType::kDrawImageWithAttr: | 
 |     case DisplayListOpType::kDrawImageRect: | 
 |     case DisplayListOpType::kDrawImageNine: | 
 |     case DisplayListOpType::kDrawImageNineWithAttr: | 
 |     case DisplayListOpType::kDrawAtlas: | 
 |     case DisplayListOpType::kDrawAtlasCulled: | 
 |     case DisplayListOpType::kDrawTextBlob: | 
 |     case DisplayListOpType::kDrawTextFrame: | 
 |     case DisplayListOpType::kDrawShadow: | 
 |     case DisplayListOpType::kDrawShadowTransparentOccluder: | 
 |       return DisplayListOpCategory::kRendering; | 
 |  | 
 |     case DisplayListOpType::kDrawDisplayList: | 
 |       return DisplayListOpCategory::kSubDisplayList; | 
 |  | 
 |     case DisplayListOpType::kInvalidOp: | 
 |       return DisplayListOpCategory::kInvalidCategory; | 
 |   } | 
 | } | 
 |  | 
 | DisplayListOpType DisplayList::GetOpType(DlIndex index) const { | 
 |   // Assert unsigned type so we can eliminate >= 0 comparison | 
 |   static_assert(std::is_unsigned_v<DlIndex>); | 
 |   if (index >= offsets_.size()) { | 
 |     return DisplayListOpType::kInvalidOp; | 
 |   } | 
 |  | 
 |   size_t offset = offsets_[index]; | 
 |   FML_DCHECK(offset < byte_count_); | 
 |   auto ptr = storage_.get() + offset; | 
 |   auto op = reinterpret_cast<const DLOp*>(ptr); | 
 |   FML_DCHECK(ptr + op->size <= storage_.get() + byte_count_); | 
 |   return op->type; | 
 | } | 
 |  | 
 | static void FillAllIndices(std::vector<DlIndex>& indices, DlIndex size) { | 
 |   indices.reserve(size); | 
 |   for (DlIndex i = 0u; i < size; i++) { | 
 |     indices.push_back(i); | 
 |   } | 
 | } | 
 |  | 
 | std::vector<DlIndex> DisplayList::GetCulledIndices( | 
 |     const SkRect& cull_rect) const { | 
 |   std::vector<DlIndex> indices; | 
 |   if (!cull_rect.isEmpty()) { | 
 |     if (rtree_) { | 
 |       std::vector<int> rect_indices; | 
 |       rtree_->search(cull_rect, &rect_indices); | 
 |       RTreeResultsToIndexVector(indices, rect_indices); | 
 |     } else { | 
 |       FillAllIndices(indices, offsets_.size()); | 
 |     } | 
 |   } | 
 |   return indices; | 
 | } | 
 |  | 
 | bool DisplayList::Dispatch(DlOpReceiver& receiver, DlIndex index) const { | 
 |   // Assert unsigned type so we can eliminate >= 0 comparison | 
 |   static_assert(std::is_unsigned_v<DlIndex>); | 
 |   if (index >= offsets_.size()) { | 
 |     return false; | 
 |   } | 
 |  | 
 |   size_t offset = offsets_[index]; | 
 |   FML_DCHECK(offset < byte_count_); | 
 |   auto ptr = storage_.get() + offset; | 
 |   FML_DCHECK(offset + reinterpret_cast<const DLOp*>(ptr)->size <= byte_count_); | 
 |  | 
 |   DispatchOneOp(receiver, ptr); | 
 |  | 
 |   return true; | 
 | } | 
 |  | 
 | static bool CompareOps(const uint8_t* ptrA, | 
 |                        const uint8_t* endA, | 
 |                        const uint8_t* ptrB, | 
 |                        const uint8_t* endB) { | 
 |   // These conditions are checked by the caller... | 
 |   FML_DCHECK((endA - ptrA) == (endB - ptrB)); | 
 |   FML_DCHECK(ptrA != ptrB); | 
 |   const uint8_t* bulk_start_a = ptrA; | 
 |   const uint8_t* bulk_start_b = ptrB; | 
 |   while (ptrA < endA && ptrB < endB) { | 
 |     auto opA = reinterpret_cast<const DLOp*>(ptrA); | 
 |     auto opB = reinterpret_cast<const DLOp*>(ptrB); | 
 |     if (opA->type != opB->type || opA->size != opB->size) { | 
 |       return false; | 
 |     } | 
 |     ptrA += opA->size; | 
 |     ptrB += opB->size; | 
 |     FML_DCHECK(ptrA <= endA); | 
 |     FML_DCHECK(ptrB <= endB); | 
 |     DisplayListCompare result; | 
 |     switch (opA->type) { | 
 | #define DL_OP_EQUALS(name)                              \ | 
 |   case DisplayListOpType::k##name:                      \ | 
 |     result = static_cast<const name##Op*>(opA)->equals( \ | 
 |         static_cast<const name##Op*>(opB));             \ | 
 |     break; | 
 |  | 
 |       FOR_EACH_DISPLAY_LIST_OP(DL_OP_EQUALS) | 
 |  | 
 | #undef DL_OP_EQUALS | 
 |  | 
 |       default: | 
 |         FML_DCHECK(false); | 
 |         return false; | 
 |     } | 
 |     switch (result) { | 
 |       case DisplayListCompare::kNotEqual: | 
 |         return false; | 
 |       case DisplayListCompare::kUseBulkCompare: | 
 |         break; | 
 |       case DisplayListCompare::kEqual: | 
 |         // Check if we have a backlog of bytes to bulk compare and then | 
 |         // reset the bulk compare pointers to the address following this op | 
 |         auto bulk_bytes = reinterpret_cast<const uint8_t*>(opA) - bulk_start_a; | 
 |         if (bulk_bytes > 0) { | 
 |           if (memcmp(bulk_start_a, bulk_start_b, bulk_bytes) != 0) { | 
 |             return false; | 
 |           } | 
 |         } | 
 |         bulk_start_a = ptrA; | 
 |         bulk_start_b = ptrB; | 
 |         break; | 
 |     } | 
 |   } | 
 |   if (ptrA != endA || ptrB != endB) { | 
 |     return false; | 
 |   } | 
 |   if (bulk_start_a < ptrA) { | 
 |     // Perform a final bulk compare if we have remaining bytes waiting | 
 |     if (memcmp(bulk_start_a, bulk_start_b, ptrA - bulk_start_a) != 0) { | 
 |       return false; | 
 |     } | 
 |   } | 
 |   return true; | 
 | } | 
 |  | 
 | bool DisplayList::Equals(const DisplayList* other) const { | 
 |   if (this == other) { | 
 |     return true; | 
 |   } | 
 |   if (byte_count_ != other->byte_count_ || op_count_ != other->op_count_) { | 
 |     return false; | 
 |   } | 
 |   const uint8_t* ptr = storage_.get(); | 
 |   const uint8_t* o_ptr = other->storage_.get(); | 
 |   if (ptr == o_ptr) { | 
 |     return true; | 
 |   } | 
 |   return CompareOps(ptr, ptr + byte_count_, o_ptr, o_ptr + other->byte_count_); | 
 | } | 
 |  | 
 | }  // namespace flutter |