diff --git a/display_list/display_list.cc b/display_list/display_list.cc index 202333459656f..9528c21c204b5 100644 --- a/display_list/display_list.cc +++ b/display_list/display_list.cc @@ -63,7 +63,7 @@ void DisplayList::ComputeBounds() { } void DisplayList::ComputeRTree() { - RTreeBoundsAccumulator accumulator; + RTreeBoundsAccumulator accumulator(&rtree_op_indices_); DisplayListBoundsCalculator calculator(accumulator, &bounds_cull_); Dispatch(calculator); if (calculator.is_unbounded()) { @@ -72,17 +72,102 @@ void DisplayList::ComputeRTree() { rtree_ = accumulator.rtree(); } +class Culler { + public: + virtual bool init(DispatchContext& context) = 0; + virtual void update(DispatchContext& context) = 0; +}; +class NopCuller : public Culler { + public: + static NopCuller instance; + + bool init(DispatchContext& context) override { + context.next_render_index = 0; + return true; + } + void update(DispatchContext& context) override {} +}; +NopCuller NopCuller::instance = NopCuller(); +class VectorCuller : public Culler { + public: + VectorCuller(const std::vector& op_indices, + const std::vector& rect_indices) + : op_indices_(op_indices), + cur_(rect_indices.begin()), + end_(rect_indices.end()) {} + + bool init(DispatchContext& context) override { + if (cur_ < end_) { + context.next_render_index = op_indices_[*cur_++]; + return true; + } else { + context.next_render_index = std::numeric_limits::max(); + return false; + } + } + void update(DispatchContext& context) override { + if (++context.cur_index > context.next_render_index) { + while (cur_ < end_) { + context.next_render_index = op_indices_[*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. + return; + } + } + context.next_render_index = std::numeric_limits::max(); + } + } + + private: + const std::vector& op_indices_; + std::vector::const_iterator cur_; + std::vector::const_iterator end_; +}; + +void DisplayList::Dispatch(Dispatcher& ctx) const { + uint8_t* ptr = storage_.get(); + Dispatch(ctx, ptr, ptr + byte_count_, NopCuller::instance); +} +void DisplayList::Dispatch(Dispatcher& ctx, const SkRect& cull_rect) { + if (cull_rect.isEmpty()) { + return; + } + if (cull_rect.contains(bounds())) { + Dispatch(ctx); + return; + } + uint8_t* ptr = storage_.get(); + std::vector rect_indices; + rtree()->search(cull_rect, &rect_indices); + VectorCuller culler(rtree_op_indices_, rect_indices); + Dispatch(ctx, ptr, ptr + byte_count_, culler); +} + void DisplayList::Dispatch(Dispatcher& dispatcher, uint8_t* ptr, - uint8_t* end) const { + uint8_t* end, + Culler& culler) const { + DispatchContext context = { + .dispatcher = dispatcher, + + .cur_index = 0, + + .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(dispatcher); \ +#define DL_OP_DISPATCH(name) \ + case DisplayListOpType::k##name: \ + static_cast(op)->dispatch(context); \ break; FOR_EACH_DISPLAY_LIST_OP(DL_OP_DISPATCH) @@ -93,6 +178,7 @@ void DisplayList::Dispatch(Dispatcher& dispatcher, FML_DCHECK(false); return; } + culler.update(context); } } @@ -187,16 +273,31 @@ static bool CompareOps(uint8_t* ptrA, } void DisplayList::RenderTo(DisplayListBuilder* builder, - SkScalar opacity) const { + SkScalar opacity, + bool cull) { // TODO(100983): Opacity is not respected and attributes are not reset. if (!builder) { return; } + if (cull) { + SkRect clip_bounds = builder->getLocalClipBounds(); + if (!clip_bounds.contains(bounds())) { + Dispatch(*builder, clip_bounds); + return; + } + } Dispatch(*builder); } -void DisplayList::RenderTo(SkCanvas* canvas, SkScalar opacity) const { +void DisplayList::RenderTo(SkCanvas* canvas, SkScalar opacity, bool cull) { DisplayListCanvasDispatcher dispatcher(canvas, opacity); + if (cull) { + SkRect clip_bounds = canvas->getLocalClipBounds(); + if (!clip_bounds.contains(bounds())) { + Dispatch(dispatcher, clip_bounds); + return; + } + } Dispatch(dispatcher); } diff --git a/display_list/display_list.h b/display_list/display_list.h index e30968a1f64a5..db9d62e4a4d35 100644 --- a/display_list/display_list.h +++ b/display_list/display_list.h @@ -215,6 +215,8 @@ class SaveLayerOptions { }; }; +class Culler; + // The base class that contains a sequence of rendering operations // for dispatch to a Dispatcher. These objects must be instantiated // through an instance of DisplayListBuilder::build(). @@ -224,15 +226,16 @@ class DisplayList : public SkRefCnt { ~DisplayList(); - void Dispatch(Dispatcher& ctx) const { - uint8_t* ptr = storage_.get(); - Dispatch(ctx, ptr, ptr + byte_count_); - } + void Dispatch(Dispatcher& ctx) const; + void Dispatch(Dispatcher& ctx, const SkRect& cull_rect); void RenderTo(DisplayListBuilder* builder, - SkScalar opacity = SK_Scalar1) const; + SkScalar opacity = SK_Scalar1, + bool cull = true); - void RenderTo(SkCanvas* canvas, SkScalar opacity = SK_Scalar1) const; + void RenderTo(SkCanvas* canvas, + SkScalar opacity = SK_Scalar1, + bool cull = true); // SkPicture always includes nested bytes, but nested ops are // only included if requested. The defaults used here for these @@ -296,6 +299,7 @@ class DisplayList : public SkRefCnt { uint32_t unique_id_; SkRect bounds_; sk_sp rtree_; + std::vector rtree_op_indices_; // Only used for drawPaint() and drawColor() SkRect bounds_cull_; @@ -304,7 +308,11 @@ class DisplayList : public SkRefCnt { void ComputeBounds(); void ComputeRTree(); - void Dispatch(Dispatcher& ctx, uint8_t* ptr, uint8_t* end) const; + + void Dispatch(Dispatcher& ctx, + uint8_t* ptr, + uint8_t* end, + Culler& culler) const; friend class DisplayListBuilder; }; diff --git a/display_list/display_list_builder.cc b/display_list/display_list_builder.cc index 7aa654730e245..e9fc4dbfdeda4 100644 --- a/display_list/display_list_builder.cc +++ b/display_list/display_list_builder.cc @@ -26,7 +26,7 @@ static void CopyV(void* dst, const S* src, int n, Rest&&... rest) { } template -void* DisplayListBuilder::Push(size_t pod, int op_inc, Args&&... args) { +void* DisplayListBuilder::Push(size_t pod, int render_op_inc, Args&&... args) { size_t size = SkAlignPtr(sizeof(T) + pod); FML_DCHECK(size < (1 << 24)); if (used_ + size > allocated_) { @@ -44,7 +44,8 @@ void* DisplayListBuilder::Push(size_t pod, int op_inc, Args&&... args) { new (op) T{std::forward(args)...}; op->type = T::kType; op->size = size; - op_count_ += op_inc; + render_op_count_ += render_op_inc; + op_index_++; return op + 1; } @@ -53,10 +54,10 @@ sk_sp DisplayListBuilder::Build() { restore(); } size_t bytes = used_; - int count = op_count_; + int count = render_op_count_; size_t nested_bytes = nested_bytes_; int nested_count = nested_op_count_; - used_ = allocated_ = op_count_ = 0; + used_ = allocated_ = render_op_count_ = op_index_ = 0; nested_bytes_ = nested_op_count_ = 0; storage_.realloc(bytes); bool compatible = layer_stack_.back().is_group_opacity_compatible(); @@ -423,7 +424,9 @@ void DisplayListBuilder::setAttributesFromPaint( void DisplayListBuilder::checkForDeferredSave() { if (current_layer_->has_deferred_save_op_) { + size_t save_offset = used_; Push(0, 1); + current_layer_->save_offset = save_offset; current_layer_->has_deferred_save_op_ = false; } } @@ -436,7 +439,10 @@ void DisplayListBuilder::save() { void DisplayListBuilder::restore() { if (layer_stack_.size() > 1) { + SaveOpBase* op = reinterpret_cast(storage_.get() + + current_layer_->save_offset); if (!current_layer_->has_deferred_save_op_) { + op->restore_index = op_index_; Push(0, 1); } // Grab the current layer info before we push the restore @@ -445,6 +451,9 @@ void DisplayListBuilder::restore() { layer_stack_.pop_back(); current_layer_ = &layer_stack_.back(); if (layer_info.has_layer) { + // Layers are never deferred for now, we need to update the + // following code if we ever do saveLayer culling... + FML_DCHECK(!layer_info.has_deferred_save_op_); if (layer_info.is_group_opacity_compatible()) { // We are now going to go back and modify the matching saveLayer // call to add the option indicating it can distribute an opacity @@ -458,8 +467,6 @@ void DisplayListBuilder::restore() { // in the DisplayList are only allowed *during* the build phase. // Once built, the DisplayList records must remain read only to // ensure consistency of rendering and |Equals()| behavior. - SaveLayerOp* op = reinterpret_cast( - storage_.get() + layer_info.save_layer_offset); op->options = op->options.with_can_distribute_opacity(); } } else { @@ -486,11 +493,11 @@ void DisplayListBuilder::saveLayer(const SkRect* bounds, size_t save_layer_offset = used_; if (backdrop) { bounds // - ? Push(0, 1, *bounds, options, backdrop) + ? Push(0, 1, options, *bounds, backdrop) : Push(0, 1, options, backdrop); } else { bounds // - ? Push(0, 1, *bounds, options) + ? Push(0, 1, options, *bounds) : Push(0, 1, options); } CheckLayerOpacityCompatibility(options.renders_with_attributes()); diff --git a/display_list/display_list_builder.h b/display_list/display_list_builder.h index 2f2cd863bd96c..fb4f58ccafe6a 100644 --- a/display_list/display_list_builder.h +++ b/display_list/display_list_builder.h @@ -360,7 +360,8 @@ class DisplayListBuilder final : public virtual Dispatcher, SkAutoTMalloc storage_; size_t used_ = 0; size_t allocated_ = 0; - int op_count_ = 0; + int render_op_count_ = 0; + int op_index_ = 0; // bytes and ops from |drawPicture| and |drawDisplayList| size_t nested_bytes_ = 0; @@ -384,9 +385,9 @@ class DisplayListBuilder final : public virtual Dispatcher, struct LayerInfo { LayerInfo(const SkM44& matrix, const SkRect& clip_bounds, - size_t save_layer_offset = 0, + size_t save_offset = 0, bool has_layer = false) - : save_layer_offset(save_layer_offset), + : save_offset(save_offset), has_layer(has_layer), cannot_inherit_opacity(false), has_compatible_op(false), @@ -407,7 +408,7 @@ class DisplayListBuilder final : public virtual Dispatcher, // the records inside the saveLayer that may impact how the saveLayer // is handled (e.g., |cannot_inherit_opacity| == false). // This offset is only valid if |has_layer| is true. - size_t save_layer_offset; + size_t save_offset; bool has_deferred_save_op_ = false; diff --git a/display_list/display_list_canvas_recorder.cc b/display_list/display_list_canvas_recorder.cc index 406ee96015bc9..a4a4ddfdc6332 100644 --- a/display_list/display_list_canvas_recorder.cc +++ b/display_list/display_list_canvas_recorder.cc @@ -20,8 +20,13 @@ namespace flutter { } while (0) DisplayListCanvasRecorder::DisplayListCanvasRecorder(const SkRect& bounds) - : SkCanvasVirtualEnforcer(bounds.width(), bounds.height()), - builder_(sk_make_sp(bounds)) {} + : SkCanvasVirtualEnforcer(bounds.right(), bounds.bottom()), + builder_(sk_make_sp(bounds)) { + if (bounds.isSorted()) { + SkCanvasVirtualEnforcer::onClipRect(bounds, SkClipOp::kIntersect, + ClipEdgeStyle::kHard_ClipEdgeStyle); + } +} sk_sp DisplayListCanvasRecorder::Build() { CHECK_DISPOSE(nullptr); @@ -56,6 +61,7 @@ void DisplayListCanvasRecorder::onClipRect(const SkRect& rect, CHECK_DISPOSE(); builder_->clipRect(rect, clip_op, edge_style == ClipEdgeStyle::kSoft_ClipEdgeStyle); + SkCanvasVirtualEnforcer::onClipRect(rect, clip_op, edge_style); } void DisplayListCanvasRecorder::onClipRRect(const SkRRect& rrect, SkClipOp clip_op, @@ -63,6 +69,7 @@ void DisplayListCanvasRecorder::onClipRRect(const SkRRect& rrect, CHECK_DISPOSE(); builder_->clipRRect(rrect, clip_op, edge_style == ClipEdgeStyle::kSoft_ClipEdgeStyle); + SkCanvasVirtualEnforcer::onClipRRect(rrect, clip_op, edge_style); } void DisplayListCanvasRecorder::onClipPath(const SkPath& path, SkClipOp clip_op, @@ -70,6 +77,7 @@ void DisplayListCanvasRecorder::onClipPath(const SkPath& path, CHECK_DISPOSE(); builder_->clipPath(path, clip_op, edge_style == ClipEdgeStyle::kSoft_ClipEdgeStyle); + SkCanvasVirtualEnforcer::onClipPath(path, clip_op, edge_style); } void DisplayListCanvasRecorder::willSave() { diff --git a/display_list/display_list_ops.h b/display_list/display_list_ops.h index 061f29365df45..f02cb67bf67ba 100644 --- a/display_list/display_list_ops.h +++ b/display_list/display_list_ops.h @@ -15,6 +15,26 @@ namespace flutter { +struct DispatchContext { + Dispatcher& dispatcher; + + uint32_t cur_index; + uint32_t next_render_index; + + uint32_t next_restore_index; + + struct SaveInfo { + SaveInfo(uint32_t previous_restore_index, bool save_was_needed) + : previous_restore_index(previous_restore_index), + save_was_needed(save_was_needed) {} + + uint32_t 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. @@ -69,8 +89,8 @@ struct DLOp { \ const bool value; \ \ - void dispatch(Dispatcher& dispatcher) const { \ - dispatcher.set##name(value); \ + void dispatch(DispatchContext& ctx) const { \ + ctx.dispatcher.set##name(value); \ } \ }; DEFINE_SET_BOOL_OP(AntiAlias) @@ -87,8 +107,8 @@ DEFINE_SET_BOOL_OP(InvertColors) \ const DlStroke##name value; \ \ - void dispatch(Dispatcher& dispatcher) const { \ - dispatcher.setStroke##name(value); \ + void dispatch(DispatchContext& ctx) const { \ + ctx.dispatcher.setStroke##name(value); \ } \ }; DEFINE_SET_ENUM_OP(Cap) @@ -103,7 +123,7 @@ struct SetStyleOp final : DLOp { const DlDrawStyle style; - void dispatch(Dispatcher& dispatcher) const { dispatcher.setStyle(style); } + void dispatch(DispatchContext& ctx) const { ctx.dispatcher.setStyle(style); } }; // 4 byte header + 4 byte payload packs into minimum 8 bytes struct SetStrokeWidthOp final : DLOp { @@ -113,8 +133,8 @@ struct SetStrokeWidthOp final : DLOp { const float width; - void dispatch(Dispatcher& dispatcher) const { - dispatcher.setStrokeWidth(width); + void dispatch(DispatchContext& ctx) const { + ctx.dispatcher.setStrokeWidth(width); } }; // 4 byte header + 4 byte payload packs into minimum 8 bytes @@ -125,8 +145,8 @@ struct SetStrokeMiterOp final : DLOp { const float limit; - void dispatch(Dispatcher& dispatcher) const { - dispatcher.setStrokeMiter(limit); + void dispatch(DispatchContext& ctx) const { + ctx.dispatcher.setStrokeMiter(limit); } }; @@ -138,7 +158,7 @@ struct SetColorOp final : DLOp { const DlColor color; - void dispatch(Dispatcher& dispatcher) const { dispatcher.setColor(color); } + void dispatch(DispatchContext& ctx) const { ctx.dispatcher.setColor(color); } }; // 4 byte header + 4 byte payload packs into minimum 8 bytes struct SetBlendModeOp final : DLOp { @@ -148,7 +168,9 @@ struct SetBlendModeOp final : DLOp { const DlBlendMode mode; - void dispatch(Dispatcher& dispatcher) const { dispatcher.setBlendMode(mode); } + void dispatch(DispatchContext& ctx) const { + ctx.dispatcher.setBlendMode(mode); + } }; // Clear: 4 byte header + unused 4 byte payload uses 8 bytes @@ -162,8 +184,8 @@ struct SetBlendModeOp final : DLOp { \ Clear##name##Op() {} \ \ - void dispatch(Dispatcher& dispatcher) const { \ - dispatcher.set##name(nullptr); \ + void dispatch(DispatchContext& ctx) const { \ + ctx.dispatcher.set##name(nullptr); \ } \ }; \ struct Set##name##Op final : DLOp { \ @@ -173,8 +195,8 @@ struct SetBlendModeOp final : DLOp { \ sk_sp field; \ \ - void dispatch(Dispatcher& dispatcher) const { \ - dispatcher.set##name(field); \ + void dispatch(DispatchContext& ctx) const { \ + ctx.dispatcher.set##name(field); \ } \ }; DEFINE_SET_CLEAR_SKREF_OP(Blender, blender) @@ -195,8 +217,8 @@ DEFINE_SET_CLEAR_SKREF_OP(Blender, blender) \ Clear##name##Op() {} \ \ - void dispatch(Dispatcher& dispatcher) const { \ - dispatcher.set##name(nullptr); \ + void dispatch(DispatchContext& ctx) const { \ + ctx.dispatcher.set##name(nullptr); \ } \ }; \ struct SetPod##name##Op final : DLOp { \ @@ -204,9 +226,9 @@ DEFINE_SET_CLEAR_SKREF_OP(Blender, blender) \ SetPod##name##Op() {} \ \ - void dispatch(Dispatcher& dispatcher) const { \ + void dispatch(DispatchContext& ctx) const { \ const Dl##name* filter = reinterpret_cast(this + 1); \ - dispatcher.set##name(filter); \ + ctx.dispatcher.set##name(filter); \ } \ }; \ struct SetSk##name##Op final : DLOp { \ @@ -216,9 +238,9 @@ DEFINE_SET_CLEAR_SKREF_OP(Blender, blender) \ sk_sp field; \ \ - void dispatch(Dispatcher& dispatcher) const { \ + void dispatch(DispatchContext& ctx) const { \ DlUnknown##name dl_filter(field); \ - dispatcher.set##name(&dl_filter); \ + ctx.dispatcher.set##name(&dl_filter); \ } \ }; DEFINE_SET_CLEAR_DLATTR_OP(ColorFilter, ColorFilter, filter) @@ -242,8 +264,8 @@ struct SetImageColorSourceOp : DLOp { const DlImageColorSource source; - void dispatch(Dispatcher& dispatcher) const { - dispatcher.setColorSource(&source); + void dispatch(DispatchContext& ctx) const { + ctx.dispatcher.setColorSource(&source); } }; @@ -259,8 +281,8 @@ struct SetRuntimeEffectColorSourceOp : DLOp { const DlRuntimeEffectColorSource source; - void dispatch(Dispatcher& dispatcher) const { - dispatcher.setColorSource(&source); + void dispatch(DispatchContext& ctx) const { + ctx.dispatcher.setColorSource(&source); } DisplayListCompare equals(const SetRuntimeEffectColorSourceOp* other) const { @@ -278,8 +300,8 @@ struct SetSharedImageFilterOp : DLOp { const std::shared_ptr filter; - void dispatch(Dispatcher& dispatcher) const { - dispatcher.setImageFilter(filter.get()); + void dispatch(DispatchContext& ctx) const { + ctx.dispatcher.setImageFilter(filter.get()); } DisplayListCompare equals(const SetSharedImageFilterOp* other) const { @@ -288,53 +310,80 @@ struct SetSharedImageFilterOp : DLOp { } }; -// 4 byte header + no payload uses minimum 8 bytes (4 bytes unused) -struct SaveOp final : DLOp { +// The base object for all save() and saveLayer() ops +// 4 byte header + 20 bytes payload packs neatly into 24 bytes +struct SaveOpBase : DLOp { + SaveOpBase() : options(), restore_index(0) {} + + SaveOpBase(const SaveLayerOptions options) + : options(options), restore_index(0) {} + + // options parameter is only used by saveLayer operations, but since + // it packs neatly into the empty space created by laying out the 64-bit + // offsets, it can be stored for free and defaulted to 0 for save operations. + SaveLayerOptions options; + uint32_t restore_index; + + 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; + } +}; +// 24 byte SaveOpBase with no additional data (options is unsed here) +struct SaveOp final : SaveOpBase { static const auto kType = DisplayListOpType::kSave; - SaveOp() {} + SaveOp() : SaveOpBase() {} - void dispatch(Dispatcher& dispatcher) const { dispatcher.save(); } + void dispatch(DispatchContext& ctx) const { + if (save_needed(ctx)) { + ctx.dispatcher.save(); + } + } }; -// 4 byte header + 4 byte payload packs into minimum 8 bytes -struct SaveLayerOp final : DLOp { +// 24 byte SaveOpBase with no additional data +struct SaveLayerOp final : SaveOpBase { static const auto kType = DisplayListOpType::kSaveLayer; - explicit SaveLayerOp(const SaveLayerOptions options) : options(options) {} - - SaveLayerOptions options; + explicit SaveLayerOp(const SaveLayerOptions options) : SaveOpBase(options) {} - void dispatch(Dispatcher& dispatcher) const { - dispatcher.saveLayer(nullptr, options); + void dispatch(DispatchContext& ctx) const { + if (save_needed(ctx)) { + ctx.dispatcher.saveLayer(nullptr, options); + } } }; -// 4 byte header + 20 byte payload packs evenly into 24 bytes -struct SaveLayerBoundsOp final : DLOp { +// 24 byte SaveOpBase + 16 byte payload packs evenly into 40 bytes +struct SaveLayerBoundsOp final : SaveOpBase { static const auto kType = DisplayListOpType::kSaveLayerBounds; - SaveLayerBoundsOp(SkRect rect, const SaveLayerOptions options) - : options(options), rect(rect) {} + SaveLayerBoundsOp(const SaveLayerOptions options, const SkRect& rect) + : SaveOpBase(options), rect(rect) {} - SaveLayerOptions options; const SkRect rect; - void dispatch(Dispatcher& dispatcher) const { - dispatcher.saveLayer(&rect, options); + void dispatch(DispatchContext& ctx) const { + if (save_needed(ctx)) { + ctx.dispatcher.saveLayer(&rect, options); + } } }; -// 4 byte header + 20 byte payload packs into minimum 24 bytes -struct SaveLayerBackdropOp final : DLOp { +// 24 byte SaveOpBase + 16 byte payload packs into minimum 40 bytes +struct SaveLayerBackdropOp final : SaveOpBase { static const auto kType = DisplayListOpType::kSaveLayerBackdrop; explicit SaveLayerBackdropOp(const SaveLayerOptions options, const DlImageFilter* backdrop) - : options(options), backdrop(backdrop->shared()) {} + : SaveOpBase(options), backdrop(backdrop->shared()) {} - SaveLayerOptions options; const std::shared_ptr backdrop; - void dispatch(Dispatcher& dispatcher) const { - dispatcher.saveLayer(nullptr, options, backdrop.get()); + void dispatch(DispatchContext& ctx) const { + if (save_needed(ctx)) { + ctx.dispatcher.saveLayer(nullptr, options, backdrop.get()); + } } DisplayListCompare equals(const SaveLayerBackdropOp* other) const { @@ -343,21 +392,22 @@ struct SaveLayerBackdropOp final : DLOp { : DisplayListCompare::kNotEqual; } }; -// 4 byte header + 36 byte payload packs evenly into 36 bytes -struct SaveLayerBackdropBoundsOp final : DLOp { +// 24 byte SaveOpBase + 32 byte payload packs into minimum 56 bytes +struct SaveLayerBackdropBoundsOp final : SaveOpBase { static const auto kType = DisplayListOpType::kSaveLayerBackdropBounds; - SaveLayerBackdropBoundsOp(SkRect rect, - const SaveLayerOptions options, + SaveLayerBackdropBoundsOp(const SaveLayerOptions options, + const SkRect& rect, const DlImageFilter* backdrop) - : options(options), rect(rect), backdrop(backdrop->shared()) {} + : SaveOpBase(options), rect(rect), backdrop(backdrop->shared()) {} - SaveLayerOptions options; const SkRect rect; const std::shared_ptr backdrop; - void dispatch(Dispatcher& dispatcher) const { - dispatcher.saveLayer(&rect, options, backdrop.get()); + void dispatch(DispatchContext& ctx) const { + if (save_needed(ctx)) { + ctx.dispatcher.saveLayer(&rect, options, backdrop.get()); + } } DisplayListCompare equals(const SaveLayerBackdropBoundsOp* other) const { @@ -373,12 +423,24 @@ struct RestoreOp final : DLOp { RestoreOp() {} - void dispatch(Dispatcher& dispatcher) const { dispatcher.restore(); } + void dispatch(DispatchContext& ctx) const { + DispatchContext::SaveInfo& info = ctx.save_infos.back(); + if (info.save_was_needed) { + ctx.dispatcher.restore(); + } + ctx.next_restore_index = info.previous_restore_index; + ctx.save_infos.pop_back(); + } }; +struct TransformClipOpBase : DLOp { + 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) -struct TranslateOp final : DLOp { +struct TranslateOp final : TransformClipOpBase { static const auto kType = DisplayListOpType::kTranslate; TranslateOp(SkScalar tx, SkScalar ty) : tx(tx), ty(ty) {} @@ -386,11 +448,15 @@ struct TranslateOp final : DLOp { const SkScalar tx; const SkScalar ty; - void dispatch(Dispatcher& dispatcher) const { dispatcher.translate(tx, ty); } + void dispatch(DispatchContext& ctx) const { + if (op_needed(ctx)) { + ctx.dispatcher.translate(tx, ty); + } + } }; // 4 byte header + 8 byte payload uses 12 bytes but is rounded up to 16 bytes // (4 bytes unused) -struct ScaleOp final : DLOp { +struct ScaleOp final : TransformClipOpBase { static const auto kType = DisplayListOpType::kScale; ScaleOp(SkScalar sx, SkScalar sy) : sx(sx), sy(sy) {} @@ -398,21 +464,29 @@ struct ScaleOp final : DLOp { const SkScalar sx; const SkScalar sy; - void dispatch(Dispatcher& dispatcher) const { dispatcher.scale(sx, sy); } + void dispatch(DispatchContext& ctx) const { + if (op_needed(ctx)) { + ctx.dispatcher.scale(sx, sy); + } + } }; // 4 byte header + 4 byte payload packs into minimum 8 bytes -struct RotateOp final : DLOp { +struct RotateOp final : TransformClipOpBase { static const auto kType = DisplayListOpType::kRotate; explicit RotateOp(SkScalar degrees) : degrees(degrees) {} const SkScalar degrees; - void dispatch(Dispatcher& dispatcher) const { dispatcher.rotate(degrees); } + void dispatch(DispatchContext& ctx) const { + if (op_needed(ctx)) { + ctx.dispatcher.rotate(degrees); + } + } }; // 4 byte header + 8 byte payload uses 12 bytes but is rounded up to 16 bytes // (4 bytes unused) -struct SkewOp final : DLOp { +struct SkewOp final : TransformClipOpBase { static const auto kType = DisplayListOpType::kSkew; SkewOp(SkScalar sx, SkScalar sy) : sx(sx), sy(sy) {} @@ -420,11 +494,15 @@ struct SkewOp final : DLOp { const SkScalar sx; const SkScalar sy; - void dispatch(Dispatcher& dispatcher) const { dispatcher.skew(sx, sy); } + void dispatch(DispatchContext& ctx) const { + if (op_needed(ctx)) { + ctx.dispatcher.skew(sx, sy); + } + } }; // 4 byte header + 24 byte payload uses 28 bytes but is rounded up to 32 bytes // (4 bytes unused) -struct Transform2DAffineOp final : DLOp { +struct Transform2DAffineOp final : TransformClipOpBase { static const auto kType = DisplayListOpType::kTransform2DAffine; // clang-format off @@ -436,14 +514,16 @@ struct Transform2DAffineOp final : DLOp { const SkScalar mxx, mxy, mxt; const SkScalar myx, myy, myt; - void dispatch(Dispatcher& dispatcher) const { - dispatcher.transform2DAffine(mxx, mxy, mxt, // - myx, myy, myt); + void dispatch(DispatchContext& ctx) const { + if (op_needed(ctx)) { + ctx.dispatcher.transform2DAffine(mxx, mxy, mxt, // + myx, myy, myt); + } } }; // 4 byte header + 64 byte payload uses 68 bytes which is rounded up to 72 bytes // (4 bytes unused) -struct TransformFullPerspectiveOp final : DLOp { +struct TransformFullPerspectiveOp final : TransformClipOpBase { static const auto kType = DisplayListOpType::kTransformFullPerspective; // clang-format off @@ -463,21 +543,27 @@ struct TransformFullPerspectiveOp final : DLOp { const SkScalar mzx, mzy, mzz, mzt; const SkScalar mwx, mwy, mwz, mwt; - void dispatch(Dispatcher& dispatcher) const { - dispatcher.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.dispatcher.transformFullPerspective(mxx, mxy, mxz, mxt, // + myx, myy, myz, myt, // + mzx, mzy, mzz, mzt, // + mwx, mwy, mwz, mwt); + } } }; // 4 byte header with no payload. -struct TransformResetOp final : DLOp { +struct TransformResetOp final : TransformClipOpBase { static const auto kType = DisplayListOpType::kTransformReset; TransformResetOp() = default; - void dispatch(Dispatcher& dispatcher) const { dispatcher.transformReset(); } + void dispatch(DispatchContext& ctx) const { + if (op_needed(ctx)) { + ctx.dispatcher.transformReset(); + } + } }; // 4 byte header + 4 byte common payload packs into minimum 8 bytes @@ -491,7 +577,7 @@ struct TransformResetOp final : DLOp { // packing into more bytes than needed (even when they are declared as // packed bit fields!) #define DEFINE_CLIP_SHAPE_OP(shapetype, clipop) \ - struct Clip##clipop##shapetype##Op final : DLOp { \ + struct Clip##clipop##shapetype##Op final : TransformClipOpBase { \ static const auto kType = DisplayListOpType::kClip##clipop##shapetype; \ \ Clip##clipop##shapetype##Op(Sk##shapetype shape, bool is_aa) \ @@ -500,8 +586,10 @@ struct TransformResetOp final : DLOp { const bool is_aa; \ const Sk##shapetype shape; \ \ - void dispatch(Dispatcher& dispatcher) const { \ - dispatcher.clip##shapetype(shape, SkClipOp::k##clipop, is_aa); \ + void dispatch(DispatchContext& ctx) const { \ + if (op_needed(ctx)) { \ + ctx.dispatcher.clip##shapetype(shape, SkClipOp::k##clipop, is_aa); \ + } \ } \ }; DEFINE_CLIP_SHAPE_OP(Rect, Intersect) @@ -511,7 +599,7 @@ DEFINE_CLIP_SHAPE_OP(RRect, Difference) #undef DEFINE_CLIP_SHAPE_OP #define DEFINE_CLIP_PATH_OP(clipop) \ - struct Clip##clipop##PathOp final : DLOp { \ + struct Clip##clipop##PathOp final : TransformClipOpBase { \ static const auto kType = DisplayListOpType::kClip##clipop##Path; \ \ Clip##clipop##PathOp(SkPath path, bool is_aa) \ @@ -520,8 +608,10 @@ DEFINE_CLIP_SHAPE_OP(RRect, Difference) const bool is_aa; \ const SkPath path; \ \ - void dispatch(Dispatcher& dispatcher) const { \ - dispatcher.clipPath(path, SkClipOp::k##clipop, is_aa); \ + void dispatch(DispatchContext& ctx) const { \ + if (op_needed(ctx)) { \ + ctx.dispatcher.clipPath(path, SkClipOp::k##clipop, is_aa); \ + } \ } \ \ DisplayListCompare equals(const Clip##clipop##PathOp* other) const { \ @@ -534,17 +624,27 @@ DEFINE_CLIP_PATH_OP(Intersect) DEFINE_CLIP_PATH_OP(Difference) #undef DEFINE_CLIP_PATH_OP +struct DrawOpBase : DLOp { + 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) -struct DrawPaintOp final : DLOp { +struct DrawPaintOp final : DrawOpBase { static const auto kType = DisplayListOpType::kDrawPaint; DrawPaintOp() {} - void dispatch(Dispatcher& dispatcher) const { dispatcher.drawPaint(); } + void dispatch(DispatchContext& ctx) const { + if (op_needed(ctx)) { + ctx.dispatcher.drawPaint(); + } + } }; // 4 byte header + 8 byte payload uses 12 bytes but is rounded up to 16 bytes // (4 bytes unused) -struct DrawColorOp final : DLOp { +struct DrawColorOp final : DrawOpBase { static const auto kType = DisplayListOpType::kDrawColor; DrawColorOp(DlColor color, DlBlendMode mode) : color(color), mode(mode) {} @@ -552,8 +652,10 @@ struct DrawColorOp final : DLOp { const DlColor color; const DlBlendMode mode; - void dispatch(Dispatcher& dispatcher) const { - dispatcher.drawColor(color, mode); + void dispatch(DispatchContext& ctx) const { + if (op_needed(ctx)) { + ctx.dispatcher.drawColor(color, mode); + } } }; @@ -563,15 +665,17 @@ struct DrawColorOp final : DLOp { // SkOval is same as SkRect // SkRRect is 52 more bytes, which packs efficiently into 56 bytes total #define DEFINE_DRAW_1ARG_OP(op_name, arg_type, arg_name) \ - struct Draw##op_name##Op final : DLOp { \ + struct Draw##op_name##Op final : DrawOpBase { \ static const auto kType = DisplayListOpType::kDraw##op_name; \ \ explicit Draw##op_name##Op(arg_type arg_name) : arg_name(arg_name) {} \ \ const arg_type arg_name; \ \ - void dispatch(Dispatcher& dispatcher) const { \ - dispatcher.draw##op_name(arg_name); \ + void dispatch(DispatchContext& ctx) const { \ + if (op_needed(ctx)) { \ + ctx.dispatcher.draw##op_name(arg_name); \ + } \ } \ }; DEFINE_DRAW_1ARG_OP(Rect, SkRect, rect) @@ -581,14 +685,18 @@ DEFINE_DRAW_1ARG_OP(RRect, SkRRect, rrect) // 4 byte header + 16 byte payload uses 20 bytes but is rounded up to 24 bytes // (4 bytes unused) -struct DrawPathOp final : DLOp { +struct DrawPathOp final : DrawOpBase { static const auto kType = DisplayListOpType::kDrawPath; explicit DrawPathOp(SkPath path) : path(path) {} const SkPath path; - void dispatch(Dispatcher& dispatcher) const { dispatcher.drawPath(path); } + void dispatch(DispatchContext& ctx) const { + if (op_needed(ctx)) { + ctx.dispatcher.drawPath(path); + } + } DisplayListCompare equals(const DrawPathOp* other) const { return path == other->path ? DisplayListCompare::kEqual @@ -603,7 +711,7 @@ struct DrawPathOp final : DLOp { // 2 x SkRRect is 104 more bytes, using 108 and rounding up to 112 bytes total // (4 bytes unused) #define DEFINE_DRAW_2ARG_OP(op_name, type1, name1, type2, name2) \ - struct Draw##op_name##Op final : DLOp { \ + struct Draw##op_name##Op final : DrawOpBase { \ static const auto kType = DisplayListOpType::kDraw##op_name; \ \ Draw##op_name##Op(type1 name1, type2 name2) \ @@ -612,8 +720,10 @@ struct DrawPathOp final : DLOp { const type1 name1; \ const type2 name2; \ \ - void dispatch(Dispatcher& dispatcher) const { \ - dispatcher.draw##op_name(name1, name2); \ + void dispatch(DispatchContext& ctx) const { \ + if (op_needed(ctx)) { \ + ctx.dispatcher.draw##op_name(name1, name2); \ + } \ } \ }; DEFINE_DRAW_2ARG_OP(Line, SkPoint, p0, SkPoint, p1) @@ -622,7 +732,7 @@ DEFINE_DRAW_2ARG_OP(DRRect, SkRRect, outer, SkRRect, inner) #undef DEFINE_DRAW_2ARG_OP // 4 byte header + 28 byte payload packs efficiently into 32 bytes -struct DrawArcOp final : DLOp { +struct DrawArcOp final : DrawOpBase { static const auto kType = DisplayListOpType::kDrawArc; DrawArcOp(SkRect bounds, SkScalar start, SkScalar sweep, bool center) @@ -633,8 +743,10 @@ struct DrawArcOp final : DLOp { const SkScalar sweep; const bool center; - void dispatch(Dispatcher& dispatcher) const { - dispatcher.drawArc(bounds, start, sweep, center); + void dispatch(DispatchContext& ctx) const { + if (op_needed(ctx)) { + ctx.dispatcher.drawArc(bounds, start, sweep, center); + } } }; @@ -644,18 +756,20 @@ struct DrawArcOp final : DLOp { // 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 : DLOp { \ - static const auto kType = DisplayListOpType::kDraw##name; \ - \ - explicit Draw##name##Op(uint32_t count) : count(count) {} \ - \ - const uint32_t count; \ - \ - void dispatch(Dispatcher& dispatcher) const { \ - const SkPoint* pts = reinterpret_cast(this + 1); \ - dispatcher.drawPoints(SkCanvas::PointMode::mode, count, pts); \ - } \ +#define DEFINE_DRAW_POINTS_OP(name, mode) \ + struct Draw##name##Op final : DrawOpBase { \ + static const 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.dispatcher.drawPoints(SkCanvas::PointMode::mode, count, pts); \ + } \ + } \ }; DEFINE_DRAW_POINTS_OP(Points, kPoints_PointMode); DEFINE_DRAW_POINTS_OP(Lines, kLines_PointMode); @@ -669,21 +783,24 @@ DEFINE_DRAW_POINTS_OP(Polygon, kPolygon_PointMode); // Note that the DlVertices object ends with an array of 16-bit // indices so the alignment can be up to 6 bytes off leading to // up to 6 bytes of overhead -struct DrawVerticesOp final : DLOp { +struct DrawVerticesOp final : DrawOpBase { static const auto kType = DisplayListOpType::kDrawVertices; DrawVerticesOp(DlBlendMode mode) : mode(mode) {} const DlBlendMode mode; - void dispatch(Dispatcher& dispatcher) const { - const DlVertices* vertices = reinterpret_cast(this + 1); - dispatcher.drawVertices(vertices, mode); + void dispatch(DispatchContext& ctx) const { + if (op_needed(ctx)) { + const DlVertices* vertices = + reinterpret_cast(this + 1); + ctx.dispatcher.drawVertices(vertices, mode); + } } }; // 4 byte header + 12 byte payload packs efficiently into 16 bytes -struct DrawSkVerticesOp final : DLOp { +struct DrawSkVerticesOp final : DrawOpBase { static const auto kType = DisplayListOpType::kDrawSkVertices; DrawSkVerticesOp(sk_sp vertices, SkBlendMode mode) @@ -692,29 +809,33 @@ struct DrawSkVerticesOp final : DLOp { const SkBlendMode mode; const sk_sp vertices; - void dispatch(Dispatcher& dispatcher) const { - dispatcher.drawSkVertices(vertices, mode); + void dispatch(DispatchContext& ctx) const { + if (op_needed(ctx)) { + ctx.dispatcher.drawSkVertices(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 : DLOp { \ - static const 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(Dispatcher& dispatcher) const { \ - dispatcher.drawImage(image, point, sampling, with_attributes); \ - } \ +#define DEFINE_DRAW_IMAGE_OP(name, with_attributes) \ + struct name##Op final : DrawOpBase { \ + static const 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.dispatcher.drawImage(image, point, sampling, with_attributes); \ + } \ + } \ }; DEFINE_DRAW_IMAGE_OP(DrawImage, false) DEFINE_DRAW_IMAGE_OP(DrawImageWithAttr, true) @@ -722,7 +843,7 @@ DEFINE_DRAW_IMAGE_OP(DrawImageWithAttr, true) // 4 byte header + 72 byte payload uses 76 bytes but is rounded up to 80 bytes // (4 bytes unused) -struct DrawImageRectOp final : DLOp { +struct DrawImageRectOp final : DrawOpBase { static const auto kType = DisplayListOpType::kDrawImageRect; DrawImageRectOp(const sk_sp image, @@ -745,15 +866,17 @@ struct DrawImageRectOp final : DLOp { const SkCanvas::SrcRectConstraint constraint; const sk_sp image; - void dispatch(Dispatcher& dispatcher) const { - dispatcher.drawImageRect(image, src, dst, sampling, render_with_attributes, - constraint); + void dispatch(DispatchContext& ctx) const { + if (op_needed(ctx)) { + ctx.dispatcher.drawImageRect(image, src, dst, sampling, + render_with_attributes, constraint); + } } }; // 4 byte header + 44 byte payload packs efficiently into 48 bytes #define DEFINE_DRAW_IMAGE_NINE_OP(name, render_with_attributes) \ - struct name##Op final : DLOp { \ + struct name##Op final : DrawOpBase { \ static const auto kType = DisplayListOpType::k##name; \ \ name##Op(const sk_sp image, \ @@ -767,9 +890,11 @@ struct DrawImageRectOp final : DLOp { const DlFilterMode filter; \ const sk_sp image; \ \ - void dispatch(Dispatcher& dispatcher) const { \ - dispatcher.drawImageNine(image, center, dst, filter, \ - render_with_attributes); \ + void dispatch(DispatchContext& ctx) const { \ + if (op_needed(ctx)) { \ + ctx.dispatcher.drawImageNine(image, center, dst, filter, \ + render_with_attributes); \ + } \ } \ }; DEFINE_DRAW_IMAGE_NINE_OP(DrawImageNine, false) @@ -777,7 +902,7 @@ DEFINE_DRAW_IMAGE_NINE_OP(DrawImageNineWithAttr, true) #undef DEFINE_DRAW_IMAGE_NINE_OP // 4 byte header + 60 byte payload packs evenly into 64 bytes -struct DrawImageLatticeOp final : DLOp { +struct DrawImageLatticeOp final : DrawOpBase { static const auto kType = DisplayListOpType::kDrawImageLattice; DrawImageLatticeOp(const sk_sp image, @@ -806,20 +931,22 @@ struct DrawImageLatticeOp final : DLOp { const SkRect dst; const sk_sp image; - void dispatch(Dispatcher& dispatcher) const { - const int* xDivs = reinterpret_cast(this + 1); - const int* yDivs = reinterpret_cast(xDivs + x_count); - const SkColor* colors = - (cell_count == 0) ? nullptr - : reinterpret_cast(yDivs + y_count); - const SkCanvas::Lattice::RectType* types = - (cell_count == 0) - ? nullptr - : reinterpret_cast(colors + - cell_count); - dispatcher.drawImageLattice( - image, {xDivs, yDivs, types, x_count, y_count, &src, colors}, dst, - filter, with_paint); + void dispatch(DispatchContext& ctx) const { + if (op_needed(ctx)) { + const int* xDivs = reinterpret_cast(this + 1); + const int* yDivs = reinterpret_cast(xDivs + x_count); + const SkColor* colors = + (cell_count == 0) ? nullptr + : reinterpret_cast(yDivs + y_count); + const SkCanvas::Lattice::RectType* types = + (cell_count == 0) + ? nullptr + : reinterpret_cast( + colors + cell_count); + ctx.dispatcher.drawImageLattice( + image, {xDivs, yDivs, types, x_count, y_count, &src, colors}, dst, + filter, with_paint); + } } }; @@ -830,7 +957,7 @@ struct DrawImageLatticeOp final : DLOp { // SkRect list is also a multiple of 16 bytes so it also packs well // DlColor list only packs well if the count is even, otherwise there // can be 4 unusued bytes at the end. -struct DrawAtlasBaseOp : DLOp { +struct DrawAtlasBaseOp : DrawOpBase { DrawAtlasBaseOp(const sk_sp atlas, int count, DlBlendMode mode, @@ -870,14 +997,16 @@ struct DrawAtlasOp final : DrawAtlasBaseOp { has_colors, render_with_attributes) {} - void dispatch(Dispatcher& dispatcher) 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); - dispatcher.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.dispatcher.drawAtlas(atlas, xform, tex, colors, count, mode, sampling, + nullptr, render_with_attributes); + } } }; @@ -905,19 +1034,21 @@ struct DrawAtlasCulledOp final : DrawAtlasBaseOp { const SkRect cull_rect; - void dispatch(Dispatcher& dispatcher) 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); - dispatcher.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.dispatcher.drawAtlas(atlas, xform, tex, colors, count, mode, sampling, + &cull_rect, render_with_attributes); + } } }; // 4 byte header + 12 byte payload packs evenly into 16 bytes -struct DrawSkPictureOp final : DLOp { +struct DrawSkPictureOp final : DrawOpBase { static const auto kType = DisplayListOpType::kDrawSkPicture; DrawSkPictureOp(sk_sp picture, bool render_with_attributes) @@ -927,13 +1058,15 @@ struct DrawSkPictureOp final : DLOp { const bool render_with_attributes; const sk_sp picture; - void dispatch(Dispatcher& dispatcher) const { - dispatcher.drawPicture(picture, nullptr, render_with_attributes); + void dispatch(DispatchContext& ctx) const { + if (op_needed(ctx)) { + ctx.dispatcher.drawPicture(picture, nullptr, render_with_attributes); + } } }; // 4 byte header + 52 byte payload packs evenly into 56 bytes -struct DrawSkPictureMatrixOp final : DLOp { +struct DrawSkPictureMatrixOp final : DrawOpBase { static const auto kType = DisplayListOpType::kDrawSkPictureMatrix; DrawSkPictureMatrixOp(sk_sp picture, @@ -947,14 +1080,16 @@ struct DrawSkPictureMatrixOp final : DLOp { const sk_sp picture; const SkMatrix matrix; - void dispatch(Dispatcher& dispatcher) const { - dispatcher.drawPicture(picture, &matrix, render_with_attributes); + void dispatch(DispatchContext& ctx) const { + if (op_needed(ctx)) { + ctx.dispatcher.drawPicture(picture, &matrix, render_with_attributes); + } } }; // 4 byte header + ptr aligned payload uses 12 bytes round up to 16 // (4 bytes unused) -struct DrawDisplayListOp final : DLOp { +struct DrawDisplayListOp final : DrawOpBase { static const auto kType = DisplayListOpType::kDrawDisplayList; explicit DrawDisplayListOp(const sk_sp display_list) @@ -962,8 +1097,10 @@ struct DrawDisplayListOp final : DLOp { sk_sp display_list; - void dispatch(Dispatcher& dispatcher) const { - dispatcher.drawDisplayList(display_list); + void dispatch(DispatchContext& ctx) const { + if (op_needed(ctx)) { + ctx.dispatcher.drawDisplayList(display_list); + } } DisplayListCompare equals(const DrawDisplayListOp* other) const { @@ -975,7 +1112,7 @@ struct DrawDisplayListOp final : DLOp { // 4 byte header + 8 payload bytes + an aligned pointer take 24 bytes // (4 unused to align the pointer) -struct DrawTextBlobOp final : DLOp { +struct DrawTextBlobOp final : DrawOpBase { static const auto kType = DisplayListOpType::kDrawTextBlob; DrawTextBlobOp(const sk_sp blob, SkScalar x, SkScalar y) @@ -985,31 +1122,35 @@ struct DrawTextBlobOp final : DLOp { const SkScalar y; const sk_sp blob; - void dispatch(Dispatcher& dispatcher) const { - dispatcher.drawTextBlob(blob, x, y); + void dispatch(DispatchContext& ctx) const { + if (op_needed(ctx)) { + ctx.dispatcher.drawTextBlob(blob, x, y); + } } }; // 4 byte header + 28 byte payload packs evenly into 32 bytes -#define DEFINE_DRAW_SHADOW_OP(name, transparent_occluder) \ - struct Draw##name##Op final : DLOp { \ - static const auto kType = DisplayListOpType::kDraw##name; \ - \ - Draw##name##Op(const SkPath& path, \ - DlColor color, \ - SkScalar elevation, \ - SkScalar dpr) \ - : color(color), elevation(elevation), dpr(dpr), path(path) {} \ - \ - const DlColor color; \ - const SkScalar elevation; \ - const SkScalar dpr; \ - const SkPath path; \ - \ - void dispatch(Dispatcher& dispatcher) const { \ - dispatcher.drawShadow(path, color, elevation, transparent_occluder, \ - dpr); \ - } \ +#define DEFINE_DRAW_SHADOW_OP(name, transparent_occluder) \ + struct Draw##name##Op final : DrawOpBase { \ + static const auto kType = DisplayListOpType::kDraw##name; \ + \ + Draw##name##Op(const SkPath& path, \ + DlColor color, \ + SkScalar elevation, \ + SkScalar dpr) \ + : color(color), elevation(elevation), dpr(dpr), path(path) {} \ + \ + const DlColor color; \ + const SkScalar elevation; \ + const SkScalar dpr; \ + const SkPath path; \ + \ + void dispatch(DispatchContext& ctx) const { \ + if (op_needed(ctx)) { \ + ctx.dispatcher.drawShadow(path, color, elevation, \ + transparent_occluder, dpr); \ + } \ + } \ }; DEFINE_DRAW_SHADOW_OP(Shadow, false) DEFINE_DRAW_SHADOW_OP(ShadowTransparentOccluder, true) diff --git a/display_list/display_list_test_utils.cc b/display_list/display_list_test_utils.cc index 144fe140d7490..9d8ee65bb9c03 100644 --- a/display_list/display_list_test_utils.cc +++ b/display_list/display_list_test_utils.cc @@ -304,7 +304,7 @@ std::vector CreateAllSaveRestoreOps() { return { {"Save(Layer)+Restore", { - {5, 104, 5, 104, + {5, 112, 5, 112, [](DisplayListBuilder& b) { b.saveLayer(nullptr, SaveLayerOptions::kNoAttributes, &kTestCFImageFilter1); @@ -320,7 +320,7 @@ std::vector CreateAllSaveRestoreOps() { // attributes to be distributed to the children. To prevent those // cases we include at least one clip operation and 2 overlapping // rendering primitives between each save/restore pair. - {5, 88, 5, 88, + {5, 96, 5, 96, [](DisplayListBuilder& b) { b.save(); b.clipRect({0, 0, 25, 25}, SkClipOp::kIntersect, true); @@ -328,7 +328,7 @@ std::vector CreateAllSaveRestoreOps() { b.drawRect({10, 10, 20, 20}); b.restore(); }}, - {5, 88, 5, 88, + {5, 96, 5, 96, [](DisplayListBuilder& b) { b.saveLayer(nullptr, false); b.clipRect({0, 0, 25, 25}, SkClipOp::kIntersect, true); @@ -336,7 +336,7 @@ std::vector CreateAllSaveRestoreOps() { b.drawRect({10, 10, 20, 20}); b.restore(); }}, - {5, 88, 5, 88, + {5, 96, 5, 96, [](DisplayListBuilder& b) { b.saveLayer(nullptr, true); b.clipRect({0, 0, 25, 25}, SkClipOp::kIntersect, true); @@ -344,7 +344,7 @@ std::vector CreateAllSaveRestoreOps() { b.drawRect({10, 10, 20, 20}); b.restore(); }}, - {5, 104, 5, 104, + {5, 112, 5, 112, [](DisplayListBuilder& b) { b.saveLayer(&kTestBounds, false); b.clipRect({0, 0, 25, 25}, SkClipOp::kIntersect, true); @@ -352,7 +352,7 @@ std::vector CreateAllSaveRestoreOps() { b.drawRect({10, 10, 20, 20}); b.restore(); }}, - {5, 104, 5, 104, + {5, 112, 5, 112, [](DisplayListBuilder& b) { b.saveLayer(&kTestBounds, true); b.clipRect({0, 0, 25, 25}, SkClipOp::kIntersect, true); @@ -369,7 +369,7 @@ std::vector CreateAllSaveRestoreOps() { // b.drawRect({10, 10, 20, 20}); // b.restore(); // }}, - {5, 104, 5, 104, + {5, 112, 5, 112, [](DisplayListBuilder& b) { b.saveLayer(nullptr, SaveLayerOptions::kWithAttributes, &kTestCFImageFilter1); @@ -378,7 +378,7 @@ std::vector CreateAllSaveRestoreOps() { b.drawRect({10, 10, 20, 20}); b.restore(); }}, - {5, 120, 5, 120, + {5, 128, 5, 128, [](DisplayListBuilder& b) { b.saveLayer(&kTestBounds, SaveLayerOptions::kNoAttributes, &kTestCFImageFilter1); @@ -387,7 +387,7 @@ std::vector CreateAllSaveRestoreOps() { b.drawRect({10, 10, 20, 20}); b.restore(); }}, - {5, 120, 5, 120, + {5, 128, 5, 128, [](DisplayListBuilder& b) { b.saveLayer(&kTestBounds, SaveLayerOptions::kWithAttributes, &kTestCFImageFilter1); diff --git a/display_list/display_list_unittests.cc b/display_list/display_list_unittests.cc index 2a9e5d7cf7e27..a4f4ba66bdb4b 100644 --- a/display_list/display_list_unittests.cc +++ b/display_list/display_list_unittests.cc @@ -106,7 +106,7 @@ TEST(DisplayList, SingleOpDisplayListsRecapturedViaSkCanvasAreEqual) { sk_sp dl = group.variants[i].Build(); DisplayListCanvasRecorder recorder(dl->bounds()); - dl->RenderTo(&recorder); + dl->RenderTo(&recorder, SK_Scalar1, false); sk_sp sk_copy = recorder.Build(); auto desc = group.op_name + "[variant " + std::to_string(i + 1) + "]"; EXPECT_EQ(static_cast(sk_copy->op_count(false)), @@ -555,7 +555,7 @@ TEST(DisplayList, DisplayListFullPerspectiveTransformHandling) { sk_sp display_list = builder.Build(); sk_sp surface = SkSurface::MakeRasterN32Premul(10, 10); SkCanvas* canvas = surface->getCanvas(); - display_list->RenderTo(canvas); + display_list->RenderTo(canvas, SK_Scalar1, false); SkM44 dl_matrix = canvas->getLocalToDevice(); ASSERT_EQ(sk_matrix, dl_matrix); } @@ -573,8 +573,11 @@ TEST(DisplayList, DisplayListFullPerspectiveTransformHandling) { sk_sp display_list = builder.Build(); sk_sp surface = SkSurface::MakeRasterN32Premul(10, 10); SkCanvas* canvas = surface->getCanvas(); - display_list->RenderTo(canvas); + ASSERT_EQ(canvas->getLocalToDevice(), SkM44()); + display_list->RenderTo(canvas, SK_Scalar1, false); SkM44 dl_matrix = canvas->getLocalToDevice(); + // Make sure that the transform call didn't get culled + ASSERT_NE(dl_matrix, SkM44()); ASSERT_NE(sk_matrix, dl_matrix); } } @@ -1106,7 +1109,7 @@ TEST(DisplayList, FlutterSvgIssue661BoundsWereEmpty) { // This is the more practical result. The bounds are "almost" 0,0,100x100 EXPECT_EQ(display_list->bounds().roundOut(), SkIRect::MakeWH(100, 100)); EXPECT_EQ(display_list->op_count(), 19u); - EXPECT_EQ(display_list->bytes(), sizeof(DisplayList) + 304u); + EXPECT_EQ(display_list->bytes(), sizeof(DisplayList) + 352u); } TEST(DisplayList, TranslateAffectsCurrentTransform) { @@ -2251,5 +2254,117 @@ TEST(DisplayList, RTreeOfClippedSaveLayerFilterScene) { test_rtree(rtree, {19, 19, 51, 51}, rects, {0, 1}); } +TEST(DisplayList, RTreeRenderCulling) { + DisplayListBuilder main_builder; + main_builder.drawRect({0, 0, 10, 10}); + main_builder.drawRect({20, 0, 30, 10}); + main_builder.drawRect({0, 20, 10, 30}); + main_builder.drawRect({20, 20, 30, 30}); + auto main = main_builder.Build(); + + { // No rects + SkRect cull_rect = {11, 11, 19, 19}; + + DisplayListBuilder expected_builder; + auto expected = expected_builder.Build(); + + DisplayListBuilder culling_builder(cull_rect); + main->RenderTo(&culling_builder); + + EXPECT_TRUE(DisplayListsEQ_Verbose(culling_builder.Build(), expected)); + + DisplayListCanvasRecorder culling_recorder(cull_rect); + main->RenderTo(&culling_recorder); + + EXPECT_TRUE(DisplayListsEQ_Verbose(culling_recorder.Build(), expected)); + } + + { // Rect 1 + SkRect cull_rect = {9, 9, 19, 19}; + + DisplayListBuilder expected_builder; + expected_builder.drawRect({0, 0, 10, 10}); + auto expected = expected_builder.Build(); + + DisplayListBuilder culling_builder(cull_rect); + main->RenderTo(&culling_builder); + + EXPECT_TRUE(DisplayListsEQ_Verbose(culling_builder.Build(), expected)); + + DisplayListCanvasRecorder culling_recorder(cull_rect); + main->RenderTo(&culling_recorder); + + EXPECT_TRUE(DisplayListsEQ_Verbose(culling_recorder.Build(), expected)); + } + + { // Rect 2 + SkRect cull_rect = {11, 9, 21, 19}; + + DisplayListBuilder expected_builder; + expected_builder.drawRect({20, 0, 30, 10}); + auto expected = expected_builder.Build(); + + DisplayListBuilder culling_builder(cull_rect); + main->RenderTo(&culling_builder); + + EXPECT_TRUE(DisplayListsEQ_Verbose(culling_builder.Build(), expected)); + + DisplayListCanvasRecorder culling_recorder(cull_rect); + main->RenderTo(&culling_recorder); + + EXPECT_TRUE(DisplayListsEQ_Verbose(culling_recorder.Build(), expected)); + } + + { // Rect 3 + SkRect cull_rect = {9, 11, 19, 21}; + + DisplayListBuilder expected_builder; + expected_builder.drawRect({0, 20, 10, 30}); + auto expected = expected_builder.Build(); + + DisplayListBuilder culling_builder(cull_rect); + main->RenderTo(&culling_builder); + + EXPECT_TRUE(DisplayListsEQ_Verbose(culling_builder.Build(), expected)); + + DisplayListCanvasRecorder culling_recorder(cull_rect); + main->RenderTo(&culling_recorder); + + EXPECT_TRUE(DisplayListsEQ_Verbose(culling_recorder.Build(), expected)); + } + + { // Rect 4 + SkRect cull_rect = {11, 11, 21, 21}; + + DisplayListBuilder expected_builder; + expected_builder.drawRect({20, 20, 30, 30}); + auto expected = expected_builder.Build(); + + DisplayListBuilder culling_builder(cull_rect); + main->RenderTo(&culling_builder); + + EXPECT_TRUE(DisplayListsEQ_Verbose(culling_builder.Build(), expected)); + + DisplayListCanvasRecorder culling_recorder(cull_rect); + main->RenderTo(&culling_recorder); + + EXPECT_TRUE(DisplayListsEQ_Verbose(culling_recorder.Build(), expected)); + } + + { // All 4 rects + SkRect cull_rect = {9, 9, 21, 21}; + + DisplayListBuilder culling_builder(cull_rect); + main->RenderTo(&culling_builder); + + EXPECT_TRUE(DisplayListsEQ_Verbose(culling_builder.Build(), main)); + + DisplayListCanvasRecorder culling_recorder(cull_rect); + main->RenderTo(&culling_recorder); + + EXPECT_TRUE(DisplayListsEQ_Verbose(culling_recorder.Build(), main)); + } +} + } // namespace testing } // namespace flutter diff --git a/display_list/display_list_utils.cc b/display_list/display_list_utils.cc index ff8ddd534dca5..4f2b19a9f19f0 100644 --- a/display_list/display_list_utils.cc +++ b/display_list/display_list_utils.cc @@ -42,28 +42,36 @@ void SkPaintDispatchHelper::restore_opacity() { void SkPaintDispatchHelper::setAntiAlias(bool aa) { paint_.setAntiAlias(aa); + count_op(); } void SkPaintDispatchHelper::setDither(bool dither) { paint_.setDither(dither); + count_op(); } void SkPaintDispatchHelper::setInvertColors(bool invert) { invert_colors_ = invert; paint_.setColorFilter(makeColorFilter()); + count_op(); } void SkPaintDispatchHelper::setStrokeCap(DlStrokeCap cap) { paint_.setStrokeCap(ToSk(cap)); + count_op(); } void SkPaintDispatchHelper::setStrokeJoin(DlStrokeJoin join) { paint_.setStrokeJoin(ToSk(join)); + count_op(); } void SkPaintDispatchHelper::setStyle(DlDrawStyle style) { paint_.setStyle(ToSk(style)); + count_op(); } void SkPaintDispatchHelper::setStrokeWidth(SkScalar width) { paint_.setStrokeWidth(width); + count_op(); } void SkPaintDispatchHelper::setStrokeMiter(SkScalar limit) { paint_.setStrokeMiter(limit); + count_op(); } void SkPaintDispatchHelper::setColor(DlColor color) { current_color_ = color; @@ -71,28 +79,36 @@ void SkPaintDispatchHelper::setColor(DlColor color) { if (has_opacity()) { paint_.setAlphaf(paint_.getAlphaf() * opacity()); } + count_op(); } void SkPaintDispatchHelper::setBlendMode(DlBlendMode mode) { paint_.setBlendMode(ToSk(mode)); + count_op(); } void SkPaintDispatchHelper::setBlender(sk_sp blender) { paint_.setBlender(blender); + count_op(); } void SkPaintDispatchHelper::setColorSource(const DlColorSource* source) { paint_.setShader(source ? source->skia_object() : nullptr); + count_op(); } void SkPaintDispatchHelper::setImageFilter(const DlImageFilter* filter) { paint_.setImageFilter(filter ? filter->skia_object() : nullptr); + count_op(); } void SkPaintDispatchHelper::setColorFilter(const DlColorFilter* filter) { color_filter_ = filter ? filter->shared() : nullptr; paint_.setColorFilter(makeColorFilter()); + count_op(); } void SkPaintDispatchHelper::setPathEffect(const DlPathEffect* effect) { paint_.setPathEffect(effect ? effect->skia_object() : nullptr); + count_op(); } void SkPaintDispatchHelper::setMaskFilter(const DlMaskFilter* filter) { paint_.setMaskFilter(filter ? filter->skia_object() : nullptr); + count_op(); } sk_sp SkPaintDispatchHelper::makeColorFilter() const { @@ -110,20 +126,24 @@ sk_sp SkPaintDispatchHelper::makeColorFilter() const { void SkMatrixDispatchHelper::translate(SkScalar tx, SkScalar ty) { matrix_.preTranslate(tx, ty); matrix33_ = matrix_.asM33(); + count_op(); } void SkMatrixDispatchHelper::scale(SkScalar sx, SkScalar sy) { matrix_.preScale(sx, sy); matrix33_ = matrix_.asM33(); + count_op(); } void SkMatrixDispatchHelper::rotate(SkScalar degrees) { matrix33_.setRotate(degrees); matrix_.preConcat(matrix33_); matrix33_ = matrix_.asM33(); + count_op(); } void SkMatrixDispatchHelper::skew(SkScalar sx, SkScalar sy) { matrix33_.setSkew(sx, sy); matrix_.preConcat(matrix33_); matrix33_ = matrix_.asM33(); + count_op(); } // clang-format off @@ -139,6 +159,7 @@ void SkMatrixDispatchHelper::transform2DAffine( 0 , 0 , 0 , 1 , }); matrix33_ = matrix_.asM33(); + count_op(); } // full 4x4 transform in row major order void SkMatrixDispatchHelper::transformFullPerspective( @@ -153,6 +174,7 @@ void SkMatrixDispatchHelper::transformFullPerspective( mwx, mwy, mwz, mwt, }); matrix33_ = matrix_.asM33(); + count_op(); } // clang-format on @@ -160,18 +182,25 @@ void SkMatrixDispatchHelper::transformFullPerspective( void SkMatrixDispatchHelper::transformReset() { matrix_ = {}; matrix33_ = {}; + count_op(); } void SkMatrixDispatchHelper::save() { saved_.push_back(matrix_); + // The op should be counted in the parent to avoid duplicate accounting + // count_op(); } void SkMatrixDispatchHelper::restore() { if (saved_.empty()) { + // The op should be counted in the parent to avoid duplicate accounting + // count_op(); return; } matrix_ = saved_.back(); matrix33_ = matrix_.asM33(); saved_.pop_back(); + // The op should be counted in the parent to avoid duplicate accounting + // count_op(); } void SkMatrixDispatchHelper::reset() { matrix_.setIdentity(); @@ -188,6 +217,7 @@ void ClipBoundsDispatchHelper::clipRect(const SkRect& rect, case SkClipOp::kDifference: break; } + count_op(); } void ClipBoundsDispatchHelper::clipRRect(const SkRRect& rrect, SkClipOp clip_op, @@ -199,6 +229,7 @@ void ClipBoundsDispatchHelper::clipRRect(const SkRRect& rrect, case SkClipOp::kDifference: break; } + count_op(); } void ClipBoundsDispatchHelper::clipPath(const SkPath& path, SkClipOp clip_op, @@ -210,6 +241,7 @@ void ClipBoundsDispatchHelper::clipPath(const SkPath& path, case SkClipOp::kDifference: break; } + count_op(); } void ClipBoundsDispatchHelper::intersect(const SkRect& rect, bool is_aa) { SkRect dev_clip_bounds = matrix().mapRect(rect); @@ -237,9 +269,13 @@ void ClipBoundsDispatchHelper::save() { } else { saved_.push_back(bounds_); } + // The op should be counted in the parent to avoid duplicate accounting + // count_op(); } void ClipBoundsDispatchHelper::restore() { if (saved_.empty()) { + // The op should be counted in the parent to avoid duplicate accounting + // count_op(); return; } bounds_ = saved_.back(); @@ -249,6 +285,8 @@ void ClipBoundsDispatchHelper::restore() { if (!has_clip_) { bounds_.setEmpty(); } + // The op should be counted in the parent to avoid duplicate accounting + // count_op(); } void ClipBoundsDispatchHelper::reset(const SkRect* cull_rect) { if ((has_clip_ = cull_rect != nullptr) && !cull_rect->isEmpty()) { @@ -258,7 +296,7 @@ void ClipBoundsDispatchHelper::reset(const SkRect* cull_rect) { } } -void RectBoundsAccumulator::accumulate(const SkRect& r) { +void RectBoundsAccumulator::accumulate(const SkRect& r, uint32_t index) { if (r.fLeft < r.fRight && r.fTop < r.fBottom) { rect_.accumulate(r.fLeft, r.fTop); rect_.accumulate(r.fRight, r.fBottom); @@ -294,7 +332,7 @@ void RectBoundsAccumulator::pop_and_accumulate(SkRect& layer_bounds, saved_rects_.pop_back(); if (clip == nullptr || layer_bounds.intersect(*clip)) { - accumulate(layer_bounds); + accumulate(layer_bounds, 0); } } @@ -325,9 +363,12 @@ SkRect RectBoundsAccumulator::AccumulationRect::bounds() const { : SkRect::MakeEmpty(); } -void RTreeBoundsAccumulator::accumulate(const SkRect& r) { +void RTreeBoundsAccumulator::accumulate(const SkRect& r, uint32_t index) { if (r.fLeft < r.fRight && r.fTop < r.fBottom) { rects_.push_back(r); + if (rect_indices_) { + rect_indices_->push_back(index); + } } } bool RTreeBoundsAccumulator::is_empty() const { @@ -380,26 +421,34 @@ sk_sp RTreeBoundsAccumulator::rtree() const { DisplayListBoundsCalculator::DisplayListBoundsCalculator( BoundsAccumulator& accumulator, const SkRect* cull_rect) - : ClipBoundsDispatchHelper(cull_rect), accumulator_(accumulator) { + : ClipBoundsDispatchHelper(cull_rect), + accumulator_(accumulator), + op_index_(0) { layer_infos_.emplace_back(std::make_unique(nullptr)); } void DisplayListBoundsCalculator::setStrokeCap(DlStrokeCap cap) { cap_is_square_ = (cap == DlStrokeCap::kSquare); + count_op(); } void DisplayListBoundsCalculator::setStrokeJoin(DlStrokeJoin join) { join_is_miter_ = (join == DlStrokeJoin::kMiter); + count_op(); } void DisplayListBoundsCalculator::setStyle(DlDrawStyle style) { style_ = style; + count_op(); } void DisplayListBoundsCalculator::setStrokeWidth(SkScalar width) { half_stroke_width_ = std::max(width * 0.5f, kMinStrokeWidth); + count_op(); } void DisplayListBoundsCalculator::setStrokeMiter(SkScalar limit) { miter_limit_ = std::max(limit, 1.0f); + count_op(); } void DisplayListBoundsCalculator::setBlendMode(DlBlendMode mode) { blend_mode_ = mode; + count_op(); } void DisplayListBoundsCalculator::setBlender(sk_sp blender) { SkPaint paint; @@ -410,24 +459,30 @@ void DisplayListBoundsCalculator::setBlender(sk_sp blender) { } else { blend_mode_ = std::nullopt; } + count_op(); } void DisplayListBoundsCalculator::setImageFilter(const DlImageFilter* filter) { image_filter_ = filter ? filter->shared() : nullptr; + count_op(); } void DisplayListBoundsCalculator::setColorFilter(const DlColorFilter* filter) { color_filter_ = filter ? filter->shared() : nullptr; + count_op(); } void DisplayListBoundsCalculator::setPathEffect(const DlPathEffect* effect) { path_effect_ = effect ? effect->shared() : nullptr; + count_op(); } void DisplayListBoundsCalculator::setMaskFilter(const DlMaskFilter* filter) { mask_filter_ = filter ? filter->shared() : nullptr; + count_op(); } void DisplayListBoundsCalculator::save() { SkMatrixDispatchHelper::save(); ClipBoundsDispatchHelper::save(); layer_infos_.emplace_back(std::make_unique(nullptr)); accumulator_.save(); + count_op(); } void DisplayListBoundsCalculator::saveLayer(const SkRect* bounds, const SaveLayerOptions options, @@ -456,6 +511,8 @@ void DisplayListBoundsCalculator::saveLayer(const SkRect* bounds, // we set them as if a clip operation were performed. if (bounds) { clipRect(*bounds, SkClipOp::kIntersect, false); + } else { + count_op(); } if (backdrop) { // A backdrop will affect up to the entire surface, bounded by the clip @@ -501,13 +558,16 @@ void DisplayListBoundsCalculator::restore() { AccumulateUnbounded(); } } + count_op(); } void DisplayListBoundsCalculator::drawPaint() { AccumulateUnbounded(); + count_op(); } void DisplayListBoundsCalculator::drawColor(DlColor color, DlBlendMode mode) { AccumulateUnbounded(); + count_op(); } void DisplayListBoundsCalculator::drawLine(const SkPoint& p0, const SkPoint& p1) { @@ -516,25 +576,31 @@ void DisplayListBoundsCalculator::drawLine(const SkPoint& p0, (bounds.width() > 0.0f && bounds.height() > 0.0f) ? kDrawLineFlags : kDrawHVLineFlags; AccumulateOpBounds(bounds, flags); + count_op(); } void DisplayListBoundsCalculator::drawRect(const SkRect& rect) { AccumulateOpBounds(rect, kDrawRectFlags); + count_op(); } void DisplayListBoundsCalculator::drawOval(const SkRect& bounds) { AccumulateOpBounds(bounds, kDrawOvalFlags); + count_op(); } void DisplayListBoundsCalculator::drawCircle(const SkPoint& center, SkScalar radius) { AccumulateOpBounds(SkRect::MakeLTRB(center.fX - radius, center.fY - radius, center.fX + radius, center.fY + radius), kDrawCircleFlags); + count_op(); } void DisplayListBoundsCalculator::drawRRect(const SkRRect& rrect) { AccumulateOpBounds(rrect.getBounds(), kDrawRRectFlags); + count_op(); } void DisplayListBoundsCalculator::drawDRRect(const SkRRect& outer, const SkRRect& inner) { AccumulateOpBounds(outer.getBounds(), kDrawDRRectFlags); + count_op(); } void DisplayListBoundsCalculator::drawPath(const SkPath& path) { if (path.isInverseFillType()) { @@ -542,6 +608,7 @@ void DisplayListBoundsCalculator::drawPath(const SkPath& path) { } else { AccumulateOpBounds(path.getBounds(), kDrawPathFlags); } + count_op(); } void DisplayListBoundsCalculator::drawArc(const SkRect& bounds, SkScalar start, @@ -554,6 +621,7 @@ void DisplayListBoundsCalculator::drawArc(const SkRect& bounds, useCenter // ? kDrawArcWithCenterFlags : kDrawArcNoCenterFlags); + count_op(); } void DisplayListBoundsCalculator::drawPoints(SkCanvas::PointMode mode, uint32_t count, @@ -576,15 +644,18 @@ void DisplayListBoundsCalculator::drawPoints(SkCanvas::PointMode mode, break; } } + count_op(); } void DisplayListBoundsCalculator::drawSkVertices( const sk_sp vertices, SkBlendMode mode) { AccumulateOpBounds(vertices->bounds(), kDrawVerticesFlags); + count_op(); } void DisplayListBoundsCalculator::drawVertices(const DlVertices* vertices, DlBlendMode mode) { AccumulateOpBounds(vertices->bounds(), kDrawVerticesFlags); + count_op(); } void DisplayListBoundsCalculator::drawImage(const sk_sp image, const SkPoint point, @@ -596,6 +667,7 @@ void DisplayListBoundsCalculator::drawImage(const sk_sp image, ? kDrawImageWithPaintFlags : kDrawImageFlags; AccumulateOpBounds(bounds, flags); + count_op(); } void DisplayListBoundsCalculator::drawImageRect( const sk_sp image, @@ -608,6 +680,7 @@ void DisplayListBoundsCalculator::drawImageRect( ? kDrawImageRectWithPaintFlags : kDrawImageRectFlags; AccumulateOpBounds(dst, flags); + count_op(); } void DisplayListBoundsCalculator::drawImageNine(const sk_sp image, const SkIRect& center, @@ -618,6 +691,7 @@ void DisplayListBoundsCalculator::drawImageNine(const sk_sp image, ? kDrawImageNineWithPaintFlags : kDrawImageNineFlags; AccumulateOpBounds(dst, flags); + count_op(); } void DisplayListBoundsCalculator::drawImageLattice( const sk_sp image, @@ -629,6 +703,7 @@ void DisplayListBoundsCalculator::drawImageLattice( ? kDrawImageLatticeWithPaintFlags : kDrawImageLatticeFlags; AccumulateOpBounds(dst, flags); + count_op(); } void DisplayListBoundsCalculator::drawAtlas(const sk_sp atlas, const SkRSXform xform[], @@ -654,6 +729,7 @@ void DisplayListBoundsCalculator::drawAtlas(const sk_sp atlas, : kDrawAtlasFlags; AccumulateOpBounds(atlas_bounds.bounds(), flags); } + count_op(); } void DisplayListBoundsCalculator::drawPicture(const sk_sp picture, const SkMatrix* pic_matrix, @@ -669,6 +745,7 @@ void DisplayListBoundsCalculator::drawPicture(const sk_sp picture, ? kDrawPictureWithPaintFlags : kDrawPictureFlags; AccumulateOpBounds(bounds, flags); + count_op(); } void DisplayListBoundsCalculator::drawDisplayList( const sk_sp display_list) { @@ -676,6 +753,7 @@ void DisplayListBoundsCalculator::drawDisplayList( switch (accumulator_.type()) { case BoundsAccumulatorType::kRect: AccumulateOpBounds(bounds, kDrawDisplayListFlags); + count_op(); return; case BoundsAccumulatorType::kRTree: std::list rects = @@ -685,6 +763,7 @@ void DisplayListBoundsCalculator::drawDisplayList( // are not necessarily `kDrawDisplayListFlags`. AccumulateOpBounds(rect, kDrawDisplayListFlags); } + count_op(); return; } @@ -694,6 +773,7 @@ void DisplayListBoundsCalculator::drawTextBlob(const sk_sp blob, SkScalar x, SkScalar y) { AccumulateOpBounds(blob->bounds().makeOffset(x, y), kDrawTextBlobFlags); + count_op(); } void DisplayListBoundsCalculator::drawShadow(const SkPath& path, const DlColor color, @@ -703,6 +783,7 @@ void DisplayListBoundsCalculator::drawShadow(const SkPath& path, SkRect shadow_bounds = DisplayListCanvasDispatcher::ComputeShadowBounds( path, elevation, dpr, matrix()); AccumulateOpBounds(shadow_bounds, kDrawShadowFlags); + count_op(); } bool DisplayListBoundsCalculator::ComputeFilteredBounds(SkRect& bounds, @@ -774,7 +855,7 @@ bool DisplayListBoundsCalculator::AdjustBoundsForPaint( void DisplayListBoundsCalculator::AccumulateUnbounded() { if (has_clip()) { - accumulator_.accumulate(clip_bounds()); + accumulator_.accumulate(clip_bounds(), op_index_); } else { layer_infos_.back()->set_unbounded(); } @@ -791,7 +872,7 @@ void DisplayListBoundsCalculator::AccumulateOpBounds( void DisplayListBoundsCalculator::AccumulateBounds(SkRect& bounds) { matrix().mapRect(&bounds); if (!has_clip() || bounds.intersect(clip_bounds())) { - accumulator_.accumulate(bounds); + accumulator_.accumulate(bounds, op_index_); } } diff --git a/display_list/display_list_utils.h b/display_list/display_list_utils.h index 406e852fbcae9..327cc3e8b9fa6 100644 --- a/display_list/display_list_utils.h +++ b/display_list/display_list_utils.h @@ -41,104 +41,145 @@ namespace flutter { +class DispatchOpCounter { + public: + virtual void count_op() {} +}; + // A utility class that will ignore all Dispatcher methods relating // to the setting of attributes. -class IgnoreAttributeDispatchHelper : public virtual Dispatcher { +class IgnoreAttributeDispatchHelper : public virtual Dispatcher, + virtual DispatchOpCounter { public: - void setAntiAlias(bool aa) override {} - void setDither(bool dither) override {} - void setInvertColors(bool invert) override {} - void setStrokeCap(DlStrokeCap cap) override {} - void setStrokeJoin(DlStrokeJoin join) override {} - void setStyle(DlDrawStyle style) override {} - void setStrokeWidth(float width) override {} - void setStrokeMiter(float limit) override {} - void setColor(DlColor color) override {} - void setBlendMode(DlBlendMode mode) override {} - void setBlender(sk_sp blender) override {} - void setColorSource(const DlColorSource* source) override {} - void setImageFilter(const DlImageFilter* filter) override {} - void setColorFilter(const DlColorFilter* filter) override {} - void setPathEffect(const DlPathEffect* effect) override {} - void setMaskFilter(const DlMaskFilter* filter) override {} + void setAntiAlias(bool aa) override { count_op(); } + void setDither(bool dither) override { count_op(); } + void setInvertColors(bool invert) override { count_op(); } + void setStrokeCap(DlStrokeCap cap) override { count_op(); } + void setStrokeJoin(DlStrokeJoin join) override { count_op(); } + void setStyle(DlDrawStyle style) override { count_op(); } + void setStrokeWidth(float width) override { count_op(); } + void setStrokeMiter(float limit) override { count_op(); } + void setColor(DlColor color) override { count_op(); } + void setBlendMode(DlBlendMode mode) override { count_op(); } + void setBlender(sk_sp blender) override { count_op(); } + void setColorSource(const DlColorSource* source) override { count_op(); } + void setImageFilter(const DlImageFilter* filter) override { count_op(); } + void setColorFilter(const DlColorFilter* filter) override { count_op(); } + void setPathEffect(const DlPathEffect* effect) override { count_op(); } + void setMaskFilter(const DlMaskFilter* filter) override { count_op(); } }; // A utility class that will ignore all Dispatcher methods relating // to setting a clip. -class IgnoreClipDispatchHelper : public virtual Dispatcher { - void clipRect(const SkRect& rect, SkClipOp clip_op, bool is_aa) override {} - void clipRRect(const SkRRect& rrect, SkClipOp clip_op, bool is_aa) override {} - void clipPath(const SkPath& path, SkClipOp clip_op, bool is_aa) override {} +class IgnoreClipDispatchHelper : public virtual Dispatcher, + virtual DispatchOpCounter { + void clipRect(const SkRect& rect, SkClipOp clip_op, bool is_aa) override { + count_op(); + } + void clipRRect(const SkRRect& rrect, SkClipOp clip_op, bool is_aa) override { + count_op(); + } + void clipPath(const SkPath& path, SkClipOp clip_op, bool is_aa) override { + count_op(); + } }; // A utility class that will ignore all Dispatcher methods relating // to modifying the transform. -class IgnoreTransformDispatchHelper : public virtual Dispatcher { +class IgnoreTransformDispatchHelper : public virtual Dispatcher, + virtual DispatchOpCounter { public: - void translate(SkScalar tx, SkScalar ty) override {} - void scale(SkScalar sx, SkScalar sy) override {} - void rotate(SkScalar degrees) override {} - void skew(SkScalar sx, SkScalar sy) override {} + void translate(SkScalar tx, SkScalar ty) override { count_op(); } + void scale(SkScalar sx, SkScalar sy) override { count_op(); } + void rotate(SkScalar degrees) override { count_op(); } + void skew(SkScalar sx, SkScalar sy) override { count_op(); } // 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 {} + SkScalar myx, SkScalar myy, SkScalar myt) override { + count_op(); + } // 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 {} + SkScalar mwx, SkScalar mwy, SkScalar mwz, SkScalar mwt) override { + count_op(); + } // clang-format on - void transformReset() override {} + void transformReset() override { count_op(); } }; -class IgnoreDrawDispatchHelper : public virtual Dispatcher { +class IgnoreDrawDispatchHelper : public virtual Dispatcher, + virtual DispatchOpCounter { public: - void save() override {} + void save() override { count_op(); } void saveLayer(const SkRect* bounds, const SaveLayerOptions options, - const DlImageFilter* backdrop) override {} - void restore() override {} - void drawColor(DlColor color, DlBlendMode mode) override {} - void drawPaint() override {} - void drawLine(const SkPoint& p0, const SkPoint& p1) override {} - void drawRect(const SkRect& rect) override {} - void drawOval(const SkRect& bounds) override {} - void drawCircle(const SkPoint& center, SkScalar radius) override {} - void drawRRect(const SkRRect& rrect) override {} - void drawDRRect(const SkRRect& outer, const SkRRect& inner) override {} - void drawPath(const SkPath& path) override {} + const DlImageFilter* backdrop) override { + count_op(); + } + void restore() override { count_op(); } + void drawColor(DlColor color, DlBlendMode mode) override { count_op(); } + void drawPaint() override { count_op(); } + void drawLine(const SkPoint& p0, const SkPoint& p1) override { count_op(); } + void drawRect(const SkRect& rect) override { count_op(); } + void drawOval(const SkRect& bounds) override { count_op(); } + void drawCircle(const SkPoint& center, SkScalar radius) override { + count_op(); + } + void drawRRect(const SkRRect& rrect) override { count_op(); } + void drawDRRect(const SkRRect& outer, const SkRRect& inner) override { + count_op(); + } + void drawPath(const SkPath& path) override { count_op(); } void drawArc(const SkRect& oval_bounds, SkScalar start_degrees, SkScalar sweep_degrees, - bool use_center) override {} + bool use_center) override { + count_op(); + } void drawPoints(SkCanvas::PointMode mode, uint32_t count, - const SkPoint points[]) override {} + const SkPoint points[]) override { + count_op(); + } void drawSkVertices(const sk_sp vertices, - SkBlendMode mode) override {} - void drawVertices(const DlVertices* vertices, DlBlendMode mode) override {} + SkBlendMode mode) override { + count_op(); + } + void drawVertices(const DlVertices* vertices, DlBlendMode mode) override { + count_op(); + } void drawImage(const sk_sp image, const SkPoint point, DlImageSampling sampling, - bool render_with_attributes) override {} + bool render_with_attributes) override { + count_op(); + } void drawImageRect(const sk_sp image, const SkRect& src, const SkRect& dst, DlImageSampling sampling, bool render_with_attributes, - SkCanvas::SrcRectConstraint constraint) override {} + SkCanvas::SrcRectConstraint constraint) override { + count_op(); + } void drawImageNine(const sk_sp image, const SkIRect& center, const SkRect& dst, DlFilterMode filter, - bool render_with_attributes) override {} + bool render_with_attributes) override { + count_op(); + } void drawImageLattice(const sk_sp image, const SkCanvas::Lattice& lattice, const SkRect& dst, DlFilterMode filter, - bool render_with_attributes) override {} + bool render_with_attributes) override { + count_op(); + } void drawAtlas(const sk_sp atlas, const SkRSXform xform[], const SkRect tex[], @@ -147,25 +188,36 @@ class IgnoreDrawDispatchHelper : public virtual Dispatcher { DlBlendMode mode, DlImageSampling sampling, const SkRect* cull_rect, - bool render_with_attributes) override {} + bool render_with_attributes) override { + count_op(); + } void drawPicture(const sk_sp picture, const SkMatrix* matrix, - bool render_with_attributes) override {} - void drawDisplayList(const sk_sp display_list) override {} + bool render_with_attributes) override { + count_op(); + } + void drawDisplayList(const sk_sp display_list) override { + count_op(); + } void drawTextBlob(const sk_sp blob, SkScalar x, - SkScalar y) override {} + SkScalar y) override { + count_op(); + } void drawShadow(const SkPath& path, const DlColor color, const SkScalar elevation, bool transparent_occluder, - SkScalar dpr) override {} + SkScalar dpr) override { + count_op(); + } }; // A utility class that will monitor the Dispatcher methods relating // to the rendering attributes and accumulate them into an SkPaint // which can be accessed at any time via paint(). -class SkPaintDispatchHelper : public virtual Dispatcher { +class SkPaintDispatchHelper : public virtual Dispatcher, + virtual DispatchOpCounter { public: SkPaintDispatchHelper(SkScalar opacity = SK_Scalar1) : current_color_(SK_ColorBLACK), opacity_(opacity) { @@ -257,7 +309,8 @@ class SkMatrixSource { // its save() and restore() methods so those methods will need to be // forwarded if overridden in more than one super class. class SkMatrixDispatchHelper : public virtual Dispatcher, - public virtual SkMatrixSource { + public virtual SkMatrixSource, + virtual DispatchOpCounter { public: void translate(SkScalar tx, SkScalar ty) override; void scale(SkScalar sx, SkScalar sy) override; @@ -307,7 +360,8 @@ class SkMatrixDispatchHelper : public virtual Dispatcher, // its save() and restore() methods so those methods will need to be // forwarded if overridden in more than one super class. class ClipBoundsDispatchHelper : public virtual Dispatcher, - private virtual SkMatrixSource { + private virtual SkMatrixSource, + virtual DispatchOpCounter { public: ClipBoundsDispatchHelper() : ClipBoundsDispatchHelper(nullptr) {} @@ -362,7 +416,9 @@ class BoundsAccumulator { /// be trusted. typedef bool BoundsModifier(const SkRect& original, SkRect* dest); - virtual void accumulate(const SkRect& r) = 0; + /// Accumulates the indicated rectangle for the indicated op index + /// into the bounds or rtree. + virtual void accumulate(const SkRect& r, uint32_t index) = 0; virtual bool is_empty() const = 0; virtual bool is_not_empty() const = 0; @@ -406,7 +462,7 @@ class RectBoundsAccumulator final : public virtual BoundsAccumulator { public: void accumulate(SkScalar x, SkScalar y) { rect_.accumulate(x, y); } void accumulate(const SkPoint& p) { rect_.accumulate(p.fX, p.fY); } - void accumulate(const SkRect& r) override; + void accumulate(const SkRect& r, uint32_t index) override; bool is_empty() const override { return rect_.is_empty(); } bool is_not_empty() const override { return rect_.is_not_empty(); } @@ -452,7 +508,9 @@ class RectBoundsAccumulator final : public virtual BoundsAccumulator { class RTreeBoundsAccumulator final : public virtual BoundsAccumulator { public: - void accumulate(const SkRect& r) override; + RTreeBoundsAccumulator(std::vector* rect_indices = nullptr) + : rect_indices_(rect_indices) {} + void accumulate(const SkRect& r, uint32_t index) override; bool is_empty() const override; bool is_not_empty() const override; @@ -473,6 +531,7 @@ class RTreeBoundsAccumulator final : public virtual BoundsAccumulator { private: std::vector rects_; std::vector saved_offsets_; + std::vector* rect_indices_; }; // This class implements all rendering methods and computes a liberal @@ -482,6 +541,7 @@ class DisplayListBoundsCalculator final public virtual IgnoreAttributeDispatchHelper, public virtual SkMatrixDispatchHelper, public virtual ClipBoundsDispatchHelper, + virtual DispatchOpCounter, DisplayListOpFlags { public: // Construct a Calculator to determine the bounds of a list of @@ -591,6 +651,8 @@ class DisplayListBoundsCalculator final private: BoundsAccumulator& accumulator_; + uint32_t op_index_; + void count_op() override { op_index_++; } // A class that remembers the information kept for a single // |save| or |saveLayer|.