From 504db15c59aab66119e493abc0375517daf9e43f Mon Sep 17 00:00:00 2001 From: Brandon DeRosier Date: Tue, 20 Aug 2024 15:10:03 -0700 Subject: [PATCH] Revert "[DisplayList] Allow random access to ops through indexing (#54484)" This reverts commit db034a514caf851cfe063a77f3b27a797316c740. --- .../benchmarking/dl_builder_benchmarks.cc | 94 ---- display_list/display_list.cc | 392 ++++----------- display_list/display_list.h | 181 +------ display_list/display_list_unittests.cc | 175 +------ display_list/dl_builder.h | 2 +- display_list/dl_op_records.h | 460 +++++++++++------- testing/display_list_testing.cc | 118 ++--- testing/display_list_testing.h | 396 --------------- 8 files changed, 463 insertions(+), 1355 deletions(-) diff --git a/display_list/benchmarking/dl_builder_benchmarks.cc b/display_list/benchmarking/dl_builder_benchmarks.cc index 3463ba55685cb..8f9f05ea895dd 100644 --- a/display_list/benchmarking/dl_builder_benchmarks.cc +++ b/display_list/benchmarking/dl_builder_benchmarks.cc @@ -220,60 +220,6 @@ static void BM_DisplayListDispatchDefault( } } -static void BM_DisplayListDispatchByIndexDefault( - benchmark::State& state, - DisplayListDispatchBenchmarkType type) { - bool prepare_rtree = NeedPrepareRTree(type); - DisplayListBuilder builder(prepare_rtree); - for (int i = 0; i < 5; i++) { - InvokeAllOps(builder); - } - auto display_list = builder.Build(); - DlOpReceiverIgnore receiver; - while (state.KeepRunning()) { - DlIndex end = display_list->GetRecordCount(); - for (DlIndex i = 0u; i < end; i++) { - display_list->Dispatch(receiver, i); - } - } -} - -static void BM_DisplayListDispatchByIteratorDefault( - benchmark::State& state, - DisplayListDispatchBenchmarkType type) { - bool prepare_rtree = NeedPrepareRTree(type); - DisplayListBuilder builder(prepare_rtree); - for (int i = 0; i < 5; i++) { - InvokeAllOps(builder); - } - auto display_list = builder.Build(); - DlOpReceiverIgnore receiver; - while (state.KeepRunning()) { - for (DlIndex i : *display_list) { - display_list->Dispatch(receiver, i); - } - } -} - -static void BM_DisplayListDispatchByVectorDefault( - benchmark::State& state, - DisplayListDispatchBenchmarkType type) { - bool prepare_rtree = NeedPrepareRTree(type); - DisplayListBuilder builder(prepare_rtree); - for (int i = 0; i < 5; i++) { - InvokeAllOps(builder); - } - auto display_list = builder.Build(); - DlOpReceiverIgnore receiver; - while (state.KeepRunning()) { - std::vector indices = - display_list->GetCulledIndices(display_list->bounds()); - for (DlIndex index : indices) { - display_list->Dispatch(receiver, index); - } - } -} - static void BM_DisplayListDispatchCull(benchmark::State& state, DisplayListDispatchBenchmarkType type) { bool prepare_rtree = NeedPrepareRTree(type); @@ -290,26 +236,6 @@ static void BM_DisplayListDispatchCull(benchmark::State& state, } } -static void BM_DisplayListDispatchByVectorCull( - benchmark::State& state, - DisplayListDispatchBenchmarkType type) { - bool prepare_rtree = NeedPrepareRTree(type); - DisplayListBuilder builder(prepare_rtree); - for (int i = 0; i < 5; i++) { - InvokeAllOps(builder); - } - auto display_list = builder.Build(); - SkRect rect = SkRect::MakeLTRB(0, 0, 100, 100); - EXPECT_FALSE(rect.contains(display_list->bounds())); - DlOpReceiverIgnore receiver; - while (state.KeepRunning()) { - std::vector indices = display_list->GetCulledIndices(rect); - for (DlIndex index : indices) { - display_list->Dispatch(receiver, index); - } - } -} - BENCHMARK_CAPTURE(BM_DisplayListBuilderDefault, kDefault, DisplayListBuilderBenchmarkType::kDefault) @@ -444,24 +370,4 @@ BENCHMARK_CAPTURE(BM_DisplayListDispatchCull, DisplayListDispatchBenchmarkType::kCulledWithRtree) ->Unit(benchmark::kMicrosecond); -BENCHMARK_CAPTURE(BM_DisplayListDispatchByIndexDefault, - kDefaultNoRtree, - DisplayListDispatchBenchmarkType::kDefaultNoRtree) - ->Unit(benchmark::kMicrosecond); - -BENCHMARK_CAPTURE(BM_DisplayListDispatchByIteratorDefault, - kDefaultNoRtree, - DisplayListDispatchBenchmarkType::kDefaultNoRtree) - ->Unit(benchmark::kMicrosecond); - -BENCHMARK_CAPTURE(BM_DisplayListDispatchByVectorDefault, - kDefaultNoRtree, - DisplayListDispatchBenchmarkType::kDefaultNoRtree) - ->Unit(benchmark::kMicrosecond); - -BENCHMARK_CAPTURE(BM_DisplayListDispatchByVectorCull, - kCulledWithRtree, - DisplayListDispatchBenchmarkType::kCulledWithRtree) - ->Unit(benchmark::kMicrosecond); - } // namespace flutter diff --git a/display_list/display_list.cc b/display_list/display_list.cc index b72256d5812d4..ccfd97e7a963e 100644 --- a/display_list/display_list.cc +++ b/display_list/display_list.cc @@ -29,21 +29,6 @@ DisplayList::DisplayList() 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 MakeOffsets(const DisplayListStorage& storage, - size_t byte_count) { - std::vector 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(ptr)->size; - } - return offsets; -} - DisplayList::DisplayList(DisplayListStorage&& storage, size_t byte_count, uint32_t op_count, @@ -59,7 +44,6 @@ DisplayList::DisplayList(DisplayListStorage&& storage, bool root_is_unbounded, sk_sp rtree) : storage_(std::move(storage)), - offsets_(MakeOffsets(storage_, byte_count)), byte_count_(byte_count), op_count_(op_count), nested_byte_count_(nested_byte_count), @@ -89,114 +73,86 @@ uint32_t DisplayList::next_unique_id() { 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; +class Culler { + public: + virtual ~Culler() = default; + virtual bool init(DispatchContext& context) = 0; + virtual void update(DispatchContext& context) = 0; }; - -void DisplayList::RTreeResultsToIndexVector( - std::vector& indices, - const std::vector& rtree_results) const { - FML_DCHECK(rtree_); - auto cur_rect = rtree_results.begin(); - auto end_rect = rtree_results.end(); - if (cur_rect >= end_rect) { - return; +class NopCuller final : public Culler { + public: + static NopCuller instance; + + ~NopCuller() = default; + + bool init(DispatchContext& context) override { + // Setting next_render_index to 0 means that + // all rendering ops will be at or after that + // index so they will execute and all restore + // indices will be after it as well so all + // clip and transform operations will execute. + context.next_render_index = 0; + return true; } - DlIndex next_render_index = rtree_->id(*cur_rect++); - DlIndex next_restore_index = std::numeric_limits::max(); - std::vector 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; - } + void update(DispatchContext& context) override {} +}; +NopCuller NopCuller::instance = NopCuller(); +class VectorCuller final : public Culler { + public: + VectorCuller(const DlRTree* rtree, const std::vector& rect_indices) + : rtree_(rtree), cur_(rect_indices.begin()), end_(rect_indices.end()) {} + + ~VectorCuller() = default; + + bool init(DispatchContext& context) override { + if (cur_ < end_) { + context.next_render_index = rtree_->id(*cur_++); + return true; + } else { + // Setting next_render_index to MAX_INT means that + // all rendering ops will be "before" that index and + // they will skip themselves and all clip and transform + // ops will see that the next render index is not + // before the next restore index (even if both are MAX_INT) + // and so they will also not execute. + // None of this really matters because returning false + // here should cause the Dispatch operation to abort, + // but this value is conceptually correct if that short + // circuit optimization isn't used. + context.next_render_index = std::numeric_limits::max(); + return false; } - const uint8_t* ptr = storage_.get() + offsets_[index]; - const DLOp* op = reinterpret_cast(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 = (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(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); + } + void update(DispatchContext& context) override { + if (++context.cur_index > context.next_render_index) { + while (cur_ < end_) { + context.next_render_index = rtree_->id(*cur_++); + if (context.next_render_index >= context.cur_index) { + // It should be rare that we have duplicate indices + // but if we do, then having a while loop is a cheap + // insurance for those cases. + // The main cause of duplicate indices is when a + // DrawDisplayListOp was added to this DisplayList and + // both are computing an R-Tree, in which case the + // builder method will forward all of the child + // DisplayList's rects to this R-Tree with the same + // op_index. + return; } - save_infos.pop_back(); - break; } - - case DisplayListOpCategory::kInvalidCategory: - FML_UNREACHABLE(); + context.next_render_index = std::numeric_limits::max(); } } -} + + private: + const DlRTree* rtree_; + std::vector::const_iterator cur_; + std::vector::const_iterator end_; +}; void DisplayList::Dispatch(DlOpReceiver& receiver) const { - const uint8_t* base = storage_.get(); - for (size_t offset : offsets_) { - DispatchOneOp(receiver, base + offset); - } + const uint8_t* ptr = storage_.get(); + Dispatch(receiver, ptr, ptr + byte_count_, NopCuller::instance); } void DisplayList::Dispatch(DlOpReceiver& receiver, @@ -211,36 +167,52 @@ void DisplayList::Dispatch(DlOpReceiver& receiver, } 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]); - } + return; } + const DlRTree* rtree = this->rtree().get(); + FML_DCHECK(rtree != nullptr); + const uint8_t* ptr = storage_.get(); + std::vector rect_indices; + rtree->search(cull_rect, &rect_indices); + VectorCuller culler(rtree, rect_indices); + Dispatch(receiver, ptr, ptr + byte_count_, culler); } -void DisplayList::DispatchOneOp(DlOpReceiver& receiver, - const uint8_t* ptr) const { - auto op = reinterpret_cast(ptr); - switch (op->type) { -#define DL_OP_DISPATCH(name) \ - case DisplayListOpType::k##name: \ - static_cast(op)->dispatch(receiver); \ +void DisplayList::Dispatch(DlOpReceiver& receiver, + const uint8_t* ptr, + const uint8_t* end, + Culler& culler) const { + DispatchContext context = { + .receiver = receiver, + .cur_index = 0, + // next_render_index will be initialized by culler.init() + .next_restore_index = std::numeric_limits::max(), + }; + if (!culler.init(context)) { + return; + } + while (ptr < end) { + auto op = reinterpret_cast(ptr); + ptr += op->size; + FML_DCHECK(ptr <= end); + switch (op->type) { +#define DL_OP_DISPATCH(name) \ + case DisplayListOpType::k##name: \ + static_cast(op)->dispatch(context); \ break; - FOR_EACH_DISPLAY_LIST_OP(DL_OP_DISPATCH) - + FOR_EACH_DISPLAY_LIST_OP(DL_OP_DISPATCH) #ifdef IMPELLER_ENABLE_3D - DL_OP_DISPATCH(SetSceneColorSource) + DL_OP_DISPATCH(SetSceneColorSource) #endif // IMPELLER_ENABLE_3D #undef DL_OP_DISPATCH - case DisplayListOpType::kInvalidOp: - default: - FML_DCHECK(false) << "Unrecognized op type: " - << static_cast(op->type); + default: + FML_DCHECK(false); + return; + } + culler.update(context); } } @@ -258,7 +230,6 @@ void DisplayList::DisposeOps(const uint8_t* ptr, const uint8_t* end) { break; FOR_EACH_DISPLAY_LIST_OP(DL_OP_DISPOSE) - #ifdef IMPELLER_ENABLE_3D DL_OP_DISPOSE(SetSceneColorSource) #endif // IMPELLER_ENABLE_3D @@ -271,154 +242,6 @@ void DisplayList::DisposeOps(const uint8_t* ptr, const uint8_t* end) { } } -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: -#ifdef IMPELLER_ENABLE_3D - case DisplayListOpType::kSetSceneColorSource: -#endif // IMPELLER_ENABLE_3D - 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); - 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(ptr); - FML_DCHECK(ptr + op->size <= storage_.get() + byte_count_); - return op->type; -} - -static void FillAllIndices(std::vector& indices, DlIndex size) { - indices.reserve(size); - for (DlIndex i = 0u; i < size; i++) { - indices.push_back(i); - } -} - -std::vector DisplayList::GetCulledIndices( - const SkRect& cull_rect) const { - std::vector indices; - if (!cull_rect.isEmpty()) { - if (rtree_) { - std::vector 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); - 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(ptr)->size <= byte_count_); - - DispatchOneOp(receiver, ptr); - - return true; -} - static bool CompareOps(const uint8_t* ptrA, const uint8_t* endA, const uint8_t* ptrB, @@ -447,7 +270,6 @@ static bool CompareOps(const uint8_t* ptrA, break; FOR_EACH_DISPLAY_LIST_OP(DL_OP_EQUALS) - #ifdef IMPELLER_ENABLE_3D DL_OP_EQUALS(SetSceneColorSource) #endif // IMPELLER_ENABLE_3D diff --git a/display_list/display_list.h b/display_list/display_list.h index 303cb581ab5db..5c3ef29625988 100644 --- a/display_list/display_list.h +++ b/display_list/display_list.h @@ -142,30 +142,12 @@ namespace flutter { #define DL_OP_TO_ENUM_VALUE(name) k##name, enum class DisplayListOpType { FOR_EACH_DISPLAY_LIST_OP(DL_OP_TO_ENUM_VALUE) - #ifdef IMPELLER_ENABLE_3D DL_OP_TO_ENUM_VALUE(SetSceneColorSource) #endif // IMPELLER_ENABLE_3D - - // empty comment to make formatter happy - kInvalidOp, - kMaxOp = kInvalidOp, }; #undef DL_OP_TO_ENUM_VALUE -enum class DisplayListOpCategory { - kAttribute, - kTransform, - kClip, - kSave, - kSaveLayer, - kRestore, - kRendering, - kSubDisplayList, - kInvalidCategory, - kMaxCategory = kInvalidCategory, -}; - class DlOpReceiver; class DisplayListBuilder; @@ -288,7 +270,7 @@ class DisplayListStorage { std::unique_ptr ptr_; }; -using DlIndex = uint32_t; +class Culler; // The base class that contains a sequence of rendering operations // for dispatch to a DlOpReceiver. These objects must be instantiated @@ -378,158 +360,6 @@ class DisplayList : public SkRefCnt { /// be required for the indicated blend mode to do its work. DlBlendMode max_root_blend_mode() const { return max_root_blend_mode_; } - /// @brief Iterator utility class used for the |DisplayList::begin| - /// and |DisplayList::end| methods. It implements just the - /// basic methods to enable iteration-style for loops. - class Iterator { - public: - DlIndex operator*() const { return value_; } - bool operator!=(const Iterator& other) { return value_ != other.value_; } - Iterator& operator++() { - value_++; - return *this; - } - - private: - explicit Iterator(DlIndex value) : value_(value) {} - - DlIndex value_; - - friend class DisplayList; - }; - - /// @brief Return the number of stored records in the DisplayList. - /// - /// Each stored record represents a dispatchable operation that will be - /// sent to a |DlOpReceiver| by the |Dispatch| method. You can directly - /// simulate the |Dispatch| method using a simple for loop on the indices: - /// - /// { - /// for (DlIndex i = 0u; i < display_list->GetRecordCount(); i++) { - /// display_list->Dispatch(my_receiver, i); - /// } - /// } - /// - /// @see |Dispatch(receiver, index)| - /// @see |begin| - /// @see |end| - /// @see |GetCulledIndices| - DlIndex GetRecordCount() const { return offsets_.size(); } - - /// @brief Return an iterator to the start of the stored records, - /// enabling the iteration form of a for loop. - /// - /// Each stored record represents a dispatchable operation that will be - /// sent to a |DlOpReceiver| by the |Dispatch| method. You can directly - /// simulate the |Dispatch| method using a simple for loop on the indices: - /// - /// { - /// for (DlIndex i : *display_list) { - /// display_list->Dispatch(my_receiver, i); - /// } - /// } - /// - /// @see |end| - /// @see |GetCulledIndices| - Iterator begin() const { return Iterator(0u); } - - /// @brief Return an iterator to the end of the stored records, - /// enabling the iteration form of a for loop. - /// - /// Each stored record represents a dispatchable operation that will be - /// sent to a |DlOpReceiver| by the |Dispatch| method. You can directly - /// simulate the |Dispatch| method using a simple for loop on the indices: - /// - /// { - /// for (DlIndex i : *display_list) { - /// display_list->Dispatch(my_receiver, i); - /// } - /// } - /// - /// @see |begin| - /// @see |GetCulledIndices| - Iterator end() const { return Iterator(offsets_.size()); } - - /// @brief Dispatch a single stored operation by its index. - /// - /// Each stored record represents a dispatchable operation that will be - /// sent to a |DlOpReceiver| by the |Dispatch| method. You can use this - /// method to dispatch a single operation to your receiver with an index - /// between |0u| (inclusive) and |GetRecordCount()| (exclusive), as in: - /// - /// { - /// for (DlIndex i = 0u; i < display_list->GetRecordCount(); i++) { - /// display_list->Dispatch(my_receiver, i); - /// } - /// } - /// - /// If the index is out of the range of the stored records, this method - /// will not call any methods on the receiver and return false. You can - /// check the return value for true if you want to make sure you are - /// using valid indices. - /// - /// @see |GetRecordCount| - /// @see |begin| - /// @see |end| - /// @see |GetCulledIndices| - bool Dispatch(DlOpReceiver& receiver, DlIndex index) const; - - /// @brief Return an enum describing the specific op type stored at - /// the indicated index. - /// - /// The specific types of the records are subject to change without notice - /// as the DisplayList code is developed and optimized. These values are - /// useful mostly for debugging purposes and should not be used in - /// production code. - /// - /// @see |GetOpCategory| for a more stable description of the records - DisplayListOpType GetOpType(DlIndex index) const; - - /// @brief Return an enum describing the general category of the - /// operation record stored at the indicated index. - /// - /// The categories are general and stable and can be used fairly safely - /// in production code to plan how to dispatch or reorder ops during - /// final rendering. - /// - /// @see |GetOpType| for a more detailed description of the records - /// primarily for debugging use - DisplayListOpCategory GetOpCategory(DlIndex index) const; - - /// @brief Return an enum describing the general category of the - /// operation record with the given type. - /// - /// @see |GetOpType| for a more detailed description of the records - /// primarily for debugging use - static DisplayListOpCategory GetOpCategory(DisplayListOpType type); - - /// @brief Return a vector of valid indices for records stored in - /// the DisplayList that must be dispatched if you are - /// restricted to the indicated cull_rect. - /// - /// This method can be used along with indexed dispatching to implement - /// RTree culling while still maintaining control over planning of - /// operations to be rendered, as in: - /// - /// { - /// std::vector indices = - /// display_list->GetCulledIndices(cull-rect); - /// for (DlIndex i : indices) { - /// display_list->Dispatch(my_receiver, i); - /// } - /// } - /// - /// The indices returned in the vector will automatically deal with - /// including or culling related operations such as attributes, clips - /// and transforms that will provide state for any rendering operations - /// selected by the culling checks. - /// - /// @see |GetOpType| for a more detailed description of the records - /// primarily for debugging use - /// - /// @see |Dispatch(receiver, index)| - std::vector GetCulledIndices(const SkRect& cull_rect) const; - private: DisplayList(DisplayListStorage&& ptr, size_t byte_count, @@ -551,7 +381,6 @@ class DisplayList : public SkRefCnt { static void DisposeOps(const uint8_t* ptr, const uint8_t* end); const DisplayListStorage storage_; - const std::vector offsets_; const size_t byte_count_; const uint32_t op_count_; @@ -572,10 +401,10 @@ class DisplayList : public SkRefCnt { const sk_sp rtree_; - void DispatchOneOp(DlOpReceiver& receiver, const uint8_t* ptr) const; - - void RTreeResultsToIndexVector(std::vector& indices, - const std::vector& rtree_results) const; + void Dispatch(DlOpReceiver& ctx, + const uint8_t* ptr, + const uint8_t* end, + Culler& culler) const; friend class DisplayListBuilder; }; diff --git a/display_list/display_list_unittests.cc b/display_list/display_list_unittests.cc index 5efd5f53da20b..f09844d25c14b 100644 --- a/display_list/display_list_unittests.cc +++ b/display_list/display_list_unittests.cc @@ -253,70 +253,6 @@ TEST_F(DisplayListTest, EmptyRebuild) { ASSERT_TRUE(dl2->Equals(dl3)); } -TEST_F(DisplayListTest, GeneralReceiverInitialValues) { - DisplayListGeneralReceiver receiver; - - EXPECT_EQ(receiver.GetOpsReceived(), 0u); - - auto max_type = static_cast(DisplayListOpType::kMaxOp); - for (int i = 0; i <= max_type; i++) { - DisplayListOpType type = static_cast(i); - EXPECT_EQ(receiver.GetOpsReceived(type), 0u) << type; - } - - auto max_category = static_cast(DisplayListOpCategory::kMaxCategory); - for (int i = 0; i <= max_category; i++) { - DisplayListOpCategory category = static_cast(i); - EXPECT_EQ(receiver.GetOpsReceived(category), 0u) << category; - } -} - -TEST_F(DisplayListTest, Iteration) { - DisplayListBuilder builder; - builder.DrawRect({10, 10, 20, 20}, DlPaint()); - auto dl = builder.Build(); - for (DlIndex i : *dl) { - EXPECT_EQ(dl->GetOpType(i), DisplayListOpType::kDrawRect) // - << "at " << i; - EXPECT_EQ(dl->GetOpCategory(i), DisplayListOpCategory::kRendering) - << "at " << i; - } -} - -TEST_F(DisplayListTest, InvalidIndices) { - DisplayListBuilder builder; - builder.DrawRect(kTestBounds, DlPaint()); - auto dl = builder.Build(); - DisplayListGeneralReceiver receiver; - - EXPECT_FALSE(dl->Dispatch(receiver, -1)); - EXPECT_FALSE(dl->Dispatch(receiver, dl->GetRecordCount())); - EXPECT_EQ(dl->GetOpType(-1), DisplayListOpType::kInvalidOp); - EXPECT_EQ(dl->GetOpType(dl->GetRecordCount()), DisplayListOpType::kInvalidOp); - EXPECT_EQ(dl->GetOpCategory(-1), DisplayListOpCategory::kInvalidCategory); - EXPECT_EQ(dl->GetOpCategory(dl->GetRecordCount()), - DisplayListOpCategory::kInvalidCategory); - EXPECT_EQ(dl->GetOpCategory(-1), DisplayListOpCategory::kInvalidCategory); - EXPECT_EQ(dl->GetOpCategory(DisplayListOpType::kInvalidOp), - DisplayListOpCategory::kInvalidCategory); - EXPECT_EQ(dl->GetOpCategory(DisplayListOpType::kMaxOp), - DisplayListOpCategory::kInvalidCategory); - EXPECT_EQ(receiver.GetOpsReceived(), 0u); -} - -TEST_F(DisplayListTest, ValidIndices) { - DisplayListBuilder builder; - builder.DrawRect(kTestBounds, DlPaint()); - auto dl = builder.Build(); - DisplayListGeneralReceiver receiver; - - EXPECT_EQ(dl->GetRecordCount(), 1u); - EXPECT_TRUE(dl->Dispatch(receiver, 0u)); - EXPECT_EQ(dl->GetOpType(0u), DisplayListOpType::kDrawRect); - EXPECT_EQ(dl->GetOpCategory(0u), DisplayListOpCategory::kRendering); - EXPECT_EQ(receiver.GetOpsReceived(), 1u); -} - TEST_F(DisplayListTest, BuilderCanBeReused) { DisplayListBuilder builder(kTestBounds); builder.DrawRect(kTestBounds, DlPaint()); @@ -742,38 +678,6 @@ TEST_F(DisplayListTest, SingleOpDisplayListsRecapturedAreEqual) { group.op_name + "(variant " + std::to_string(i + 1) + " == copy)"; ASSERT_TRUE(DisplayListsEQ_Verbose(dl, copy)); ASSERT_EQ(copy->op_count(false), dl->op_count(false)) << desc; - ASSERT_EQ(copy->GetRecordCount(), dl->GetRecordCount()); - ASSERT_EQ(copy->bytes(false), dl->bytes(false)) << desc; - ASSERT_EQ(copy->op_count(true), dl->op_count(true)) << desc; - ASSERT_EQ(copy->bytes(true), dl->bytes(true)) << desc; - ASSERT_EQ(copy->total_depth(), dl->total_depth()) << desc; - ASSERT_EQ(copy->bounds(), dl->bounds()) << desc; - ASSERT_TRUE(copy->Equals(*dl)) << desc; - ASSERT_TRUE(dl->Equals(*copy)) << desc; - } - } -} - -TEST_F(DisplayListTest, SingleOpDisplayListsRecapturedByIndexAreEqual) { - for (auto& group : allGroups) { - for (size_t i = 0; i < group.variants.size(); i++) { - sk_sp dl = Build(group.variants[i]); - // Verify recapturing the replay of the display list is Equals() - // when dispatching directly from the DL to another builder - DisplayListBuilder copy_builder; - DlOpReceiver& r = ToReceiver(copy_builder); - for (DlIndex i = 0; i < dl->GetRecordCount(); i++) { - EXPECT_NE(dl->GetOpType(i), DisplayListOpType::kInvalidOp); - EXPECT_NE(dl->GetOpCategory(i), - DisplayListOpCategory::kInvalidCategory); - EXPECT_TRUE(dl->Dispatch(r, i)); - } - sk_sp copy = copy_builder.Build(); - auto desc = - group.op_name + "(variant " + std::to_string(i + 1) + " == copy)"; - ASSERT_TRUE(DisplayListsEQ_Verbose(dl, copy)); - ASSERT_EQ(copy->op_count(false), dl->op_count(false)) << desc; - ASSERT_EQ(copy->GetRecordCount(), dl->GetRecordCount()); ASSERT_EQ(copy->bytes(false), dl->bytes(false)) << desc; ASSERT_EQ(copy->op_count(true), dl->op_count(true)) << desc; ASSERT_EQ(copy->bytes(true), dl->bytes(true)) << desc; @@ -3236,55 +3140,27 @@ TEST_F(DisplayListTest, RTreeOfClippedSaveLayerFilterScene) { } TEST_F(DisplayListTest, RTreeRenderCulling) { - SkRect rect1 = SkRect::MakeLTRB(0, 0, 10, 10); - SkRect rect2 = SkRect::MakeLTRB(20, 0, 30, 10); - SkRect rect3 = SkRect::MakeLTRB(0, 20, 10, 30); - SkRect rect4 = SkRect::MakeLTRB(20, 20, 30, 30); - DlPaint paint1 = DlPaint().setColor(DlColor::kRed()); - DlPaint paint2 = DlPaint().setColor(DlColor::kGreen()); - DlPaint paint3 = DlPaint().setColor(DlColor::kBlue()); - DlPaint paint4 = DlPaint().setColor(DlColor::kMagenta()); - DisplayListBuilder main_builder(true); - main_builder.DrawRect(rect1, paint1); - main_builder.DrawRect(rect2, paint2); - main_builder.DrawRect(rect3, paint3); - main_builder.DrawRect(rect4, paint4); + DlOpReceiver& main_receiver = ToReceiver(main_builder); + main_receiver.drawRect({0, 0, 10, 10}); + main_receiver.drawRect({20, 0, 30, 10}); + main_receiver.drawRect({0, 20, 10, 30}); + main_receiver.drawRect({20, 20, 30, 30}); auto main = main_builder.Build(); - auto test = [main](SkIRect cull_rect, const sk_sp& expected, - const std::string& label) { - SkRect cull_rectf = SkRect::Make(cull_rect); - + auto test = [main](SkIRect cull_rect, const sk_sp& expected) { { // Test SkIRect culling DisplayListBuilder culling_builder; main->Dispatch(ToReceiver(culling_builder), cull_rect); - EXPECT_TRUE(DisplayListsEQ_Verbose(culling_builder.Build(), expected)) - << "using cull rect " << cull_rect // - << " where " << label; + EXPECT_TRUE(DisplayListsEQ_Verbose(culling_builder.Build(), expected)); } { // Test SkRect culling DisplayListBuilder culling_builder; - main->Dispatch(ToReceiver(culling_builder), cull_rectf); - - EXPECT_TRUE(DisplayListsEQ_Verbose(culling_builder.Build(), expected)) - << "using cull rect " << cull_rectf // - << " where " << label; - } - - { // Test using vector of culled indices - DisplayListBuilder culling_builder; - DlOpReceiver& receiver = ToReceiver(culling_builder); - auto indices = main->GetCulledIndices(cull_rectf); - for (DlIndex i : indices) { - EXPECT_TRUE(main->Dispatch(receiver, i)); - } + main->Dispatch(ToReceiver(culling_builder), SkRect::Make(cull_rect)); - EXPECT_TRUE(DisplayListsEQ_Verbose(culling_builder.Build(), expected)) - << "using culled indices on cull rect " << cull_rectf // - << " where " << label; + EXPECT_TRUE(DisplayListsEQ_Verbose(culling_builder.Build(), expected)); } }; @@ -3294,62 +3170,57 @@ TEST_F(DisplayListTest, RTreeRenderCulling) { DisplayListBuilder expected_builder; auto expected = expected_builder.Build(); - test(cull_rect, expected, "no rects intersect"); + test(cull_rect, expected); } { // Rect 1 SkIRect cull_rect = {9, 9, 19, 19}; DisplayListBuilder expected_builder; - expected_builder.DrawRect(rect1, paint1); + DlOpReceiver& expected_receiver = ToReceiver(expected_builder); + expected_receiver.drawRect({0, 0, 10, 10}); auto expected = expected_builder.Build(); - test(cull_rect, expected, "rect 1 intersects"); + test(cull_rect, expected); } { // Rect 2 SkIRect cull_rect = {11, 9, 21, 19}; DisplayListBuilder expected_builder; - // Unfortunately we don't cull attribute records (yet?) until the last op - ToReceiver(expected_builder).setColor(paint1.getColor()); - expected_builder.DrawRect(rect2, paint2); + DlOpReceiver& expected_receiver = ToReceiver(expected_builder); + expected_receiver.drawRect({20, 0, 30, 10}); auto expected = expected_builder.Build(); - test(cull_rect, expected, "rect 2 intersects"); + test(cull_rect, expected); } { // Rect 3 SkIRect cull_rect = {9, 11, 19, 21}; DisplayListBuilder expected_builder; - // Unfortunately we don't cull attribute records (yet?) until the last op - ToReceiver(expected_builder).setColor(paint1.getColor()); - ToReceiver(expected_builder).setColor(paint2.getColor()); - expected_builder.DrawRect(rect3, paint3); + DlOpReceiver& expected_receiver = ToReceiver(expected_builder); + expected_receiver.drawRect({0, 20, 10, 30}); auto expected = expected_builder.Build(); - test(cull_rect, expected, "rect 3 intersects"); + test(cull_rect, expected); } { // Rect 4 SkIRect cull_rect = {11, 11, 21, 21}; DisplayListBuilder expected_builder; - // Unfortunately we don't cull attribute records (yet?) until the last op - ToReceiver(expected_builder).setColor(paint1.getColor()); - ToReceiver(expected_builder).setColor(paint2.getColor()); - ToReceiver(expected_builder).setColor(paint3.getColor()); - expected_builder.DrawRect(rect4, paint4); + DlOpReceiver& expected_receiver = ToReceiver(expected_builder); + expected_receiver.drawRect({20, 20, 30, 30}); auto expected = expected_builder.Build(); - test(cull_rect, expected, "rect 4 intersects"); + test(cull_rect, expected); } { // All 4 rects SkIRect cull_rect = {9, 9, 21, 21}; - test(cull_rect, main, "all rects intersect"); + test(cull_rect, main); } } diff --git a/display_list/dl_builder.h b/display_list/dl_builder.h index d4944d491dc54..29cc795aeb980 100644 --- a/display_list/dl_builder.h +++ b/display_list/dl_builder.h @@ -510,7 +510,7 @@ class DisplayListBuilder final : public virtual DlCanvas, // Most rendering ops will use 1 depth value, but some attributes may // require an additional depth value (due to implicit saveLayers) uint32_t render_op_depth_cost_ = 1u; - DlIndex op_index_ = 0; + int op_index_ = 0; // bytes and ops from |drawPicture| and |drawDisplayList| size_t nested_bytes_ = 0; diff --git a/display_list/dl_op_records.h b/display_list/dl_op_records.h index 2b5210abcbd51..aeb0379e2371f 100644 --- a/display_list/dl_op_records.h +++ b/display_list/dl_op_records.h @@ -18,6 +18,47 @@ namespace flutter { +// Structure holding the information necessary to dispatch and +// potentially cull the DLOps during playback. +// +// Generally drawing ops will execute as long as |cur_index| +// is at or after |next_render_index|, so setting the latter +// to 0 will render all primitives and setting it to MAX_INT +// will skip all remaining rendering primitives. +// +// Save and saveLayer ops will execute as long as the next +// rendering index is before their closing restore index. +// They will also store their own restore index into the +// |next_restore_index| field for use by clip and transform ops. +// +// Clip and transform ops will only execute if the next +// render index is before the next restore index. Otherwise +// their modified state will not be used before it gets +// restored. +// +// Attribute ops always execute as they are too numerous and +// cheap to deal with a complicated "lifetime" tracking to +// determine if they will be used. +struct DispatchContext { + DlOpReceiver& receiver; + + int cur_index; + int next_render_index; + + int next_restore_index; + + struct SaveInfo { + SaveInfo(int previous_restore_index, bool save_was_needed) + : previous_restore_index(previous_restore_index), + save_was_needed(save_was_needed) {} + + int previous_restore_index; + bool save_was_needed; + }; + + std::vector save_infos; +}; + // Most Ops can be bulk compared using memcmp because they contain // only numeric values or constructs that are constructed from numeric // values. @@ -75,8 +116,8 @@ struct DLOp { \ const bool value; \ \ - void dispatch(DlOpReceiver& receiver) const { \ - receiver.set##name(value); \ + void dispatch(DispatchContext& ctx) const { \ + ctx.receiver.set##name(value); \ } \ }; DEFINE_SET_BOOL_OP(AntiAlias) @@ -92,8 +133,8 @@ DEFINE_SET_BOOL_OP(InvertColors) \ const DlStroke##name value; \ \ - void dispatch(DlOpReceiver& receiver) const { \ - receiver.setStroke##name(value); \ + void dispatch(DispatchContext& ctx) const { \ + ctx.receiver.setStroke##name(value); \ } \ }; DEFINE_SET_ENUM_OP(Cap) @@ -108,8 +149,8 @@ struct SetStyleOp final : DLOp { const DlDrawStyle style; - void dispatch(DlOpReceiver& receiver) const { // - receiver.setDrawStyle(style); + void dispatch(DispatchContext& ctx) const { + ctx.receiver.setDrawStyle(style); } }; // 4 byte header + 4 byte payload packs into minimum 8 bytes @@ -120,8 +161,8 @@ struct SetStrokeWidthOp final : DLOp { const float width; - void dispatch(DlOpReceiver& receiver) const { - receiver.setStrokeWidth(width); + void dispatch(DispatchContext& ctx) const { + ctx.receiver.setStrokeWidth(width); } }; // 4 byte header + 4 byte payload packs into minimum 8 bytes @@ -132,8 +173,8 @@ struct SetStrokeMiterOp final : DLOp { const float limit; - void dispatch(DlOpReceiver& receiver) const { - receiver.setStrokeMiter(limit); + void dispatch(DispatchContext& ctx) const { + ctx.receiver.setStrokeMiter(limit); } }; @@ -145,7 +186,7 @@ struct SetColorOp final : DLOp { const DlColor color; - void dispatch(DlOpReceiver& receiver) const { receiver.setColor(color); } + void dispatch(DispatchContext& ctx) const { ctx.receiver.setColor(color); } }; // 4 byte header + 4 byte payload packs into minimum 8 bytes struct SetBlendModeOp final : DLOp { @@ -155,8 +196,8 @@ struct SetBlendModeOp final : DLOp { const DlBlendMode mode; - void dispatch(DlOpReceiver& receiver) const { // - receiver.setBlendMode(mode); + void dispatch(DispatchContext& ctx) const { // + ctx.receiver.setBlendMode(mode); } }; @@ -172,8 +213,8 @@ struct SetBlendModeOp final : DLOp { \ Clear##name##Op() {} \ \ - void dispatch(DlOpReceiver& receiver) const { \ - receiver.set##name(nullptr); \ + void dispatch(DispatchContext& ctx) const { \ + ctx.receiver.set##name(nullptr); \ } \ }; \ struct SetPod##name##Op final : DLOp { \ @@ -181,9 +222,9 @@ struct SetBlendModeOp final : DLOp { \ SetPod##name##Op() {} \ \ - void dispatch(DlOpReceiver& receiver) const { \ + void dispatch(DispatchContext& ctx) const { \ const Dl##name* filter = reinterpret_cast(this + 1); \ - receiver.set##name(filter); \ + ctx.receiver.set##name(filter); \ } \ }; DEFINE_SET_CLEAR_DLATTR_OP(ColorFilter, ColorFilter, filter) @@ -206,8 +247,8 @@ struct SetImageColorSourceOp : DLOp { const DlImageColorSource source; - void dispatch(DlOpReceiver& receiver) const { - receiver.setColorSource(&source); + void dispatch(DispatchContext& ctx) const { + ctx.receiver.setColorSource(&source); } }; @@ -224,8 +265,8 @@ struct SetRuntimeEffectColorSourceOp : DLOp { const DlRuntimeEffectColorSource source; - void dispatch(DlOpReceiver& receiver) const { - receiver.setColorSource(&source); + void dispatch(DispatchContext& ctx) const { + ctx.receiver.setColorSource(&source); } DisplayListCompare equals(const SetRuntimeEffectColorSourceOp* other) const { @@ -243,8 +284,8 @@ struct SetSceneColorSourceOp : DLOp { const DlSceneColorSource source; - void dispatch(DlOpReceiver& receiver) const { - receiver.setColorSource(&source); + void dispatch(DispatchContext& ctx) const { + ctx.receiver.setColorSource(&source); } DisplayListCompare equals(const SetSceneColorSourceOp* other) const { @@ -263,8 +304,8 @@ struct SetSharedImageFilterOp : DLOp { const std::shared_ptr filter; - void dispatch(DlOpReceiver& receiver) const { - receiver.setImageFilter(filter.get()); + void dispatch(DispatchContext& ctx) const { + ctx.receiver.setImageFilter(filter.get()); } DisplayListCompare equals(const SetSharedImageFilterOp* other) const { @@ -289,8 +330,15 @@ struct SaveOpBase : DLOp { // of the data here, it can be stored for free and defaulted to 0 for // save operations. SaveLayerOptions options; - DlIndex restore_index; + int restore_index; uint32_t total_content_depth; + + inline bool save_needed(DispatchContext& ctx) const { + bool needed = ctx.next_render_index <= restore_index; + ctx.save_infos.emplace_back(ctx.next_restore_index, needed); + ctx.next_restore_index = restore_index; + return needed; + } }; // 16 byte SaveOpBase with no additional data (options is unsed here) struct SaveOp final : SaveOpBase { @@ -298,8 +346,10 @@ struct SaveOp final : SaveOpBase { SaveOp() : SaveOpBase() {} - void dispatch(DlOpReceiver& receiver) const { - receiver.save(total_content_depth); + void dispatch(DispatchContext& ctx) const { + if (save_needed(ctx)) { + ctx.receiver.save(total_content_depth); + } } }; // The base struct for all saveLayer() ops @@ -319,8 +369,11 @@ struct SaveLayerOp final : SaveLayerOpBase { SaveLayerOp(const SaveLayerOptions& options, const SkRect& rect) : SaveLayerOpBase(options, rect) {} - void dispatch(DlOpReceiver& receiver) const { - receiver.saveLayer(rect, options, total_content_depth, max_blend_mode); + void dispatch(DispatchContext& ctx) const { + if (save_needed(ctx)) { + ctx.receiver.saveLayer(rect, options, total_content_depth, + max_blend_mode); + } } }; // 36 byte SaveLayerOpBase + 4 bytes for alignment + 16 byte payload packs @@ -335,9 +388,11 @@ struct SaveLayerBackdropOp final : SaveLayerOpBase { const std::shared_ptr backdrop; - void dispatch(DlOpReceiver& receiver) const { - receiver.saveLayer(rect, options, total_content_depth, max_blend_mode, - backdrop.get()); + void dispatch(DispatchContext& ctx) const { + if (save_needed(ctx)) { + ctx.receiver.saveLayer(rect, options, total_content_depth, max_blend_mode, + backdrop.get()); + } } DisplayListCompare equals(const SaveLayerBackdropOp* other) const { @@ -355,14 +410,23 @@ struct RestoreOp final : DLOp { RestoreOp() {} - void dispatch(DlOpReceiver& receiver) const { // - receiver.restore(); + void dispatch(DispatchContext& ctx) const { + DispatchContext::SaveInfo& info = ctx.save_infos.back(); + if (info.save_was_needed) { + ctx.receiver.restore(); + } + ctx.next_restore_index = info.previous_restore_index; + ctx.save_infos.pop_back(); } }; struct TransformClipOpBase : DLOp { static constexpr uint32_t kDepthInc = 0; static constexpr uint32_t kRenderOpInc = 1; + + inline bool op_needed(const DispatchContext& context) const { + return context.next_render_index <= context.next_restore_index; + } }; // 4 byte header + 8 byte payload uses 12 bytes but is rounded up to 16 bytes // (4 bytes unused) @@ -374,8 +438,10 @@ struct TranslateOp final : TransformClipOpBase { const SkScalar tx; const SkScalar ty; - void dispatch(DlOpReceiver& receiver) const { // - receiver.translate(tx, ty); + void dispatch(DispatchContext& ctx) const { + if (op_needed(ctx)) { + ctx.receiver.translate(tx, ty); + } } }; // 4 byte header + 8 byte payload uses 12 bytes but is rounded up to 16 bytes @@ -388,8 +454,10 @@ struct ScaleOp final : TransformClipOpBase { const SkScalar sx; const SkScalar sy; - void dispatch(DlOpReceiver& receiver) const { // - receiver.scale(sx, sy); + void dispatch(DispatchContext& ctx) const { + if (op_needed(ctx)) { + ctx.receiver.scale(sx, sy); + } } }; // 4 byte header + 4 byte payload packs into minimum 8 bytes @@ -400,8 +468,10 @@ struct RotateOp final : TransformClipOpBase { const SkScalar degrees; - void dispatch(DlOpReceiver& receiver) const { // - receiver.rotate(degrees); + void dispatch(DispatchContext& ctx) const { + if (op_needed(ctx)) { + ctx.receiver.rotate(degrees); + } } }; // 4 byte header + 8 byte payload uses 12 bytes but is rounded up to 16 bytes @@ -414,8 +484,10 @@ struct SkewOp final : TransformClipOpBase { const SkScalar sx; const SkScalar sy; - void dispatch(DlOpReceiver& receiver) const { // - receiver.skew(sx, sy); + void dispatch(DispatchContext& ctx) const { + if (op_needed(ctx)) { + ctx.receiver.skew(sx, sy); + } } }; // 4 byte header + 24 byte payload uses 28 bytes but is rounded up to 32 bytes @@ -432,9 +504,11 @@ struct Transform2DAffineOp final : TransformClipOpBase { const SkScalar mxx, mxy, mxt; const SkScalar myx, myy, myt; - void dispatch(DlOpReceiver& receiver) const { - receiver.transform2DAffine(mxx, mxy, mxt, // - myx, myy, myt); + void dispatch(DispatchContext& ctx) const { + if (op_needed(ctx)) { + ctx.receiver.transform2DAffine(mxx, mxy, mxt, // + myx, myy, myt); + } } }; // 4 byte header + 64 byte payload uses 68 bytes which is rounded up to 72 bytes @@ -459,11 +533,13 @@ struct TransformFullPerspectiveOp final : TransformClipOpBase { const SkScalar mzx, mzy, mzz, mzt; const SkScalar mwx, mwy, mwz, mwt; - void dispatch(DlOpReceiver& receiver) const { - receiver.transformFullPerspective(mxx, mxy, mxz, mxt, // - myx, myy, myz, myt, // - mzx, mzy, mzz, mzt, // - mwx, mwy, mwz, mwt); + void dispatch(DispatchContext& ctx) const { + if (op_needed(ctx)) { + ctx.receiver.transformFullPerspective(mxx, mxy, mxz, mxt, // + myx, myy, myz, myt, // + mzx, mzy, mzz, mzt, // + mwx, mwy, mwz, mwt); + } } }; @@ -473,8 +549,10 @@ struct TransformResetOp final : TransformClipOpBase { TransformResetOp() = default; - void dispatch(DlOpReceiver& receiver) const { // - receiver.transformReset(); + void dispatch(DispatchContext& ctx) const { + if (op_needed(ctx)) { + ctx.receiver.transformReset(); + } } }; @@ -498,8 +576,11 @@ struct TransformResetOp final : TransformClipOpBase { const bool is_aa; \ const Sk##shapetype shape; \ \ - void dispatch(DlOpReceiver& receiver) const { \ - receiver.clip##shapename(shape, DlCanvas::ClipOp::k##clipop, is_aa); \ + void dispatch(DispatchContext& ctx) const { \ + if (op_needed(ctx)) { \ + ctx.receiver.clip##shapename(shape, DlCanvas::ClipOp::k##clipop, \ + is_aa); \ + } \ } \ }; DEFINE_CLIP_SHAPE_OP(Rect, Rect, Intersect) @@ -510,30 +591,33 @@ DEFINE_CLIP_SHAPE_OP(Oval, Rect, Difference) DEFINE_CLIP_SHAPE_OP(RRect, RRect, Difference) #undef DEFINE_CLIP_SHAPE_OP -#define DEFINE_CLIP_PATH_OP(clipop) \ - struct Clip##clipop##PathOp final : TransformClipOpBase { \ - static constexpr auto kType = DisplayListOpType::kClip##clipop##Path; \ - \ - Clip##clipop##PathOp(const SkPath& path, bool is_aa) \ - : is_aa(is_aa), cached_path(path) {} \ - \ - const bool is_aa; \ - const DlOpReceiver::CacheablePath cached_path; \ - \ - void dispatch(DlOpReceiver& receiver) const { \ - if (receiver.PrefersImpellerPaths()) { \ - receiver.clipPath(cached_path, DlCanvas::ClipOp::k##clipop, is_aa); \ - } else { \ - receiver.clipPath(cached_path.sk_path, DlCanvas::ClipOp::k##clipop, \ - is_aa); \ - } \ - } \ - \ - DisplayListCompare equals(const Clip##clipop##PathOp* other) const { \ - return is_aa == other->is_aa && cached_path == other->cached_path \ - ? DisplayListCompare::kEqual \ - : DisplayListCompare::kNotEqual; \ - } \ +#define DEFINE_CLIP_PATH_OP(clipop) \ + struct Clip##clipop##PathOp final : TransformClipOpBase { \ + static constexpr auto kType = DisplayListOpType::kClip##clipop##Path; \ + \ + Clip##clipop##PathOp(const SkPath& path, bool is_aa) \ + : is_aa(is_aa), cached_path(path) {} \ + \ + const bool is_aa; \ + const DlOpReceiver::CacheablePath cached_path; \ + \ + void dispatch(DispatchContext& ctx) const { \ + if (op_needed(ctx)) { \ + if (ctx.receiver.PrefersImpellerPaths()) { \ + ctx.receiver.clipPath(cached_path, DlCanvas::ClipOp::k##clipop, \ + is_aa); \ + } else { \ + ctx.receiver.clipPath(cached_path.sk_path, \ + DlCanvas::ClipOp::k##clipop, is_aa); \ + } \ + } \ + } \ + \ + DisplayListCompare equals(const Clip##clipop##PathOp* other) const { \ + return is_aa == other->is_aa && cached_path == other->cached_path \ + ? DisplayListCompare::kEqual \ + : DisplayListCompare::kNotEqual; \ + } \ }; DEFINE_CLIP_PATH_OP(Intersect) DEFINE_CLIP_PATH_OP(Difference) @@ -542,6 +626,10 @@ DEFINE_CLIP_PATH_OP(Difference) struct DrawOpBase : DLOp { static constexpr uint32_t kDepthInc = 1; static constexpr uint32_t kRenderOpInc = 1; + + inline bool op_needed(const DispatchContext& ctx) const { + return ctx.cur_index >= ctx.next_render_index; + } }; // 4 byte header + no payload uses minimum 8 bytes (4 bytes unused) @@ -550,8 +638,10 @@ struct DrawPaintOp final : DrawOpBase { DrawPaintOp() {} - void dispatch(DlOpReceiver& receiver) const { // - receiver.drawPaint(); + void dispatch(DispatchContext& ctx) const { + if (op_needed(ctx)) { + ctx.receiver.drawPaint(); + } } }; // 4 byte header + 8 byte payload uses 12 bytes but is rounded up to 16 bytes @@ -564,8 +654,10 @@ struct DrawColorOp final : DrawOpBase { const DlColor color; const DlBlendMode mode; - void dispatch(DlOpReceiver& receiver) const { - receiver.drawColor(color, mode); + void dispatch(DispatchContext& ctx) const { + if (op_needed(ctx)) { + ctx.receiver.drawColor(color, mode); + } } }; @@ -582,8 +674,10 @@ struct DrawColorOp final : DrawOpBase { \ const arg_type arg_name; \ \ - void dispatch(DlOpReceiver& receiver) const { \ - receiver.draw##op_name(arg_name); \ + void dispatch(DispatchContext& ctx) const { \ + if (op_needed(ctx)) { \ + ctx.receiver.draw##op_name(arg_name); \ + } \ } \ }; DEFINE_DRAW_1ARG_OP(Rect, SkRect, rect) @@ -600,11 +694,13 @@ struct DrawPathOp final : DrawOpBase { const DlOpReceiver::CacheablePath cached_path; - void dispatch(DlOpReceiver& receiver) const { - if (receiver.PrefersImpellerPaths()) { - receiver.drawPath(cached_path); - } else { - receiver.drawPath(cached_path.sk_path); + void dispatch(DispatchContext& ctx) const { + if (op_needed(ctx)) { + if (ctx.receiver.PrefersImpellerPaths()) { + ctx.receiver.drawPath(cached_path); + } else { + ctx.receiver.drawPath(cached_path.sk_path); + } } } @@ -630,8 +726,10 @@ struct DrawPathOp final : DrawOpBase { const type1 name1; \ const type2 name2; \ \ - void dispatch(DlOpReceiver& receiver) const { \ - receiver.draw##op_name(name1, name2); \ + void dispatch(DispatchContext& ctx) const { \ + if (op_needed(ctx)) { \ + ctx.receiver.draw##op_name(name1, name2); \ + } \ } \ }; DEFINE_DRAW_2ARG_OP(Line, SkPoint, p0, SkPoint, p1) @@ -654,8 +752,10 @@ struct DrawDashedLineOp final : DrawOpBase { const SkScalar on_length; const SkScalar off_length; - void dispatch(DlOpReceiver& receiver) const { - receiver.drawDashedLine(p0, p1, on_length, off_length); + void dispatch(DispatchContext& ctx) const { + if (op_needed(ctx)) { + ctx.receiver.drawDashedLine(p0, p1, on_length, off_length); + } } }; @@ -671,8 +771,10 @@ struct DrawArcOp final : DrawOpBase { const SkScalar sweep; const bool center; - void dispatch(DlOpReceiver& receiver) const { - receiver.drawArc(bounds, start, sweep, center); + void dispatch(DispatchContext& ctx) const { + if (op_needed(ctx)) { + ctx.receiver.drawArc(bounds, start, sweep, center); + } } }; @@ -682,18 +784,20 @@ struct DrawArcOp final : DrawOpBase { // so this op will always pack efficiently // The point type is packed into 3 different OpTypes to avoid expanding // the fixed payload beyond the 8 bytes -#define DEFINE_DRAW_POINTS_OP(name, mode) \ - struct Draw##name##Op final : DrawOpBase { \ - static constexpr auto kType = DisplayListOpType::kDraw##name; \ - \ - explicit Draw##name##Op(uint32_t count) : count(count) {} \ - \ - const uint32_t count; \ - \ - void dispatch(DlOpReceiver& receiver) const { \ - const SkPoint* pts = reinterpret_cast(this + 1); \ - receiver.drawPoints(DlCanvas::PointMode::mode, count, pts); \ - } \ +#define DEFINE_DRAW_POINTS_OP(name, mode) \ + struct Draw##name##Op final : DrawOpBase { \ + static constexpr auto kType = DisplayListOpType::kDraw##name; \ + \ + explicit Draw##name##Op(uint32_t count) : count(count) {} \ + \ + const uint32_t count; \ + \ + void dispatch(DispatchContext& ctx) const { \ + if (op_needed(ctx)) { \ + const SkPoint* pts = reinterpret_cast(this + 1); \ + ctx.receiver.drawPoints(DlCanvas::PointMode::mode, count, pts); \ + } \ + } \ }; DEFINE_DRAW_POINTS_OP(Points, kPoints); DEFINE_DRAW_POINTS_OP(Lines, kLines); @@ -711,36 +815,40 @@ struct DrawVerticesOp final : DrawOpBase { const DlBlendMode mode; const std::shared_ptr vertices; - void dispatch(DlOpReceiver& receiver) const { - receiver.drawVertices(vertices, mode); + void dispatch(DispatchContext& ctx) const { + if (op_needed(ctx)) { + ctx.receiver.drawVertices(vertices, mode); + } } }; // 4 byte header + 40 byte payload uses 44 bytes but is rounded up to 48 bytes // (4 bytes unused) -#define DEFINE_DRAW_IMAGE_OP(name, with_attributes) \ - struct name##Op final : DrawOpBase { \ - static constexpr auto kType = DisplayListOpType::k##name; \ - \ - name##Op(const sk_sp& image, \ - const SkPoint& point, \ - DlImageSampling sampling) \ - : point(point), sampling(sampling), image(std::move(image)) {} \ - \ - const SkPoint point; \ - const DlImageSampling sampling; \ - const sk_sp image; \ - \ - void dispatch(DlOpReceiver& receiver) const { \ - receiver.drawImage(image, point, sampling, with_attributes); \ - } \ - \ - DisplayListCompare equals(const name##Op* other) const { \ - return (point == other->point && sampling == other->sampling && \ - image->Equals(other->image)) \ - ? DisplayListCompare::kEqual \ - : DisplayListCompare::kNotEqual; \ - } \ +#define DEFINE_DRAW_IMAGE_OP(name, with_attributes) \ + struct name##Op final : DrawOpBase { \ + static constexpr auto kType = DisplayListOpType::k##name; \ + \ + name##Op(const sk_sp& image, \ + const SkPoint& point, \ + DlImageSampling sampling) \ + : point(point), sampling(sampling), image(std::move(image)) {} \ + \ + const SkPoint point; \ + const DlImageSampling sampling; \ + const sk_sp image; \ + \ + void dispatch(DispatchContext& ctx) const { \ + if (op_needed(ctx)) { \ + ctx.receiver.drawImage(image, point, sampling, with_attributes); \ + } \ + } \ + \ + DisplayListCompare equals(const name##Op* other) const { \ + return (point == other->point && sampling == other->sampling && \ + image->Equals(other->image)) \ + ? DisplayListCompare::kEqual \ + : DisplayListCompare::kNotEqual; \ + } \ }; DEFINE_DRAW_IMAGE_OP(DrawImage, false) DEFINE_DRAW_IMAGE_OP(DrawImageWithAttr, true) @@ -771,9 +879,11 @@ struct DrawImageRectOp final : DrawOpBase { const DlCanvas::SrcRectConstraint constraint; const sk_sp image; - void dispatch(DlOpReceiver& receiver) const { - receiver.drawImageRect(image, src, dst, sampling, render_with_attributes, - constraint); + void dispatch(DispatchContext& ctx) const { + if (op_needed(ctx)) { + ctx.receiver.drawImageRect(image, src, dst, sampling, + render_with_attributes, constraint); + } } DisplayListCompare equals(const DrawImageRectOp* other) const { @@ -802,9 +912,11 @@ struct DrawImageRectOp final : DrawOpBase { const DlFilterMode mode; \ const sk_sp image; \ \ - void dispatch(DlOpReceiver& receiver) const { \ - receiver.drawImageNine(image, center, dst, mode, \ - render_with_attributes); \ + void dispatch(DispatchContext& ctx) const { \ + if (op_needed(ctx)) { \ + ctx.receiver.drawImageNine(image, center, dst, mode, \ + render_with_attributes); \ + } \ } \ \ DisplayListCompare equals(const name##Op* other) const { \ @@ -882,14 +994,16 @@ struct DrawAtlasOp final : DrawAtlasBaseOp { has_colors, render_with_attributes) {} - void dispatch(DlOpReceiver& receiver) const { - const SkRSXform* xform = reinterpret_cast(this + 1); - const SkRect* tex = reinterpret_cast(xform + count); - const DlColor* colors = - has_colors ? reinterpret_cast(tex + count) : nullptr; - const DlBlendMode mode = static_cast(mode_index); - receiver.drawAtlas(atlas, xform, tex, colors, count, mode, sampling, - nullptr, render_with_attributes); + void dispatch(DispatchContext& ctx) const { + if (op_needed(ctx)) { + const SkRSXform* xform = reinterpret_cast(this + 1); + const SkRect* tex = reinterpret_cast(xform + count); + const DlColor* colors = + has_colors ? reinterpret_cast(tex + count) : nullptr; + const DlBlendMode mode = static_cast(mode_index); + ctx.receiver.drawAtlas(atlas, xform, tex, colors, count, mode, sampling, + nullptr, render_with_attributes); + } } DisplayListCompare equals(const DrawAtlasOp* other) const { @@ -925,14 +1039,16 @@ struct DrawAtlasCulledOp final : DrawAtlasBaseOp { const SkRect cull_rect; - void dispatch(DlOpReceiver& receiver) const { - const SkRSXform* xform = reinterpret_cast(this + 1); - const SkRect* tex = reinterpret_cast(xform + count); - const DlColor* colors = - has_colors ? reinterpret_cast(tex + count) : nullptr; - const DlBlendMode mode = static_cast(mode_index); - receiver.drawAtlas(atlas, xform, tex, colors, count, mode, sampling, - &cull_rect, render_with_attributes); + void dispatch(DispatchContext& ctx) const { + if (op_needed(ctx)) { + const SkRSXform* xform = reinterpret_cast(this + 1); + const SkRect* tex = reinterpret_cast(xform + count); + const DlColor* colors = + has_colors ? reinterpret_cast(tex + count) : nullptr; + const DlBlendMode mode = static_cast(mode_index); + ctx.receiver.drawAtlas(atlas, xform, tex, colors, count, mode, sampling, + &cull_rect, render_with_attributes); + } } DisplayListCompare equals(const DrawAtlasCulledOp* other) const { @@ -957,8 +1073,10 @@ struct DrawDisplayListOp final : DrawOpBase { SkScalar opacity; const sk_sp display_list; - void dispatch(DlOpReceiver& receiver) const { - receiver.drawDisplayList(display_list, opacity); + void dispatch(DispatchContext& ctx) const { + if (op_needed(ctx)) { + ctx.receiver.drawDisplayList(display_list, opacity); + } } DisplayListCompare equals(const DrawDisplayListOp* other) const { @@ -981,8 +1099,10 @@ struct DrawTextBlobOp final : DrawOpBase { const SkScalar y; const sk_sp blob; - void dispatch(DlOpReceiver& receiver) const { - receiver.drawTextBlob(blob, x, y); + void dispatch(DispatchContext& ctx) const { + if (op_needed(ctx)) { + ctx.receiver.drawTextBlob(blob, x, y); + } } }; @@ -998,8 +1118,10 @@ struct DrawTextFrameOp final : DrawOpBase { const SkScalar y; const std::shared_ptr text_frame; - void dispatch(DlOpReceiver& receiver) const { - receiver.drawTextFrame(text_frame, x, y); + void dispatch(DispatchContext& ctx) const { + if (op_needed(ctx)) { + ctx.receiver.drawTextFrame(text_frame, x, y); + } } }; @@ -1019,13 +1141,15 @@ struct DrawTextFrameOp final : DrawOpBase { const SkScalar dpr; \ const DlOpReceiver::CacheablePath cached_path; \ \ - void dispatch(DlOpReceiver& receiver) const { \ - if (receiver.PrefersImpellerPaths()) { \ - receiver.drawShadow(cached_path, color, elevation, \ - transparent_occluder, dpr); \ - } else { \ - receiver.drawShadow(cached_path.sk_path, color, elevation, \ - transparent_occluder, dpr); \ + void dispatch(DispatchContext& ctx) const { \ + if (op_needed(ctx)) { \ + if (ctx.receiver.PrefersImpellerPaths()) { \ + ctx.receiver.drawShadow(cached_path, color, elevation, \ + transparent_occluder, dpr); \ + } else { \ + ctx.receiver.drawShadow(cached_path.sk_path, color, elevation, \ + transparent_occluder, dpr); \ + } \ } \ } \ \ diff --git a/testing/display_list_testing.cc b/testing/display_list_testing.cc index 0663aea860de9..1ab19357f8139 100644 --- a/testing/display_list_testing.cc +++ b/testing/display_list_testing.cc @@ -55,8 +55,6 @@ using DlVertexMode = flutter::DlVertexMode; using DlTileMode = flutter::DlTileMode; using DlImageSampling = flutter::DlImageSampling; using SaveLayerOptions = flutter::SaveLayerOptions; -using DisplayListOpType = flutter::DisplayListOpType; -using DisplayListOpCategory = flutter::DisplayListOpCategory; using DisplayListStreamDispatcher = flutter::testing::DisplayListStreamDispatcher; @@ -101,83 +99,43 @@ std::ostream& operator<<(std::ostream& os, const DlPaint& paint) { return os << ")"; } -#define DLT_OSTREAM_CASE(enum_name, value_name) \ - case enum_name::k##value_name: return os << #enum_name "::k" #value_name - std::ostream& operator<<(std::ostream& os, const DlBlendMode& mode) { switch (mode) { - DLT_OSTREAM_CASE(DlBlendMode, Clear); - DLT_OSTREAM_CASE(DlBlendMode, Src); - DLT_OSTREAM_CASE(DlBlendMode, Dst); - DLT_OSTREAM_CASE(DlBlendMode, SrcOver); - DLT_OSTREAM_CASE(DlBlendMode, DstOver); - DLT_OSTREAM_CASE(DlBlendMode, SrcIn); - DLT_OSTREAM_CASE(DlBlendMode, DstIn); - DLT_OSTREAM_CASE(DlBlendMode, SrcOut); - DLT_OSTREAM_CASE(DlBlendMode, DstOut); - DLT_OSTREAM_CASE(DlBlendMode, SrcATop); - DLT_OSTREAM_CASE(DlBlendMode, DstATop); - DLT_OSTREAM_CASE(DlBlendMode, Xor); - DLT_OSTREAM_CASE(DlBlendMode, Plus); - DLT_OSTREAM_CASE(DlBlendMode, Modulate); - DLT_OSTREAM_CASE(DlBlendMode, Screen); - - DLT_OSTREAM_CASE(DlBlendMode, Overlay); - DLT_OSTREAM_CASE(DlBlendMode, Darken); - DLT_OSTREAM_CASE(DlBlendMode, Lighten); - DLT_OSTREAM_CASE(DlBlendMode, ColorDodge); - DLT_OSTREAM_CASE(DlBlendMode, ColorBurn); - DLT_OSTREAM_CASE(DlBlendMode, HardLight); - DLT_OSTREAM_CASE(DlBlendMode, SoftLight); - DLT_OSTREAM_CASE(DlBlendMode, Difference); - DLT_OSTREAM_CASE(DlBlendMode, Exclusion); - DLT_OSTREAM_CASE(DlBlendMode, Multiply); - - DLT_OSTREAM_CASE(DlBlendMode, Hue); - DLT_OSTREAM_CASE(DlBlendMode, Saturation); - DLT_OSTREAM_CASE(DlBlendMode, Color); - DLT_OSTREAM_CASE(DlBlendMode, Luminosity); - } - // Not a valid enum, should never happen, but in case we encounter bad data. - return os << "DlBlendMode::????"; -} - -extern std::ostream& operator<<(std::ostream& os, - const flutter::DisplayListOpType& type) { - switch (type) { -#define DLT_OP_TYPE_CASE(V) DLT_OSTREAM_CASE(DisplayListOpType, V); - FOR_EACH_DISPLAY_LIST_OP(DLT_OP_TYPE_CASE) - DLT_OP_TYPE_CASE(InvalidOp) - -#ifdef IMPELLER_ENABLE_3D - DLT_OP_TYPE_CASE(SetSceneColorSource) -#endif // IMPELLER_ENABLE_3D - - -#undef DLT_OP_TYPE_CASE - } - // Not a valid enum, should never happen, but in case we encounter bad data. - return os << "DisplayListOpType::???"; -} - -extern std::ostream& operator<<( - std::ostream& os, const flutter::DisplayListOpCategory& category) { - switch (category) { - DLT_OSTREAM_CASE(DisplayListOpCategory, Attribute); - DLT_OSTREAM_CASE(DisplayListOpCategory, Transform); - DLT_OSTREAM_CASE(DisplayListOpCategory, Clip); - DLT_OSTREAM_CASE(DisplayListOpCategory, Save); - DLT_OSTREAM_CASE(DisplayListOpCategory, SaveLayer); - DLT_OSTREAM_CASE(DisplayListOpCategory, Restore); - DLT_OSTREAM_CASE(DisplayListOpCategory, Rendering); - DLT_OSTREAM_CASE(DisplayListOpCategory, SubDisplayList); - DLT_OSTREAM_CASE(DisplayListOpCategory, InvalidCategory); - } - // Not a valid enum, should never happen, but in case we encounter bad data. - return os << "DisplayListOpCategory::???"; -} - -#undef DLT_OSTREAM_CASE + case DlBlendMode::kClear: return os << "BlendMode::kClear"; + case DlBlendMode::kSrc: return os << "BlendMode::kSrc"; + case DlBlendMode::kDst: return os << "BlendMode::kDst"; + case DlBlendMode::kSrcOver: return os << "BlendMode::kSrcOver"; + case DlBlendMode::kDstOver: return os << "BlendMode::kDstOver"; + case DlBlendMode::kSrcIn: return os << "BlendMode::kSrcIn"; + case DlBlendMode::kDstIn: return os << "BlendMode::kDstIn"; + case DlBlendMode::kSrcOut: return os << "BlendMode::kSrcOut"; + case DlBlendMode::kDstOut: return os << "BlendMode::kDstOut"; + case DlBlendMode::kSrcATop: return os << "BlendMode::kSrcATop"; + case DlBlendMode::kDstATop: return os << "BlendMode::kDstATop"; + case DlBlendMode::kXor: return os << "BlendMode::kXor"; + case DlBlendMode::kPlus: return os << "BlendMode::kPlus"; + case DlBlendMode::kModulate: return os << "BlendMode::kModulate"; + case DlBlendMode::kScreen: return os << "BlendMode::kScreen"; + + case DlBlendMode::kOverlay: return os << "BlendMode::kOverlay"; + case DlBlendMode::kDarken: return os << "BlendMode::kDarken"; + case DlBlendMode::kLighten: return os << "BlendMode::kLighten"; + case DlBlendMode::kColorDodge: return os << "BlendMode::kColorDodge"; + case DlBlendMode::kColorBurn: return os << "BlendMode::kColorBurn"; + case DlBlendMode::kHardLight: return os << "BlendMode::kHardLight"; + case DlBlendMode::kSoftLight: return os << "BlendMode::kSoftLight"; + case DlBlendMode::kDifference: return os << "BlendMode::kDifference"; + case DlBlendMode::kExclusion: return os << "BlendMode::kExclusion"; + case DlBlendMode::kMultiply: return os << "BlendMode::kMultiply"; + + case DlBlendMode::kHue: return os << "BlendMode::kHue"; + case DlBlendMode::kSaturation: return os << "BlendMode::kSaturation"; + case DlBlendMode::kColor: return os << "BlendMode::kColor"; + case DlBlendMode::kLuminosity: return os << "BlendMode::kLuminosity"; + + default: return os << "BlendMode::????"; + } +} std::ostream& operator<<(std::ostream& os, const SaveLayerOptions& options) { return os << "SaveLayerOptions(" @@ -539,12 +497,6 @@ void DisplayListStreamDispatcher::setColorSource(const DlColorSource* source) { << sweep_src->tile_mode() << ", " << sweep_src->matrix_ptr() << ")"; break; } -#ifdef IMPELLER_ENABLE_3D - case DlColorSourceType::kScene: { - os_ << "DlSceneColorSource()"; - break; - } -#endif // IMPELLER_ENABLE_3D default: os_ << "?DlUnknownColorSource?()"; break; diff --git a/testing/display_list_testing.h b/testing/display_list_testing.h index 3ce9ec2bfe65e..f238360e44e8c 100644 --- a/testing/display_list_testing.h +++ b/testing/display_list_testing.h @@ -75,10 +75,6 @@ extern std::ostream& operator<<(std::ostream& os, const flutter::DlImage* image); extern std::ostream& operator<<(std::ostream& os, const flutter::SaveLayerOptions& image); -extern std::ostream& operator<<(std::ostream& os, - const flutter::DisplayListOpType& type); -extern std::ostream& operator<<(std::ostream& os, - const flutter::DisplayListOpCategory& category); } // namespace std @@ -213,398 +209,6 @@ class DisplayListStreamDispatcher final : public DlOpReceiver { void out(const DlImageFilter* filter); }; -class DisplayListGeneralReceiver : public DlOpReceiver { - public: - DisplayListGeneralReceiver() { - type_counts_.fill(0u); - category_counts_.fill(0u); - } - - void setAntiAlias(bool aa) override { - RecordByType(DisplayListOpType::kSetAntiAlias); - } - void setInvertColors(bool invert) override { - RecordByType(DisplayListOpType::kSetInvertColors); - } - void setStrokeCap(DlStrokeCap cap) override { - RecordByType(DisplayListOpType::kSetStrokeCap); - } - void setStrokeJoin(DlStrokeJoin join) override { - RecordByType(DisplayListOpType::kSetStrokeJoin); - } - void setDrawStyle(DlDrawStyle style) override { - RecordByType(DisplayListOpType::kSetStyle); - } - void setStrokeWidth(float width) override { - RecordByType(DisplayListOpType::kSetStrokeWidth); - } - void setStrokeMiter(float limit) override { - RecordByType(DisplayListOpType::kSetStrokeMiter); - } - void setColor(DlColor color) override { - RecordByType(DisplayListOpType::kSetColor); - } - void setBlendMode(DlBlendMode mode) override { - RecordByType(DisplayListOpType::kSetBlendMode); - } - void setColorSource(const DlColorSource* source) override { - if (source) { - switch (source->type()) { - case DlColorSourceType::kImage: - RecordByType(DisplayListOpType::kSetImageColorSource); - break; - case DlColorSourceType::kRuntimeEffect: - RecordByType(DisplayListOpType::kSetRuntimeEffectColorSource); - break; - case DlColorSourceType::kColor: - case DlColorSourceType::kLinearGradient: - case DlColorSourceType::kRadialGradient: - case DlColorSourceType::kConicalGradient: - case DlColorSourceType::kSweepGradient: - RecordByType(DisplayListOpType::kSetPodColorSource); - break; -#ifdef IMPELLER_ENABLE_3D - case DlColorSourceType::kScene: - RecordByType(DisplayListOpType::kSetSceneColorSource); - break; -#endif // IMPELLER_ENABLE_3D - } - } else { - RecordByType(DisplayListOpType::kClearColorSource); - } - } - void setImageFilter(const DlImageFilter* filter) override { - if (filter) { - switch (filter->type()) { - case DlImageFilterType::kBlur: - case DlImageFilterType::kDilate: - case DlImageFilterType::kErode: - case DlImageFilterType::kMatrix: - RecordByType(DisplayListOpType::kSetPodImageFilter); - break; - case DlImageFilterType::kCompose: - case DlImageFilterType::kLocalMatrix: - case DlImageFilterType::kColorFilter: - RecordByType(DisplayListOpType::kSetSharedImageFilter); - break; - } - } else { - RecordByType(DisplayListOpType::kClearImageFilter); - } - } - void setColorFilter(const DlColorFilter* filter) override { - if (filter) { - switch (filter->type()) { - case DlColorFilterType::kBlend: - case DlColorFilterType::kMatrix: - case DlColorFilterType::kLinearToSrgbGamma: - case DlColorFilterType::kSrgbToLinearGamma: - RecordByType(DisplayListOpType::kSetPodColorFilter); - break; - } - } else { - RecordByType(DisplayListOpType::kClearColorFilter); - } - } - void setMaskFilter(const DlMaskFilter* filter) override { - if (filter) { - switch (filter->type()) { - case DlMaskFilterType::kBlur: - RecordByType(DisplayListOpType::kSetPodMaskFilter); - break; - } - } else { - RecordByType(DisplayListOpType::kClearMaskFilter); - } - } - - void translate(SkScalar tx, SkScalar ty) override { - RecordByType(DisplayListOpType::kTranslate); - } - void scale(SkScalar sx, SkScalar sy) override { - RecordByType(DisplayListOpType::kScale); - } - void rotate(SkScalar degrees) override { - RecordByType(DisplayListOpType::kRotate); - } - void skew(SkScalar sx, SkScalar sy) override { - RecordByType(DisplayListOpType::kSkew); - } - // clang-format off - // 2x3 2D affine subset of a 4x4 transform in row major order - void transform2DAffine(SkScalar mxx, SkScalar mxy, SkScalar mxt, - SkScalar myx, SkScalar myy, SkScalar myt) override { - RecordByType(DisplayListOpType::kTransform2DAffine); - } - // full 4x4 transform in row major order - void transformFullPerspective( - SkScalar mxx, SkScalar mxy, SkScalar mxz, SkScalar mxt, - SkScalar myx, SkScalar myy, SkScalar myz, SkScalar myt, - SkScalar mzx, SkScalar mzy, SkScalar mzz, SkScalar mzt, - SkScalar mwx, SkScalar mwy, SkScalar mwz, SkScalar mwt) override { - RecordByType(DisplayListOpType::kTransformFullPerspective); - } - // clang-format on - void transformReset() override { - RecordByType(DisplayListOpType::kTransformReset); - } - - void clipRect(const SkRect& rect, - DlCanvas::ClipOp clip_op, - bool is_aa) override { - switch (clip_op) { - case DlCanvas::ClipOp::kIntersect: - RecordByType(DisplayListOpType::kClipIntersectRect); - break; - case DlCanvas::ClipOp::kDifference: - RecordByType(DisplayListOpType::kClipDifferenceRect); - break; - } - } - void clipOval(const SkRect& bounds, - DlCanvas::ClipOp clip_op, - bool is_aa) override { - switch (clip_op) { - case DlCanvas::ClipOp::kIntersect: - RecordByType(DisplayListOpType::kClipIntersectOval); - break; - case DlCanvas::ClipOp::kDifference: - RecordByType(DisplayListOpType::kClipDifferenceOval); - break; - } - } - void clipRRect(const SkRRect& rrect, - DlCanvas::ClipOp clip_op, - bool is_aa) override { - switch (clip_op) { - case DlCanvas::ClipOp::kIntersect: - RecordByType(DisplayListOpType::kClipIntersectRRect); - break; - case DlCanvas::ClipOp::kDifference: - RecordByType(DisplayListOpType::kClipDifferenceRRect); - break; - } - } - void clipPath(const SkPath& path, - DlCanvas::ClipOp clip_op, - bool is_aa) override { - switch (clip_op) { - case DlCanvas::ClipOp::kIntersect: - RecordByType(DisplayListOpType::kClipIntersectPath); - break; - case DlCanvas::ClipOp::kDifference: - RecordByType(DisplayListOpType::kClipDifferencePath); - break; - } - } - - void save() override { RecordByType(DisplayListOpType::kSave); } - void saveLayer(const SkRect& bounds, - const SaveLayerOptions options, - const DlImageFilter* backdrop) override { - if (backdrop) { - RecordByType(DisplayListOpType::kSaveLayerBackdrop); - } else { - RecordByType(DisplayListOpType::kSaveLayer); - } - } - void restore() override { RecordByType(DisplayListOpType::kRestore); } - - void drawColor(DlColor color, DlBlendMode mode) override { - RecordByType(DisplayListOpType::kDrawColor); - } - void drawPaint() override { RecordByType(DisplayListOpType::kDrawPaint); } - void drawLine(const SkPoint& p0, const SkPoint& p1) override { - RecordByType(DisplayListOpType::kDrawLine); - } - void drawDashedLine(const DlPoint& p0, - const DlPoint& p1, - DlScalar on_length, - DlScalar off_length) override { - RecordByType(DisplayListOpType::kDrawDashedLine); - } - void drawRect(const SkRect& rect) override { - RecordByType(DisplayListOpType::kDrawRect); - } - void drawOval(const SkRect& bounds) override { - RecordByType(DisplayListOpType::kDrawOval); - } - void drawCircle(const SkPoint& center, SkScalar radius) override { - RecordByType(DisplayListOpType::kDrawCircle); - } - void drawRRect(const SkRRect& rrect) override { - RecordByType(DisplayListOpType::kDrawRRect); - } - void drawDRRect(const SkRRect& outer, const SkRRect& inner) override { - RecordByType(DisplayListOpType::kDrawDRRect); - } - void drawPath(const SkPath& path) override { - RecordByType(DisplayListOpType::kDrawPath); - } - void drawArc(const SkRect& oval_bounds, - SkScalar start_degrees, - SkScalar sweep_degrees, - bool use_center) override { - RecordByType(DisplayListOpType::kDrawArc); - } - void drawPoints(DlCanvas::PointMode mode, - uint32_t count, - const SkPoint points[]) override { - switch (mode) { - case DlCanvas::PointMode::kPoints: - RecordByType(DisplayListOpType::kDrawPoints); - break; - case DlCanvas::PointMode::kLines: - RecordByType(DisplayListOpType::kDrawLines); - break; - case DlCanvas::PointMode::kPolygon: - RecordByType(DisplayListOpType::kDrawPolygon); - break; - } - } - void drawVertices(const std::shared_ptr& vertices, - DlBlendMode mode) override { - RecordByType(DisplayListOpType::kDrawVertices); - } - void drawImage(const sk_sp image, - const SkPoint point, - DlImageSampling sampling, - bool render_with_attributes) override { - if (render_with_attributes) { - RecordByType(DisplayListOpType::kDrawImageWithAttr); - } else { - RecordByType(DisplayListOpType::kDrawImage); - } - } - void drawImageRect(const sk_sp image, - const SkRect& src, - const SkRect& dst, - DlImageSampling sampling, - bool render_with_attributes, - SrcRectConstraint constraint) override { - RecordByType(DisplayListOpType::kDrawImageRect); - } - void drawImageNine(const sk_sp image, - const SkIRect& center, - const SkRect& dst, - DlFilterMode filter, - bool render_with_attributes) override { - if (render_with_attributes) { - RecordByType(DisplayListOpType::kDrawImageNineWithAttr); - } else { - RecordByType(DisplayListOpType::kDrawImageNine); - } - } - void drawAtlas(const sk_sp atlas, - const SkRSXform xform[], - const SkRect tex[], - const DlColor colors[], - int count, - DlBlendMode mode, - DlImageSampling sampling, - const SkRect* cull_rect, - bool render_with_attributes) override { - if (cull_rect) { - RecordByType(DisplayListOpType::kDrawAtlasCulled); - } else { - RecordByType(DisplayListOpType::kDrawAtlas); - } - } - void drawDisplayList(const sk_sp display_list, - SkScalar opacity) override { - RecordByType(DisplayListOpType::kDrawDisplayList); - } - void drawTextBlob(const sk_sp blob, - SkScalar x, - SkScalar y) override { - RecordByType(DisplayListOpType::kDrawTextBlob); - } - void drawTextFrame(const std::shared_ptr& text_frame, - SkScalar x, - SkScalar y) override { - RecordByType(DisplayListOpType::kDrawTextFrame); - } - void drawShadow(const SkPath& path, - const DlColor color, - const SkScalar elevation, - bool transparent_occluder, - SkScalar dpr) override { - if (transparent_occluder) { - RecordByType(DisplayListOpType::kDrawShadowTransparentOccluder); - } else { - RecordByType(DisplayListOpType::kDrawShadow); - } - } - - uint32_t GetOpsReceived() { return op_count_; } - uint32_t GetOpsReceived(DisplayListOpCategory category) { - return category_counts_[static_cast(category)]; - } - uint32_t GetOpsReceived(DisplayListOpType type) { - return type_counts_[static_cast(type)]; - } - - protected: - virtual void RecordByType(DisplayListOpType type) { - type_counts_[static_cast(type)]++; - RecordByCategory(DisplayList::GetOpCategory(type)); - } - - virtual void RecordByCategory(DisplayListOpCategory category) { - category_counts_[static_cast(category)]++; - switch (category) { - case DisplayListOpCategory::kAttribute: - RecordAttribute(); - break; - case DisplayListOpCategory::kTransform: - RecordTransform(); - break; - case DisplayListOpCategory::kClip: - RecordClip(); - break; - case DisplayListOpCategory::kSave: - RecordSave(); - break; - case DisplayListOpCategory::kSaveLayer: - RecordSaveLayer(); - break; - case DisplayListOpCategory::kRestore: - RecordRestore(); - break; - case DisplayListOpCategory::kRendering: - RecordRendering(); - break; - case DisplayListOpCategory::kSubDisplayList: - RecordSubDisplayList(); - break; - case DisplayListOpCategory::kInvalidCategory: - RecordInvalid(); - break; - } - } - - virtual void RecordAttribute() { RecordOp(); } - virtual void RecordTransform() { RecordOp(); } - virtual void RecordClip() { RecordOp(); } - virtual void RecordSave() { RecordOp(); } - virtual void RecordSaveLayer() { RecordOp(); } - virtual void RecordRestore() { RecordOp(); } - virtual void RecordRendering() { RecordOp(); } - virtual void RecordSubDisplayList() { RecordOp(); } - virtual void RecordInvalid() { RecordOp(); } - - virtual void RecordOp() { op_count_++; } - - static constexpr size_t kTypeCount = - static_cast(DisplayListOpType::kMaxOp) + 1; - static constexpr size_t kCategoryCount = - static_cast(DisplayListOpCategory::kMaxCategory) + 1; - - std::array type_counts_; - std::array category_counts_; - uint32_t op_count_ = 0u; -}; - } // namespace testing } // namespace flutter