From b9f69e3af174a291a88939e7c13e8b53efc1b261 Mon Sep 17 00:00:00 2001 From: Jim Graham Date: Tue, 15 Jun 2021 00:52:06 -0700 Subject: [PATCH 01/26] Implement a DisplayList mechanism similar to the Skia SkLiteDL mechanism The mechanism is a drop-in replacement for: SkPictureRecorder -> DisplayListBuilder SkPicture -> DisplayList SkCanvas -> DisplayListCanvasDispatcher The current Flutter Picture/Canvas mechanism redirects to DisplayList with minimal changes --- common/settings.h | 3 + flow/BUILD.gn | 11 + flow/display_list.cc | 1247 +++++++++++++++++++ flow/display_list.h | 427 +++++++ flow/display_list_canvas.cc | 470 +++++++ flow/display_list_canvas.h | 311 +++++ flow/display_list_canvas_unittests.cc | 894 +++++++++++++ flow/display_list_unittests.cc | 612 +++++++++ flow/display_list_utils.cc | 411 ++++++ flow/display_list_utils.h | 369 ++++++ flow/layers/display_list_layer.cc | 130 ++ flow/layers/display_list_layer.h | 59 + flow/layers/display_list_layer_unittests.cc | 161 +++ flow/layers/layer.h | 4 + flow/raster_cache.cc | 148 ++- flow/raster_cache.h | 31 + flow/raster_cache_key.h | 3 + flow/testing/diff_context_test.cc | 14 + flow/testing/diff_context_test.h | 9 + lib/ui/compositing/scene_builder.cc | 16 +- lib/ui/painting/canvas.cc | 52 +- lib/ui/painting/canvas.h | 11 + lib/ui/painting/image_dispose_unittests.cc | 23 +- lib/ui/painting/picture.cc | 49 +- lib/ui/painting/picture.h | 12 + lib/ui/painting/picture_recorder.cc | 21 +- lib/ui/painting/picture_recorder.h | 8 + lib/ui/snapshot_delegate.h | 4 + lib/ui/ui_dart_state.cc | 6 + lib/ui/ui_dart_state.h | 4 + runtime/dart_isolate.cc | 1 + shell/common/rasterizer.cc | 6 + shell/common/rasterizer.h | 5 + shell/common/shell.cc | 3 + shell/common/switches.cc | 12 + 35 files changed, 5514 insertions(+), 33 deletions(-) create mode 100644 flow/display_list.cc create mode 100644 flow/display_list.h create mode 100644 flow/display_list_canvas.cc create mode 100644 flow/display_list_canvas.h create mode 100644 flow/display_list_canvas_unittests.cc create mode 100644 flow/display_list_unittests.cc create mode 100644 flow/display_list_utils.cc create mode 100644 flow/display_list_utils.h create mode 100644 flow/layers/display_list_layer.cc create mode 100644 flow/layers/display_list_layer.h create mode 100644 flow/layers/display_list_layer_unittests.cc diff --git a/common/settings.h b/common/settings.h index 4b9ff999dc541..9fa5f76f116d4 100644 --- a/common/settings.h +++ b/common/settings.h @@ -163,6 +163,9 @@ struct Settings { // Selects the SkParagraph implementation of the text layout engine. bool enable_skparagraph = false; + // Selects the DisplayList for storage of rendering operations. + bool enable_display_list = true; + // All shells in the process share the same VM. The last shell to shutdown // should typically shut down the VM as well. However, applications depend on // the behavior of "warming-up" the VM by creating a shell that does not do diff --git a/flow/BUILD.gn b/flow/BUILD.gn index 29df9651107a5..b6c881f902f7d 100644 --- a/flow/BUILD.gn +++ b/flow/BUILD.gn @@ -12,6 +12,12 @@ source_set("flow") { "compositor_context.h", "diff_context.cc", "diff_context.h", + "display_list.cc", + "display_list.h", + "display_list_canvas.cc", + "display_list_canvas.h", + "display_list_utils.cc", + "display_list_utils.h", "embedded_views.cc", "embedded_views.h", "frame_timings.cc", @@ -30,6 +36,8 @@ source_set("flow") { "layers/color_filter_layer.h", "layers/container_layer.cc", "layers/container_layer.h", + "layers/display_list_layer.cc", + "layers/display_list_layer.h", "layers/image_filter_layer.cc", "layers/image_filter_layer.h", "layers/layer.cc", @@ -123,6 +131,8 @@ if (enable_unittests) { testonly = true sources = [ + "display_list_canvas_unittests.cc", + "display_list_unittests.cc", "embedded_view_params_unittests.cc", "flow_run_all_unittests.cc", "flow_test_utils.cc", @@ -136,6 +146,7 @@ if (enable_unittests) { "layers/clip_rrect_layer_unittests.cc", "layers/color_filter_layer_unittests.cc", "layers/container_layer_unittests.cc", + "layers/display_list_layer_unittests.cc", "layers/image_filter_layer_unittests.cc", "layers/layer_tree_unittests.cc", "layers/opacity_layer_unittests.cc", diff --git a/flow/display_list.cc b/flow/display_list.cc new file mode 100644 index 0000000000000..7055cc93a7ac0 --- /dev/null +++ b/flow/display_list.cc @@ -0,0 +1,1247 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include + +#include "flutter/flow/display_list.h" +#include "flutter/flow/display_list_canvas.h" +#include "flutter/flow/display_list_utils.h" +#include "flutter/fml/logging.h" + +#include "third_party/skia/include/core/SkImageFilter.h" +#include "third_party/skia/include/core/SkMaskFilter.h" +#include "third_party/skia/include/core/SkPath.h" +#include "third_party/skia/include/core/SkRSXform.h" +#include "third_party/skia/include/core/SkTextBlob.h" + +// This header file cannot be included here, but we cannot +// record calls made by the SkShadowUtils without it. +// #include "third_party/skia/src/core/SkDrawShadowInfo.h" + +namespace flutter { + +const SkSamplingOptions DisplayList::NearestSampling = + SkSamplingOptions(SkFilterMode::kNearest, SkMipmapMode::kNone); +const SkSamplingOptions DisplayList::LinearSampling = + SkSamplingOptions(SkFilterMode::kLinear, SkMipmapMode::kNone); +const SkSamplingOptions DisplayList::MipmapSampling = + SkSamplingOptions(SkFilterMode::kLinear, SkMipmapMode::kLinear); +const SkSamplingOptions DisplayList::CubicSampling = + SkSamplingOptions(SkCubicResampler{1 / 3.0f, 1 / 3.0f}); + +enum dlCompare { + NOT_EQUAL, + BULK_COMPARE, + EQUAL, +}; + +// Assuming a 64-bit platform (most of our platforms at this time?) +// +// Struct allocation in the DL memory is aligned to a void* boundary +// which means that the minimum (aligned) struct size will be 8 bytes. +// The DLOp base uses 4 bytes so each Op-specific struct gets 4 bytes +// of data for "free" and works best when it packs well into an 8-byte +// aligned size. +struct DLOp { + uint8_t type : 8; + uint32_t size : 24; + dlCompare equals(const DLOp* other) const { return BULK_COMPARE; } +}; + +// 4 byte header + 4 byte payload packs into minimum 8 bytes +#define DEFINE_SET_BOOL_OP(name) \ + struct Set##name##Op final : DLOp { \ + static const auto kType = DisplayListOpType::Set##name; \ + \ + Set##name##Op(bool value) : value(value) {} \ + \ + const bool value; \ + \ + void dispatch(Dispatcher& dispatcher) const { \ + dispatcher.set##name(value); \ + } \ + }; +DEFINE_SET_BOOL_OP(AA) +DEFINE_SET_BOOL_OP(Dither) +DEFINE_SET_BOOL_OP(InvertColors) +#undef DEFINE_SET_CLEAR_BOOL_OP + +// 4 byte header + 4 byte payload packs into minimum 8 bytes +#define DEFINE_SET_ENUM_OP(name) \ + struct Set##name##s##Op final : DLOp { \ + static const auto kType = DisplayListOpType::Set##name##s; \ + \ + Set##name##s##Op(SkPaint::name value) : value(value) {} \ + \ + const SkPaint::name value; \ + \ + void dispatch(Dispatcher& dispatcher) const { \ + dispatcher.set##name##s(value); \ + } \ + }; +DEFINE_SET_ENUM_OP(Cap) +DEFINE_SET_ENUM_OP(Join) +#undef DEFINE_SET_ENUM_OP + +// 4 byte header + 4 byte payload packs into minimum 8 bytes +struct SetDrawStyleOp final : DLOp { + static const auto kType = DisplayListOpType::SetDrawStyle; + + SetDrawStyleOp(SkPaint::Style style) : style(style) {} + + const SkPaint::Style style; + + void dispatch(Dispatcher& dispatcher) const { + dispatcher.setDrawStyle(style); + } +}; +// 4 byte header + 4 byte payload packs into minimum 8 bytes +struct SetStrokeWidthOp final : DLOp { + static const auto kType = DisplayListOpType::SetStrokeWidth; + + SetStrokeWidthOp(SkScalar width) : width(width) {} + + const SkScalar width; + + void dispatch(Dispatcher& dispatcher) const { + dispatcher.setStrokeWidth(width); + } +}; +// 4 byte header + 4 byte payload packs into minimum 8 bytes +struct SetMiterLimitOp final : DLOp { + static const auto kType = DisplayListOpType::SetMiterLimit; + + SetMiterLimitOp(SkScalar limit) : limit(limit) {} + + const SkScalar limit; + + void dispatch(Dispatcher& dispatcher) const { + dispatcher.setMiterLimit(limit); + } +}; + +// 4 byte header + 4 byte payload packs into minimum 8 bytes +struct SetColorOp final : DLOp { + static const auto kType = DisplayListOpType::SetColor; + + SetColorOp(SkColor color) : color(color) {} + + const SkColor color; + + void dispatch(Dispatcher& dispatcher) const { dispatcher.setColor(color); } +}; +// 4 byte header + 4 byte payload packs into minimum 8 bytes +struct SetBlendModeOp final : DLOp { + static const auto kType = DisplayListOpType::SetBlendMode; + + SetBlendModeOp(SkBlendMode mode) : mode(mode) {} + + const SkBlendMode mode; + + void dispatch(Dispatcher& dispatcher) const { dispatcher.setBlendMode(mode); } +}; + +// 4 byte header + 4 byte payload packs into minimum 8 bytes +struct SetFilterQualityOp final : DLOp { + static const auto kType = DisplayListOpType::SetFilterQuality; + + SetFilterQualityOp(SkFilterQuality quality) : quality(quality) {} + + const SkFilterQuality quality; + + void dispatch(Dispatcher& dispatcher) const { + dispatcher.setFilterQuality(quality); + } +}; + +// Clear: 4 byte header + unused 4 byte payload uses 8 bytes +// (4 bytes unused) +// Set: 4 byte header + an sk_sp (ptr) uses 16 bytes due to the +// alignment of the ptr. +// (4 bytes unused) +#define DEFINE_SET_CLEAR_SKREF_OP(name, field) \ + struct Clear##name##Op final : DLOp { \ + static const auto kType = DisplayListOpType::Clear##name; \ + \ + Clear##name##Op() {} \ + \ + void dispatch(Dispatcher& dispatcher) const { \ + dispatcher.set##name(nullptr); \ + } \ + }; \ + struct Set##name##Op final : DLOp { \ + static const auto kType = DisplayListOpType::Set##name; \ + \ + Set##name##Op(sk_sp field) : field(std::move(field)) {} \ + \ + sk_sp field; \ + \ + void dispatch(Dispatcher& dispatcher) const { \ + dispatcher.set##name(field); \ + } \ + }; +DEFINE_SET_CLEAR_SKREF_OP(Shader, shader) +DEFINE_SET_CLEAR_SKREF_OP(ImageFilter, filter) +DEFINE_SET_CLEAR_SKREF_OP(ColorFilter, filter) +DEFINE_SET_CLEAR_SKREF_OP(MaskFilter, filter) +#undef DEFINE_SET_CLEAR_SKREF_OP + +// 4 byte header + 4 byte payload packs into minimum 8 bytes +// Note that the "blur style" is packed into the OpType to prevent +// needing an additional 8 bytes for a 4-value enum. +#define DEFINE_MASK_BLUR_FILTER_OP(name, style) \ + struct SetMaskBlurFilter##name##Op final : DLOp { \ + static const auto kType = DisplayListOpType::SetMaskBlurFilter##name; \ + \ + SetMaskBlurFilter##name##Op(SkScalar sigma) : sigma(sigma) {} \ + \ + SkScalar sigma; \ + \ + void dispatch(Dispatcher& dispatcher) const { \ + dispatcher.setMaskBlurFilter(style, sigma); \ + } \ + }; +DEFINE_MASK_BLUR_FILTER_OP(Normal, kNormal_SkBlurStyle) +DEFINE_MASK_BLUR_FILTER_OP(Solid, kSolid_SkBlurStyle) +DEFINE_MASK_BLUR_FILTER_OP(Inner, kInner_SkBlurStyle) +DEFINE_MASK_BLUR_FILTER_OP(Outer, kOuter_SkBlurStyle) +#undef DEFINE_MASK_BLUR_FILTER_OP + +// 4 byte header + no payload uses minimum 8 bytes (4 bytes unused) +struct SaveOp final : DLOp { + static const auto kType = DisplayListOpType::Save; + + SaveOp() {} + + void dispatch(Dispatcher& dispatcher) const { dispatcher.save(); } +}; +// 4 byte header + 4 byte payload packs into minimum 8 bytes +struct SaveLayerOp final : DLOp { + static const auto kType = DisplayListOpType::SaveLayer; + + SaveLayerOp(bool withPaint) : withPaint(withPaint) {} + + bool withPaint; + + void dispatch(Dispatcher& dispatcher) const { + dispatcher.saveLayer(nullptr, withPaint); + } +}; +// 4 byte header + 20 byte payload packs evenly into 24 bytes +struct SaveLayerBoundsOp final : DLOp { + static const auto kType = DisplayListOpType::SaveLayerBounds; + + SaveLayerBoundsOp(SkRect rect, bool withPaint) + : withPaint(withPaint), rect(rect) {} + + bool withPaint; + const SkRect rect; + + void dispatch(Dispatcher& dispatcher) const { + dispatcher.saveLayer(&rect, withPaint); + } +}; +// 4 byte header + no payload uses minimum 8 bytes (4 bytes unused) +struct RestoreOp final : DLOp { + static const auto kType = DisplayListOpType::Restore; + + RestoreOp() {} + + void dispatch(Dispatcher& dispatcher) const { dispatcher.restore(); } +}; + +// 4 byte header + 8 byte payload uses 12 bytes but is rounded up to 16 bytes +// (4 bytes unused) +struct TranslateOp final : DLOp { + static const auto kType = DisplayListOpType::Translate; + + TranslateOp(SkScalar tx, SkScalar ty) : tx(tx), ty(ty) {} + + const SkScalar tx; + const SkScalar ty; + + void dispatch(Dispatcher& dispatcher) const { 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 { + static const auto kType = DisplayListOpType::Scale; + + ScaleOp(SkScalar sx, SkScalar sy) : sx(sx), sy(sy) {} + + const SkScalar sx; + const SkScalar sy; + + void dispatch(Dispatcher& dispatcher) const { dispatcher.scale(sx, sy); } +}; +// 4 byte header + 4 byte payload packs into minimum 8 bytes +struct RotateOp final : DLOp { + static const auto kType = DisplayListOpType::Rotate; + + RotateOp(SkScalar degrees) : degrees(degrees) {} + + const SkScalar degrees; + + void dispatch(Dispatcher& dispatcher) const { 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 { + static const auto kType = DisplayListOpType::Skew; + + SkewOp(SkScalar sx, SkScalar sy) : sx(sx), sy(sy) {} + + const SkScalar sx; + const SkScalar sy; + + void dispatch(Dispatcher& dispatcher) const { dispatcher.skew(sx, sy); } +}; +// 4 byte header + 24 byte payload uses 28 bytes but is rounded up to 32 bytes +// (4 bytes unused) +struct Transform2x3Op final : DLOp { + static const auto kType = DisplayListOpType::Transform2x3; + + Transform2x3Op(SkScalar mxx, + SkScalar mxy, + SkScalar mxt, + SkScalar myx, + SkScalar myy, + SkScalar myt) + : mxx(mxx), mxy(mxy), mxt(mxt), myx(myx), myy(myy), myt(myt) {} + + const SkScalar mxx, mxy, mxt; + const SkScalar myx, myy, myt; + + void dispatch(Dispatcher& dispatcher) const { + dispatcher.transform2x3(mxx, mxy, mxt, myx, myy, myt); + } +}; +// 4 byte header + 36 byte payload packs evenly into 40 bytes +struct Transform3x3Op final : DLOp { + static const auto kType = DisplayListOpType::Transform3x3; + + Transform3x3Op(SkScalar mxx, + SkScalar mxy, + SkScalar mxt, + SkScalar myx, + SkScalar myy, + SkScalar myt, + SkScalar px, + SkScalar py, + SkScalar pt) + : mxx(mxx), + mxy(mxy), + mxt(mxt), + myx(myx), + myy(myy), + myt(myt), + px(px), + py(py), + pt(pt) {} + + const SkScalar mxx, mxy, mxt; + const SkScalar myx, myy, myt; + const SkScalar px, py, pt; + + void dispatch(Dispatcher& dispatcher) const { + dispatcher.transform3x3(mxx, mxy, mxt, myx, myy, myt, px, py, pt); + } +}; + +// The common data is a 4 byte header + a 4 byte common payload which +// packs evenly into 8 common bytes +// SkRect is 16 more bytes, which packs efficiently into 24 bytes total +// SkRRect is 52 more bytes, which rounds up to 56 bytes (4 bytes unused) +// which packs into 64 bytes total +// SkPath is 16 more bytes, which packs efficiently into 24 bytes total +#define DEFINE_CLIP_SHAPE_OP(shapetype) \ + struct Clip##shapetype##Op final : DLOp { \ + static const auto kType = DisplayListOpType::Clip##shapetype; \ + \ + Clip##shapetype##Op(Sk##shapetype shape, bool isAA, SkClipOp op) \ + : isAA(isAA), op(op), shape(shape) {} \ + \ + const bool isAA : 16; \ + const SkClipOp op : 16; \ + const Sk##shapetype shape; \ + \ + void dispatch(Dispatcher& dispatcher) const { \ + dispatcher.clip##shapetype(shape, isAA, op); \ + } \ + }; +DEFINE_CLIP_SHAPE_OP(Rect) +DEFINE_CLIP_SHAPE_OP(RRect) +DEFINE_CLIP_SHAPE_OP(Path) +#undef DEFINE_CLIP_SHAPE_OP + +// 4 byte header + no payload uses minimum 8 bytes (4 bytes unused) +struct DrawPaintOp final : DLOp { + static const auto kType = DisplayListOpType::DrawPaint; + + DrawPaintOp() {} + + void dispatch(Dispatcher& dispatcher) const { 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 { + static const auto kType = DisplayListOpType::DrawColor; + + DrawColorOp(SkColor color, SkBlendMode mode) : color(color), mode(mode) {} + + const SkColor color; + const SkBlendMode mode; + + void dispatch(Dispatcher& dispatcher) const { + dispatcher.drawColor(color, mode); + } +}; + +// The common data is a 4 byte header with an unused 4 bytes +// SkRect is 16 more bytes, using 20 bytes which rounds up to 24 bytes total +// (4 bytes unused) +// SkRRect is 52 more bytes, which packs efficiently into 56 bytes total +// SkPath is 16 more bytes, using 20 bytes which rounds up to 24 bytes total +// (4 bytes unused) +#define DEFINE_DRAW_1ARG_OP(op_name, arg_type, arg_name) \ + struct Draw##op_name##Op final : DLOp { \ + static const auto kType = DisplayListOpType::Draw##op_name; \ + \ + 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); \ + } \ + }; +DEFINE_DRAW_1ARG_OP(Rect, SkRect, rect) +DEFINE_DRAW_1ARG_OP(Oval, SkRect, oval) +DEFINE_DRAW_1ARG_OP(RRect, SkRRect, rrect) +DEFINE_DRAW_1ARG_OP(Path, SkPath, path) +#undef DEFINE_DRAW_1ARG_OP + +// The common data is a 4 byte header with an unused 4 bytes +// 2 x SkPoint is 16 more bytes, using 20 bytes rounding up to 24 bytes total +// (4 bytes unused) +// SkPoint + SkScalar is 12 more bytes, packing efficiently into 16 bytes total +// 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 { \ + static const auto kType = DisplayListOpType::Draw##op_name; \ + \ + Draw##op_name##Op(type1 name1, type2 name2) \ + : name1(name1), name2(name2) {} \ + \ + const type1 name1; \ + const type2 name2; \ + \ + void dispatch(Dispatcher& dispatcher) const { \ + dispatcher.draw##op_name(name1, name2); \ + } \ + }; +DEFINE_DRAW_2ARG_OP(Line, SkPoint, p0, SkPoint, p1) +DEFINE_DRAW_2ARG_OP(Circle, SkPoint, center, SkScalar, radius) +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 { + static const auto kType = DisplayListOpType::DrawArc; + + DrawArcOp(SkRect bounds, SkScalar start, SkScalar sweep, bool center) + : bounds(bounds), start(start), sweep(sweep), center(center) {} + + const SkRect bounds; + const SkScalar start; + const SkScalar sweep; + const bool center; + + void dispatch(Dispatcher& dispatcher) const { + dispatcher.drawArc(bounds, start, sweep, center); + } +}; + +// 4 byte header + 4 byte fixed payload packs efficiently into 8 bytes +// But then there is a list of points following the structure which +// is guaranteed to be a multiple of 8 bytes (SkPoint is 8 bytes) +// 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::Draw##name; \ + \ + 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_DRAW_POINTS_OP(Points, kPoints_PointMode); +DEFINE_DRAW_POINTS_OP(Lines, kLines_PointMode); +DEFINE_DRAW_POINTS_OP(Polygon, kPolygon_PointMode); +#undef DEFINE_DRAW_POINTS_OP + +// 4 byte header + 12 byte payload packs efficiently into 16 bytes +struct DrawVerticesOp final : DLOp { + static const auto kType = DisplayListOpType::DrawVertices; + + DrawVerticesOp(sk_sp vertices, SkBlendMode mode) + : mode(mode), vertices(std::move(vertices)) {} + + const SkBlendMode mode; + const sk_sp vertices; + + void dispatch(Dispatcher& dispatcher) const { + dispatcher.drawVertices(vertices, mode); + } +}; + +// 4 byte header + 36 byte payload packs efficiently into 40 bytes +struct DrawImageOp final : DLOp { + static const auto kType = DisplayListOpType::DrawImage; + + DrawImageOp(const sk_sp image, + const SkPoint& point, + const SkSamplingOptions& sampling) + : point(point), sampling(sampling), image(std::move(image)) {} + + const SkPoint point; + const SkSamplingOptions sampling; + const sk_sp image; + + void dispatch(Dispatcher& dispatcher) const { + dispatcher.drawImage(image, point, sampling); + } +}; + +// 4 byte header + 60 byte payload packs efficiently into 64 bytes +struct DrawImageRectOp final : DLOp { + static const auto kType = DisplayListOpType::DrawImageRect; + + DrawImageRectOp(const sk_sp image, + const SkRect& src, + const SkRect& dst, + const SkSamplingOptions& sampling) + : src(src), dst(dst), sampling(sampling), image(std::move(image)) {} + + const SkRect src; + const SkRect dst; + const SkSamplingOptions sampling; + const sk_sp image; + + void dispatch(Dispatcher& dispatcher) const { + dispatcher.drawImageRect(image, src, dst, sampling); + } +}; + +// 4 byte header + 44 byte payload packs efficiently into 48 bytes +struct DrawImageNineOp final : DLOp { + static const auto kType = DisplayListOpType::DrawImageNine; + + DrawImageNineOp(const sk_sp image, + const SkIRect& center, + const SkRect& dst, + SkFilterMode filter) + : center(center), dst(dst), filter(filter), image(std::move(image)) {} + + const SkIRect center; + const SkRect dst; + const SkFilterMode filter; + const sk_sp image; + + void dispatch(Dispatcher& dispatcher) const { + dispatcher.drawImageNine(image, center, dst, filter); + } +}; + +struct DrawImageLatticeOp final : DLOp { + static const auto kType = DisplayListOpType::DrawImageLattice; + + DrawImageLatticeOp(const sk_sp image, + int xDivCount, + int yDivCount, + int cellCount, + const SkIRect& src, + const SkRect& dst, + SkFilterMode filter) + : xDivCount(xDivCount), + yDivCount(yDivCount), + cellCount(cellCount), + src(src), + dst(dst), + filter(filter), + image(std::move(image)) {} + + const int xDivCount; + const int yDivCount; + const int cellCount; + const SkIRect src; + const SkRect dst; + const SkFilterMode filter; + const sk_sp image; + + void dispatch(Dispatcher& dispatcher) const { + const int* xDivs = reinterpret_cast(this + 1); + const int* yDivs = reinterpret_cast(xDivs + xDivCount); + const SkColor* colors = + (cellCount == 0) ? nullptr + : reinterpret_cast(yDivs + yDivCount); + const SkCanvas::Lattice::RectType* rTypes = + (cellCount == 0) ? nullptr + : reinterpret_cast( + colors + cellCount); + dispatcher.drawImageLattice( + image, {xDivs, yDivs, rTypes, xDivCount, yDivCount, &src, colors}, dst, + filter); + } +}; + +#define DRAW_ATLAS_NO_COLORS_ARRAY(tex, count) nullptr +#define DRAW_ATLAS_HAS_COLORS_ARRAY(tex, count) \ + reinterpret_cast(tex + count) + +#define DRAW_ATLAS_NO_CULLING_ARGS \ + const sk_sp atlas, int count, SkBlendMode mode, \ + const SkSamplingOptions &sampling +#define DRAW_ATLAS_NO_CULLING_INIT \ + count(count), mode(mode), sampling(sampling), atlas(std::move(atlas)) +#define DRAW_ATLAS_NO_CULLING_FIELDS \ + const int count; \ + const SkBlendMode mode; \ + const SkSamplingOptions sampling; \ + const sk_sp atlas +#define DRAW_ATLAS_NO_CULLING_P_ARG nullptr + +#define DRAW_ATLAS_HAS_CULLING_ARGS \ + DRAW_ATLAS_NO_CULLING_ARGS, const SkRect& cull +#define DRAW_ATLAS_HAS_CULLING_INIT DRAW_ATLAS_NO_CULLING_INIT, cull(cull) +#define DRAW_ATLAS_HAS_CULLING_FIELDS \ + DRAW_ATLAS_NO_CULLING_FIELDS; \ + const SkRect cull +#define DRAW_ATLAS_HAS_CULLING_P_ARG &cull + +// 4 byte header + 36 byte common payload packs efficiently into 40 bytes +// Culling version has an additional 16 bytes of payload for 56 bytes +// So all 4 versions of the base structure pack well. +// Each of these is then followed by a number of lists. +// SkRSXform list is a multiple of 16 bytes so it is always packed well +// SkRect list is also a multiple of 16 bytes so it also packs well +// SkColor list only packs well if the count is even, otherwise there +// can be 4 unusued bytes at the end. +#define DEFINE_DRAW_ATLAS_OP(name, colors, cull) \ + struct Draw##name##Op final : DLOp { \ + static const auto kType = DisplayListOpType::Draw##name; \ + \ + Draw##name##Op(DRAW_ATLAS_##cull##_ARGS) : DRAW_ATLAS_##cull##_INIT {} \ + \ + DRAW_ATLAS_##cull##_FIELDS; \ + \ + void dispatch(Dispatcher& dispatcher) const { \ + const SkRSXform* xform = reinterpret_cast(this + 1); \ + const SkRect* tex = reinterpret_cast(xform + count); \ + const SkColor* colors = DRAW_ATLAS_##colors##_ARRAY(tex, count); \ + dispatcher.drawAtlas(atlas, xform, tex, colors, count, mode, sampling, \ + DRAW_ATLAS_##cull##_P_ARG); \ + } \ + }; +DEFINE_DRAW_ATLAS_OP(Atlas, NO_COLORS, NO_CULLING) +DEFINE_DRAW_ATLAS_OP(AtlasColored, HAS_COLORS, NO_CULLING) +DEFINE_DRAW_ATLAS_OP(AtlasCulled, NO_COLORS, HAS_CULLING) +DEFINE_DRAW_ATLAS_OP(AtlasColoredCulled, HAS_COLORS, HAS_CULLING) +#undef DEFINE_DRAW_ATLAS_OP +#undef DRAW_ATLAS_NO_COLORS_ARRAY +#undef DRAW_ATLAS_HAS_COLORS_ARRAY +#undef DRAW_ATLAS_NO_CULLING_ARGS +#undef DRAW_ATLAS_NO_CULLING_INIT +#undef DRAW_ATLAS_NO_CULLING_FIELDS +#undef DRAW_ATLAS_NO_CULLING_P_ARG +#undef DRAW_ATLAS_HAS_CULLING_ARGS +#undef DRAW_ATLAS_HAS_CULLING_INIT +#undef DRAW_ATLAS_HAS_CULLING_FIELDS +#undef DRAW_ATLAS_HAS_CULLING_P_ARG + +// 4 byte header + ptr aligned payload uses 12 bytes rounde up to 16 +// (4 bytes unused) +struct DrawSkPictureOp final : DLOp { + static const auto kType = DisplayListOpType::DrawSkPicture; + + DrawSkPictureOp(sk_sp picture) : picture(std::move(picture)) {} + + sk_sp picture; + + void dispatch(Dispatcher& dispatcher) const { + dispatcher.drawPicture(picture); + } +}; + +// 4 byte header + ptr aligned payload uses 12 bytes rounde up to 16 +// (4 bytes unused) +struct DrawDisplayListOp final : DLOp { + static const auto kType = DisplayListOpType::DrawDisplayList; + + DrawDisplayListOp(const sk_sp display_list) + : display_list(std::move(display_list)) {} + + sk_sp display_list; + + void dispatch(Dispatcher& dispatcher) const { + dispatcher.drawDisplayList(display_list); + } +}; + +struct DrawTextBlobOp final : DLOp { + static const auto kType = DisplayListOpType::DrawTextBlob; + + DrawTextBlobOp(const sk_sp blob, SkScalar x, SkScalar y) + : x(x), y(y), blob(std::move(blob)) {} + + const SkScalar x; + const SkScalar y; + const sk_sp blob; + + void dispatch(Dispatcher& dispatcher) const { + dispatcher.drawTextBlob(blob, x, y); + } +}; + +// struct DrawShadowRecOp final : DLOp { +// static const auto kType = DisplayListOpType::DrawShadowRec; +// +// DrawShadowRecOp(const SkPath& path, const SkDrawShadowRec& rec) +// : path(path), rec(rec) {} +// +// const SkPath path; +// const SkDrawShadowRec rec; +// +// void dispatch(Dispatcher& dispatcher) const { +// dispatcher.drawShadowRec(path, rec); +// } +// }; + +// 4 byte header + 28 byte payload packs evenly into 32 bytes +struct DrawShadowOp final : DLOp { + static const auto kType = DisplayListOpType::DrawShadow; + + DrawShadowOp(const SkPath& path, + SkColor color, + SkScalar elevation, + bool occludes) + : color(color), elevation(elevation), occludes(occludes), path(path) {} + + const SkColor color; + const SkScalar elevation; + const bool occludes; + const SkPath path; + + void dispatch(Dispatcher& dispatcher) const { + dispatcher.drawShadow(path, color, elevation, occludes); + } +}; + +void DisplayList::computeBounds() { + DisplayListBoundsCalculator calculator(boundsCull_); + dispatch(calculator); + bounds_ = calculator.getBounds(); +} + +void DisplayList::dispatch(Dispatcher& dispatcher, + uint8_t* ptr, + uint8_t* end) const { + while (ptr < end) { + auto op = (const DLOp*)ptr; + ptr += op->size; + FML_DCHECK(ptr <= end); + switch (op->type) { +#define DL_OP_DISPATCH(name) \ + case DisplayListOpType::name: \ + static_cast(op)->dispatch(dispatcher); \ + break; + + FOR_EACH_DISPLAY_LIST_OP(DL_OP_DISPATCH) + +#undef DL_OP_DISPATCH + + default: + FML_DCHECK(false); + return; + } + } +} + +static void DisposeOps(uint8_t* ptr, uint8_t* end) { + while (ptr < end) { + auto op = (const DLOp*)ptr; + ptr += op->size; + FML_DCHECK(ptr <= end); + switch (op->type) { +#define DL_OP_DISPOSE(name) \ + case DisplayListOpType::name: \ + if (!std::is_trivially_destructible_v) { \ + static_cast(op)->~name##Op(); \ + } \ + break; + + FOR_EACH_DISPLAY_LIST_OP(DL_OP_DISPOSE) + +#undef DL_OP_DISPATCH + + default: + FML_DCHECK(false); + return; + } + } +} + +static bool CompareOps(uint8_t* ptrA, + uint8_t* endA, + uint8_t* ptrB, + uint8_t* endB) { + if (endA - ptrA != endB - ptrB) { + return false; + } + uint8_t* bulkStartA = ptrA; + uint8_t* bulkStartB = ptrB; + while (ptrA < endA && ptrB < endB) { + auto opA = (const DLOp*)ptrA; + auto opB = (const DLOp*)ptrB; + if (opA->type != opB->type || opA->size != opB->size) { + return false; + } + ptrA += opA->size; + ptrB += opB->size; + FML_DCHECK(ptrA <= endA); + FML_DCHECK(ptrB <= endB); + dlCompare result; + switch (opA->type) { +#define DL_OP_EQUALS(name) \ + case DisplayListOpType::name: \ + result = static_cast(opA)->equals( \ + static_cast(opB)); \ + break; + + FOR_EACH_DISPLAY_LIST_OP(DL_OP_EQUALS) + +#undef DL_OP_DISPATCH + + default: + FML_DCHECK(false); + return false; + } + switch (result) { + case NOT_EQUAL: + return false; + case BULK_COMPARE: + break; + case EQUAL: + // Check if we have a backlog of bytes to bulk compare and then + // reset the bulk compare pointers to the address following this op + auto bulkBytes = reinterpret_cast(opA) - bulkStartA; + if (bulkBytes > 0) { + if (memcmp(bulkStartA, bulkStartB, bulkBytes) != 0) { + return false; + } + } + bulkStartA = ptrA; + bulkStartB = ptrB; + break; + } + } + if (ptrA != endA || ptrB != endB) { + return false; + } + if (bulkStartA < ptrA) { + // Perform a final bulk compare if we have remaining bytes waiting + if (memcmp(bulkStartA, bulkStartB, ptrA - bulkStartA) != 0) { + return false; + } + } + return true; +} + +void DisplayList::renderTo(SkCanvas* canvas) const { + DisplayListCanvasDispatcher dispatcher(canvas); + dispatch(dispatcher); +} + +bool DisplayList::equals(const DisplayList& other) const { + return CompareOps(ptr_, ptr_ + used_, other.ptr_, other.ptr_ + other.used_); +} + +DisplayList::DisplayList(uint8_t* ptr, + size_t used, + int opCount, + const SkRect& cull) + : ptr_(ptr), + used_(used), + opCount_(opCount), + bounds_({0, 0, -1, -1}), + boundsCull_(cull) { + static std::atomic nextID{1}; + do { + uniqueID_ = nextID.fetch_add(+1, std::memory_order_relaxed); + } while (uniqueID_ == 0); +} + +DisplayList::~DisplayList() { + DisposeOps(ptr_, ptr_ + used_); +} + +#define DL_BUILDER_PAGE 4096 + +// copy_v(dst, src,n, src,n, ...) copies any number of typed srcs into dst. +static void copy_v(void* dst) {} + +template +static void copy_v(void* dst, const S* src, int n, Rest&&... rest) { + SkASSERTF(((uintptr_t)dst & (alignof(S) - 1)) == 0, + "Expected %p to be aligned for at least %zu bytes.", dst, + alignof(S)); + sk_careful_memcpy(dst, src, n * sizeof(S)); + copy_v(SkTAddOffset(dst, n * sizeof(S)), std::forward(rest)...); +} + +template +void* DisplayListBuilder::push(size_t pod, Args&&... args) { + size_t size = SkAlignPtr(sizeof(T) + pod); + SkASSERT(size < (1 << 24)); + if (used_ + size > allocated_) { + static_assert(SkIsPow2(DL_BUILDER_PAGE), + "This math needs updating for non-pow2."); + // Next greater multiple of DL_BUILDER_PAGE. + allocated_ = (used_ + size + DL_BUILDER_PAGE) & ~(DL_BUILDER_PAGE - 1); + storage_.realloc(allocated_); + FML_DCHECK(storage_.get()); + } + SkASSERT(used_ + size <= allocated_); + auto op = (T*)(storage_.get() + used_); + used_ += size; + new (op) T{std::forward(args)...}; + op->type = (uint32_t)T::kType; + op->size = size; + opCount_++; + return op + 1; +} + +sk_sp DisplayListBuilder::build() { + while (saveLevel_ > 0) { + restore(); + } + size_t used = used_; + int count = opCount_; + used_ = allocated_ = opCount_ = 0; + storage_.realloc(used); + return sk_sp( + new DisplayList(storage_.release(), used, count, cull_)); +} + +DisplayListBuilder::DisplayListBuilder(const SkRect& cull) : cull_(cull) {} + +DisplayListBuilder::~DisplayListBuilder() { + uint8_t* ptr = storage_.get(); + if (ptr) { + DisposeOps(ptr, ptr + used_); + } +} + +void DisplayListBuilder::setAA(bool aa) { + push(0, aa); +} +void DisplayListBuilder::setDither(bool dither) { + push(0, dither); +} +void DisplayListBuilder::setInvertColors(bool invert) { + push(0, invert); +} +void DisplayListBuilder::setCaps(SkPaint::Cap cap) { + push(0, cap); +} +void DisplayListBuilder::setJoins(SkPaint::Join join) { + push(0, join); +} +void DisplayListBuilder::setDrawStyle(SkPaint::Style style) { + push(0, style); +} +void DisplayListBuilder::setStrokeWidth(SkScalar width) { + push(0, width); +} +void DisplayListBuilder::setMiterLimit(SkScalar limit) { + push(0, limit); +} +void DisplayListBuilder::setColor(SkColor color) { + push(0, color); +} +void DisplayListBuilder::setBlendMode(SkBlendMode mode) { + push(0, mode); +} +void DisplayListBuilder::setFilterQuality(SkFilterQuality quality) { + push(0, quality); +} +void DisplayListBuilder::setShader(sk_sp shader) { + shader // + ? push(0, std::move(shader)) + : push(0); +} +void DisplayListBuilder::setImageFilter(sk_sp filter) { + filter // + ? push(0, std::move(filter)) + : push(0); +} +void DisplayListBuilder::setColorFilter(sk_sp filter) { + filter // + ? push(0, std::move(filter)) + : push(0); +} +void DisplayListBuilder::setMaskFilter(sk_sp filter) { + push(0, std::move(filter)); +} +void DisplayListBuilder::setMaskBlurFilter(SkBlurStyle style, SkScalar sigma) { + switch (style) { + case kNormal_SkBlurStyle: + push(0, sigma); + break; + case kSolid_SkBlurStyle: + push(0, sigma); + break; + case kOuter_SkBlurStyle: + push(0, sigma); + break; + case kInner_SkBlurStyle: + push(0, sigma); + break; + } +} + +void DisplayListBuilder::save() { + saveLevel_++; + push(0); +} +void DisplayListBuilder::restore() { + if (saveLevel_ > 0) { + push(0); + saveLevel_--; + } +} +void DisplayListBuilder::saveLayer(const SkRect* bounds, bool withPaint) { + saveLevel_++; + bounds // + ? push(0, *bounds, withPaint) + : push(0, withPaint); +} + +void DisplayListBuilder::translate(SkScalar tx, SkScalar ty) { + push(0, tx, ty); +} +void DisplayListBuilder::scale(SkScalar sx, SkScalar sy) { + push(0, sx, sy); +} +void DisplayListBuilder::rotate(SkScalar degrees) { + push(0, degrees); +} +void DisplayListBuilder::skew(SkScalar sx, SkScalar sy) { + push(0, sx, sy); +} +void DisplayListBuilder::transform2x3(SkScalar mxx, + SkScalar mxy, + SkScalar mxt, + SkScalar myx, + SkScalar myy, + SkScalar myt) { + push(0, mxx, mxy, mxt, myx, myy, myt); +} +void DisplayListBuilder::transform3x3(SkScalar mxx, + SkScalar mxy, + SkScalar mxt, + SkScalar myx, + SkScalar myy, + SkScalar myt, + SkScalar px, + SkScalar py, + SkScalar pt) { + push(0, mxx, mxy, mxt, myx, myy, myt, px, py, pt); +} + +void DisplayListBuilder::clipRect(const SkRect& rect, + bool isAA, + SkClipOp clip_op) { + push(0, rect, isAA, clip_op); +} +void DisplayListBuilder::clipRRect(const SkRRect& rrect, + bool isAA, + SkClipOp clip_op) { + if (rrect.isRect()) { + clipRect(rrect.rect(), isAA, clip_op); + } else { + push(0, rrect, isAA, clip_op); + } +} +void DisplayListBuilder::clipPath(const SkPath& path, + bool isAA, + SkClipOp clip_op) { + push(0, path, isAA, clip_op); +} + +void DisplayListBuilder::drawPaint() { + push(0); +} +void DisplayListBuilder::drawColor(SkColor color, SkBlendMode mode) { + push(0, color, mode); +} +void DisplayListBuilder::drawLine(const SkPoint& p0, const SkPoint& p1) { + push(0, p0, p1); +} +void DisplayListBuilder::drawRect(const SkRect& rect) { + push(0, rect); +} +void DisplayListBuilder::drawOval(const SkRect& bounds) { + push(0, bounds); +} +void DisplayListBuilder::drawCircle(const SkPoint& center, SkScalar radius) { + push(0, center, radius); +} +void DisplayListBuilder::drawRRect(const SkRRect& rrect) { + if (rrect.isRect()) { + drawRect(rrect.rect()); + } else if (rrect.isOval()) { + drawOval(rrect.rect()); + } else { + push(0, rrect); + } +} +void DisplayListBuilder::drawDRRect(const SkRRect& outer, + const SkRRect& inner) { + push(0, outer, inner); +} +void DisplayListBuilder::drawPath(const SkPath& path) { + push(0, path); +} + +void DisplayListBuilder::drawArc(const SkRect& bounds, + SkScalar start, + SkScalar sweep, + bool useCenter) { + push(0, bounds, start, sweep, useCenter); +} +void DisplayListBuilder::drawPoints(SkCanvas::PointMode mode, + uint32_t count, + const SkPoint pts[]) { + void* data_ptr; + FML_DCHECK(count < MaxDrawPointsCount); + int bytes = count * sizeof(SkPoint); + switch (mode) { + case SkCanvas::PointMode::kPoints_PointMode: + data_ptr = push(bytes, count); + break; + case SkCanvas::PointMode::kLines_PointMode: + data_ptr = push(bytes, count); + break; + case SkCanvas::PointMode::kPolygon_PointMode: + data_ptr = push(bytes, count); + break; + default: + FML_DCHECK(false); + return; + } + copy_v(data_ptr, pts, count); +} +void DisplayListBuilder::drawVertices(const sk_sp vertices, + SkBlendMode mode) { + push(0, std::move(vertices), mode); +} + +void DisplayListBuilder::drawImage(const sk_sp image, + const SkPoint point, + const SkSamplingOptions& sampling) { + push(0, std::move(image), point, sampling); +} +void DisplayListBuilder::drawImageRect(const sk_sp image, + const SkRect& src, + const SkRect& dst, + const SkSamplingOptions& sampling) { + push(0, std::move(image), src, dst, sampling); +} +void DisplayListBuilder::drawImageNine(const sk_sp image, + const SkIRect& center, + const SkRect& dst, + SkFilterMode filter) { + push(0, std::move(image), center, dst, filter); +} +void DisplayListBuilder::drawImageLattice(const sk_sp image, + const SkCanvas::Lattice& lattice, + const SkRect& dst, + SkFilterMode filter) { + int xDivCount = lattice.fXCount; + int yDivCount = lattice.fYCount; + int cellCount = lattice.fRectTypes ? (xDivCount + 1) * (yDivCount + 1) : 0; + size_t bytes = + (xDivCount + yDivCount) * sizeof(int) + + cellCount * (sizeof(SkColor) + sizeof(SkCanvas::Lattice::RectType)); + SkASSERT(lattice.fBounds); + void* pod = this->push(bytes, std::move(image), xDivCount, + yDivCount, cellCount, + *lattice.fBounds, dst, filter); + copy_v(pod, lattice.fXDivs, xDivCount, lattice.fYDivs, yDivCount, + lattice.fColors, cellCount, lattice.fRectTypes, cellCount); +} +void DisplayListBuilder::drawAtlas(const sk_sp atlas, + const SkRSXform xform[], + const SkRect tex[], + const SkColor colors[], + int count, + SkBlendMode mode, + const SkSamplingOptions& sampling, + const SkRect* cullRect) { + int bytes = count * (sizeof(SkRSXform) + sizeof(SkRect)); + void* data_ptr; + if (colors) { + bytes += count * sizeof(SkColor); + if (cullRect) { + data_ptr = push(bytes, std::move(atlas), count, + mode, sampling, *cullRect); + } else { + data_ptr = push(bytes, std::move(atlas), count, mode, + sampling); + } + copy_v(data_ptr, xform, count, tex, count, colors, count); + } else { + if (cullRect) { + data_ptr = push(bytes, std::move(atlas), count, mode, + sampling, *cullRect); + } else { + data_ptr = + push(bytes, std::move(atlas), count, mode, sampling); + } + copy_v(data_ptr, xform, count, tex, count); + } +} + +void DisplayListBuilder::drawPicture(const sk_sp picture) { + push(0, std::move(picture)); +} +void DisplayListBuilder::drawDisplayList( + const sk_sp display_list) { + push(0, std::move(display_list)); +} +void DisplayListBuilder::drawTextBlob(const sk_sp blob, + SkScalar x, + SkScalar y) { + push(0, std::move(blob), x, y); +} +// void DisplayListBuilder::drawShadowRec(const SkPath& path, +// const SkDrawShadowRec& rec) { +// push(0, path, rec); +// } +void DisplayListBuilder::drawShadow(const SkPath& path, + const SkColor color, + const SkScalar elevation, + bool occludes) { + push(0, path, color, elevation, occludes); +} + +} // namespace flutter diff --git a/flow/display_list.h b/flow/display_list.h new file mode 100644 index 0000000000000..7d4e4063f39ea --- /dev/null +++ b/flow/display_list.h @@ -0,0 +1,427 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FLUTTER_FLOW_DISPLAY_LIST_H_ +#define FLUTTER_FLOW_DISPLAY_LIST_H_ + +#include "third_party/skia/include/core/SkBlurTypes.h" +#include "third_party/skia/include/core/SkCanvas.h" +#include "third_party/skia/include/core/SkColorFilter.h" +#include "third_party/skia/include/core/SkImage.h" +#include "third_party/skia/include/core/SkImageFilter.h" +#include "third_party/skia/include/core/SkPicture.h" +#include "third_party/skia/include/core/SkShader.h" +#include "third_party/skia/include/core/SkVertices.h" + +// The Flutter DisplayList mechanism is used in place of the Skia SkPicture +// mechanism. It encapsulates a sequence of rendering operations into a +// persistent list that can be replayed upon request by calling its +// dispatch() method with a Dispatcher object that responds to the various +// rendering methods encapsulated therein. The mechanism is inspired by +// the SkLiteDL class that is not directly supported by Skia, but has been +// recommended as a basis for custom display lists for a number of their +// customers. +// +// This file contains the definitions for: +// DisplayList: the base class that holds the information about the +// sequence of operations and can dispatch them to a Dispatcher +// Dispatcher: a pure virtual interface which can be implemented to field +// the requests for purposes such as sending them to an SkCanvas +// or detecting various rendering optimization scenarios +// DisplayListBuilder: a class for constructing a DisplayList from the same +// calls defined in the Dispatcher +// +// Other files include various class definitions for dealing with display +// lists, such as: +// display_list_canvas.h: classes to interact between SkCanvas and DisplayList +// (SkCanvas->DisplayList adapter and vice versa) +// +// display_list_utils.h: various utility classes to ease implementing +// a Dispatcher, including NOP implementations of +// the attribute, clip, and transform methods, +// classes to track attributes, clips, and transforms +// and a class to compute the bounds of a DisplayList +// Any class implementing Dispatcher can inherit from +// these utility classes to simplify its creation + +namespace flutter { + +#define FOR_EACH_DISPLAY_LIST_OP(V) \ + V(SetAA) \ + V(SetDither) \ + V(SetInvertColors) \ + \ + V(SetCaps) \ + V(SetJoins) \ + \ + V(SetDrawStyle) \ + V(SetStrokeWidth) \ + V(SetMiterLimit) \ + \ + V(SetColor) \ + V(SetBlendMode) \ + \ + V(SetFilterQuality) \ + \ + V(SetShader) \ + V(ClearShader) \ + V(SetColorFilter) \ + V(ClearColorFilter) \ + V(SetImageFilter) \ + V(ClearImageFilter) \ + \ + V(ClearMaskFilter) \ + V(SetMaskFilter) \ + V(SetMaskBlurFilterNormal) \ + V(SetMaskBlurFilterSolid) \ + V(SetMaskBlurFilterOuter) \ + V(SetMaskBlurFilterInner) \ + \ + V(Save) \ + V(SaveLayer) \ + V(SaveLayerBounds) \ + V(Restore) \ + \ + V(Translate) \ + V(Scale) \ + V(Rotate) \ + V(Skew) \ + V(Transform2x3) \ + V(Transform3x3) \ + \ + V(ClipRect) \ + V(ClipRRect) \ + V(ClipPath) \ + \ + V(DrawPaint) \ + V(DrawColor) \ + \ + V(DrawLine) \ + V(DrawRect) \ + V(DrawOval) \ + V(DrawCircle) \ + V(DrawRRect) \ + V(DrawDRRect) \ + V(DrawArc) \ + V(DrawPath) \ + \ + V(DrawPoints) \ + V(DrawLines) \ + V(DrawPolygon) \ + V(DrawVertices) \ + \ + V(DrawImage) \ + V(DrawImageRect) \ + V(DrawImageNine) \ + V(DrawImageLattice) \ + V(DrawAtlas) \ + V(DrawAtlasColored) \ + V(DrawAtlasCulled) \ + V(DrawAtlasColoredCulled) \ + \ + V(DrawSkPicture) \ + V(DrawDisplayList) \ + V(DrawTextBlob) \ + /* V(DrawShadowRec) */ \ + \ + V(DrawShadow) + +#define DL_OP_TO_ENUM_VALUE(name) name, +enum DisplayListOpType { FOR_EACH_DISPLAY_LIST_OP(DL_OP_TO_ENUM_VALUE) }; +#undef DL_OP_TO_ENUM_VALUE + +class Dispatcher; +class DisplayListBuilder; + +// 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(). +class DisplayList : public SkRefCnt { + public: + static const SkSamplingOptions NearestSampling; + static const SkSamplingOptions LinearSampling; + static const SkSamplingOptions MipmapSampling; + static const SkSamplingOptions CubicSampling; + + DisplayList() + : ptr_(nullptr), + used_(0), + opCount_(0), + uniqueID_(0), + bounds_({0, 0, 0, 0}), + boundsCull_({0, 0, 0, 0}) {} + + ~DisplayList(); + + void dispatch(Dispatcher& ctx) const { dispatch(ctx, ptr_, ptr_ + used_); } + + void renderTo(SkCanvas* canvas) const; + + size_t bytes() const { return used_; } + int opCount() const { return opCount_; } + uint32_t uniqueID() const { return uniqueID_; } + + const SkRect& bounds() { + if (bounds_.width() < 0.0) { + // computeBounds() will leave the variable with a + // non-negative width and height + computeBounds(); + } + return bounds_; + } + + bool equals(const DisplayList& other) const; + + private: + DisplayList(uint8_t* ptr, size_t used, int opCount, const SkRect& cullRect); + + uint8_t* ptr_; + size_t used_; + int opCount_; + + uint32_t uniqueID_; + SkRect bounds_; + + // Only used for drawPaint() and drawColor() + SkRect boundsCull_; + + void computeBounds(); + void dispatch(Dispatcher& ctx, uint8_t* ptr, uint8_t* end) const; + + friend class DisplayListBuilder; +}; + +// The pure virtual interface for interacting with a display list. +// This interface represents the methods used to build a list +// through the DisplayListBuilder and also the methods that will +// be invoked through the DisplayList::dispatch() method. +class Dispatcher { + public: + // MaxDrawPointsCount * sizeof(SkPoint) must be less than 1 << 32 + static constexpr int MaxDrawPointsCount = ((1 << 29) - 1); + + virtual void setAA(bool aa) = 0; + virtual void setDither(bool dither) = 0; + virtual void setInvertColors(bool invert) = 0; + virtual void setCaps(SkPaint::Cap cap) = 0; + virtual void setJoins(SkPaint::Join join) = 0; + virtual void setDrawStyle(SkPaint::Style style) = 0; + virtual void setStrokeWidth(SkScalar width) = 0; + virtual void setMiterLimit(SkScalar limit) = 0; + virtual void setColor(SkColor color) = 0; + virtual void setBlendMode(SkBlendMode mode) = 0; + virtual void setFilterQuality(SkFilterQuality quality) = 0; + virtual void setShader(const sk_sp shader) = 0; + virtual void setImageFilter(const sk_sp filter) = 0; + virtual void setColorFilter(const sk_sp filter) = 0; + virtual void setMaskFilter(const sk_sp filter) = 0; + virtual void setMaskBlurFilter(SkBlurStyle style, SkScalar sigma) = 0; + + virtual void save() = 0; + virtual void restore() = 0; + virtual void saveLayer(const SkRect* bounds, bool restoreWithPaint) = 0; + + virtual void translate(SkScalar tx, SkScalar ty) = 0; + virtual void scale(SkScalar sx, SkScalar sy) = 0; + virtual void rotate(SkScalar degrees) = 0; + virtual void skew(SkScalar sx, SkScalar sy) = 0; + virtual void transform2x3(SkScalar mxx, + SkScalar mxy, + SkScalar mxt, + SkScalar myx, + SkScalar myy, + SkScalar myt) = 0; + virtual void transform3x3(SkScalar mxx, + SkScalar mxy, + SkScalar mxt, + SkScalar myx, + SkScalar myy, + SkScalar myt, + SkScalar px, + SkScalar py, + SkScalar pt) = 0; + + virtual void clipRect(const SkRect& rect, bool isAA, SkClipOp clip_op) = 0; + virtual void clipRRect(const SkRRect& rrect, bool isAA, SkClipOp clip_op) = 0; + virtual void clipPath(const SkPath& path, bool isAA, SkClipOp clip_op) = 0; + + virtual void drawPaint() = 0; + virtual void drawColor(SkColor color, SkBlendMode mode) = 0; + virtual void drawLine(const SkPoint& p0, const SkPoint& p1) = 0; + virtual void drawRect(const SkRect& rect) = 0; + virtual void drawOval(const SkRect& bounds) = 0; + virtual void drawCircle(const SkPoint& center, SkScalar radius) = 0; + virtual void drawRRect(const SkRRect& rrect) = 0; + virtual void drawDRRect(const SkRRect& outer, const SkRRect& inner) = 0; + virtual void drawPath(const SkPath& path) = 0; + virtual void drawArc(const SkRect& bounds, + SkScalar start, + SkScalar sweep, + bool useCenter) = 0; + virtual void drawPoints(SkCanvas::PointMode mode, + uint32_t count, + const SkPoint pts[]) = 0; + virtual void drawVertices(const sk_sp vertices, + SkBlendMode mode) = 0; + virtual void drawImage(const sk_sp image, + const SkPoint point, + const SkSamplingOptions& sampling) = 0; + virtual void drawImageRect(const sk_sp image, + const SkRect& src, + const SkRect& dst, + const SkSamplingOptions& sampling) = 0; + virtual void drawImageNine(const sk_sp image, + const SkIRect& center, + const SkRect& dst, + SkFilterMode filter) = 0; + virtual void drawImageLattice(const sk_sp image, + const SkCanvas::Lattice& lattice, + const SkRect& dst, + SkFilterMode filter) = 0; + virtual void drawAtlas(const sk_sp atlas, + const SkRSXform xform[], + const SkRect tex[], + const SkColor colors[], + int count, + SkBlendMode mode, + const SkSamplingOptions& sampling, + const SkRect* cullRect) = 0; + virtual void drawPicture(const sk_sp picture) = 0; + virtual void drawDisplayList(const sk_sp display_list) = 0; + virtual void drawTextBlob(const sk_sp blob, + SkScalar x, + SkScalar y) = 0; + // Unfortunately SkDrawShadowRec requires including an internal Skia header + // virtual void drawShadowRec(const SkPath&, const SkDrawShadowRec&) = 0; + virtual void drawShadow(const SkPath& path, + const SkColor color, + const SkScalar elevation, + bool occludes) = 0; +}; + +// The primary class used to build a display list. The list of methods +// here matches the list of methods invoked during dispatch(). +// If there is some code that already renders to an SkCanvas object, +// those rendering commands can be captured into a DisplayList using +// the DisplayListCanvasRecorder class. +class DisplayListBuilder final : public virtual Dispatcher, public SkRefCnt { + public: + DisplayListBuilder(const SkRect& cull = SkRect::MakeEmpty()); + ~DisplayListBuilder(); + + void setAA(bool aa) override; + void setDither(bool dither) override; + void setInvertColors(bool invert) override; + void setCaps(SkPaint::Cap cap) override; + void setJoins(SkPaint::Join join) override; + void setDrawStyle(SkPaint::Style style) override; + void setStrokeWidth(SkScalar width) override; + void setMiterLimit(SkScalar limit) override; + void setColor(SkColor color) override; + void setBlendMode(SkBlendMode mode) override; + void setFilterQuality(SkFilterQuality quality) override; + void setShader(sk_sp shader) override; + void setImageFilter(sk_sp filter) override; + void setColorFilter(sk_sp filter) override; + void setMaskFilter(sk_sp filter) override; + void setMaskBlurFilter(SkBlurStyle style, SkScalar sigma) override; + + void save() override; + void restore() override; + void saveLayer(const SkRect* bounds, bool restoreWithPaint) override; + + 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 transform2x3(SkScalar mxx, + SkScalar mxy, + SkScalar mxt, + SkScalar myx, + SkScalar myy, + SkScalar myt) override; + void transform3x3(SkScalar mxx, + SkScalar mxy, + SkScalar mxt, + SkScalar myx, + SkScalar myy, + SkScalar myt, + SkScalar px, + SkScalar py, + SkScalar pt) override; + + void clipRect(const SkRect& rect, bool isAA, SkClipOp clip_op) override; + void clipRRect(const SkRRect& rrect, bool isAA, SkClipOp clip_op) override; + void clipPath(const SkPath& path, bool isAA, SkClipOp clip_op) override; + + void drawPaint() override; + void drawColor(SkColor color, SkBlendMode mode) 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; + void drawArc(const SkRect& bounds, + SkScalar start, + SkScalar sweep, + bool useCenter) override; + void drawPoints(SkCanvas::PointMode mode, + uint32_t count, + const SkPoint pts[]) override; + void drawVertices(const sk_sp vertices, + SkBlendMode mode) override; + void drawImage(const sk_sp image, + const SkPoint point, + const SkSamplingOptions& sampling) override; + void drawImageRect(const sk_sp image, + const SkRect& src, + const SkRect& dst, + const SkSamplingOptions& sampling) override; + void drawImageNine(const sk_sp image, + const SkIRect& center, + const SkRect& dst, + SkFilterMode filter) override; + void drawImageLattice(const sk_sp image, + const SkCanvas::Lattice& lattice, + const SkRect& dst, + SkFilterMode filter) override; + void drawAtlas(const sk_sp atlas, + const SkRSXform xform[], + const SkRect tex[], + const SkColor colors[], + int count, + SkBlendMode mode, + const SkSamplingOptions& sampling, + const SkRect* cullRect) override; + void drawPicture(const sk_sp picture) override; + void drawDisplayList(const sk_sp display_list) override; + void drawTextBlob(const sk_sp blob, + SkScalar x, + SkScalar y) override; + // void drawShadowRec(const SkPath&, const SkDrawShadowRec&) override; + void drawShadow(const SkPath& path, + const SkColor color, + const SkScalar elevation, + bool occludes) override; + + sk_sp build(); + + private: + SkAutoTMalloc storage_; + size_t used_ = 0; + size_t allocated_ = 0; + int opCount_ = 0; + int saveLevel_ = 0; + + SkRect cull_; + + template + void* push(size_t extra, Args&&... args); +}; + +} // namespace flutter + +#endif // FLUTTER_FLOW_DISPLAY_LIST_H_ diff --git a/flow/display_list_canvas.cc b/flow/display_list_canvas.cc new file mode 100644 index 0000000000000..7749f7ca17d30 --- /dev/null +++ b/flow/display_list_canvas.cc @@ -0,0 +1,470 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "flutter/flow/display_list_canvas.h" + +#include "flutter/flow/layers/physical_shape_layer.h" + +#include "third_party/skia/include/core/SkMaskFilter.h" +#include "third_party/skia/include/core/SkTextBlob.h" + +// This header file cannot be included here, but we cannot +// record calls made by the SkShadowUtils without it. +// #include "third_party/skia/src/core/SkDrawShadowInfo.h" + +namespace flutter { + +void DisplayListCanvasDispatcher::save() { + canvas_->save(); +} +void DisplayListCanvasDispatcher::restore() { + canvas_->restore(); +} +void DisplayListCanvasDispatcher::saveLayer(const SkRect* bounds, + bool restoreWithPaint) { + canvas_->saveLayer(bounds, restoreWithPaint ? &paint() : nullptr); +} + +void DisplayListCanvasDispatcher::translate(SkScalar tx, SkScalar ty) { + canvas_->translate(tx, ty); +} +void DisplayListCanvasDispatcher::scale(SkScalar sx, SkScalar sy) { + canvas_->scale(sx, sy); +} +void DisplayListCanvasDispatcher::rotate(SkScalar degrees) { + canvas_->rotate(degrees); +} +void DisplayListCanvasDispatcher::skew(SkScalar sx, SkScalar sy) { + canvas_->skew(sx, sy); +} +void DisplayListCanvasDispatcher::transform2x3(SkScalar mxx, + SkScalar mxy, + SkScalar mxt, + SkScalar myx, + SkScalar myy, + SkScalar myt) { + canvas_->concat(SkMatrix::MakeAll(mxx, mxy, mxt, myx, myy, myt, 0, 0, 1)); +} +void DisplayListCanvasDispatcher::transform3x3(SkScalar mxx, + SkScalar mxy, + SkScalar mxt, + SkScalar myx, + SkScalar myy, + SkScalar myt, + SkScalar px, + SkScalar py, + SkScalar pt) { + canvas_->concat(SkMatrix::MakeAll(mxx, mxy, mxt, myx, myy, myt, px, py, pt)); +} + +void DisplayListCanvasDispatcher::clipRect(const SkRect& rect, + bool isAA, + SkClipOp clip_op) { + canvas_->clipRect(rect, clip_op, isAA); +} +void DisplayListCanvasDispatcher::clipRRect(const SkRRect& rrect, + bool isAA, + SkClipOp clip_op) { + canvas_->clipRRect(rrect, isAA); +} +void DisplayListCanvasDispatcher::clipPath(const SkPath& path, + bool isAA, + SkClipOp clip_op) { + canvas_->clipPath(path, isAA); +} + +void DisplayListCanvasDispatcher::drawPaint() { + canvas_->drawPaint(paint()); +} +void DisplayListCanvasDispatcher::drawColor(SkColor color, SkBlendMode mode) { + canvas_->drawColor(color, mode); +} +void DisplayListCanvasDispatcher::drawLine(const SkPoint& p0, + const SkPoint& p1) { + canvas_->drawLine(p0, p1, paint()); +} +void DisplayListCanvasDispatcher::drawRect(const SkRect& rect) { + canvas_->drawRect(rect, paint()); +} +void DisplayListCanvasDispatcher::drawOval(const SkRect& bounds) { + canvas_->drawOval(bounds, paint()); +} +void DisplayListCanvasDispatcher::drawCircle(const SkPoint& center, + SkScalar radius) { + canvas_->drawCircle(center, radius, paint()); +} +void DisplayListCanvasDispatcher::drawRRect(const SkRRect& rrect) { + canvas_->drawRRect(rrect, paint()); +} +void DisplayListCanvasDispatcher::drawDRRect(const SkRRect& outer, + const SkRRect& inner) { + canvas_->drawDRRect(outer, inner, paint()); +} +void DisplayListCanvasDispatcher::drawPath(const SkPath& path) { + canvas_->drawPath(path, paint()); +} +void DisplayListCanvasDispatcher::drawArc(const SkRect& bounds, + SkScalar start, + SkScalar sweep, + bool useCenter) { + canvas_->drawArc(bounds, start, sweep, useCenter, paint()); +} +void DisplayListCanvasDispatcher::drawPoints(SkCanvas::PointMode mode, + uint32_t count, + const SkPoint pts[]) { + canvas_->drawPoints(mode, count, pts, paint()); +} +void DisplayListCanvasDispatcher::drawVertices(const sk_sp vertices, + SkBlendMode mode) { + canvas_->drawVertices(vertices, mode, paint()); +} +void DisplayListCanvasDispatcher::drawImage(const sk_sp image, + const SkPoint point, + const SkSamplingOptions& sampling) { + canvas_->drawImage(image, point.fX, point.fY, sampling, &paint()); +} +void DisplayListCanvasDispatcher::drawImageRect( + const sk_sp image, + const SkRect& src, + const SkRect& dst, + const SkSamplingOptions& sampling) { + canvas_->drawImageRect(image, src, dst, sampling, &paint(), + SkCanvas::kFast_SrcRectConstraint); +} +void DisplayListCanvasDispatcher::drawImageNine(const sk_sp image, + const SkIRect& center, + const SkRect& dst, + SkFilterMode filter) { + canvas_->drawImageNine(image.get(), center, dst, filter, &paint()); +} +void DisplayListCanvasDispatcher::drawImageLattice( + const sk_sp image, + const SkCanvas::Lattice& lattice, + const SkRect& dst, + SkFilterMode filter) { + canvas_->drawImageLattice(image.get(), lattice, dst, filter, &paint()); +} +void DisplayListCanvasDispatcher::drawAtlas(const sk_sp atlas, + const SkRSXform xform[], + const SkRect tex[], + const SkColor colors[], + int count, + SkBlendMode mode, + const SkSamplingOptions& sampling, + const SkRect* cullRect) { + canvas_->drawAtlas(atlas.get(), xform, tex, colors, count, mode, sampling, + cullRect, &paint()); +} +void DisplayListCanvasDispatcher::drawPicture(const sk_sp picture) { + canvas_->drawPicture(picture); +} +void DisplayListCanvasDispatcher::drawDisplayList( + const sk_sp display_list) { + int save_count = canvas_->save(); + { + DisplayListCanvasDispatcher dispatcher(canvas_); + display_list->dispatch(dispatcher); + } + canvas_->restoreToCount(save_count); +} +void DisplayListCanvasDispatcher::drawTextBlob(const sk_sp blob, + SkScalar x, + SkScalar y) { + canvas_->drawTextBlob(blob, x, y, paint()); +} +// void DisplayListCanvasDispatcher::drawShadowRec(const SkPath& path, +// const SkDrawShadowRec& rec) { +// canvas_->private_draw_shadow_rec(path, rec); +// } +void DisplayListCanvasDispatcher::drawShadow(const SkPath& path, + const SkColor color, + const SkScalar elevation, + bool occludes) { + flutter::PhysicalShapeLayer::DrawShadow(canvas_, path, color, elevation, + occludes, 1.0); +} + +DisplayListCanvasRecorder::DisplayListCanvasRecorder(const SkRect& bounds) + : SkCanvasVirtualEnforcer(bounds.width(), bounds.height()), + builder_(sk_make_sp(bounds)) {} + +sk_sp DisplayListCanvasRecorder::build() { + sk_sp display_list = builder_->build(); + builder_.reset(); + return display_list; +} + +void DisplayListCanvasRecorder::didConcat44(const SkM44& m44) { + SkMatrix m = m44.asM33(); + if (m.hasPerspective()) { + builder_->transform3x3(m[0], m[1], m[2], m[3], m[4], m[5], m[6], m[7], + m[8]); + } else { + builder_->transform2x3(m[0], m[1], m[2], m[3], m[4], m[5]); + } +} +void DisplayListCanvasRecorder::didTranslate(SkScalar tx, SkScalar ty) { + builder_->translate(tx, ty); +} +void DisplayListCanvasRecorder::didScale(SkScalar sx, SkScalar sy) { + builder_->scale(sx, sy); +} + +void DisplayListCanvasRecorder::onClipRect(const SkRect& rect, + SkClipOp clip_op, + ClipEdgeStyle edgeStyle) { + builder_->clipRect(rect, edgeStyle == ClipEdgeStyle::kSoft_ClipEdgeStyle, + clip_op); +} +void DisplayListCanvasRecorder::onClipRRect(const SkRRect& rrect, + SkClipOp clip_op, + ClipEdgeStyle edgeStyle) { + builder_->clipRRect(rrect, edgeStyle == ClipEdgeStyle::kSoft_ClipEdgeStyle, + clip_op); +} +void DisplayListCanvasRecorder::onClipPath(const SkPath& path, + SkClipOp clip_op, + ClipEdgeStyle edgeStyle) { + builder_->clipPath(path, edgeStyle == ClipEdgeStyle::kSoft_ClipEdgeStyle, + clip_op); +} + +void DisplayListCanvasRecorder::willSave() { + builder_->save(); +} +SkCanvas::SaveLayerStrategy DisplayListCanvasRecorder::getSaveLayerStrategy( + const SaveLayerRec& rec) { + if (rec.fPaint) { + recordPaintAttributes(rec.fPaint, saveLayerOp); + builder_->saveLayer(rec.fBounds, true); + } else { + builder_->saveLayer(rec.fBounds, false); + } + return SaveLayerStrategy::kNoLayer_SaveLayerStrategy; +} +void DisplayListCanvasRecorder::didRestore() { + builder_->restore(); +} + +void DisplayListCanvasRecorder::onDrawPaint(const SkPaint& paint) { + recordPaintAttributes(&paint, fillOp); + builder_->drawPaint(); +} +void DisplayListCanvasRecorder::onDrawRect(const SkRect& rect, + const SkPaint& paint) { + recordPaintAttributes(&paint, drawOp); + builder_->drawRect(rect); +} +void DisplayListCanvasRecorder::onDrawRRect(const SkRRect& rrect, + const SkPaint& paint) { + recordPaintAttributes(&paint, drawOp); + builder_->drawRRect(rrect); +} +void DisplayListCanvasRecorder::onDrawDRRect(const SkRRect& outer, + const SkRRect& inner, + const SkPaint& paint) { + recordPaintAttributes(&paint, drawOp); + builder_->drawDRRect(outer, inner); +} +void DisplayListCanvasRecorder::onDrawOval(const SkRect& rect, + const SkPaint& paint) { + recordPaintAttributes(&paint, drawOp); + builder_->drawOval(rect); +} +void DisplayListCanvasRecorder::onDrawArc(const SkRect& rect, + SkScalar startAngle, + SkScalar sweepAngle, + bool useCenter, + const SkPaint& paint) { + recordPaintAttributes(&paint, drawOp); + builder_->drawArc(rect, startAngle, sweepAngle, useCenter); +} +void DisplayListCanvasRecorder::onDrawPath(const SkPath& path, + const SkPaint& paint) { + recordPaintAttributes(&paint, drawOp); + builder_->drawPath(path); +} + +void DisplayListCanvasRecorder::onDrawPoints(SkCanvas::PointMode mode, + size_t count, + const SkPoint pts[], + const SkPaint& paint) { + recordPaintAttributes(&paint, strokeOp); + if (mode == SkCanvas::PointMode::kLines_PointMode && count == 2) { + builder_->drawLine(pts[0], pts[1]); + } else { + uint32_t count32 = static_cast(count); + FML_DCHECK(count32 == count); + builder_->drawPoints(mode, count32, pts); + } +} +void DisplayListCanvasRecorder::onDrawVerticesObject(const SkVertices* vertices, + SkBlendMode mode, + const SkPaint& paint) { + recordPaintAttributes(&paint, drawOp); + builder_->drawVertices(sk_ref_sp(vertices), mode); +} + +void DisplayListCanvasRecorder::onDrawImage2(const SkImage* image, + SkScalar dx, + SkScalar dy, + const SkSamplingOptions& sampling, + const SkPaint* paint) { + recordPaintAttributes(paint, imageOp); + builder_->drawImage(sk_ref_sp(image), SkPoint::Make(dx, dy), sampling); +} +void DisplayListCanvasRecorder::onDrawImageRect2( + const SkImage* image, + const SkRect& src, + const SkRect& dst, + const SkSamplingOptions& sampling, + const SkPaint* paint, + SrcRectConstraint constraint) { + FML_DCHECK(constraint == SrcRectConstraint::kFast_SrcRectConstraint); + recordPaintAttributes(paint, imageRectOp); + builder_->drawImageRect(sk_ref_sp(image), src, dst, sampling); +} +void DisplayListCanvasRecorder::onDrawImageLattice2(const SkImage* image, + const Lattice& lattice, + const SkRect& dst, + SkFilterMode filter, + const SkPaint* paint) { + recordPaintAttributes(paint, imageOp); + builder_->drawImageLattice(sk_ref_sp(image), lattice, dst, filter); +} +void DisplayListCanvasRecorder::onDrawAtlas2(const SkImage* image, + const SkRSXform xform[], + const SkRect src[], + const SkColor colors[], + int count, + SkBlendMode mode, + const SkSamplingOptions& sampling, + const SkRect* cull, + const SkPaint* paint) { + recordPaintAttributes(paint, imageOp); + builder_->drawAtlas(sk_ref_sp(image), xform, src, colors, count, mode, + sampling, cull); +} + +void DisplayListCanvasRecorder::onDrawTextBlob(const SkTextBlob* blob, + SkScalar x, + SkScalar y, + const SkPaint& paint) { + recordPaintAttributes(&paint, drawOp); + builder_->drawTextBlob(sk_ref_sp(blob), x, y); +} +void DisplayListCanvasRecorder::onDrawShadowRec(const SkPath& path, + const SkDrawShadowRec& rec) { + // builder_->drawShadowRec(path, rec); + FML_DCHECK(false); +} + +void DisplayListCanvasRecorder::onDrawPicture(const SkPicture* picture, + const SkMatrix* matrix, + const SkPaint* paint) { + FML_DCHECK(matrix == nullptr); + FML_DCHECK(paint == nullptr); + builder_->drawPicture(sk_ref_sp(picture)); +} + +const SkPaint DisplayListCanvasRecorder::defaultPaint; + +void DisplayListCanvasRecorder::recordPaintAttributes(const SkPaint* paint, + DrawType type) { + int dataNeeded; + switch (type) { + case drawOp: + dataNeeded = drawMask_; + break; + case fillOp: + dataNeeded = paintMask_; + break; + case strokeOp: + dataNeeded = strokeMask_; + break; + case imageOp: + dataNeeded = imageMask_; + break; + case imageRectOp: + dataNeeded = imageRectMask_; + break; + default: + FML_DCHECK(false); + return; + } + if (paint == nullptr) { + paint = &defaultPaint; + } + if ((dataNeeded & aaNeeded_) != 0 && currentAA_ != paint->isAntiAlias()) { + builder_->setAA(currentAA_ = paint->isAntiAlias()); + } + if ((dataNeeded & ditherNeeded_) != 0 && + currentDither_ != paint->isDither()) { + builder_->setDither(currentDither_ = paint->isDither()); + } + if ((dataNeeded & colorNeeded_) != 0 && currentColor_ != paint->getColor()) { + builder_->setColor(currentColor_ = paint->getColor()); + } + if ((dataNeeded & blendNeeded_) != 0 && + currentBlendMode_ != paint->getBlendMode()) { + builder_->setBlendMode(currentBlendMode_ = paint->getBlendMode()); + } + // invert colors is a Flutter::Paint thing, not an SkPaint thing + // if ((dataNeeded & invertColorsNeeded_) != 0 && + // currentInvertColors_ != paint->???) { + // currentInvertColors_ = paint->invertColors; + // addOp_(currentInvertColors_ + // ? _CanvasOp.setInvertColors + // : _CanvasOp.clearInvertColors, 0); + // } + if ((dataNeeded & paintStyleNeeded_) != 0) { + if (currentPaintStyle_ != paint->getStyle()) { + FML_DCHECK(paint->getStyle() != SkPaint::kStrokeAndFill_Style); + builder_->setDrawStyle(currentPaintStyle_ = paint->getStyle()); + } + if (currentPaintStyle_ == SkPaint::Style::kStroke_Style) { + dataNeeded |= strokeStyleNeeded_; + } + } + if ((dataNeeded & strokeStyleNeeded_) != 0) { + if (currentStrokeWidth_ != paint->getStrokeWidth()) { + builder_->setStrokeWidth(currentStrokeWidth_ = paint->getStrokeWidth()); + } + if (currentStrokeCap_ != paint->getStrokeCap()) { + builder_->setCaps(currentStrokeCap_ = paint->getStrokeCap()); + } + if (currentStrokeJoin_ != paint->getStrokeJoin()) { + builder_->setJoins(currentStrokeJoin_ = paint->getStrokeJoin()); + } + if (currentMiterLimit_ != paint->getStrokeMiter()) { + builder_->setMiterLimit(currentMiterLimit_ = paint->getStrokeMiter()); + } + } + if ((dataNeeded & filterQualityNeeded_) != 0 && + currentFilterQuality_ != paint->getFilterQuality()) { + builder_->setFilterQuality(currentFilterQuality_ = + paint->getFilterQuality()); + } + if ((dataNeeded & shaderNeeded_) != 0 && + currentShader_.get() != paint->getShader()) { + builder_->setShader(currentShader_ = sk_ref_sp(paint->getShader())); + } + if ((dataNeeded & colorFilterNeeded_) != 0 && + currentColorFilter_.get() != paint->getColorFilter()) { + builder_->setColorFilter(currentColorFilter_ = + sk_ref_sp(paint->getColorFilter())); + } + if ((dataNeeded & imageFilterNeeded_) != 0 && + currentImageFilter_.get() != paint->getImageFilter()) { + builder_->setImageFilter(currentImageFilter_ = + sk_ref_sp(paint->getImageFilter())); + } + if ((dataNeeded & maskFilterNeeded_) != 0 && + currentMaskFilter_.get() != paint->getMaskFilter()) { + builder_->setMaskFilter(currentMaskFilter_ = + sk_ref_sp(paint->getMaskFilter())); + } +} + +} // namespace flutter diff --git a/flow/display_list_canvas.h b/flow/display_list_canvas.h new file mode 100644 index 0000000000000..d0207ae120bde --- /dev/null +++ b/flow/display_list_canvas.h @@ -0,0 +1,311 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FLUTTER_FLOW_DISPLAY_LIST_CANVAS_H_ +#define FLUTTER_FLOW_DISPLAY_LIST_CANVAS_H_ + +#include "flutter/flow/display_list.h" +#include "flutter/flow/display_list_utils.h" +#include "flutter/fml/logging.h" + +#include "third_party/skia/include/core/SkCanvasVirtualEnforcer.h" +#include "third_party/skia/include/utils/SkNoDrawCanvas.h" + +// Classes to interact between SkCanvas and DisplayList, including: +// DisplayListCanvasDispatcher: +// Can be fed to the dispatch() method of a DisplayList to feed +// the resulting rendering operations to an SkCanvas instance. +// DisplayListCanvasRecorder +// An adapter that implements an SkCanvas interface which can +// then be handed to code that outputs to an SkCanvas to capture +// the output into a Flutter DisplayList. + +namespace flutter { + +// Receives all methods on Dispatcher and sends them to an SkCanvas +class DisplayListCanvasDispatcher : public virtual Dispatcher, + public SkPaintDispatchHelper { + public: + DisplayListCanvasDispatcher(SkCanvas* canvas) : canvas_(canvas) {} + + void save() override; + void restore() override; + void saveLayer(const SkRect* bounds, bool restoreWithBounds) override; + + 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 transform2x3(SkScalar mxx, + SkScalar mxy, + SkScalar mxt, + SkScalar myx, + SkScalar myy, + SkScalar myt) override; + void transform3x3(SkScalar mxx, + SkScalar mxy, + SkScalar mxt, + SkScalar myx, + SkScalar myy, + SkScalar myt, + SkScalar px, + SkScalar py, + SkScalar pt) override; + + void clipRect(const SkRect& rect, bool isAA, SkClipOp clip_op) override; + void clipRRect(const SkRRect& rrect, bool isAA, SkClipOp clip_op) override; + void clipPath(const SkPath& path, bool isAA, SkClipOp clip_op) override; + + void drawPaint() override; + void drawColor(SkColor color, SkBlendMode mode) 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; + void drawArc(const SkRect& bounds, + SkScalar start, + SkScalar sweep, + bool useCenter) override; + void drawPoints(SkCanvas::PointMode mode, + uint32_t count, + const SkPoint pts[]) override; + void drawVertices(const sk_sp vertices, + SkBlendMode mode) override; + void drawImage(const sk_sp image, + const SkPoint point, + const SkSamplingOptions& sampling) override; + void drawImageRect(const sk_sp image, + const SkRect& src, + const SkRect& dst, + const SkSamplingOptions& sampling) override; + void drawImageNine(const sk_sp image, + const SkIRect& center, + const SkRect& dst, + SkFilterMode filter) override; + void drawImageLattice(const sk_sp image, + const SkCanvas::Lattice& lattice, + const SkRect& dst, + SkFilterMode filter) override; + void drawAtlas(const sk_sp atlas, + const SkRSXform xform[], + const SkRect tex[], + const SkColor colors[], + int count, + SkBlendMode mode, + const SkSamplingOptions& sampling, + const SkRect* cullRect) override; + void drawPicture(const sk_sp picture) override; + void drawDisplayList(const sk_sp display_list) override; + void drawTextBlob(const sk_sp blob, + SkScalar x, + SkScalar y) override; + // void drawShadowRec(const SkPath&, const SkDrawShadowRec&) override; + void drawShadow(const SkPath& path, + const SkColor color, + const SkScalar elevation, + bool occludes) override; + + private: + SkCanvas* canvas_; +}; + +// Receives all methods on SkCanvas and sends them to a DisplayListBuilder +class DisplayListCanvasRecorder + : public SkCanvasVirtualEnforcer, + public SkRefCnt { + public: + DisplayListCanvasRecorder(const SkRect& bounds); + + const sk_sp builder() { return builder_; } + + sk_sp build(); + + void didConcat44(const SkM44&) override; + void didSetM44(const SkM44&) override { FML_DCHECK(false); } + void didTranslate(SkScalar, SkScalar) override; + void didScale(SkScalar, SkScalar) override; + + void onClipRect(const SkRect& rect, + SkClipOp op, + ClipEdgeStyle edgeStyle) override; + void onClipRRect(const SkRRect& rrect, + SkClipOp op, + ClipEdgeStyle edgeStyle) override; + void onClipPath(const SkPath& path, + SkClipOp op, + ClipEdgeStyle edgeStyle) override; + + void willSave() override; + SaveLayerStrategy getSaveLayerStrategy(const SaveLayerRec&) override; + void didRestore() override; + + void onDrawPaint(const SkPaint& paint) override; + void onDrawBehind(const SkPaint&) override { FML_DCHECK(false); } + void onDrawRect(const SkRect& rect, const SkPaint& paint) override; + void onDrawRRect(const SkRRect& rrect, const SkPaint& paint) override; + void onDrawDRRect(const SkRRect& outer, + const SkRRect& inner, + const SkPaint& paint) override; + void onDrawOval(const SkRect& rect, const SkPaint& paint) override; + void onDrawArc(const SkRect& rect, + SkScalar startAngle, + SkScalar sweepAngle, + bool useCenter, + const SkPaint& paint) override; + void onDrawPath(const SkPath& path, const SkPaint& paint) override; + void onDrawRegion(const SkRegion& region, const SkPaint& paint) override { + FML_DCHECK(false); + } + + void onDrawTextBlob(const SkTextBlob* blob, + SkScalar x, + SkScalar y, + const SkPaint& paint) override; + + void onDrawPatch(const SkPoint cubics[12], + const SkColor colors[4], + const SkPoint texCoords[4], + SkBlendMode mode, + const SkPaint& paint) override { + FML_DCHECK(false); + } + void onDrawPoints(SkCanvas::PointMode mode, + size_t count, + const SkPoint pts[], + const SkPaint& paint) override; + void onDrawVerticesObject(const SkVertices* vertices, + SkBlendMode mode, + const SkPaint& paint) override; + + void onDrawImage2(const SkImage*, + SkScalar dx, + SkScalar dy, + const SkSamplingOptions&, + const SkPaint*) override; + void onDrawImageRect2(const SkImage*, + const SkRect& src, + const SkRect& dst, + const SkSamplingOptions&, + const SkPaint*, + SrcRectConstraint) override; + void onDrawImageLattice2(const SkImage*, + const Lattice&, + const SkRect& dst, + SkFilterMode, + const SkPaint*) override; + void onDrawAtlas2(const SkImage*, + const SkRSXform[], + const SkRect src[], + const SkColor[], + int count, + SkBlendMode, + const SkSamplingOptions&, + const SkRect* cull, + const SkPaint*) override; + + void onDrawEdgeAAQuad(const SkRect& rect, + const SkPoint clip[4], + SkCanvas::QuadAAFlags aaFlags, + const SkColor4f& color, + SkBlendMode mode) override { + FML_DCHECK(0); + } + + void onDrawAnnotation(const SkRect& rect, + const char key[], + SkData* value) override { + FML_DCHECK(false); + } + void onDrawShadowRec(const SkPath&, const SkDrawShadowRec&) override; + + void onDrawDrawable(SkDrawable* drawable, const SkMatrix* matrix) override { + FML_DCHECK(false); + } + void onDrawPicture(const SkPicture* picture, + const SkMatrix* matrix, + const SkPaint* paint) override; + + enum DrawType { + // The operation will be an image operation + imageOp, + // The operation will be an imageRect operation + imageRectOp, + // The operation will be a fill or stroke depending on the paint.style + drawOp, + // The operation will be a fill (ignoring paint.style) + fillOp, + // The operation will be a stroke (ignoring paint.style) + strokeOp, + // The operation will be a saveLayer with a paint object + saveLayerOp, + }; + + void recordPaintAttributes(const SkPaint* paint, DrawType type); + + private: + sk_sp builder_; + + // Mask bits for the various attributes that might be needed for a given + // operation. + // clang-format off + static const int aaNeeded_ = 1 << 0; + static const int colorNeeded_ = 1 << 1; + static const int blendNeeded_ = 1 << 2; + static const int invertColorsNeeded_ = 1 << 3; + static const int filterQualityNeeded_ = 1 << 4; + static const int paintStyleNeeded_ = 1 << 5; + static const int strokeStyleNeeded_ = 1 << 6; + static const int shaderNeeded_ = 1 << 7; + static const int colorFilterNeeded_ = 1 << 8; + static const int imageFilterNeeded_ = 1 << 9; + static const int maskFilterNeeded_ = 1 << 10; + static const int ditherNeeded_ = 1 << 11; + // clang-format on + + // Combinations of the above mask bits that are common to typical "draw" + // calls. + // Note that the strokeStyle_ is handled conditionally depending on whether + // the paintStyle_ attribute value is synchronized. It can also be manually + // specified for operations that will be always stroking, like [drawLine]. + static const int paintMask_ = + aaNeeded_ | colorNeeded_ | blendNeeded_ | invertColorsNeeded_ | + colorFilterNeeded_ | shaderNeeded_ | ditherNeeded_ | imageFilterNeeded_; + static const int drawMask_ = + paintMask_ | paintStyleNeeded_ | maskFilterNeeded_; + static const int strokeMask_ = + paintMask_ | strokeStyleNeeded_ | maskFilterNeeded_; + static const int imageMask_ = colorNeeded_ | blendNeeded_ | + invertColorsNeeded_ | colorFilterNeeded_ | + ditherNeeded_ | imageFilterNeeded_ | + filterQualityNeeded_ | maskFilterNeeded_; + static const int imageRectMask_ = imageMask_ | aaNeeded_; + static const int saveLayerFlags_ = colorNeeded_ | blendNeeded_ | + invertColorsNeeded_ | colorFilterNeeded_ | + imageFilterNeeded_; + + static const SkPaint defaultPaint; + + bool currentAA_ = false; + bool currentDither_ = false; + SkColor currentColor_ = 0xFF000000; + SkBlendMode currentBlendMode_ = SkBlendMode::kSrcOver; + SkPaint::Style currentPaintStyle_ = SkPaint::Style::kFill_Style; + SkScalar currentStrokeWidth_ = 0.0; + SkScalar currentMiterLimit_ = 4.0; + SkPaint::Cap currentStrokeCap_ = SkPaint::Cap::kButt_Cap; + SkPaint::Join currentStrokeJoin_ = SkPaint::Join::kMiter_Join; + SkFilterQuality currentFilterQuality_ = + SkFilterQuality::kNone_SkFilterQuality; + sk_sp currentShader_; + sk_sp currentColorFilter_; + sk_sp currentImageFilter_; + sk_sp currentMaskFilter_; +}; + +} // namespace flutter + +#endif // FLUTTER_FLOW_DISPLAY_LIST_CANVAS_H_ diff --git a/flow/display_list_canvas_unittests.cc b/flow/display_list_canvas_unittests.cc new file mode 100644 index 0000000000000..30cbbd44d56cb --- /dev/null +++ b/flow/display_list_canvas_unittests.cc @@ -0,0 +1,894 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "flutter/flow/display_list_canvas.h" + +#include "third_party/skia/include/core/SkColor.h" +#include "third_party/skia/include/core/SkImageInfo.h" +#include "third_party/skia/include/core/SkPath.h" +#include "third_party/skia/include/core/SkPictureRecorder.h" +#include "third_party/skia/include/core/SkRRect.h" +#include "third_party/skia/include/core/SkSurface.h" +#include "third_party/skia/include/core/SkVertices.h" +#include "third_party/skia/include/effects/SkGradientShader.h" +#include "third_party/skia/include/effects/SkImageFilters.h" + +#include + +#include "gtest/gtest.h" + +namespace flutter { +namespace testing { + +constexpr int TestWidth = 200; +constexpr int TestHeight = 200; +constexpr int RenderWidth = 100; +constexpr int RenderHeight = 100; +constexpr int RenderLeft = (TestWidth - RenderWidth) / 2; +constexpr int RenderTop = (TestHeight - RenderHeight) / 2; +constexpr int RenderRight = RenderLeft + RenderWidth; +constexpr int RenderBottom = RenderTop + RenderHeight; +constexpr int RenderCenterX = (RenderLeft + RenderRight) / 2; +constexpr int RenderCenterY = (RenderTop + RenderBottom) / 2; +constexpr SkScalar RenderRadius = std::min(RenderWidth, RenderHeight) / 2.0; +constexpr SkScalar RenderCornerRadius = RenderRadius / 5.0; + +constexpr SkPoint TestCenter = SkPoint::Make(TestWidth / 2, TestHeight / 2); +constexpr SkRect TestBounds = SkRect::MakeWH(TestWidth, TestHeight); +constexpr SkRect RenderBounds = + SkRect::MakeLTRB(RenderLeft, RenderTop, RenderRight, RenderBottom); + +static bool skipTheColorFilter = false; + +class CanvasCompareTester { + public: + typedef const std::function CvRenderer; + typedef const std::function DlRenderer; + + static void renderAll(CvRenderer& cv_renderer, DlRenderer& dl_renderer) { + renderWithAttributes(cv_renderer, dl_renderer); + renderWithTransforms(cv_renderer, dl_renderer); + renderWithClips(cv_renderer, dl_renderer); + } + + static void renderWithAttributes(CvRenderer& cv_renderer, + DlRenderer& dl_renderer) { + renderWith([=](SkCanvas*, SkPaint& p) {}, // + [=](DisplayListBuilder& d) {}, // + cv_renderer, dl_renderer, "Base Test"); + + renderWith([=](SkCanvas*, SkPaint& p) { p.setAntiAlias(true); }, // + [=](DisplayListBuilder& b) { b.setAA(true); }, // + cv_renderer, dl_renderer, "AA == True"); + renderWith([=](SkCanvas*, SkPaint& p) { p.setAntiAlias(false); }, // + [=](DisplayListBuilder& b) { b.setAA(false); }, // + cv_renderer, dl_renderer, "AA == False"); + + // Not testing setInvertColors here because there is no SkPaint version + + renderWith([=](SkCanvas*, SkPaint& p) { p.setDither(true); }, // + [=](DisplayListBuilder& b) { b.setDither(true); }, // + cv_renderer, dl_renderer, "Dither == True"); + renderWith([=](SkCanvas*, SkPaint& p) { p.setDither(false); }, // + [=](DisplayListBuilder& b) { b.setDither(false); }, // + cv_renderer, dl_renderer, "Dither = False"); + + renderWith([=](SkCanvas*, SkPaint& p) { p.setColor(SK_ColorBLUE); }, // + [=](DisplayListBuilder& b) { b.setColor(SK_ColorBLUE); }, // + cv_renderer, dl_renderer, "Color == Blue"); + renderWith([=](SkCanvas*, SkPaint& p) { p.setColor(SK_ColorGREEN); }, // + [=](DisplayListBuilder& b) { b.setColor(SK_ColorGREEN); }, // + cv_renderer, dl_renderer, "Color == Green"); + + renderWithStrokes(cv_renderer, dl_renderer); + + // Not testing FilterQuality here because there is no SkPaint version + + { + // half opaque cyan + SkColor blendableColor = SkColorSetARGB(0x7f, 0x00, 0xff, 0xff); + SkColor bg = SK_ColorWHITE; + + renderWith( + [=](SkCanvas*, SkPaint& p) { + p.setBlendMode(SkBlendMode::kSrcIn); + p.setColor(blendableColor); + }, + [=](DisplayListBuilder& b) { + b.setBlendMode(SkBlendMode::kSrcIn); + b.setColor(blendableColor); + }, + cv_renderer, dl_renderer, "Blend == SrcIn", &bg); + renderWith( + [=](SkCanvas*, SkPaint& p) { + p.setBlendMode(SkBlendMode::kDstIn); + p.setColor(blendableColor); + }, + [=](DisplayListBuilder& b) { + b.setBlendMode(SkBlendMode::kDstIn); + b.setColor(blendableColor); + }, + cv_renderer, dl_renderer, "Blend == DstIn", &bg); + } + + { + sk_sp filter = + SkImageFilters::Blur(5.0, 5.0, SkTileMode::kDecal, nullptr, nullptr); + { + renderWith([=](SkCanvas*, SkPaint& p) { p.setImageFilter(filter); }, + [=](DisplayListBuilder& b) { b.setImageFilter(filter); }, + cv_renderer, dl_renderer, "ImageFilter == Decal Blur 5"); + } + ASSERT_TRUE(filter->unique()) << "ImageFilter Cleanup"; + } + + if (skipTheColorFilter) { + // drawVertices + ColorFilter outputs nothing on the CPU renderer + FML_LOG(ERROR) << "Skipping the ColorFilter test"; + } else { + constexpr float rotate_color_matrix[20] = { + 0, 1, 0, 0, 0, // + 0, 0, 1, 0, 0, // + 1, 0, 0, 0, 0, // + 0, 0, 0, 1, 0, // + }; + sk_sp filter = SkColorFilters::Matrix(rotate_color_matrix); + { + SkColor bg = SK_ColorWHITE; + renderWith( + [=](SkCanvas*, SkPaint& p) { + p.setColor(SK_ColorYELLOW); + p.setColorFilter(filter); + }, + [=](DisplayListBuilder& b) { + b.setColor(SK_ColorYELLOW); + b.setColorFilter(filter); + }, + cv_renderer, dl_renderer, "ColorFilter == RotateRGB", &bg); + } + ASSERT_TRUE(filter->unique()) << "ColorFilter Cleanup"; + } + + { + sk_sp filter = + SkMaskFilter::MakeBlur(kNormal_SkBlurStyle, 5.0); + { + renderWith([=](SkCanvas*, SkPaint& p) { p.setMaskFilter(filter); }, + [=](DisplayListBuilder& b) { b.setMaskFilter(filter); }, + cv_renderer, dl_renderer, "MaskFilter == Blur 5"); + } + ASSERT_TRUE(filter->unique()) << "MaskFilter Cleanup"; + { + renderWith([=](SkCanvas*, SkPaint& p) { p.setMaskFilter(filter); }, + [=](DisplayListBuilder& b) { + b.setMaskBlurFilter(kNormal_SkBlurStyle, 5.0); + }, + cv_renderer, dl_renderer, "MaskFilter == Blur(Normal, 5.0)"); + } + ASSERT_TRUE(filter->unique()) << "MaskFilter Cleanup"; + } + + { + SkPoint end_points[] = { + SkPoint::Make(RenderBounds.fLeft, RenderBounds.fTop), + SkPoint::Make(RenderBounds.fRight, RenderBounds.fBottom), + }; + SkColor colors[] = { + SK_ColorGREEN, + SK_ColorYELLOW, + SK_ColorBLUE, + }; + float stops[] = { + 0.0, + 0.5, + 1.0, + }; + sk_sp shader = SkGradientShader::MakeLinear( + end_points, colors, stops, 3, SkTileMode::kMirror, 0, nullptr); + { + renderWith([=](SkCanvas*, SkPaint& p) { p.setShader(shader); }, + [=](DisplayListBuilder& b) { b.setShader(shader); }, + cv_renderer, dl_renderer, "LinearGradient GYB"); + } + ASSERT_TRUE(shader->unique()) << "Shader Cleanup"; + } + } + + static void renderWithStrokes(CvRenderer& cv_renderer, + DlRenderer& dl_renderer) { + renderWith( + [=](SkCanvas*, SkPaint& p) { p.setStyle(SkPaint::kFill_Style); }, + [=](DisplayListBuilder& b) { b.setDrawStyle(SkPaint::kFill_Style); }, + cv_renderer, dl_renderer, "Fill"); + renderWith( + [=](SkCanvas*, SkPaint& p) { p.setStyle(SkPaint::kStroke_Style); }, + [=](DisplayListBuilder& b) { b.setDrawStyle(SkPaint::kStroke_Style); }, + cv_renderer, dl_renderer, "Stroke + defaults"); + + renderWith( + [=](SkCanvas*, SkPaint& p) { + p.setStyle(SkPaint::kFill_Style); + p.setStrokeWidth(10.0); + }, + [=](DisplayListBuilder& b) { + b.setDrawStyle(SkPaint::kFill_Style); + b.setStrokeWidth(10.0); + }, + cv_renderer, dl_renderer, "Fill + unnecessary StrokeWidth 10"); + + renderWith( + [=](SkCanvas*, SkPaint& p) { + p.setStyle(SkPaint::kStroke_Style); + p.setStrokeWidth(10.0); + }, + [=](DisplayListBuilder& b) { + b.setDrawStyle(SkPaint::kStroke_Style); + b.setStrokeWidth(10.0); + }, + cv_renderer, dl_renderer, "Stroke Width 10"); + renderWith( + [=](SkCanvas*, SkPaint& p) { + p.setStyle(SkPaint::kStroke_Style); + p.setStrokeWidth(5.0); + }, + [=](DisplayListBuilder& b) { + b.setDrawStyle(SkPaint::kStroke_Style); + b.setStrokeWidth(5.0); + }, + cv_renderer, dl_renderer, "Stroke Width 5"); + + renderWith( + [=](SkCanvas*, SkPaint& p) { + p.setStyle(SkPaint::kStroke_Style); + p.setStrokeWidth(5.0); + p.setStrokeCap(SkPaint::kButt_Cap); + }, + [=](DisplayListBuilder& b) { + b.setDrawStyle(SkPaint::kStroke_Style); + b.setStrokeWidth(5.0); + b.setCaps(SkPaint::kButt_Cap); + }, + cv_renderer, dl_renderer, "Stroke Width 5, Butt Cap"); + renderWith( + [=](SkCanvas*, SkPaint& p) { + p.setStyle(SkPaint::kStroke_Style); + p.setStrokeWidth(5.0); + p.setStrokeCap(SkPaint::kRound_Cap); + }, + [=](DisplayListBuilder& b) { + b.setDrawStyle(SkPaint::kStroke_Style); + b.setStrokeWidth(5.0); + b.setCaps(SkPaint::kRound_Cap); + }, + cv_renderer, dl_renderer, "Stroke Width 5, Round Cap"); + + renderWith( + [=](SkCanvas*, SkPaint& p) { + p.setStyle(SkPaint::kStroke_Style); + p.setStrokeWidth(5.0); + p.setStrokeJoin(SkPaint::kBevel_Join); + }, + [=](DisplayListBuilder& b) { + b.setDrawStyle(SkPaint::kStroke_Style); + b.setStrokeWidth(5.0); + b.setJoins(SkPaint::kBevel_Join); + }, + cv_renderer, dl_renderer, "Stroke Width 5, Bevel Join"); + renderWith( + [=](SkCanvas*, SkPaint& p) { + p.setStyle(SkPaint::kStroke_Style); + p.setStrokeWidth(5.0); + p.setStrokeJoin(SkPaint::kRound_Join); + }, + [=](DisplayListBuilder& b) { + b.setDrawStyle(SkPaint::kStroke_Style); + b.setStrokeWidth(5.0); + b.setJoins(SkPaint::kRound_Join); + }, + cv_renderer, dl_renderer, "Stroke Width 5, Round Join"); + + renderWith( + [=](SkCanvas*, SkPaint& p) { + p.setStyle(SkPaint::kStroke_Style); + p.setStrokeWidth(5.0); + p.setStrokeMiter(100.0); + p.setStrokeJoin(SkPaint::kMiter_Join); + }, + [=](DisplayListBuilder& b) { + b.setDrawStyle(SkPaint::kStroke_Style); + b.setStrokeWidth(5.0); + b.setMiterLimit(100.0); + b.setJoins(SkPaint::kMiter_Join); + }, + cv_renderer, dl_renderer, "Stroke Width 5, Miter 100"); + + renderWith( + [=](SkCanvas*, SkPaint& p) { + p.setStyle(SkPaint::kStroke_Style); + p.setStrokeWidth(5.0); + p.setStrokeMiter(0.0); + p.setStrokeJoin(SkPaint::kMiter_Join); + }, + [=](DisplayListBuilder& b) { + b.setDrawStyle(SkPaint::kStroke_Style); + b.setStrokeWidth(5.0); + b.setMiterLimit(0.0); + b.setJoins(SkPaint::kMiter_Join); + }, + cv_renderer, dl_renderer, "Stroke Width 5, Miter 0"); + } + + static void renderWithTransforms(CvRenderer& cv_renderer, + DlRenderer& dl_renderer) { + renderWith([=](SkCanvas* c, SkPaint&) { c->translate(5, 10); }, // + [=](DisplayListBuilder& b) { b.translate(5, 10); }, // + cv_renderer, dl_renderer, "Translate 5, 10"); + renderWith([=](SkCanvas* c, SkPaint&) { c->scale(0.95, 0.95); }, // + [=](DisplayListBuilder& b) { b.scale(0.95, 0.95); }, // + cv_renderer, dl_renderer, "Scale 95%"); + renderWith([=](SkCanvas* c, SkPaint&) { c->rotate(5); }, // + [=](DisplayListBuilder& b) { b.rotate(5); }, // + cv_renderer, dl_renderer, "Rotate 5 degrees"); + renderWith([=](SkCanvas* c, SkPaint&) { c->skew(0.05, 0.05); }, // + [=](DisplayListBuilder& b) { b.skew(0.05, 0.05); }, // + cv_renderer, dl_renderer, "Skew 5%"); + } + + static void renderWithClips(CvRenderer& cv_renderer, + DlRenderer& dl_renderer) { + renderWith( + [=](SkCanvas* c, SkPaint&) { + c->clipRect(RenderBounds.makeInset(25.5, 25.5), // + SkClipOp::kIntersect, false); + }, + [=](DisplayListBuilder& b) { + b.clipRect(RenderBounds.makeInset(25.5, 25.5), // + false, SkClipOp::kIntersect); + }, + cv_renderer, dl_renderer, "Hard ClipRect inset by 25.5"); + renderWith( + [=](SkCanvas* c, SkPaint&) { + c->clipRect(RenderBounds.makeInset(25.5, 25.5), // + SkClipOp::kIntersect, true); + }, + [=](DisplayListBuilder& b) { + b.clipRect(RenderBounds.makeInset(25.5, 25.5), // + true, SkClipOp::kIntersect); + }, + cv_renderer, dl_renderer, "AA ClipRect inset by 25.5"); + renderWith( + [=](SkCanvas* c, SkPaint&) { + c->clipRect(RenderBounds.makeInset(25.5, 25.5), // + SkClipOp::kDifference, false); + }, + [=](DisplayListBuilder& b) { + b.clipRect(RenderBounds.makeInset(25.5, 25.5), // + false, SkClipOp::kDifference); + }, + cv_renderer, dl_renderer, "Hard ClipRect Diff, inset by 25.5"); + } + + static SkRect getSkBounds(CvRenderer& cv_setup, CvRenderer& cv_render) { + SkPictureRecorder recorder; + SkRTreeFactory rtree_factory; + SkCanvas* cv = recorder.beginRecording(TestBounds, &rtree_factory); + SkPaint p; + cv_setup(cv, p); + cv_render(cv, p); + return recorder.finishRecordingAsPicture()->cullRect(); + } + + static void renderWith(CvRenderer& cv_setup, + DlRenderer& dl_setup, + CvRenderer& cv_render, + DlRenderer& dl_render, + const std::string info, + const SkColor* bg = nullptr) { + // surface1 is direct rendering via SkCanvas to SkSurface + // DisplayList mechanisms are not involved in this operation + sk_sp ref_surface = makeSurface(bg); + SkPaint paint1; + cv_setup(ref_surface->getCanvas(), paint1); + cv_render(ref_surface->getCanvas(), paint1); + SkRect ref_bounds = getSkBounds(cv_setup, cv_render); + SkPixmap ref_pixels; + ASSERT_TRUE(ref_surface->peekPixels(&ref_pixels)) << info; + ASSERT_EQ(ref_pixels.width(), TestWidth) << info; + ASSERT_EQ(ref_pixels.height(), TestHeight) << info; + ASSERT_EQ(ref_pixels.info().bytesPerPixel(), 4) << info; + checkPixels(&ref_pixels, ref_bounds, info, bg); + + { + // This sequence plays the provided equivalently constructed + // DisplayList onto the SkCanvas of the surface + // DisplayList => direct rendering + sk_sp test_surface = makeSurface(bg); + DisplayListBuilder builder(TestBounds); + dl_setup(builder); + dl_render(builder); + sk_sp display_list = builder.build(); + SkRect dl_bounds = display_list->bounds(); +#ifdef DISPLAY_LIST_BOUNDS_ACCURACY_CHECKING + if (dl_bounds != ref_bounds) { + FML_LOG(ERROR) << "For " << info; + FML_LOG(ERROR) << "ref: " << ref_bounds.fLeft << ", " << ref_bounds.fTop + << " => " << ref_bounds.fRight << ", " + << ref_bounds.fBottom; + FML_LOG(ERROR) << "dl: " << dl_bounds.fLeft << ", " << dl_bounds.fTop + << " => " << dl_bounds.fRight << ", " + << dl_bounds.fBottom; + if (!dl_bounds.contains(ref_bounds)) { + FML_LOG(ERROR) << "DisplayList bounds are too small!"; + } + } +#endif // DISPLAY_LIST_BOUNDS_ACCURACY_CHECKING + // This sometimes triggers, but when it triggers and I examine + // the ref_bounds, they are always unnecessarily large and + // since the pixel OOB tests in the compare method do not + // trigger, we will trust the DL bounds. + // EXPECT_TRUE(dl_bounds.contains(ref_bounds)) << info; + display_list->renderTo(test_surface->getCanvas()); + compareToReference(test_surface.get(), &ref_pixels, info + " (DL render)", + &dl_bounds, bg); + } + + { + // This sequence renders SkCanvas calls to a DisplayList and then + // plays them back on SkCanvas to SkSurface + // SkCanvas calls => DisplayList => rendering + sk_sp test_surface = makeSurface(bg); + DisplayListCanvasRecorder dl_recorder(TestBounds); + SkPaint test_paint; + cv_setup(&dl_recorder, test_paint); + cv_render(&dl_recorder, test_paint); + dl_recorder.builder()->build()->renderTo(test_surface->getCanvas()); + compareToReference(test_surface.get(), &ref_pixels, + info + " (Sk->DL render)", nullptr, nullptr); + } + } + + static void checkPixels(SkPixmap* ref_pixels, + SkRect ref_bounds, + const std::string info, + const SkColor* bg) { + SkPMColor untouched = (bg) ? SkPreMultiplyColor(*bg) : 0; + int pixels_touched = 0; + int pixels_oob = 0; + for (int y = 0; y < TestHeight; y++) { + const uint32_t* ref_row = ref_pixels->addr32(0, y); + for (int x = 0; x < TestWidth; x++) { + if (ref_row[x] != untouched) { + pixels_touched++; + if (!ref_bounds.intersects(SkRect::MakeXYWH(x, y, 1, 1))) { + pixels_oob++; + } + } + } + } + ASSERT_EQ(pixels_oob, 0) << info; + ASSERT_GT(pixels_touched, 0) << info; + } + + static void compareToReference(SkSurface* test_surface, + SkPixmap* reference, + const std::string info, + SkRect* bounds, + const SkColor* bg) { + SkPMColor untouched = (bg) ? SkPreMultiplyColor(*bg) : 0; + SkPixmap test_pixels; + ASSERT_TRUE(test_surface->peekPixels(&test_pixels)) << info; + ASSERT_EQ(test_pixels.width(), TestWidth) << info; + ASSERT_EQ(test_pixels.height(), TestHeight) << info; + ASSERT_EQ(test_pixels.info().bytesPerPixel(), 4) << info; + + int pixels_different = 0; + int pixels_oob = 0; + int minX = TestWidth; + int minY = TestWidth; + int maxX = 0; + int maxY = 0; + for (int y = 0; y < TestHeight; y++) { + const uint32_t* ref_row = reference->addr32(0, y); + const uint32_t* test_row = test_pixels.addr32(0, y); + for (int x = 0; x < TestWidth; x++) { + if (bounds && test_row[x] != untouched) { + if (minX > x) + minX = x; + if (minY > y) + minY = y; + if (maxX < x) + maxX = x; + if (maxY < y) + maxY = y; + if (!bounds->intersects(SkRect::MakeXYWH(x, y, 1, 1))) { + pixels_oob++; + } + } + if (test_row[x] != ref_row[x]) { + pixels_different++; + } + } + } +#ifdef DISPLAY_LIST_BOUNDS_ACCURACY_CHECKING + if (bounds && *bounds != SkRect::MakeLTRB(minX, minY, maxX + 1, maxY + 1)) { + FML_LOG(ERROR) << "inaccurate bounds for " << info; + FML_LOG(ERROR) << "dl: " << bounds->fLeft << ", " << bounds->fTop + << " => " << bounds->fRight << ", " << bounds->fBottom; + FML_LOG(ERROR) << "pixels: " << minX << ", " << minY << " => " + << (maxX + 1) << ", " << (maxY + 1); + } +#endif // DISPLAY_LIST_BOUNDS_ACCURACY_CHECKING + ASSERT_EQ(pixels_oob, 0) << info; + ASSERT_EQ(pixels_different, 0) << info; + } + + static sk_sp makeSurface(const SkColor* bg) { + sk_sp surface = + SkSurface::MakeRasterN32Premul(TestWidth, TestHeight); + if (bg) { + surface->getCanvas()->drawColor(*bg); + } + return surface; + } + + static const sk_sp testImage; + static const sk_sp makeTestImage() { + sk_sp surface = + SkSurface::MakeRasterN32Premul(RenderWidth, RenderHeight); + SkCanvas* canvas = surface->getCanvas(); + SkPaint p0, p1; + p0.setStyle(SkPaint::kFill_Style); + p0.setColor(SK_ColorGREEN); + p1.setStyle(SkPaint::kFill_Style); + p1.setColor(SK_ColorBLUE); + // Some pixels need some transparency for DstIn testing + p1.setAlpha(128); + int cbdim = 5; + for (int y = 0; y < RenderHeight; y += cbdim) { + for (int x = 0; x < RenderWidth; x += cbdim) { + SkPaint& cellp = ((x + y) & 1) == 0 ? p0 : p1; + canvas->drawRect(SkRect::MakeXYWH(x, y, cbdim, cbdim), cellp); + } + } + return surface->makeImageSnapshot(); + } +}; + +const sk_sp CanvasCompareTester::testImage = + CanvasCompareTester::makeTestImage(); + +TEST(DisplayListCanvas, DrawPaint) { + CanvasCompareTester::renderAll( + [=](SkCanvas* canvas, SkPaint& paint) { // + canvas->drawPaint(paint); + }, + [=](DisplayListBuilder& builder) { // + builder.drawPaint(); + }); +} + +TEST(DisplayListCanvas, DrawColor) { + CanvasCompareTester::renderWith( // + [=](SkCanvas*, SkPaint& p) {}, // + [=](DisplayListBuilder& b) {}, // + [=](SkCanvas* canvas, SkPaint& paint) { // + canvas->drawColor(SK_ColorMAGENTA); + }, + [=](DisplayListBuilder& builder) { // + builder.drawColor(SK_ColorMAGENTA, SkBlendMode::kSrcOver); + }, + "No SkPaint"); + CanvasCompareTester::renderWithTransforms( // + [=](SkCanvas* canvas, SkPaint& paint) { // + canvas->drawColor(SK_ColorMAGENTA); + }, + [=](DisplayListBuilder& builder) { // + builder.drawColor(SK_ColorMAGENTA, SkBlendMode::kSrcOver); + }); + CanvasCompareTester::renderWithClips( // + [=](SkCanvas* canvas, SkPaint& paint) { // + canvas->drawColor(SK_ColorMAGENTA); + }, + [=](DisplayListBuilder& builder) { // + builder.drawColor(SK_ColorMAGENTA, SkBlendMode::kSrcOver); + }); +} + +TEST(DisplayListCanvas, DrawLine) { + SkRect rect = RenderBounds.makeInset(20, 20); + SkPoint p1 = SkPoint::Make(rect.fLeft, rect.fTop); + SkPoint p2 = SkPoint::Make(rect.fRight, rect.fBottom); + + CanvasCompareTester::renderAll( + [=](SkCanvas* canvas, SkPaint& paint) { // + canvas->drawLine(p1, p2, paint); + }, + [=](DisplayListBuilder& builder) { // + builder.drawLine(p1, p2); + }); +} + +TEST(DisplayListCanvas, DrawRect) { + CanvasCompareTester::renderAll( + [=](SkCanvas* canvas, SkPaint& paint) { // + canvas->drawRect(RenderBounds, paint); + }, + [=](DisplayListBuilder& builder) { // + builder.drawRect(RenderBounds); + }); +} + +TEST(DisplayListCanvas, DrawOval) { + SkRect rect = RenderBounds.makeInset(0, 10); + + CanvasCompareTester::renderAll( + [=](SkCanvas* canvas, SkPaint& paint) { // + canvas->drawOval(rect, paint); + }, + [=](DisplayListBuilder& builder) { // + builder.drawOval(rect); + }); +} + +TEST(DisplayListCanvas, DrawCircle) { + CanvasCompareTester::renderAll( + [=](SkCanvas* canvas, SkPaint& paint) { // + canvas->drawCircle(TestCenter, RenderRadius, paint); + }, + [=](DisplayListBuilder& builder) { // + builder.drawCircle(TestCenter, RenderRadius); + }); +} + +TEST(DisplayListCanvas, DrawRRect) { + SkRRect rrect = + SkRRect::MakeRectXY(RenderBounds, RenderCornerRadius, RenderCornerRadius); + CanvasCompareTester::renderAll( + [=](SkCanvas* canvas, SkPaint& paint) { // + canvas->drawRRect(rrect, paint); + }, + [=](DisplayListBuilder& builder) { // + builder.drawRRect(rrect); + }); +} + +TEST(DisplayListCanvas, DrawDRRect) { + SkRRect outer = + SkRRect::MakeRectXY(RenderBounds, RenderCornerRadius, RenderCornerRadius); + SkRect innerBounds = RenderBounds.makeInset(30.0, 30.0); + SkRRect inner = + SkRRect::MakeRectXY(innerBounds, RenderCornerRadius, RenderCornerRadius); + CanvasCompareTester::renderAll( + [=](SkCanvas* canvas, SkPaint& paint) { // + canvas->drawDRRect(outer, inner, paint); + }, + [=](DisplayListBuilder& builder) { // + builder.drawDRRect(outer, inner); + }); +} + +TEST(DisplayListCanvas, DrawPath) { + SkPath path; + path.moveTo(RenderCenterX, RenderTop); + path.lineTo(RenderRight, RenderBottom); + path.lineTo(RenderLeft, RenderCenterY); + path.lineTo(RenderRight, RenderCenterY); + path.lineTo(RenderLeft, RenderBottom); + CanvasCompareTester::renderAll( + [=](SkCanvas* canvas, SkPaint& paint) { // + canvas->drawPath(path, paint); + }, + [=](DisplayListBuilder& builder) { // + builder.drawPath(path); + }); +} + +TEST(DisplayListCanvas, DrawArc) { + CanvasCompareTester::renderAll( + [=](SkCanvas* canvas, SkPaint& paint) { // + canvas->drawArc(RenderBounds, 30, 270, false, paint); + }, + [=](DisplayListBuilder& builder) { // + builder.drawArc(RenderBounds, 30, 270, false); + }); +} + +TEST(DisplayListCanvas, DrawArcCenter) { + CanvasCompareTester::renderAll( + [=](SkCanvas* canvas, SkPaint& paint) { // + canvas->drawArc(RenderBounds, 30, 270, true, paint); + }, + [=](DisplayListBuilder& builder) { // + builder.drawArc(RenderBounds, 30, 270, true); + }); +} + +TEST(DisplayListCanvas, DrawPointsAsPoints) { + const SkPoint points[] = { + SkPoint::Make(RenderLeft, RenderTop), + SkPoint::Make(RenderRight, RenderBottom), + SkPoint::Make(RenderRight, RenderTop), + SkPoint::Make(RenderLeft, RenderBottom), + SkPoint::Make(RenderCenterX, RenderCenterY), + }; + CanvasCompareTester::renderAll( + [=](SkCanvas* canvas, SkPaint& paint) { // + canvas->drawPoints(SkCanvas::kPoints_PointMode, 5, points, paint); + }, + [=](DisplayListBuilder& builder) { // + builder.drawPoints(SkCanvas::kPoints_PointMode, 5, points); + }); +} + +TEST(DisplayListCanvas, DrawPointsAsLines) { + const SkPoint points[] = { + SkPoint::Make(RenderLeft, RenderTop), + SkPoint::Make(RenderRight, RenderBottom), + SkPoint::Make(RenderRight, RenderTop), + SkPoint::Make(RenderLeft, RenderBottom), + }; + CanvasCompareTester::renderAll( + [=](SkCanvas* canvas, SkPaint& paint) { // + canvas->drawPoints(SkCanvas::kLines_PointMode, 4, points, paint); + }, + [=](DisplayListBuilder& builder) { // + builder.drawPoints(SkCanvas::kLines_PointMode, 4, points); + }); +} + +TEST(DisplayListCanvas, DrawPointsAsPolygon) { + const SkPoint points[] = { + SkPoint::Make(RenderLeft, RenderTop), + SkPoint::Make(RenderRight, RenderBottom), + SkPoint::Make(RenderRight, RenderTop), + SkPoint::Make(RenderLeft, RenderBottom), + }; + CanvasCompareTester::renderAll( + [=](SkCanvas* canvas, SkPaint& paint) { // + canvas->drawPoints(SkCanvas::kPolygon_PointMode, 4, points, paint); + }, + [=](DisplayListBuilder& builder) { // + builder.drawPoints(SkCanvas::kPolygon_PointMode, 4, points); + }); +} + +TEST(DisplayListCanvas, DrawVerticesWithColors) { + skipTheColorFilter = true; + const SkPoint pts[3] = { + SkPoint::Make(RenderCenterX, RenderTop), + SkPoint::Make(RenderLeft, RenderBottom), + SkPoint::Make(RenderRight, RenderBottom), + }; + const SkColor colors[3] = {SK_ColorRED, SK_ColorBLUE, SK_ColorGREEN}; + const sk_sp vertices = SkVertices::MakeCopy( + SkVertices::kTriangles_VertexMode, 3, pts, nullptr, colors); + CanvasCompareTester::renderAll( + [=](SkCanvas* canvas, SkPaint& paint) { // + canvas->drawVertices(vertices.get(), SkBlendMode::kSrcOver, paint); + }, + [=](DisplayListBuilder& builder) { // + builder.drawVertices(vertices, SkBlendMode::kSrcOver); + }); + // Since there are no ASSERT macros above here, this line will execute + skipTheColorFilter = false; + ASSERT_TRUE(vertices->unique()); +} + +TEST(DisplayListCanvas, DrawVerticesWithImage) { + skipTheColorFilter = true; + const SkPoint pts[3] = { + SkPoint::Make(RenderCenterX, RenderTop), + SkPoint::Make(RenderLeft, RenderBottom), + SkPoint::Make(RenderRight, RenderBottom), + }; + const SkPoint tex[3] = { + SkPoint::Make(RenderWidth / 2.0, 0), + SkPoint::Make(0, RenderHeight), + SkPoint::Make(RenderWidth, RenderHeight), + }; + const sk_sp vertices = SkVertices::MakeCopy( + SkVertices::kTriangles_VertexMode, 3, pts, tex, nullptr); + const sk_sp shader = CanvasCompareTester::testImage->makeShader( + SkTileMode::kRepeat, SkTileMode::kRepeat, SkSamplingOptions()); + CanvasCompareTester::renderAll( + [=](SkCanvas* canvas, SkPaint& paint) { // + paint.setShader(shader); + canvas->drawVertices(vertices.get(), SkBlendMode::kSrcOver, paint); + }, + [=](DisplayListBuilder& builder) { // + builder.setShader(shader); + builder.drawVertices(vertices, SkBlendMode::kSrcOver); + }); + // Since there are no ASSERT macros above here, this line will execute + skipTheColorFilter = false; + ASSERT_TRUE(vertices->unique()); + ASSERT_TRUE(shader->unique()); +} + +TEST(DisplayListCanvas, DrawImageNearest) { + CanvasCompareTester::renderAll( + [=](SkCanvas* canvas, SkPaint& paint) { // + canvas->drawImage(CanvasCompareTester::testImage, RenderLeft, RenderTop, + DisplayList::NearestSampling, &paint); + }, + [=](DisplayListBuilder& builder) { // + builder.drawImage(CanvasCompareTester::testImage, + SkPoint::Make(RenderLeft, RenderTop), + DisplayList::NearestSampling); + }); +} + +TEST(DisplayListCanvas, DrawImageLinear) { + CanvasCompareTester::renderAll( + [=](SkCanvas* canvas, SkPaint& paint) { // + canvas->drawImage(CanvasCompareTester::testImage, RenderLeft, RenderTop, + DisplayList::LinearSampling, &paint); + }, + [=](DisplayListBuilder& builder) { // + builder.drawImage(CanvasCompareTester::testImage, + SkPoint::Make(RenderLeft, RenderTop), + DisplayList::LinearSampling); + }); +} + +TEST(DisplayListCanvas, DrawImageRectNearest) { + SkRect src = SkRect::MakeIWH(RenderWidth, RenderHeight).makeInset(5, 5); + SkRect dst = RenderBounds.makeInset(15.5, 10.5); + CanvasCompareTester::renderAll( + [=](SkCanvas* canvas, SkPaint& paint) { // + canvas->drawImageRect(CanvasCompareTester::testImage, src, dst, + DisplayList::NearestSampling, &paint, + SkCanvas::kFast_SrcRectConstraint); + }, + [=](DisplayListBuilder& builder) { // + builder.drawImageRect(CanvasCompareTester::testImage, src, dst, + DisplayList::NearestSampling); + }); +} + +TEST(DisplayListCanvas, DrawImageRectLinear) { + SkRect src = SkRect::MakeIWH(RenderWidth, RenderHeight).makeInset(5, 5); + SkRect dst = RenderBounds.makeInset(15.5, 10.5); + CanvasCompareTester::renderAll( + [=](SkCanvas* canvas, SkPaint& paint) { // + canvas->drawImageRect(CanvasCompareTester::testImage, src, dst, + DisplayList::LinearSampling, &paint, + SkCanvas::kFast_SrcRectConstraint); + }, + [=](DisplayListBuilder& builder) { // + builder.drawImageRect(CanvasCompareTester::testImage, src, dst, + DisplayList::LinearSampling); + }); +} + +TEST(DisplayListCanvas, DrawImageNineNearest) { + SkIRect src = SkIRect::MakeWH(RenderWidth, RenderHeight).makeInset(5, 5); + SkRect dst = RenderBounds.makeInset(15.5, 10.5); + CanvasCompareTester::renderAll( + [=](SkCanvas* canvas, SkPaint& paint) { // + canvas->drawImageNine(CanvasCompareTester::testImage.get(), src, dst, + SkFilterMode::kNearest, &paint); + }, + [=](DisplayListBuilder& builder) { // + builder.drawImageNine(CanvasCompareTester::testImage, src, dst, + SkFilterMode::kNearest); + }); +} + +TEST(DisplayListCanvas, DrawImageNineLinear) { + SkIRect src = SkIRect::MakeWH(RenderWidth, RenderHeight).makeInset(5, 5); + SkRect dst = RenderBounds.makeInset(15.5, 10.5); + CanvasCompareTester::renderAll( + [=](SkCanvas* canvas, SkPaint& paint) { // + canvas->drawImageNine(CanvasCompareTester::testImage.get(), src, dst, + SkFilterMode::kLinear, &paint); + }, + [=](DisplayListBuilder& builder) { // + builder.drawImageNine(CanvasCompareTester::testImage, src, dst, + SkFilterMode::kLinear); + }); +} + +} // namespace testing +} // namespace flutter diff --git a/flow/display_list_unittests.cc b/flow/display_list_unittests.cc new file mode 100644 index 0000000000000..cfe640c340a80 --- /dev/null +++ b/flow/display_list_unittests.cc @@ -0,0 +1,612 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "flutter/flow/display_list_canvas.h" + +#include "third_party/skia/include/core/SkColor.h" +#include "third_party/skia/include/core/SkImageInfo.h" +#include "third_party/skia/include/core/SkPath.h" +#include "third_party/skia/include/core/SkPicture.h" +#include "third_party/skia/include/core/SkPictureRecorder.h" +#include "third_party/skia/include/core/SkRRect.h" +#include "third_party/skia/include/core/SkRSXform.h" +#include "third_party/skia/include/core/SkSurface.h" +#include "third_party/skia/include/core/SkVertices.h" +#include "third_party/skia/include/effects/SkGradientShader.h" +#include "third_party/skia/include/effects/SkImageFilters.h" + +#include + +#include "gtest/gtest.h" + +namespace flutter { +namespace testing { + +constexpr SkPoint end_points[] = { + {0, 0}, + {100, 100}, +}; +constexpr SkColor colors[] = { + SK_ColorGREEN, + SK_ColorYELLOW, + SK_ColorBLUE, +}; +constexpr float stops[] = { + 0.0, + 0.5, + 1.0, +}; +constexpr float rotate_color_matrix[20] = { + 0, 1, 0, 0, 0, // + 0, 0, 1, 0, 0, // + 1, 0, 0, 0, 0, // + 0, 0, 0, 1, 0, // +}; + +constexpr SkPoint TestPoints[] = { + {10, 10}, + {20, 20}, + {10, 20}, + {20, 10}, +}; +#define TestPointCount sizeof(TestPoints) / (sizeof(TestPoints[0])) + +static const sk_sp TestShader1 = + SkGradientShader::MakeLinear(end_points, + colors, + stops, + 3, + SkTileMode::kMirror, + 0, + nullptr); +// TestShader2 is identical to TestShader1 and points out that we cannot +// perform a deep compare over our various sk_sp objects because the +// DisplayLists constructed with the two do not compare == below. +static const sk_sp TestShader2 = + SkGradientShader::MakeLinear(end_points, + colors, + stops, + 3, + SkTileMode::kMirror, + 0, + nullptr); +static const sk_sp TestShader3 = + SkGradientShader::MakeLinear(end_points, + colors, + stops, + 3, + SkTileMode::kDecal, + 0, + nullptr); +static const sk_sp TestImageFilter = + SkImageFilters::Blur(5.0, 5.0, SkTileMode::kDecal, nullptr, nullptr); +static const sk_sp TestColorFilter = + SkColorFilters::Matrix(rotate_color_matrix); +static const sk_sp TestMaskFilter = + SkMaskFilter::MakeBlur(kNormal_SkBlurStyle, 5.0); +constexpr SkRect TestBounds = SkRect::MakeLTRB(10, 10, 50, 60); +static const SkRRect TestRRect = SkRRect::MakeRectXY(TestBounds, 5, 5); +static const SkRRect TestInnerRRect = + SkRRect::MakeRectXY(TestBounds.makeInset(5, 5), 2, 2); +static const SkPath TestPath1 = SkPath::Rect(TestBounds); +static const SkPath TestPath2 = SkPath::Oval(TestBounds); + +static sk_sp MakeTestImage(int w, int h, int checker_size) { + sk_sp surface = SkSurface::MakeRasterN32Premul(w, h); + SkCanvas* canvas = surface->getCanvas(); + SkPaint p0, p1; + p0.setStyle(SkPaint::kFill_Style); + p0.setColor(SK_ColorGREEN); + p1.setStyle(SkPaint::kFill_Style); + p1.setColor(SK_ColorBLUE); + p1.setAlpha(128); + for (int y = 0; y < w; y += checker_size) { + for (int x = 0; x < h; x += checker_size) { + SkPaint& cellp = ((x + y) & 1) == 0 ? p0 : p1; + canvas->drawRect(SkRect::MakeXYWH(x, y, checker_size, checker_size), + cellp); + } + } + return surface->makeImageSnapshot(); +} +static sk_sp TestImage1 = MakeTestImage(40, 40, 5); +static sk_sp TestImage2 = MakeTestImage(20, 20, 5); + +static sk_sp TestVertices1 = + SkVertices::MakeCopy(SkVertices::kTriangles_VertexMode, + 3, + TestPoints, + nullptr, + colors); +static sk_sp TestVertices2 = + SkVertices::MakeCopy(SkVertices::kTriangleFan_VertexMode, + 3, + TestPoints, + nullptr, + colors); + +static sk_sp MakeTestPicture(int w, int h, SkColor color) { + SkPictureRecorder recorder; + SkCanvas* cv = recorder.beginRecording(TestBounds); + SkPaint paint; + paint.setColor(color); + paint.setStyle(SkPaint::kFill_Style); + cv->drawRect(SkRect::MakeWH(w, h), paint); + return recorder.finishRecordingAsPicture(); +} +static sk_sp TestPicture1 = MakeTestPicture(20, 20, SK_ColorGREEN); +static sk_sp TestPicture2 = MakeTestPicture(25, 25, SK_ColorBLUE); + +static sk_sp MakeTestDisplayList(int w, int h, SkColor color) { + DisplayListBuilder builder; + builder.setColor(color); + builder.drawRect(SkRect::MakeWH(w, h)); + return builder.build(); +} +static sk_sp TestDisplayList1 = + MakeTestDisplayList(20, 20, SK_ColorGREEN); +static sk_sp TestDisplayList2 = + MakeTestDisplayList(25, 25, SK_ColorBLUE); + +typedef const std::function DlInvoker; + +struct DisplayListInvocation { + int opCount; + size_t byteCount; + DlInvoker invoker; + sk_sp build() { + DisplayListBuilder builder; + invoker(builder); + return builder.build(); + } +}; + +struct DisplayListInvocationGroup { + std::string op_name; + std::vector variants; +}; + +std::vector allGroups = { + { "SetAA", { + {1, 8, [](DisplayListBuilder& b) {b.setAA(false);}}, + {1, 8, [](DisplayListBuilder& b) {b.setAA(true);}}, + } + }, + { "SetDither", { + {1, 8, [](DisplayListBuilder& b) {b.setDither(false);}}, + {1, 8, [](DisplayListBuilder& b) {b.setDither(true);}}, + } + }, + { "SetInvertColors", { + {1, 8, [](DisplayListBuilder& b) {b.setInvertColors(false);}}, + {1, 8, [](DisplayListBuilder& b) {b.setInvertColors(true);}}, + } + }, + { "SetStrokeCap", { + {1, 8, [](DisplayListBuilder& b) {b.setCaps(SkPaint::kButt_Cap);}}, + {1, 8, [](DisplayListBuilder& b) {b.setCaps(SkPaint::kRound_Cap);}}, + {1, 8, [](DisplayListBuilder& b) {b.setCaps(SkPaint::kSquare_Cap);}}, + } + }, + { "SetStrokeJoin", { + {1, 8, [](DisplayListBuilder& b) {b.setJoins(SkPaint::kBevel_Join);}}, + {1, 8, [](DisplayListBuilder& b) {b.setJoins(SkPaint::kRound_Join);}}, + {1, 8, [](DisplayListBuilder& b) {b.setJoins(SkPaint::kMiter_Join);}}, + } + }, + { "SetDrawStyle", { + {1, 8, [](DisplayListBuilder& b) {b.setDrawStyle(SkPaint::kFill_Style);}}, + {1, 8, [](DisplayListBuilder& b) {b.setDrawStyle(SkPaint::kStroke_Style);}}, + } + }, + { "SetStrokeWidth", { + {1, 8, [](DisplayListBuilder& b) {b.setStrokeWidth(0.0);}}, + {1, 8, [](DisplayListBuilder& b) {b.setStrokeWidth(5.0);}}, + } + }, + { "SetMiterLimit", { + {1, 8, [](DisplayListBuilder& b) {b.setMiterLimit(0.0);}}, + {1, 8, [](DisplayListBuilder& b) {b.setMiterLimit(5.0);}}, + } + }, + { "SetColor", { + {1, 8, [](DisplayListBuilder& b) {b.setColor(SK_ColorGREEN);}}, + {1, 8, [](DisplayListBuilder& b) {b.setColor(SK_ColorBLUE);}}, + } + }, + { "SetBlendMode", { + {1, 8, [](DisplayListBuilder& b) {b.setBlendMode(SkBlendMode::kSrcIn);}}, + {1, 8, [](DisplayListBuilder& b) {b.setBlendMode(SkBlendMode::kDstIn);}}, + } + }, + { "SetFilterQuality", { + {1, 8, [](DisplayListBuilder& b) {b.setFilterQuality(kNone_SkFilterQuality);}}, + {1, 8, [](DisplayListBuilder& b) {b.setFilterQuality(kLow_SkFilterQuality);}}, + {1, 8, [](DisplayListBuilder& b) {b.setFilterQuality(kMedium_SkFilterQuality);}}, + {1, 8, [](DisplayListBuilder& b) {b.setFilterQuality(kHigh_SkFilterQuality);}}, + } + }, + { "SetShader", { + {1, 8, [](DisplayListBuilder& b) {b.setShader(nullptr);}}, + {1, 16, [](DisplayListBuilder& b) {b.setShader(TestShader1);}}, + {1, 16, [](DisplayListBuilder& b) {b.setShader(TestShader2);}}, + {1, 16, [](DisplayListBuilder& b) {b.setShader(TestShader3);}}, + } + }, + { "SetImageFilter", { + {1, 8, [](DisplayListBuilder& b) {b.setImageFilter(nullptr);}}, + {1, 16, [](DisplayListBuilder& b) {b.setImageFilter(TestImageFilter);}}, + } + }, + { "SetColorFilter", { + {1, 8, [](DisplayListBuilder& b) {b.setColorFilter(nullptr);}}, + {1, 16, [](DisplayListBuilder& b) {b.setColorFilter(TestColorFilter);}}, + } + }, + { "SetMaskFilter", { + {1, 16, [](DisplayListBuilder& b) {b.setMaskFilter(nullptr);}}, + {1, 16, [](DisplayListBuilder& b) {b.setMaskFilter(TestMaskFilter);}}, + {1, 8, [](DisplayListBuilder& b) {b.setMaskBlurFilter(kNormal_SkBlurStyle, 3.0);}}, + {1, 8, [](DisplayListBuilder& b) {b.setMaskBlurFilter(kNormal_SkBlurStyle, 5.0);}}, + {1, 8, [](DisplayListBuilder& b) {b.setMaskBlurFilter(kSolid_SkBlurStyle, 3.0);}}, + {1, 8, [](DisplayListBuilder& b) {b.setMaskBlurFilter(kInner_SkBlurStyle, 3.0);}}, + {1, 8, [](DisplayListBuilder& b) {b.setMaskBlurFilter(kOuter_SkBlurStyle, 3.0);}}, + } + }, + { "Save(Layer)+Restore", { + {2, 16, [](DisplayListBuilder& b) {b.save(); b.restore();}}, + {2, 16, [](DisplayListBuilder& b) {b.saveLayer(nullptr, false); b.restore(); }}, + {2, 16, [](DisplayListBuilder& b) {b.saveLayer(nullptr, true); b.restore(); }}, + {2, 32, [](DisplayListBuilder& b) {b.saveLayer(&TestBounds, false); b.restore(); }}, + {2, 32, [](DisplayListBuilder& b) {b.saveLayer(&TestBounds, true); b.restore(); }}, + } + }, + { "Translate", { + {1, 16, [](DisplayListBuilder& b) {b.translate(0, 0);}}, + {1, 16, [](DisplayListBuilder& b) {b.translate(5, 15);}}, + } + }, + { "Scale", { + {1, 16, [](DisplayListBuilder& b) {b.scale(1, 1);}}, + {1, 16, [](DisplayListBuilder& b) {b.scale(2, 3);}}, + } + }, + { "Rotate", { + {1, 8, [](DisplayListBuilder& b) {b.rotate(0);}}, + {1, 8, [](DisplayListBuilder& b) {b.rotate(45);}}, + } + }, + { "Skew", { + {1, 16, [](DisplayListBuilder& b) {b.skew(0, 0);}}, + {1, 16, [](DisplayListBuilder& b) {b.skew(0.1, 0.2);}}, + } + }, + { "Transform2x3", { + {1, 32, [](DisplayListBuilder& b) {b.transform2x3(1, 0, 0, 0, 1, 0);}}, + {1, 32, [](DisplayListBuilder& b) {b.transform2x3(0, 1, 12, 1, 0, 33);}}, + } + }, + { "Transform3x3", { + {1, 40, [](DisplayListBuilder& b) {b.transform3x3(1, 0, 0, 0, 1, 0, 0, 0, 1);}}, + {1, 40, [](DisplayListBuilder& b) {b.transform3x3(0, 1, 12, 1, 0, 33, 0, 0, 12);}}, + } + }, + { "ClipRect", { + {1, 24, [](DisplayListBuilder& b) {b.clipRect(TestBounds, true, SkClipOp::kIntersect);}}, + {1, 24, [](DisplayListBuilder& b) {b.clipRect(TestBounds.makeOffset(1, 1), + true, SkClipOp::kIntersect);}}, + {1, 24, [](DisplayListBuilder& b) {b.clipRect(TestBounds, false, SkClipOp::kIntersect);}}, + {1, 24, [](DisplayListBuilder& b) {b.clipRect(TestBounds, true, SkClipOp::kDifference);}}, + {1, 24, [](DisplayListBuilder& b) {b.clipRect(TestBounds, false, SkClipOp::kDifference);}}, + } + }, + { "ClipRRect", { + {1, 64, [](DisplayListBuilder& b) {b.clipRRect(TestRRect, true, SkClipOp::kIntersect);}}, + {1, 64, [](DisplayListBuilder& b) {b.clipRRect(TestRRect.makeOffset(1, 1), + true, SkClipOp::kIntersect);}}, + {1, 64, [](DisplayListBuilder& b) {b.clipRRect(TestRRect, false, SkClipOp::kIntersect);}}, + {1, 64, [](DisplayListBuilder& b) {b.clipRRect(TestRRect, true, SkClipOp::kDifference);}}, + {1, 64, [](DisplayListBuilder& b) {b.clipRRect(TestRRect, false, SkClipOp::kDifference);}}, + } + }, + { "ClipPath", { + {1, 24, [](DisplayListBuilder& b) {b.clipPath(TestPath1, true, SkClipOp::kIntersect);}}, + {1, 24, [](DisplayListBuilder& b) {b.clipPath(TestPath2, true, SkClipOp::kIntersect);}}, + {1, 24, [](DisplayListBuilder& b) {b.clipPath(TestPath1, false, SkClipOp::kIntersect);}}, + {1, 24, [](DisplayListBuilder& b) {b.clipPath(TestPath1, true, SkClipOp::kDifference);}}, + {1, 24, [](DisplayListBuilder& b) {b.clipPath(TestPath1, false, SkClipOp::kDifference);}}, + } + }, + { "DrawPaint", { + {1, 8, [](DisplayListBuilder& b) {b.drawPaint();}}, + } + }, + { "DrawColor", { + {1, 16, [](DisplayListBuilder& b) {b.drawColor(SK_ColorBLUE, SkBlendMode::kSrcIn);}}, + {1, 16, [](DisplayListBuilder& b) {b.drawColor(SK_ColorBLUE, SkBlendMode::kDstIn);}}, + {1, 16, [](DisplayListBuilder& b) {b.drawColor(SK_ColorCYAN, SkBlendMode::kSrcIn);}}, + } + }, + { "DrawLine", { + {1, 24, [](DisplayListBuilder& b) {b.drawLine({0, 0}, {10, 10});}}, + {1, 24, [](DisplayListBuilder& b) {b.drawLine({0, 1}, {10, 10});}}, + {1, 24, [](DisplayListBuilder& b) {b.drawLine({0, 0}, {20, 10});}}, + {1, 24, [](DisplayListBuilder& b) {b.drawLine({0, 0}, {10, 20});}}, + } + }, + { "DrawRect", { + {1, 24, [](DisplayListBuilder& b) {b.drawRect({0, 0, 10, 10});}}, + {1, 24, [](DisplayListBuilder& b) {b.drawRect({0, 1, 10, 10});}}, + {1, 24, [](DisplayListBuilder& b) {b.drawRect({0, 0, 20, 10});}}, + {1, 24, [](DisplayListBuilder& b) {b.drawRect({0, 0, 10, 20});}}, + } + }, + { "DrawOval", { + {1, 24, [](DisplayListBuilder& b) {b.drawOval({0, 0, 10, 10});}}, + {1, 24, [](DisplayListBuilder& b) {b.drawOval({0, 1, 10, 10});}}, + {1, 24, [](DisplayListBuilder& b) {b.drawOval({0, 0, 20, 10});}}, + {1, 24, [](DisplayListBuilder& b) {b.drawOval({0, 0, 10, 20});}}, + } + }, + { "DrawCircle", { + {1, 16, [](DisplayListBuilder& b) {b.drawCircle({0, 0}, 10);}}, + {1, 16, [](DisplayListBuilder& b) {b.drawCircle({0, 5}, 10);}}, + {1, 16, [](DisplayListBuilder& b) {b.drawCircle({0, 0}, 20);}}, + } + }, + { "DrawRRect", { + {1, 56, [](DisplayListBuilder& b) {b.drawRRect(TestRRect);}}, + {1, 56, [](DisplayListBuilder& b) {b.drawRRect(TestRRect.makeOffset(5, 5));}}, + } + }, + { "DrawDRRect", { + {1, 112, [](DisplayListBuilder& b) {b.drawDRRect(TestRRect, TestInnerRRect);}}, + {1, 112, [](DisplayListBuilder& b) {b.drawDRRect(TestRRect.makeOffset(5, 5), + TestInnerRRect.makeOffset(4, 4));}}, + } + }, + { "DrawPath", { + {1, 24, [](DisplayListBuilder& b) {b.drawPath(TestPath1);}}, + {1, 24, [](DisplayListBuilder& b) {b.drawPath(TestPath2);}}, + } + }, + { "DrawArc", { + {1, 32, [](DisplayListBuilder& b) {b.drawArc(TestBounds, 45, 270, false);}}, + {1, 32, [](DisplayListBuilder& b) {b.drawArc(TestBounds.makeOffset(1, 1), + 45, 270, false);}}, + {1, 32, [](DisplayListBuilder& b) {b.drawArc(TestBounds, 30, 270, false);}}, + {1, 32, [](DisplayListBuilder& b) {b.drawArc(TestBounds, 45, 260, false);}}, + {1, 32, [](DisplayListBuilder& b) {b.drawArc(TestBounds, 45, 270, true);}}, + } + }, + { "DrawPoints", { + {1, 8 + TestPointCount * 8, + [](DisplayListBuilder& b) {b.drawPoints(SkCanvas::kPoints_PointMode, + TestPointCount, + TestPoints);}}, + {1, 8 + (TestPointCount - 1) * 8, + [](DisplayListBuilder& b) {b.drawPoints(SkCanvas::kPoints_PointMode, + TestPointCount - 1, + TestPoints);}}, + {1, 8 + TestPointCount * 8, + [](DisplayListBuilder& b) {b.drawPoints(SkCanvas::kLines_PointMode, + TestPointCount, + TestPoints);}}, + {1, 8 + TestPointCount * 8, + [](DisplayListBuilder& b) {b.drawPoints(SkCanvas::kPolygon_PointMode, + TestPointCount, + TestPoints);}}, + } + }, + { "DrawVertices", { + {1, 16, [](DisplayListBuilder& b) {b.drawVertices(TestVertices1, SkBlendMode::kSrcIn);}}, + {1, 16, [](DisplayListBuilder& b) {b.drawVertices(TestVertices1, SkBlendMode::kDstIn);}}, + {1, 16, [](DisplayListBuilder& b) {b.drawVertices(TestVertices2, SkBlendMode::kSrcIn);}}, + } + }, + { "DrawImage", { + {1, 40, [](DisplayListBuilder& b) {b.drawImage(TestImage1, {10, 10}, DisplayList::NearestSampling);}}, + {1, 40, [](DisplayListBuilder& b) {b.drawImage(TestImage1, {20, 10}, DisplayList::NearestSampling);}}, + {1, 40, [](DisplayListBuilder& b) {b.drawImage(TestImage1, {10, 20}, DisplayList::NearestSampling);}}, + {1, 40, [](DisplayListBuilder& b) {b.drawImage(TestImage1, {10, 10}, DisplayList::LinearSampling);}}, + {1, 40, [](DisplayListBuilder& b) {b.drawImage(TestImage2, {10, 10}, DisplayList::NearestSampling);}}, + } + }, + { "DrawImageRect", { + {1, 64, [](DisplayListBuilder& b) {b.drawImageRect(TestImage1, {10, 10, 20, 20}, {10, 10, 40, 40}, + DisplayList::NearestSampling);}}, + {1, 64, [](DisplayListBuilder& b) {b.drawImageRect(TestImage1, {10, 10, 25, 20}, {10, 10, 40, 40}, + DisplayList::NearestSampling);}}, + {1, 64, [](DisplayListBuilder& b) {b.drawImageRect(TestImage1, {10, 10, 20, 20}, {10, 10, 45, 40}, + DisplayList::NearestSampling);}}, + {1, 64, [](DisplayListBuilder& b) {b.drawImageRect(TestImage1, {10, 10, 20, 20}, {10, 10, 40, 40}, + DisplayList::LinearSampling);}}, + {1, 64, [](DisplayListBuilder& b) {b.drawImageRect(TestImage2, {10, 10, 20, 20}, {10, 10, 40, 40}, + DisplayList::NearestSampling);}}, + } + }, + { "DrawImageNine", { + {1, 48, [](DisplayListBuilder& b) {b.drawImageNine(TestImage1, {10, 10, 20, 20}, {10, 10, 40, 40}, + SkFilterMode::kNearest);}}, + {1, 48, [](DisplayListBuilder& b) {b.drawImageNine(TestImage1, {10, 10, 25, 20}, {10, 10, 40, 40}, + SkFilterMode::kNearest);}}, + {1, 48, [](DisplayListBuilder& b) {b.drawImageNine(TestImage1, {10, 10, 20, 20}, {10, 10, 45, 40}, + SkFilterMode::kNearest);}}, + {1, 48, [](DisplayListBuilder& b) {b.drawImageNine(TestImage1, {10, 10, 20, 20}, {10, 10, 40, 40}, + SkFilterMode::kLinear);}}, + {1, 48, [](DisplayListBuilder& b) {b.drawImageNine(TestImage2, {10, 10, 20, 20}, {10, 10, 40, 40}, + SkFilterMode::kNearest);}}, + } + }, + // TODO(flar): Skipping DrawLattice for now + { "DrawAtlas", { + {1, 40 + 32 + 32, [](DisplayListBuilder& b) { + static SkRSXform xforms[] = { {1, 0, 0, 0}, {0, 1, 0, 0} }; + static SkRect texs[] = { { 10, 10, 20, 20 }, {20, 20, 30, 30} }; + b.drawAtlas(TestImage1, xforms, texs, nullptr, 2, SkBlendMode::kSrcIn, + DisplayList::NearestSampling, nullptr);}}, + {1, 40 + 32 + 32, [](DisplayListBuilder& b) { + static SkRSXform xforms[] = { {0, 1, 0, 0}, {0, 1, 0, 0} }; + static SkRect texs[] = { { 10, 10, 20, 20 }, {20, 20, 30, 30} }; + b.drawAtlas(TestImage1, xforms, texs, nullptr, 2, SkBlendMode::kSrcIn, + DisplayList::NearestSampling, nullptr);}}, + {1, 40 + 32 + 32, [](DisplayListBuilder& b) { + static SkRSXform xforms[] = { {1, 0, 0, 0}, {0, 1, 0, 0} }; + static SkRect texs[] = { { 10, 10, 20, 20 }, {20, 25, 30, 30} }; + b.drawAtlas(TestImage1, xforms, texs, nullptr, 2, SkBlendMode::kSrcIn, + DisplayList::NearestSampling, nullptr);}}, + {1, 40 + 32 + 32, [](DisplayListBuilder& b) { + static SkRSXform xforms[] = { {1, 0, 0, 0}, {0, 1, 0, 0} }; + static SkRect texs[] = { { 10, 10, 20, 20 }, {20, 20, 30, 30} }; + b.drawAtlas(TestImage1, xforms, texs, nullptr, 2, SkBlendMode::kSrcIn, + DisplayList::LinearSampling, nullptr);}}, + {1, 40 + 32 + 32, [](DisplayListBuilder& b) { + static SkRSXform xforms[] = { {1, 0, 0, 0}, {0, 1, 0, 0} }; + static SkRect texs[] = { { 10, 10, 20, 20 }, {20, 20, 30, 30} }; + b.drawAtlas(TestImage1, xforms, texs, nullptr, 2, SkBlendMode::kDstIn, + DisplayList::NearestSampling, nullptr);}}, + {1, 56 + 32 + 32, [](DisplayListBuilder& b) { + static SkRSXform xforms[] = { {1, 0, 0, 0}, {0, 1, 0, 0} }; + static SkRect texs[] = { { 10, 10, 20, 20 }, {20, 20, 30, 30} }; + static SkRect cullRect = { 0, 0, 200, 200 }; + b.drawAtlas(TestImage2, xforms, texs, nullptr, 2, SkBlendMode::kSrcIn, + DisplayList::NearestSampling, &cullRect);}}, + {1, 40 + 32 + 32 + 8, [](DisplayListBuilder& b) { + static SkRSXform xforms[] = { {1, 0, 0, 0}, {0, 1, 0, 0} }; + static SkRect texs[] = { { 10, 10, 20, 20 }, {20, 20, 30, 30} }; + static SkColor colors[] = { SK_ColorBLUE, SK_ColorGREEN }; + b.drawAtlas(TestImage1, xforms, texs, colors, 2, SkBlendMode::kSrcIn, + DisplayList::NearestSampling, nullptr);}}, + {1, 56 + 32 + 32 + 8, [](DisplayListBuilder& b) { + static SkRSXform xforms[] = { {1, 0, 0, 0}, {0, 1, 0, 0} }; + static SkRect texs[] = { { 10, 10, 20, 20 }, {20, 20, 30, 30} }; + static SkColor colors[] = { SK_ColorBLUE, SK_ColorGREEN }; + static SkRect cullRect = { 0, 0, 200, 200 }; + b.drawAtlas(TestImage1, xforms, texs, colors, 2, SkBlendMode::kSrcIn, + DisplayList::NearestSampling, &cullRect);}}, + } + }, + { "DrawPicture", { + {1, 16, [](DisplayListBuilder& b) {b.drawPicture(TestPicture1);}}, + {1, 16, [](DisplayListBuilder& b) {b.drawPicture(TestPicture2);}}, + } + }, + { "DrawDisplayList", { + {1, 16, [](DisplayListBuilder& b) {b.drawDisplayList(TestDisplayList1);}}, + {1, 16, [](DisplayListBuilder& b) {b.drawDisplayList(TestDisplayList2);}}, + } + }, + // TODO(flar): Skipping DrawTextBlob for now + // TODO(flar): Skipping DrawShadowRec for now + { "DrawShadow", { + {1, 32, [](DisplayListBuilder& b) {b.drawShadow(TestPath1, SK_ColorGREEN, 1.0, false);}}, + {1, 32, [](DisplayListBuilder& b) {b.drawShadow(TestPath2, SK_ColorGREEN, 1.0, false);}}, + {1, 32, [](DisplayListBuilder& b) {b.drawShadow(TestPath1, SK_ColorBLUE, 1.0, false);}}, + {1, 32, [](DisplayListBuilder& b) {b.drawShadow(TestPath1, SK_ColorGREEN, 2.0, false);}}, + {1, 32, [](DisplayListBuilder& b) {b.drawShadow(TestPath1, SK_ColorGREEN, 1.0, true);}}, + } + }, +}; + +TEST(DisplayList, SingleOpSizes) { + for (auto& group : allGroups) { + for (size_t i = 0; i < group.variants.size(); i++) { + auto& invocation = group.variants[i]; + sk_sp dl = invocation.build(); + auto desc = group.op_name + "(variant " + std::to_string(i) + ")"; + ASSERT_EQ(dl->opCount(), invocation.opCount) << desc; + ASSERT_EQ(dl->bytes(), invocation.byteCount) << desc; + } + } +} + +TEST(DisplayList, SingleOpCompares) { + sk_sp empty = DisplayListBuilder().build(); + for (auto& group : allGroups) { + std::vector> lists1; + std::vector> lists2; + for (size_t i = 0; i < group.variants.size(); i++) { + lists1.push_back(group.variants[i].build()); + lists2.push_back(group.variants[i].build()); + auto desc = + group.op_name + "(variant " + std::to_string(i) + " == empty)"; + ASSERT_FALSE(lists1[i]->equals(*empty)) << desc; + ASSERT_FALSE(lists2[i]->equals(*empty)) << desc; + ASSERT_FALSE(empty->equals(*lists1[i])) << desc; + ASSERT_FALSE(empty->equals(*lists2[i])) << desc; + } + for (size_t i = 0; i < lists1.size(); i++) { + for (size_t j = 0; j < lists2.size(); j++) { + auto desc = group.op_name + "(variant " + std::to_string(i) + + " ==? variant " + std::to_string(j) + ")"; + if (i == j) { + ASSERT_TRUE(lists1[i]->equals(*lists2[j])) << desc; + ASSERT_TRUE(lists2[j]->equals(*lists1[i])) << desc; + } else { + ASSERT_FALSE(lists1[i]->equals(*lists2[j])) << desc; + ASSERT_FALSE(lists2[j]->equals(*lists1[i])) << desc; + } + } + } + } +} + +#ifdef DL_TEST_EXPERIMENTAL +static sk_sp build(size_t g_index, size_t v_index) { + DisplayListBuilder builder; + int opCount = 0; + size_t byteCount = 0; + for (size_t i = 0; i < allGroups.size(); i++) { + int j = (i == g_index ? v_index : 0); + if (j < 0) + continue; + DisplayListInvocationGroup& group = allGroups[i]; + DisplayListInvocation& invocation = group.variants[j]; + opCount += invocation.numOps; + byteCount += invocation.numBytes; + invocation.invoker(builder); + } + sk_sp dl = builder.build(); + std::string name; + if (g_index < 0) { + name = "Default"; + } else { + name = allGroups[g_index].op_name; + if (v_index < 0) { + name += " skipped"; + } else { + name += " variant " + std::to_string(v_index); + } + } + EXPECT_EQ(dl->opCount(), opCount) << name; + EXPECT_EQ(dl->bytes(), byteCount) << name; + return dl; +} + +TEST(DisplayList, OpListEquals) { + sk_sp default_dl = build(-1, -1); + ASSERT_EQ(default_dl.get(), default_dl.get()); + int group_count = static_cast(allGroups.size()); + for (int gi = 0; gi < group_count; gi++) { + sk_sp missing_dl = build(gi, -1); + ASSERT_EQ(missing_dl.get(), missing_dl.get()); + ASSERT_NE(default_dl.get(), missing_dl.get()); + DisplayListInvocationGroup& group = allGroups[gi]; + for (size_t vi = 0; vi < group.variants.size(); vi++) { + sk_sp variant_dl = build(gi, vi); + ASSERT_EQ(variant_dl.get(), variant_dl.get()); + if (vi == 0) { + ASSERT_TRUE(default_dl->equals(*variant_dl.get())); + ASSERT_EQ(default_dl.get(), variant_dl.get()); + } else { + ASSERT_NE(default_dl.get(), variant_dl.get()); + } + ASSERT_NE(missing_dl.get(), variant_dl.get()); + } + } +} +#endif // DL_TEST_EXPERIMENTAL + +} // namespace testing +} // namespace flutter diff --git a/flow/display_list_utils.cc b/flow/display_list_utils.cc new file mode 100644 index 0000000000000..734fa997c1a84 --- /dev/null +++ b/flow/display_list_utils.cc @@ -0,0 +1,411 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include +#include + +#include "flutter/flow/display_list_utils.h" +#include "flutter/flow/layers/physical_shape_layer.h" +#include "flutter/fml/logging.h" + +#include "third_party/skia/include/core/SkMaskFilter.h" +#include "third_party/skia/include/core/SkPath.h" +#include "third_party/skia/include/core/SkRSXform.h" +#include "third_party/skia/include/core/SkTextBlob.h" +#include "third_party/skia/include/utils/SkShadowUtils.h" + +// This header file cannot be included here, but we cannot +// record calls made by the SkShadowUtils without it. +// #include "third_party/skia/src/core/SkDrawShadowInfo.h" + +namespace flutter { + +// clang-format off +constexpr float invert_color_matrix[20] = { + -1.0, 0, 0, 1.0, 0, + 0, -1.0, 0, 1.0, 0, + 0, 0, -1.0, 1.0, 0, + 1.0, 1.0, 1.0, 1.0, 0 +}; +// clang-format on + +void SkPaintDispatchHelper::setAA(bool aa) { + paint_.setAntiAlias(aa); +} +void SkPaintDispatchHelper::setDither(bool dither) { + paint_.setDither(dither); +} +void SkPaintDispatchHelper::setInvertColors(bool invert) { + invert_colors_ = invert; + paint_.setColorFilter(makeColorFilter()); +} +void SkPaintDispatchHelper::setCaps(SkPaint::Cap cap) { + paint_.setStrokeCap(cap); +} +void SkPaintDispatchHelper::setJoins(SkPaint::Join join) { + paint_.setStrokeJoin(join); +} +void SkPaintDispatchHelper::setDrawStyle(SkPaint::Style style) { + paint_.setStyle(style); +} +void SkPaintDispatchHelper::setStrokeWidth(SkScalar width) { + paint_.setStrokeWidth(width); +} +void SkPaintDispatchHelper::setMiterLimit(SkScalar limit) { + paint_.setStrokeMiter(limit); +} +void SkPaintDispatchHelper::setColor(SkColor color) { + paint_.setColor(color); +} +void SkPaintDispatchHelper::setBlendMode(SkBlendMode mode) { + paint_.setBlendMode(mode); +} +void SkPaintDispatchHelper::setFilterQuality(SkFilterQuality quality) { + paint_.setFilterQuality(quality); +} +void SkPaintDispatchHelper::setShader(sk_sp shader) { + paint_.setShader(shader); +} +void SkPaintDispatchHelper::setImageFilter(sk_sp filter) { + paint_.setImageFilter(filter); +} +void SkPaintDispatchHelper::setColorFilter(sk_sp filter) { + color_filter_ = filter; + paint_.setColorFilter(makeColorFilter()); +} +void SkPaintDispatchHelper::setMaskFilter(sk_sp filter) { + paint_.setMaskFilter(filter); +} +void SkPaintDispatchHelper::setMaskBlurFilter(SkBlurStyle style, + SkScalar sigma) { + paint_.setMaskFilter(SkMaskFilter::MakeBlur(style, sigma)); +} + +sk_sp SkPaintDispatchHelper::makeColorFilter() { + if (!invert_colors_) { + return color_filter_; + } + sk_sp invert_filter = + SkColorFilters::Matrix(invert_color_matrix); + if (color_filter_) { + invert_filter = invert_filter->makeComposed(color_filter_); + } + return invert_filter; +} + +void SkMatrixDispatchHelper::translate(SkScalar tx, SkScalar ty) { + matrix_.preTranslate(tx, ty); +} +void SkMatrixDispatchHelper::scale(SkScalar sx, SkScalar sy) { + matrix_.preScale(sx, sy); +} +void SkMatrixDispatchHelper::rotate(SkScalar degrees) { + matrix_.preRotate(degrees); +} +void SkMatrixDispatchHelper::skew(SkScalar sx, SkScalar sy) { + matrix_.preSkew(sx, sy); +} +void SkMatrixDispatchHelper::transform2x3(SkScalar mxx, + SkScalar mxy, + SkScalar mxt, + SkScalar myx, + SkScalar myy, + SkScalar myt) { + matrix_.preConcat(SkMatrix::MakeAll(mxx, mxy, mxt, myx, myy, myt, 0, 0, 1)); +} +void SkMatrixDispatchHelper::transform3x3(SkScalar mxx, + SkScalar mxy, + SkScalar mxt, + SkScalar myx, + SkScalar myy, + SkScalar myt, + SkScalar px, + SkScalar py, + SkScalar pt) { + matrix_.preConcat( + SkMatrix::MakeAll(mxx, mxy, mxt, myx, myy, myt, px, py, pt)); +} +void SkMatrixDispatchHelper::save() { + saved_.push_back(matrix_); +} +void SkMatrixDispatchHelper::restore() { + matrix_ = saved_.back(); + saved_.pop_back(); +} +void SkMatrixDispatchHelper::reset() { + matrix_.reset(); +} + +void ClipBoundsDispatchHelper::clipRect(const SkRect& rect, + bool isAA, + SkClipOp clip_op) { + if (clip_op == SkClipOp::kIntersect) { + intersect(rect); + } +} +void ClipBoundsDispatchHelper::clipRRect(const SkRRect& rrect, + bool isAA, + SkClipOp clip_op) { + if (clip_op == SkClipOp::kIntersect) { + intersect(rrect.getBounds()); + } +} +void ClipBoundsDispatchHelper::clipPath(const SkPath& path, + bool isAA, + SkClipOp clip_op) { + if (clip_op == SkClipOp::kIntersect) { + intersect(path.getBounds()); + } +} +void ClipBoundsDispatchHelper::intersect(const SkRect& rect) { + SkRect devClipBounds = matrix().mapRect(rect); + if (!bounds_.intersect(devClipBounds)) { + bounds_.setEmpty(); + } +} +void ClipBoundsDispatchHelper::save() { + saved_.push_back(bounds_); +} +void ClipBoundsDispatchHelper::restore() { + bounds_ = saved_.back(); + saved_.pop_back(); +} + +void DisplayListBoundsCalculator::saveLayer(const SkRect* bounds, + bool withPaint) { + SkMatrixDispatchHelper::save(); + ClipBoundsDispatchHelper::save(); + SaveInfo info = + withPaint ? SaveLayerWithPaintInfo(this, accumulator_, matrix(), paint()) + : SaveLayerInfo(accumulator_, matrix()); + savedInfos_.push_back(info); + accumulator_ = info.save(); + SkMatrixDispatchHelper::reset(); +} +void DisplayListBoundsCalculator::save() { + SkMatrixDispatchHelper::save(); + ClipBoundsDispatchHelper::save(); + SaveInfo info = SaveInfo(accumulator_); + savedInfos_.push_back(info); + accumulator_ = info.save(); +} +void DisplayListBoundsCalculator::restore() { + if (!savedInfos_.empty()) { + SkMatrixDispatchHelper::restore(); + ClipBoundsDispatchHelper::restore(); + SaveInfo info = savedInfos_.back(); + savedInfos_.pop_back(); + accumulator_ = info.restore(); + } +} + +void DisplayListBoundsCalculator::drawPaint() { + if (!boundsCull_.isEmpty()) { + baseAccumulator_.accumulate(boundsCull_); + } +} +void DisplayListBoundsCalculator::drawColor(SkColor color, SkBlendMode mode) { + if (!boundsCull_.isEmpty()) { + baseAccumulator_.accumulate(boundsCull_); + } +} +void DisplayListBoundsCalculator::drawLine(const SkPoint& p0, + const SkPoint& p1) { + SkRect bounds = SkRect::MakeLTRB(p0.fX, p0.fY, p1.fX, p1.fY).makeSorted(); + accumulateRect(bounds, true); +} +void DisplayListBoundsCalculator::drawRect(const SkRect& rect) { + accumulateRect(rect); +} +void DisplayListBoundsCalculator::drawOval(const SkRect& bounds) { + accumulateRect(bounds); +} +void DisplayListBoundsCalculator::drawCircle(const SkPoint& center, + SkScalar radius) { + accumulateRect(SkRect::MakeLTRB(center.fX - radius, center.fY - radius, + center.fX + radius, center.fY + radius)); +} +void DisplayListBoundsCalculator::drawRRect(const SkRRect& rrect) { + accumulateRect(rrect.getBounds()); +} +void DisplayListBoundsCalculator::drawDRRect(const SkRRect& outer, + const SkRRect& inner) { + accumulateRect(outer.getBounds()); +} +void DisplayListBoundsCalculator::drawPath(const SkPath& path) { + accumulateRect(path.getBounds()); +} +void DisplayListBoundsCalculator::drawArc(const SkRect& bounds, + SkScalar start, + SkScalar sweep, + bool useCenter) { + // This could be tighter if we compute where the start and end + // angles are and then also consider the quadrants swept and + // the center if specified. + accumulateRect(bounds); +} +void DisplayListBoundsCalculator::drawPoints(SkCanvas::PointMode mode, + uint32_t count, + const SkPoint pts[]) { + if (count > 0) { + BoundsAccumulator ptBounds; + for (size_t i = 0; i < count; i++) { + ptBounds.accumulate(pts[i]); + } + accumulateRect(ptBounds.getBounds(), true); + } +} +void DisplayListBoundsCalculator::drawVertices(const sk_sp vertices, + SkBlendMode mode) { + accumulateRect(vertices->bounds()); +} +void DisplayListBoundsCalculator::drawImage(const sk_sp image, + const SkPoint point, + const SkSamplingOptions& sampling) { + SkRect bounds = SkRect::Make(image->bounds()); + bounds.offset(point); + accumulateRect(bounds); +} +void DisplayListBoundsCalculator::drawImageRect( + const sk_sp image, + const SkRect& src, + const SkRect& dst, + const SkSamplingOptions& sampling) { + accumulateRect(dst); +} +void DisplayListBoundsCalculator::drawImageNine(const sk_sp image, + const SkIRect& center, + const SkRect& dst, + SkFilterMode filter) { + accumulateRect(dst); +} +void DisplayListBoundsCalculator::drawImageLattice( + const sk_sp image, + const SkCanvas::Lattice& lattice, + const SkRect& dst, + SkFilterMode filter) { + accumulateRect(dst); +} +void DisplayListBoundsCalculator::drawAtlas(const sk_sp atlas, + const SkRSXform xform[], + const SkRect tex[], + const SkColor colors[], + int count, + SkBlendMode mode, + const SkSamplingOptions& sampling, + const SkRect* cullRect) { + SkPoint quad[4]; + BoundsAccumulator atlasBounds; + for (int i = 0; i < count; i++) { + const SkRect& src = tex[i]; + xform[i].toQuad(src.width(), src.height(), quad); + for (int j = 0; j < 4; j++) { + atlasBounds.accumulate(quad[j]); + } + } + if (atlasBounds.isNotEmpty()) { + accumulateRect(atlasBounds.getBounds()); + } +} +void DisplayListBoundsCalculator::drawPicture(const sk_sp picture) { + // TODO(flar) cull rect really cannot be trusted in general, but + // it will work for SkPictures generated from our own PictureRecorder. + accumulateRect(picture->cullRect()); +} +void DisplayListBoundsCalculator::drawDisplayList( + const sk_sp display_list) { + accumulateRect(display_list->bounds()); +} +void DisplayListBoundsCalculator::drawTextBlob(const sk_sp blob, + SkScalar x, + SkScalar y) { + accumulateRect(blob->bounds().makeOffset(x, y)); +} +// void DisplayListBoundsCalculator::drawShadowRec(const SkPath& path, +// const SkDrawShadowRec& rec) { +// SkRect bounds; +// SkDrawShadowMetrics::GetLocalBounds(path, rec, SkMatrix::I(), &bounds); +// accumulateRect(bounds, NON_GEOM); +// } +void DisplayListBoundsCalculator::drawShadow(const SkPath& path, + const SkColor color, + const SkScalar elevation, + bool occludes) { + SkRect bounds = + PhysicalShapeLayer::ComputeShadowBounds(path.getBounds(), elevation, 1.0); + accumulateRect(bounds); +} + +void DisplayListBoundsCalculator::accumulateRect(const SkRect& rect, + bool forceStroke) { + SkRect dstRect = rect; + const SkPaint& p = paint(); + if (forceStroke) { + if (p.getStyle() == SkPaint::kFill_Style) { + setDrawStyle(SkPaint::kStroke_Style); + } else { + forceStroke = false; + } + } + if (p.canComputeFastBounds()) { + dstRect = p.computeFastBounds(rect, &dstRect); + matrix().mapRect(&dstRect); + accumulator_->accumulate(dstRect); + } else { + baseAccumulator_.accumulate(boundsCull_); + } + if (forceStroke) { + setDrawStyle(SkPaint::kFill_Style); + } +} + +DisplayListBoundsCalculator::SaveInfo::SaveInfo(BoundsAccumulator* accumulator) + : savedAccumulator_(accumulator) {} +BoundsAccumulator* DisplayListBoundsCalculator::SaveInfo::save() { + // No need to swap out the accumulator for a normal save + return savedAccumulator_; +} +BoundsAccumulator* DisplayListBoundsCalculator::SaveInfo::restore() { + return savedAccumulator_; +} + +DisplayListBoundsCalculator::SaveLayerInfo::SaveLayerInfo( + BoundsAccumulator* accumulator, + const SkMatrix& matrix) + : SaveInfo(accumulator), matrix(matrix) {} +BoundsAccumulator* DisplayListBoundsCalculator::SaveLayerInfo::save() { + // Use the local layerAccumulator until restore is called and + // then transform (and adjust with paint if necessary) on restore() + return &layerAccumulator_; +} +BoundsAccumulator* DisplayListBoundsCalculator::SaveLayerInfo::restore() { + SkRect layerBounds = layerAccumulator_.getBounds(); + matrix.mapRect(&layerBounds); + savedAccumulator_->accumulate(layerBounds); + return savedAccumulator_; +} + +DisplayListBoundsCalculator::SaveLayerWithPaintInfo::SaveLayerWithPaintInfo( + DisplayListBoundsCalculator* calculator, + BoundsAccumulator* accumulator, + const SkMatrix& saveMatrix, + const SkPaint& savePaint) + : SaveLayerInfo(accumulator, saveMatrix), + calculator_(calculator), + paint_(savePaint) {} + +BoundsAccumulator* +DisplayListBoundsCalculator::SaveLayerWithPaintInfo::restore() { + SkRect layerBounds = layerAccumulator_.getBounds(); + if (paint_.canComputeFastBounds()) { + layerBounds = paint_.computeFastBounds(layerBounds, &layerBounds); + matrix.mapRect(&layerBounds); + savedAccumulator_->accumulate(layerBounds); + } else { + calculator_->baseAccumulator_.accumulate(calculator_->boundsCull_); + } + return savedAccumulator_; +} + +} // namespace flutter diff --git a/flow/display_list_utils.h b/flow/display_list_utils.h new file mode 100644 index 0000000000000..3715514e6511e --- /dev/null +++ b/flow/display_list_utils.h @@ -0,0 +1,369 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FLUTTER_FLOW_DISPLAY_LIST_UTILS_H_ +#define FLUTTER_FLOW_DISPLAY_LIST_UTILS_H_ + +#include "flutter/flow/display_list.h" + +#include "third_party/skia/include/core/SkMaskFilter.h" + +// This file contains various utility classes to ease implementing +// a Flutter DisplayList Dispatcher, including: +// +// IngoreAttributeDispatchHelper: +// IngoreClipDispatchHelper: +// IngoreTransformDispatchHelper +// Empty overrides of all of the associated methods of Dispatcher +// for dispatchers that only track some of the rendering operations +// +// SkPaintAttributeDispatchHelper: +// Tracks the attribute methods and maintains their state in an +// SkPaint object. +// SkMatrixTransformDispatchHelper: +// Tracks the transform methods and maintains their state in a +// (save/restore stack of) SkMatrix object. +// ClipBoundsDispatchHelper: +// Tracks the clip methods and maintains a culling box in a +// (save/restore stack of) SkRect culling rectangle. +// +// DisplayListBoundsCalculator: +// A class that can traverse an entire display list and compute +// a conservative estimate of the bounds of all of the rendering +// operations. + +namespace flutter { + +// A utility class that will ignore all Dispatcher methods relating +// to the setting of attributes. +class IngoreAttributeDispatchHelper : public virtual Dispatcher { + public: + void setAA(bool aa) override {} + void setDither(bool dither) override {} + void setInvertColors(bool invert) override {} + void setCaps(SkPaint::Cap cap) override {} + void setJoins(SkPaint::Join join) override {} + void setDrawStyle(SkPaint::Style style) override {} + void setStrokeWidth(SkScalar width) override {} + void setMiterLimit(SkScalar limit) override {} + void setColor(SkColor color) override {} + void setBlendMode(SkBlendMode mode) override {} + void setFilterQuality(SkFilterQuality quality) override {} + void setShader(sk_sp shader) override {} + void setImageFilter(sk_sp filter) override {} + void setColorFilter(sk_sp filter) override {} + void setMaskFilter(sk_sp filter) override {} + void setMaskBlurFilter(SkBlurStyle style, SkScalar sigma) override {} +}; + +// A utility class that will ignore all Dispatcher methods relating +// to setting a clip. +class IngoreClipDispatchHelper : public virtual Dispatcher { + void clipRect(const SkRect& rect, bool isAA, SkClipOp clip_op) override {} + void clipRRect(const SkRRect& rrect, bool isAA, SkClipOp clip_op) override {} + void clipPath(const SkPath& path, bool isAA, SkClipOp clip_op) override {} +}; + +// A utility class that will ignore all Dispatcher methods relating +// to modifying the transform. +class IngoreTransformDispatchHelper : public virtual Dispatcher { + 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 transform2x3(SkScalar mxx, + SkScalar mxy, + SkScalar mxt, + SkScalar myx, + SkScalar myy, + SkScalar myt) override {} + void transform3x3(SkScalar mxx, + SkScalar mxy, + SkScalar mxt, + SkScalar myx, + SkScalar myy, + SkScalar myt, + SkScalar px, + SkScalar py, + SkScalar pt) override {} +}; + +// 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 { + public: + void setAA(bool aa) override; + void setDither(bool dither) override; + void setInvertColors(bool invert) override; + void setCaps(SkPaint::Cap cap) override; + void setJoins(SkPaint::Join join) override; + void setDrawStyle(SkPaint::Style style) override; + void setStrokeWidth(SkScalar width) override; + void setMiterLimit(SkScalar limit) override; + void setColor(SkColor color) override; + void setBlendMode(SkBlendMode mode) override; + void setFilterQuality(SkFilterQuality quality) override; + void setShader(sk_sp shader) override; + void setImageFilter(sk_sp filter) override; + void setColorFilter(sk_sp filter) override; + void setMaskFilter(sk_sp filter) override; + void setMaskBlurFilter(SkBlurStyle style, SkScalar sigma) override; + + const SkPaint& paint() { return paint_; } + + private: + SkPaint paint_; + bool invert_colors_ = false; + sk_sp color_filter_; + + sk_sp makeColorFilter(); +}; + +class SkMatrixSource { + public: + virtual const SkMatrix& matrix() const = 0; +}; + +// A utility class that will monitor the Dispatcher methods relating +// to the transform and accumulate them into an SkMatrix which can +// be accessed at any time via getMatrix(). +// +// This class also implements an appropriate stack of transforms via +// 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: + 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 transform2x3(SkScalar mxx, + SkScalar mxy, + SkScalar mxt, + SkScalar myx, + SkScalar myy, + SkScalar myt) override; + void transform3x3(SkScalar mxx, + SkScalar mxy, + SkScalar mxt, + SkScalar myx, + SkScalar myy, + SkScalar myt, + SkScalar px, + SkScalar py, + SkScalar pt) override; + + void save() override; + void restore() override; + + const SkMatrix& matrix() const override { return matrix_; } + + protected: + void reset(); + + private: + SkMatrix matrix_; + std::vector saved_; +}; + +// A utility class that will monitor the Dispatcher methods relating +// to the clip and accumulate a conservative bounds into an SkRect +// which can be accessed at any time via getCullingBounds(). +// +// The subclass must implement a single virtual method matrix() +// which will happen automatically if the subclass also inherits +// from SkMatrixTransformDispatchHelper. +// +// This class also implements an appropriate stack of transforms via +// 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 { + public: + void clipRect(const SkRect& rect, bool isAA, SkClipOp clip_op) override; + void clipRRect(const SkRRect& rrect, bool isAA, SkClipOp clip_op) override; + void clipPath(const SkPath& path, bool isAA, SkClipOp clip_op) override; + + void save() override; + void restore() override; + + const SkRect& getCullingBounds() const { return bounds_; } + + private: + SkRect bounds_; + std::vector saved_; + + void intersect(const SkRect& clipBounds); +}; + +class BoundsAccumulator { + public: + void accumulate(const SkPoint& p) { accumulate(p.fX, p.fY); } + void accumulate(SkScalar x, SkScalar y) { + if (minX_ > x) + minX_ = x; + if (minY_ > y) + minY_ = y; + if (maxX_ < x) + maxX_ = x; + if (maxY_ < y) + maxY_ = y; + } + void accumulate(const SkRect& r) { + if (r.fLeft <= r.fRight && r.fTop <= r.fBottom) { + accumulate(r.fLeft, r.fTop); + accumulate(r.fRight, r.fBottom); + } + } + + bool isEmpty() const { return minX_ >= maxX_ || minY_ >= maxY_; } + bool isNotEmpty() const { return minX_ < maxX_ && minY_ < maxY_; } + + SkRect getBounds() const { + return (maxX_ > minX_ && maxY_ > minY_) + ? SkRect::MakeLTRB(minX_, minY_, maxX_, maxY_) + : SkRect::MakeEmpty(); + } + + private: + SkScalar minX_ = +MAXFLOAT; + SkScalar minY_ = +MAXFLOAT; + SkScalar maxX_ = -MAXFLOAT; + SkScalar maxY_ = -MAXFLOAT; +}; + +// This class implements all rendering methods and computes a liberal +// bounds of the rendering operations. +class DisplayListBoundsCalculator final + : public virtual Dispatcher, + public virtual SkPaintDispatchHelper, + public virtual SkMatrixDispatchHelper, + public virtual ClipBoundsDispatchHelper { + public: + // Construct a Calculator to determine the bounds of a list of + // DisplayList dispatcher method calls. Since 2 of the method calls + // have no intrinsic size because they render to the entire available, + // the |cullRect| provides a bounds for them to include. + DisplayListBoundsCalculator(const SkRect& cullRect = SkRect::MakeEmpty()) + : accumulator_(&baseAccumulator_), boundsCull_(cullRect) {} + + void saveLayer(const SkRect* bounds, bool withPaint) override; + void save() override; + void restore() override; + + void drawPaint() override; + void drawColor(SkColor color, SkBlendMode mode) 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; + void drawArc(const SkRect& bounds, + SkScalar start, + SkScalar sweep, + bool useCenter) override; + void drawPoints(SkCanvas::PointMode mode, + uint32_t count, + const SkPoint pts[]) override; + void drawVertices(const sk_sp vertices, + SkBlendMode mode) override; + void drawImage(const sk_sp image, + const SkPoint point, + const SkSamplingOptions& sampling) override; + void drawImageRect(const sk_sp image, + const SkRect& src, + const SkRect& dst, + const SkSamplingOptions& sampling) override; + void drawImageNine(const sk_sp image, + const SkIRect& center, + const SkRect& dst, + SkFilterMode filter) override; + void drawImageLattice(const sk_sp image, + const SkCanvas::Lattice& lattice, + const SkRect& dst, + SkFilterMode filter) override; + void drawAtlas(const sk_sp atlas, + const SkRSXform xform[], + const SkRect tex[], + const SkColor colors[], + int count, + SkBlendMode mode, + const SkSamplingOptions& sampling, + const SkRect* cullRect) override; + void drawPicture(const sk_sp picture) override; + void drawDisplayList(const sk_sp display_list) override; + void drawTextBlob(const sk_sp blob, + SkScalar x, + SkScalar y) override; + // void drawShadowRec(const SkPath&, const SkDrawShadowRec&) override; + void drawShadow(const SkPath& path, + const SkColor color, + const SkScalar elevation, + bool occludes) override; + + SkRect getBounds() { return accumulator_->getBounds(); } + + private: + // current accumulator based on saveLayer history + BoundsAccumulator* accumulator_; + + // Only used for drawColor and drawPaint and paint objects that + // cannot support fast bounds. + SkRect boundsCull_; + BoundsAccumulator baseAccumulator_; + + class SaveInfo { + public: + SaveInfo(BoundsAccumulator* accumulator); + virtual ~SaveInfo() = default; + + virtual BoundsAccumulator* save(); + virtual BoundsAccumulator* restore(); + + protected: + BoundsAccumulator* savedAccumulator_; + }; + + class SaveLayerInfo : public SaveInfo { + public: + SaveLayerInfo(BoundsAccumulator* accumulator, const SkMatrix& matrix); + virtual ~SaveLayerInfo() = default; + + BoundsAccumulator* save() override; + BoundsAccumulator* restore() override; + + protected: + BoundsAccumulator layerAccumulator_; + const SkMatrix matrix; + }; + + class SaveLayerWithPaintInfo : public SaveLayerInfo { + public: + SaveLayerWithPaintInfo(DisplayListBoundsCalculator* calculator, + BoundsAccumulator* accumulator, + const SkMatrix& saveMatrix, + const SkPaint& savePaint); + virtual ~SaveLayerWithPaintInfo() = default; + + BoundsAccumulator* restore() override; + + protected: + DisplayListBoundsCalculator* calculator_; + + SkPaint paint_; + }; + + std::vector savedInfos_; + + void accumulateRect(const SkRect& rect, bool forceStroke = false); +}; + +} // namespace flutter + +#endif // FLUTTER_FLOW_DISPLAY_LIST_UTILS_H_ diff --git a/flow/layers/display_list_layer.cc b/flow/layers/display_list_layer.cc new file mode 100644 index 0000000000000..e27dee3c9431f --- /dev/null +++ b/flow/layers/display_list_layer.cc @@ -0,0 +1,130 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "flutter/flow/layers/display_list_layer.h" + +#include "flutter/flow/display_list_canvas.h" + +namespace flutter { + +DisplayListLayer::DisplayListLayer(const SkPoint& offset, + sk_sp display_list, + bool is_complex, + bool will_change) + : offset_(offset), + display_list_(display_list), + is_complex_(is_complex), + will_change_(will_change) {} + +#ifdef FLUTTER_ENABLE_DIFF_CONTEXT + +bool DisplayListLayer::IsReplacing(DiffContext* context, + const Layer* layer) const { + // Only return true for identical display lists; This way + // ContainerLayer::DiffChildren can detect when a display list layer + // got inserted between other display list layers + auto old_layer = layer->as_display_list_layer(); + return old_layer != nullptr && offset_ == old_layer->offset_ && + Compare(context->statistics(), this, old_layer); +} + +void DisplayListLayer::Diff(DiffContext* context, const Layer* old_layer) { + DiffContext::AutoSubtreeRestore subtree(context); + if (!context->IsSubtreeDirty()) { +#ifndef NDEBUG + FML_DCHECK(old_layer); + auto prev = old_layer->as_display_list_layer(); + DiffContext::Statistics dummy_statistics; + // IsReplacing has already determined that the display list is same + FML_DCHECK(prev->offset_ == offset_ && + Compare(dummy_statistics, this, prev)); +#endif + } + context->PushTransform(SkMatrix::Translate(offset_.x(), offset_.y())); + context->AddLayerBounds(display_list_->bounds()); + context->SetLayerPaintRegion(this, context->CurrentSubtreeRegion()); +} + +bool DisplayListLayer::Compare(DiffContext::Statistics& statistics, + const DisplayListLayer* l1, + const DisplayListLayer* l2) { + const auto& dl1 = l1->display_list_; + const auto& dl2 = l2->display_list_; + if (dl1.get() == dl2.get()) { + statistics.AddSameInstancePicture(); + return true; + } + auto op_cnt_1 = dl1->opCount(); + auto op_cnt_2 = dl2->opCount(); + if (op_cnt_1 != op_cnt_2 || dl1->bounds() != dl2->bounds()) { + statistics.AddNewPicture(); + return false; + } + + if (op_cnt_1 > 10) { + statistics.AddPictureTooComplexToCompare(); + return false; + } + + statistics.AddDeepComparePicture(); + + auto res = dl1->equals(*dl2); + if (res) { + statistics.AddDifferentInstanceButEqualPicture(); + } else { + statistics.AddNewPicture(); + } + return res; +} + +#endif // FLUTTER_ENABLE_DIFF_CONTEXT + +void DisplayListLayer::Preroll(PrerollContext* context, + const SkMatrix& matrix) { + TRACE_EVENT0("flutter", "DisplayListLayer::Preroll"); + +#if defined(LEGACY_FUCHSIA_EMBEDDER) + CheckForChildLayerBelow(context); +#endif + + DisplayList* disp_list = display_list(); + + if (auto* cache = context->raster_cache) { + TRACE_EVENT0("flutter", "DisplayListLayer::RasterCache (Preroll)"); + + SkMatrix ctm = matrix; + ctm.preTranslate(offset_.x(), offset_.y()); +#ifndef SUPPORT_FRACTIONAL_TRANSLATION + ctm = RasterCache::GetIntegralTransCTM(ctm); +#endif + cache->Prepare(context->gr_context, disp_list, ctm, + context->dst_color_space, is_complex_, will_change_); + } + + SkRect bounds = disp_list->bounds().makeOffset(offset_.x(), offset_.y()); + set_paint_bounds(bounds); +} + +void DisplayListLayer::Paint(PaintContext& context) const { + TRACE_EVENT0("flutter", "DisplayListLayer::Paint"); + FML_DCHECK(display_list_.get()); + FML_DCHECK(needs_painting(context)); + + SkAutoCanvasRestore save(context.leaf_nodes_canvas, true); + context.leaf_nodes_canvas->translate(offset_.x(), offset_.y()); +#ifndef SUPPORT_FRACTIONAL_TRANSLATION + context.leaf_nodes_canvas->setMatrix(RasterCache::GetIntegralTransCTM( + context.leaf_nodes_canvas->getTotalMatrix())); +#endif + + if (context.raster_cache && + context.raster_cache->Draw(*display_list(), *context.leaf_nodes_canvas)) { + TRACE_EVENT_INSTANT0("flutter", "raster cache hit"); + return; + } + + display_list()->renderTo(context.leaf_nodes_canvas); +} + +} // namespace flutter diff --git a/flow/layers/display_list_layer.h b/flow/layers/display_list_layer.h new file mode 100644 index 0000000000000..5376e7543c903 --- /dev/null +++ b/flow/layers/display_list_layer.h @@ -0,0 +1,59 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FLUTTER_FLOW_LAYERS_DISPLAY_LIST_LAYER_H_ +#define FLUTTER_FLOW_LAYERS_DISPLAY_LIST_LAYER_H_ + +#include "flutter/flow/display_list.h" +#include "flutter/flow/layers/layer.h" + +namespace flutter { + +class DisplayListLayer : public Layer { + public: + DisplayListLayer(const SkPoint& offset, + sk_sp display_list, + bool is_complex, + bool will_change); + + DisplayList* display_list() const { return display_list_.get(); } + +#ifdef FLUTTER_ENABLE_DIFF_CONTEXT + + bool IsReplacing(DiffContext* context, const Layer* layer) const override; + + void Diff(DiffContext* context, const Layer* old_layer) override; + + const DisplayListLayer* as_display_list_layer() const override { + return this; + } + +#endif // FLUTTER_ENABLE_DIFF_CONTEXT + + void Preroll(PrerollContext* frame, const SkMatrix& matrix) override; + + void Paint(PaintContext& context) const override; + + private: + SkPoint offset_; + sk_sp display_list_; + bool is_complex_ = false; + bool will_change_ = false; + +#ifdef FLUTTER_ENABLE_DIFF_CONTEXT + + sk_sp SerializedPicture() const; + mutable sk_sp cached_serialized_picture_; + static bool Compare(DiffContext::Statistics& statistics, + const DisplayListLayer* l1, + const DisplayListLayer* l2); + +#endif // FLUTTER_ENABLE_DIFF_CONTEXT + + FML_DISALLOW_COPY_AND_ASSIGN(DisplayListLayer); +}; + +} // namespace flutter + +#endif // FLUTTER_FLOW_LAYERS_DISPLAY_LIST_LAYER_H_ diff --git a/flow/layers/display_list_layer_unittests.cc b/flow/layers/display_list_layer_unittests.cc new file mode 100644 index 0000000000000..1ccd0f291c791 --- /dev/null +++ b/flow/layers/display_list_layer_unittests.cc @@ -0,0 +1,161 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#define FML_USED_ON_EMBEDDER + +#include "flutter/flow/layers/display_list_layer.h" + +#include "flutter/flow/testing/diff_context_test.h" +#include "flutter/fml/macros.h" +#include "flutter/testing/mock_canvas.h" + +#ifndef SUPPORT_FRACTIONAL_TRANSLATION +#include "flutter/flow/raster_cache.h" +#endif + +namespace flutter { +namespace testing { + +using DisplayListLayerTest = LayerTest; + +#ifndef NDEBUG +TEST_F(DisplayListLayerTest, PaintBeforePrerollInvalidDisplayListDies) { + const SkPoint layer_offset = SkPoint::Make(0.0f, 0.0f); + auto layer = std::make_shared( + layer_offset, sk_ref_sp(nullptr), false, false); + + EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()), + "display_list_\\.get\\(\\)"); +} + +TEST_F(DisplayListLayerTest, PaintBeforePrerollDies) { + const SkPoint layer_offset = SkPoint::Make(0.0f, 0.0f); + auto layer = std::make_shared( + layer_offset, sk_make_sp(), false, false); + + EXPECT_EQ(layer->paint_bounds(), SkRect::MakeEmpty()); + EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()), + "needs_painting\\(context\\)"); +} + +TEST_F(DisplayListLayerTest, PaintingEmptyLayerDies) { + const SkPoint layer_offset = SkPoint::Make(0.0f, 0.0f); + auto layer = std::make_shared( + layer_offset, sk_make_sp(), false, false); + + layer->Preroll(preroll_context(), SkMatrix()); + EXPECT_EQ(layer->paint_bounds(), SkRect::MakeEmpty()); + EXPECT_FALSE(layer->needs_painting(paint_context())); + EXPECT_FALSE(layer->needs_system_composite()); + + EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()), + "needs_painting\\(context\\)"); +} +#endif + +TEST_F(DisplayListLayerTest, InvalidDisplayListDies) { + const SkPoint layer_offset = SkPoint::Make(0.0f, 0.0f); + auto layer = std::make_shared( + layer_offset, sk_ref_sp(nullptr), false, false); + + // Crashes reading a nullptr. + EXPECT_DEATH_IF_SUPPORTED(layer->Preroll(preroll_context(), SkMatrix()), ""); +} + +TEST_F(DisplayListLayerTest, SimpleDisplayList) { + const SkPoint layer_offset = SkPoint::Make(1.5f, -0.5f); + const SkMatrix layer_offset_matrix = + SkMatrix::Translate(layer_offset.fX, layer_offset.fY); + const SkRect picture_bounds = SkRect::MakeLTRB(5.0f, 6.0f, 20.5f, 21.5f); + DisplayListBuilder builder; + builder.drawRect(picture_bounds); + auto display_list = builder.build(); + auto layer = std::make_shared(layer_offset, display_list, + false, false); + + layer->Preroll(preroll_context(), SkMatrix()); + EXPECT_EQ(layer->paint_bounds(), + picture_bounds.makeOffset(layer_offset.fX, layer_offset.fY)); + EXPECT_EQ(layer->display_list(), display_list.get()); + EXPECT_TRUE(layer->needs_painting(paint_context())); + EXPECT_FALSE(layer->needs_system_composite()); + + layer->Paint(paint_context()); + auto expected_draw_calls = std::vector( + {MockCanvas::DrawCall{0, MockCanvas::SaveData{1}}, + MockCanvas::DrawCall{ + 1, MockCanvas::ConcatMatrixData{SkM44(layer_offset_matrix)}}, +#ifndef SUPPORT_FRACTIONAL_TRANSLATION + MockCanvas::DrawCall{ + 1, MockCanvas::SetMatrixData{SkM44( + RasterCache::GetIntegralTransCTM(layer_offset_matrix))}}, +#endif + MockCanvas::DrawCall{ + 1, MockCanvas::DrawRectData{picture_bounds, SkPaint()}}, + MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}}); + EXPECT_EQ(mock_canvas().draw_calls(), expected_draw_calls); +} + +#ifdef FLUTTER_ENABLE_DIFF_CONTEXT + +using DisplayListLayerDiffTest = DiffContextTest; + +TEST_F(DisplayListLayerDiffTest, SimpleDisplayList) { + auto display_list = CreateDisplayList(SkRect::MakeLTRB(10, 10, 60, 60), 1); + + MockLayerTree tree1; + tree1.root()->Add(CreateDisplayListLayer(display_list)); + + auto damage = DiffLayerTree(tree1, MockLayerTree()); + EXPECT_EQ(damage.frame_damage, SkIRect::MakeLTRB(10, 10, 60, 60)); + + MockLayerTree tree2; + tree2.root()->Add(CreateDisplayListLayer(display_list)); + + damage = DiffLayerTree(tree2, tree1); + EXPECT_TRUE(damage.frame_damage.isEmpty()); + + MockLayerTree tree3; + damage = DiffLayerTree(tree3, tree2); + EXPECT_EQ(damage.frame_damage, SkIRect::MakeLTRB(10, 10, 60, 60)); +} + +TEST_F(DisplayListLayerDiffTest, DisplayListCompare) { + MockLayerTree tree1; + auto display_list1 = CreateDisplayList(SkRect::MakeLTRB(10, 10, 60, 60), 1); + tree1.root()->Add(CreateDisplayListLayer(display_list1)); + + auto damage = DiffLayerTree(tree1, MockLayerTree()); + EXPECT_EQ(damage.frame_damage, SkIRect::MakeLTRB(10, 10, 60, 60)); + + MockLayerTree tree2; + auto display_list2 = CreateDisplayList(SkRect::MakeLTRB(10, 10, 60, 60), 1); + tree2.root()->Add(CreateDisplayListLayer(display_list2)); + + damage = DiffLayerTree(tree2, tree1); + EXPECT_EQ(damage.frame_damage, SkIRect::MakeEmpty()); + + MockLayerTree tree3; + auto display_list3 = CreateDisplayList(SkRect::MakeLTRB(10, 10, 60, 60), 1); + // add offset + tree3.root()->Add( + CreateDisplayListLayer(display_list3, SkPoint::Make(10, 10))); + + damage = DiffLayerTree(tree3, tree2); + EXPECT_EQ(damage.frame_damage, SkIRect::MakeLTRB(10, 10, 70, 70)); + + MockLayerTree tree4; + // different color + auto display_list4 = CreateDisplayList(SkRect::MakeLTRB(10, 10, 60, 60), 2); + tree4.root()->Add( + CreateDisplayListLayer(display_list4, SkPoint::Make(10, 10))); + + damage = DiffLayerTree(tree4, tree3); + EXPECT_EQ(damage.frame_damage, SkIRect::MakeLTRB(20, 20, 70, 70)); +} + +#endif + +} // namespace testing +} // namespace flutter diff --git a/flow/layers/layer.h b/flow/layers/layer.h index ec7c00d6247fb..9ad88e6b7843e 100644 --- a/flow/layers/layer.h +++ b/flow/layers/layer.h @@ -64,6 +64,7 @@ struct PrerollContext { }; class PictureLayer; +class DisplayListLayer; class PerformanceOverlayLayer; class TextureLayer; @@ -239,6 +240,9 @@ class Layer { #ifdef FLUTTER_ENABLE_DIFF_CONTEXT virtual const PictureLayer* as_picture_layer() const { return nullptr; } + virtual const DisplayListLayer* as_display_list_layer() const { + return nullptr; + } virtual const TextureLayer* as_texture_layer() const { return nullptr; } virtual const PerformanceOverlayLayer* as_performance_overlay_layer() const { return nullptr; diff --git a/flow/raster_cache.cc b/flow/raster_cache.cc index e748520006724..bb2f67fe40d07 100644 --- a/flow/raster_cache.cc +++ b/flow/raster_cache.cc @@ -62,6 +62,26 @@ static bool CanRasterizePicture(SkPicture* picture) { return true; } +static bool CanRasterizeDisplayList(DisplayList* display_list) { + if (display_list == nullptr) { + return false; + } + + const SkRect cull_rect = display_list->bounds(); + + if (cull_rect.isEmpty()) { + // No point in ever rasterizing an empty display list. + return false; + } + + if (!cull_rect.isFinite()) { + // Cannot attempt to rasterize into an infinitely large surface. + return false; + } + + return true; +} + static bool IsPictureWorthRasterizing(SkPicture* picture, bool will_change, bool is_complex) { @@ -88,6 +108,32 @@ static bool IsPictureWorthRasterizing(SkPicture* picture, return picture->approximateOpCount() > 5; } +static bool IsDisplayListWorthRasterizing(DisplayList* display_list, + bool will_change, + bool is_complex) { + if (will_change) { + // If the display list is going to change in the future, there is no point + // in doing to extra work to rasterize. + return false; + } + + if (!CanRasterizeDisplayList(display_list)) { + // No point in deciding whether the display list is worth rasterizing if it + // cannot be rasterized at all. + return false; + } + + if (is_complex) { + // The caller seems to have extra information about the display list and + // thinks the display list is always worth rasterizing. + return true; + } + + // TODO(abarth): We should find a better heuristic here that lets us avoid + // wasting memory on trivial layers that are easy to re-rasterize every frame. + return display_list->opCount() > 5; +} + /// @note Procedure doesn't copy all closures. static std::unique_ptr Rasterize( GrDirectContext* context, @@ -136,6 +182,17 @@ std::unique_ptr RasterCache::RasterizePicture( [=](SkCanvas* canvas) { canvas->drawPicture(picture); }); } +std::unique_ptr RasterCache::RasterizeDisplayList( + DisplayList* display_list, + GrDirectContext* context, + const SkMatrix& ctm, + SkColorSpace* dst_color_space, + bool checkerboard) const { + return Rasterize(context, ctm, dst_color_space, checkerboard, + display_list->bounds(), + [=](SkCanvas* canvas) { display_list->renderTo(canvas); }); +} + void RasterCache::Prepare(PrerollContext* context, Layer* layer, const SkMatrix& ctm) { @@ -223,6 +280,52 @@ bool RasterCache::Prepare(GrDirectContext* context, return true; } +bool RasterCache::Prepare(GrDirectContext* context, + DisplayList* display_list, + const SkMatrix& transformation_matrix, + SkColorSpace* dst_color_space, + bool is_complex, + bool will_change) { + // Disabling caching when access_threshold is zero is historic behavior. + if (access_threshold_ == 0) { + return false; + } + if (picture_cached_this_frame_ >= picture_cache_limit_per_frame_) { + return false; + } + if (!IsDisplayListWorthRasterizing(display_list, will_change, is_complex)) { + // We only deal with display lists that are worthy of rasterization. + return false; + } + + // Decompose the matrix (once) for all subsequent operations. We want to make + // sure to avoid volumetric distortions while accounting for scaling. + const MatrixDecomposition matrix(transformation_matrix); + + if (!matrix.IsValid()) { + // The matrix was singular. No point in going further. + return false; + } + + DisplayListRasterCacheKey cache_key(display_list->uniqueID(), + transformation_matrix); + + // Creates an entry, if not present prior. + Entry& entry = display_list_cache_[cache_key]; + if (entry.access_count < access_threshold_) { + // Frame threshold has not yet been reached. + return false; + } + + if (!entry.image) { + entry.image = + RasterizeDisplayList(display_list, context, transformation_matrix, + dst_color_space, checkerboard_images_); + picture_cached_this_frame_++; + } + return true; +} + bool RasterCache::Draw(const SkPicture& picture, SkCanvas& canvas) const { PictureRasterCacheKey cache_key(picture.uniqueID(), canvas.getTotalMatrix()); auto it = picture_cache_.find(cache_key); @@ -242,6 +345,27 @@ bool RasterCache::Draw(const SkPicture& picture, SkCanvas& canvas) const { return false; } +bool RasterCache::Draw(const DisplayList& display_list, + SkCanvas& canvas) const { + DisplayListRasterCacheKey cache_key(display_list.uniqueID(), + canvas.getTotalMatrix()); + auto it = display_list_cache_.find(cache_key); + if (it == display_list_cache_.end()) { + return false; + } + + Entry& entry = it->second; + entry.access_count++; + entry.used_this_frame = true; + + if (entry.image) { + entry.image->draw(canvas, nullptr); + return true; + } + + return false; +} + bool RasterCache::Draw(const Layer* layer, SkCanvas& canvas, SkPaint* paint) const { @@ -265,6 +389,7 @@ bool RasterCache::Draw(const Layer* layer, void RasterCache::SweepAfterFrame() { SweepOneCacheAfterFrame(picture_cache_); + SweepOneCacheAfterFrame(display_list_cache_); SweepOneCacheAfterFrame(layer_cache_); picture_cached_this_frame_ = 0; TraceStatsToTimeline(); @@ -272,11 +397,13 @@ void RasterCache::SweepAfterFrame() { void RasterCache::Clear() { picture_cache_.clear(); + display_list_cache_.clear(); layer_cache_.clear(); } size_t RasterCache::GetCachedEntriesCount() const { - return layer_cache_.size() + picture_cache_.size(); + return layer_cache_.size() + picture_cache_.size() + + display_list_cache_.size(); } size_t RasterCache::GetLayerCachedEntriesCount() const { @@ -287,6 +414,10 @@ size_t RasterCache::GetPictureCachedEntriesCount() const { return picture_cache_.size(); } +size_t RasterCache::GetDisplayListCachedEntriesCount() const { + return display_list_cache_.size(); +} + void RasterCache::SetCheckboardCacheImages(bool checkerboard) { if (checkerboard_images_ == checkerboard) { return; @@ -305,7 +436,10 @@ void RasterCache::TraceStatsToTimeline() const { "LayerCount", layer_cache_.size(), "LayerMBytes", EstimateLayerCacheByteSize() / kMegaByteSizeInBytes, "PictureCount", picture_cache_.size(), "PictureMBytes", - EstimatePictureCacheByteSize() / kMegaByteSizeInBytes); + EstimatePictureCacheByteSize() / kMegaByteSizeInBytes, + "DisplayListCount", display_list_cache_.size(), + "DisplayListMBytes", + EstimateDisplayListCacheByteSize() / kMegaByteSizeInBytes); #endif // !FLUTTER_RELEASE } @@ -330,4 +464,14 @@ size_t RasterCache::EstimatePictureCacheByteSize() const { return picture_cache_bytes; } +size_t RasterCache::EstimateDisplayListCacheByteSize() const { + size_t display_list_cache_bytes = 0; + for (const auto& item : display_list_cache_) { + if (item.second.image) { + display_list_cache_bytes += item.second.image->image_bytes(); + } + } + return display_list_cache_bytes; +} + } // namespace flutter diff --git a/flow/raster_cache.h b/flow/raster_cache.h index e2b317cb55d11..a440ade7919d9 100644 --- a/flow/raster_cache.h +++ b/flow/raster_cache.h @@ -8,6 +8,7 @@ #include #include +#include "flutter/flow/display_list.h" #include "flutter/flow/raster_cache_key.h" #include "flutter/fml/macros.h" #include "flutter/fml/memory/weak_ptr.h" @@ -74,6 +75,12 @@ class RasterCache { const SkMatrix& ctm, SkColorSpace* dst_color_space, bool checkerboard) const; + virtual std::unique_ptr RasterizeDisplayList( + DisplayList* display_list, + GrDirectContext* context, + const SkMatrix& ctm, + SkColorSpace* dst_color_space, + bool checkerboard) const; /** * @brief Rasterize an engine Layer and produce a RasterCacheResult @@ -138,6 +145,12 @@ class RasterCache { SkColorSpace* dst_color_space, bool is_complex, bool will_change); + bool Prepare(GrDirectContext* context, + DisplayList* display_list, + const SkMatrix& transformation_matrix, + SkColorSpace* dst_color_space, + bool is_complex, + bool will_change); void Prepare(PrerollContext* context, Layer* layer, const SkMatrix& ctm); @@ -146,6 +159,11 @@ class RasterCache { // Return true if it's found and drawn. bool Draw(const SkPicture& picture, SkCanvas& canvas) const; + // Find the raster cache for the display list and draw it to the canvas. + // + // Return true if it's found and drawn. + bool Draw(const DisplayList& display_list, SkCanvas& canvas) const; + // Find the raster cache for the layer and draw it to the canvas. // // Additional paint can be given to change how the raster cache is drawn @@ -168,6 +186,8 @@ class RasterCache { size_t GetPictureCachedEntriesCount() const; + size_t GetDisplayListCachedEntriesCount() const; + /** * @brief Estimate how much memory is used by picture raster cache entries in * bytes. @@ -178,6 +198,16 @@ class RasterCache { */ size_t EstimatePictureCacheByteSize() const; + /** + * @brief Estimate how much memory is used by display list raster cache + * entries in bytes. + * + * Only SkImage's memory usage is counted as other objects are often much + * smaller compared to SkImage. SkImageInfo::computeMinByteSize is used to + * estimate the SkImage memory usage. + */ + size_t EstimateDisplayListCacheByteSize() const; + /** * @brief Estimate how much memory is used by layer raster cache entries in * bytes. @@ -216,6 +246,7 @@ class RasterCache { const size_t picture_cache_limit_per_frame_; size_t picture_cached_this_frame_ = 0; mutable PictureRasterCacheKey::Map picture_cache_; + mutable DisplayListRasterCacheKey::Map display_list_cache_; mutable LayerRasterCacheKey::Map layer_cache_; bool checkerboard_images_; diff --git a/flow/raster_cache_key.h b/flow/raster_cache_key.h index cc4c5fe2b0c7d..21d6bfe3dfdcb 100644 --- a/flow/raster_cache_key.h +++ b/flow/raster_cache_key.h @@ -52,6 +52,9 @@ class RasterCacheKey { // The ID is the uint32_t picture uniqueID using PictureRasterCacheKey = RasterCacheKey; +// The ID is the uint32_t DisplayList uniqueID +using DisplayListRasterCacheKey = RasterCacheKey; + class Layer; // The ID is the uint64_t layer unique_id diff --git a/flow/testing/diff_context_test.cc b/flow/testing/diff_context_test.cc index 1321c617ef12a..ee6c69216c192 100644 --- a/flow/testing/diff_context_test.cc +++ b/flow/testing/diff_context_test.cc @@ -42,6 +42,20 @@ std::shared_ptr DiffContextTest::CreatePictureLayer( offset, SkiaGPUObject(picture, unref_queue()), false, false); } +sk_sp DiffContextTest::CreateDisplayList(const SkRect& bounds, + SkColor color) { + DisplayListBuilder builder; + builder.setColor(color); + builder.drawRect(bounds); + return builder.build(); +} + +std::shared_ptr DiffContextTest::CreateDisplayListLayer( + sk_sp display_list, + const SkPoint& offset) { + return std::make_shared(offset, display_list, false, false); +} + std::shared_ptr DiffContextTest::CreateContainerLayer( std::initializer_list> layers) { auto res = std::make_shared(); diff --git a/flow/testing/diff_context_test.h b/flow/testing/diff_context_test.h index 5c5b574b735ac..75341146d98e6 100644 --- a/flow/testing/diff_context_test.h +++ b/flow/testing/diff_context_test.h @@ -3,6 +3,7 @@ // found in the LICENSE file. #include "flutter/flow/layers/container_layer.h" +#include "flutter/flow/layers/display_list_layer.h" #include "flutter/flow/layers/picture_layer.h" #include "flutter/flow/testing/skia_gpu_object_layer_test.h" #include "third_party/skia/include/core/SkPicture.h" @@ -48,6 +49,14 @@ class DiffContextTest : public ThreadTest { sk_sp picture, const SkPoint& offset = SkPoint::Make(0, 0)); + // Create display list consisting of filled rect with given color; Being able + // to specify different color is useful to test deep comparison of pictures + sk_sp CreateDisplayList(const SkRect& bounds, uint32_t color); + + std::shared_ptr CreateDisplayListLayer( + sk_sp display_list, + const SkPoint& offset = SkPoint::Make(0, 0)); + std::shared_ptr CreateContainerLayer( std::initializer_list> layers); diff --git a/lib/ui/compositing/scene_builder.cc b/lib/ui/compositing/scene_builder.cc index c3e5377287697..e7c2b3ea151cc 100644 --- a/lib/ui/compositing/scene_builder.cc +++ b/lib/ui/compositing/scene_builder.cc @@ -10,6 +10,7 @@ #include "flutter/flow/layers/clip_rrect_layer.h" #include "flutter/flow/layers/color_filter_layer.h" #include "flutter/flow/layers/container_layer.h" +#include "flutter/flow/layers/display_list_layer.h" #include "flutter/flow/layers/image_filter_layer.h" #include "flutter/flow/layers/layer.h" #include "flutter/flow/layers/layer_tree.h" @@ -265,10 +266,17 @@ void SceneBuilder::addPicture(double dx, double dy, Picture* picture, int hints) { - auto layer = std::make_unique( - SkPoint::Make(dx, dy), UIDartState::CreateGPUObject(picture->picture()), - !!(hints & 1), !!(hints & 2)); - AddLayer(std::move(layer)); + if (picture->picture()) { + auto layer = std::make_unique( + SkPoint::Make(dx, dy), UIDartState::CreateGPUObject(picture->picture()), + !!(hints & 1), !!(hints & 2)); + AddLayer(std::move(layer)); + } else { + auto layer = std::make_unique( + SkPoint::Make(dx, dy), picture->display_list(), !!(hints & 1), + !!(hints & 2)); + AddLayer(std::move(layer)); + } } void SceneBuilder::addTexture(double dx, diff --git a/lib/ui/painting/canvas.cc b/lib/ui/painting/canvas.cc index 728540c2d4dd4..2d1c3b41c861b 100644 --- a/lib/ui/painting/canvas.cc +++ b/lib/ui/painting/canvas.cc @@ -85,6 +85,7 @@ fml::RefPtr Canvas::Create(PictureRecorder* recorder, fml::RefPtr canvas = fml::MakeRefCounted( recorder->BeginRecording(SkRect::MakeLTRB(left, top, right, bottom))); recorder->set_canvas(canvas); + canvas->display_list_recorder_ = recorder->display_list_recorder(); return canvas; } @@ -383,8 +384,19 @@ void Canvas::drawImageNine(const CanvasImage* image, center.round(&icenter); SkRect dst = SkRect::MakeLTRB(dst_left, dst_top, dst_right, dst_bottom); auto filter = ImageFilter::FilterModeFromIndex(bitmapSamplingIndex); - canvas_->drawImageNine(image->image().get(), icenter, dst, filter, - paint.paint()); + if (display_list_recorder_) { + // SkCanvas turns a simple 2-rect DrawImageNine operation into a + // drawImageLattice operation which has arrays to allocate and + // pass along. For simplicity, we will bypass the canvas and ask + // the recorder to record our paint attributes and record a much + // simpler DrawImageNineOp record directly. + display_list_recorder_->recordPaintAttributes( + paint.paint(), DisplayListCanvasRecorder::DrawType::imageOp); + builder()->drawImageNine(image->image(), icenter, dst, filter); + } else { + canvas_->drawImageNine(image->image().get(), icenter, dst, filter, + paint.paint()); + } } void Canvas::drawPicture(Picture* picture) { @@ -396,7 +408,17 @@ void Canvas::drawPicture(Picture* picture) { ToDart("Canvas.drawPicture called with non-genuine Picture.")); return; } - canvas_->drawPicture(picture->picture().get()); + if (picture->picture()) { + canvas_->drawPicture(picture->picture().get()); + } else if (picture->display_list()) { + if (display_list_recorder_) { + builder()->drawDisplayList(picture->display_list()); + } else { + picture->display_list()->renderTo(canvas_); + } + } else { + FML_DCHECK(false); + } } void Canvas::drawPoints(const Paint& paint, @@ -477,13 +499,23 @@ void Canvas::drawShadow(const CanvasPath* path, ToDart("Canvas.drawShader called with non-genuine Path.")); return; } - SkScalar dpr = UIDartState::Current() - ->platform_configuration() - ->get_window(0) - ->viewport_metrics() - .device_pixel_ratio; - flutter::PhysicalShapeLayer::DrawShadow(canvas_, path->path(), color, - elevation, transparentOccluder, dpr); + if (display_list_recorder_) { + // The DrawShadow mechanism results in non-public operations to be + // performed on the canvas involving an SkDrawShadowRec. Since we + // cannot include the header that defines that structure, we cannot + // record an operation that it injects into an SkCanvas. To prevent + // that situation we bypass the canvas interface and inject the + // shadow parameters directly into the underlying DisplayList. + builder()->drawShadow(path->path(), color, elevation, transparentOccluder); + } else { + SkScalar dpr = UIDartState::Current() + ->platform_configuration() + ->get_window(0) + ->viewport_metrics() + .device_pixel_ratio; + flutter::PhysicalShapeLayer::DrawShadow( + canvas_, path->path(), color, elevation, transparentOccluder, dpr); + } } void Canvas::Invalidate() { diff --git a/lib/ui/painting/canvas.h b/lib/ui/painting/canvas.h index cf067d2b3f95a..ca328d310dbb6 100644 --- a/lib/ui/painting/canvas.h +++ b/lib/ui/painting/canvas.h @@ -180,6 +180,17 @@ class Canvas : public RefCountedDartWrappable { // which does not transfer ownership. For this reason, we hold a raw // pointer and manually set to null in Clear. SkCanvas* canvas_; + + // A copy of the recorder used by the SkCanvas->DisplayList adapter for cases + // where we cannot record the SkCanvas method call through the various OnOp() + // virtual methods or where we can be more efficient by talking directly in + // the DisplayList operation lexicon. The recorder has a method for recording + // paint attributes from an SkPaint and an operation type as well as access + // to the raw DisplayListBuilder for emitting custom rendering operations. + sk_sp display_list_recorder_; + sk_sp builder() { + return display_list_recorder_->builder(); + } }; } // namespace flutter diff --git a/lib/ui/painting/image_dispose_unittests.cc b/lib/ui/painting/image_dispose_unittests.cc index 245d0a58ee631..3a673328932ea 100644 --- a/lib/ui/painting/image_dispose_unittests.cc +++ b/lib/ui/painting/image_dispose_unittests.cc @@ -6,8 +6,10 @@ #include "flutter/common/task_runners.h" #include "flutter/fml/synchronization/waitable_event.h" +#include "flutter/lib/ui/painting/canvas.h" #include "flutter/lib/ui/painting/image.h" #include "flutter/lib/ui/painting/picture.h" +#include "flutter/lib/ui/painting/picture_recorder.h" #include "flutter/runtime/dart_vm.h" #include "flutter/shell/common/shell_test.h" #include "flutter/shell/common/thread_host.h" @@ -31,6 +33,7 @@ class ImageDisposeTest : public ShellTest { fml::AutoResetWaitableEvent message_latch_; sk_sp current_picture_; + sk_sp current_display_list_; sk_sp current_image_; }; @@ -45,9 +48,14 @@ TEST_F(ImageDisposeTest, ImageReleasedAfterFrameAndDisposePictureAndLayer) { CanvasImage* image = GetNativePeer(native_image_handle); Picture* picture = GetNativePeer(Dart_GetNativeArgument(args, 1)); ASSERT_FALSE(image->image()->unique()); - ASSERT_FALSE(picture->picture()->unique()); + if (picture->display_list()) { + ASSERT_FALSE(picture->display_list()->unique()); + current_display_list_ = picture->display_list(); + } else { + ASSERT_FALSE(picture->picture()->unique()); + current_picture_ = picture->picture(); + } current_image_ = image->image(); - current_picture_ = picture->picture(); }; auto native_finish = [&](Dart_NativeArguments args) { @@ -84,7 +92,7 @@ TEST_F(ImageDisposeTest, ImageReleasedAfterFrameAndDisposePictureAndLayer) { message_latch_.Wait(); - ASSERT_TRUE(current_picture_); + ASSERT_TRUE(current_display_list_ || current_picture_); ASSERT_TRUE(current_image_); // Force a drain the SkiaUnrefQueue. The engine does this normally as frames @@ -96,8 +104,13 @@ TEST_F(ImageDisposeTest, ImageReleasedAfterFrameAndDisposePictureAndLayer) { }); message_latch_.Wait(); - EXPECT_TRUE(current_picture_->unique()); - current_picture_.reset(); + if (current_display_list_) { + EXPECT_TRUE(current_display_list_->unique()); + current_display_list_.reset(); + } else { + EXPECT_TRUE(current_picture_->unique()); + current_picture_.reset(); + } EXPECT_TRUE(current_image_->unique()); current_image_.reset(); diff --git a/lib/ui/painting/picture.cc b/lib/ui/painting/picture.cc index 5225d993ba4fb..661058e3b0fdd 100644 --- a/lib/ui/painting/picture.cc +++ b/lib/ui/painting/picture.cc @@ -37,29 +37,50 @@ fml::RefPtr Picture::Create( return canvas_picture; } +fml::RefPtr Picture::Create(Dart_Handle dart_handle, + sk_sp display_list) { + auto canvas_picture = fml::MakeRefCounted(std::move(display_list)); + + canvas_picture->AssociateWithDartWrapper(dart_handle); + return canvas_picture; +} + Picture::Picture(flutter::SkiaGPUObject picture) : picture_(std::move(picture)) {} +Picture::Picture(sk_sp display_list) + : display_list_(std::move(display_list)) {} + Picture::~Picture() = default; Dart_Handle Picture::toImage(uint32_t width, uint32_t height, Dart_Handle raw_image_callback) { - if (!picture_.get()) { - return tonic::ToDart("Picture is null"); + if (display_list_) { + return RasterizeToImage( + [display_list = display_list_.get()](SkCanvas* canvas) { + display_list->renderTo(canvas); + }, + width, height, raw_image_callback); + } else { + if (!picture_.get()) { + return tonic::ToDart("Picture is null"); + } + return RasterizeToImage(picture_.get(), width, height, raw_image_callback); } - - return RasterizeToImage(picture_.get(), width, height, raw_image_callback); } void Picture::dispose() { picture_.reset(); + display_list_.reset(); ClearDartWrapper(); } size_t Picture::GetAllocationSize() const { if (auto picture = picture_.get()) { return picture->approximateBytesUsed() + sizeof(Picture); + } else if (auto display_list = display_list_.get()) { + return display_list_->bytes() + sizeof(Picture); } else { return sizeof(Picture); } @@ -69,6 +90,18 @@ Dart_Handle Picture::RasterizeToImage(sk_sp picture, uint32_t width, uint32_t height, Dart_Handle raw_image_callback) { + return RasterizeToImage( + [sk_picture = picture.get()](SkCanvas* canvas) { + canvas->drawPicture(sk_picture); + }, + width, height, raw_image_callback); +} + +Dart_Handle Picture::RasterizeToImage( + std::function draw_callback, + uint32_t width, + uint32_t height, + Dart_Handle raw_image_callback) { if (Dart_IsNull(raw_image_callback) || !Dart_IsClosure(raw_image_callback)) { return tonic::ToDart("Image callback was invalid"); } @@ -121,10 +154,10 @@ Dart_Handle Picture::RasterizeToImage(sk_sp picture, // Kick things off on the raster rask runner. fml::TaskRunner::RunNowOrPostTask( - raster_task_runner, - [ui_task_runner, snapshot_delegate, picture, picture_bounds, ui_task] { - sk_sp raster_image = - snapshot_delegate->MakeRasterSnapshot(picture, picture_bounds); + raster_task_runner, [ui_task_runner, snapshot_delegate, draw_callback, + picture_bounds, ui_task] { + sk_sp raster_image = snapshot_delegate->MakeRasterSnapshot( + draw_callback, picture_bounds); fml::TaskRunner::RunNowOrPostTask( ui_task_runner, diff --git a/lib/ui/painting/picture.h b/lib/ui/painting/picture.h index e0158d400ff5e..432d9d32fb1b4 100644 --- a/lib/ui/painting/picture.h +++ b/lib/ui/painting/picture.h @@ -5,6 +5,7 @@ #ifndef FLUTTER_LIB_UI_PAINTING_PICTURE_H_ #define FLUTTER_LIB_UI_PAINTING_PICTURE_H_ +#include "flutter/flow/display_list.h" #include "flutter/flow/skia_gpu_object.h" #include "flutter/lib/ui/dart_wrapper.h" #include "flutter/lib/ui/painting/image.h" @@ -26,8 +27,11 @@ class Picture : public RefCountedDartWrappable { ~Picture() override; static fml::RefPtr Create(Dart_Handle dart_handle, flutter::SkiaGPUObject picture); + static fml::RefPtr Create(Dart_Handle dart_handle, + sk_sp display_list); sk_sp picture() const { return picture_.get(); } + sk_sp display_list() const { return display_list_; } Dart_Handle toImage(uint32_t width, uint32_t height, @@ -44,10 +48,18 @@ class Picture : public RefCountedDartWrappable { uint32_t height, Dart_Handle raw_image_callback); + static Dart_Handle RasterizeToImage( + std::function draw_callback, + uint32_t width, + uint32_t height, + Dart_Handle raw_image_callback); + private: Picture(flutter::SkiaGPUObject picture); + Picture(sk_sp display_list); flutter::SkiaGPUObject picture_; + sk_sp display_list_; }; } // namespace flutter diff --git a/lib/ui/painting/picture_recorder.cc b/lib/ui/painting/picture_recorder.cc index 5084f30d7812f..1e0cf02e8f6aa 100644 --- a/lib/ui/painting/picture_recorder.cc +++ b/lib/ui/painting/picture_recorder.cc @@ -39,7 +39,13 @@ PictureRecorder::PictureRecorder() {} PictureRecorder::~PictureRecorder() {} SkCanvas* PictureRecorder::BeginRecording(SkRect bounds) { - return picture_recorder_.beginRecording(bounds, &rtree_factory_); + bool enable_display_list = UIDartState::Current()->enable_display_list(); + if (enable_display_list) { + display_list_recorder_ = sk_make_sp(bounds); + return display_list_recorder_.get(); + } else { + return picture_recorder_.beginRecording(bounds, &rtree_factory_); + } } fml::RefPtr PictureRecorder::endRecording(Dart_Handle dart_picture) { @@ -47,9 +53,16 @@ fml::RefPtr PictureRecorder::endRecording(Dart_Handle dart_picture) { return nullptr; } - fml::RefPtr picture = Picture::Create( - dart_picture, UIDartState::CreateGPUObject( - picture_recorder_.finishRecordingAsPicture())); + fml::RefPtr picture; + + if (display_list_recorder_) { + picture = Picture::Create(dart_picture, display_list_recorder_->build()); + display_list_recorder_ = nullptr; + } else { + picture = Picture::Create( + dart_picture, UIDartState::CreateGPUObject( + picture_recorder_.finishRecordingAsPicture())); + } canvas_->Invalidate(); canvas_ = nullptr; diff --git a/lib/ui/painting/picture_recorder.h b/lib/ui/painting/picture_recorder.h index 59d0d782c57ae..7d2303284d32b 100644 --- a/lib/ui/painting/picture_recorder.h +++ b/lib/ui/painting/picture_recorder.h @@ -5,6 +5,7 @@ #ifndef FLUTTER_LIB_UI_PAINTING_PICTURE_RECORDER_H_ #define FLUTTER_LIB_UI_PAINTING_PICTURE_RECORDER_H_ +#include "flutter/flow/display_list_canvas.h" #include "flutter/lib/ui/dart_wrapper.h" #include "third_party/skia/include/core/SkPictureRecorder.h" @@ -28,6 +29,10 @@ class PictureRecorder : public RefCountedDartWrappable { SkCanvas* BeginRecording(SkRect bounds); fml::RefPtr endRecording(Dart_Handle dart_picture); + sk_sp display_list_recorder() { + return display_list_recorder_; + } + void set_canvas(fml::RefPtr canvas) { canvas_ = std::move(canvas); } static void RegisterNatives(tonic::DartLibraryNatives* natives); @@ -37,6 +42,9 @@ class PictureRecorder : public RefCountedDartWrappable { SkRTreeFactory rtree_factory_; SkPictureRecorder picture_recorder_; + + sk_sp display_list_recorder_; + fml::RefPtr canvas_; }; diff --git a/lib/ui/snapshot_delegate.h b/lib/ui/snapshot_delegate.h index ad9b8ef1f3612..a1f7c618b0653 100644 --- a/lib/ui/snapshot_delegate.h +++ b/lib/ui/snapshot_delegate.h @@ -12,6 +12,10 @@ namespace flutter { class SnapshotDelegate { public: + virtual sk_sp MakeRasterSnapshot( + std::function draw_callback, + SkISize picture_size) = 0; + virtual sk_sp MakeRasterSnapshot(sk_sp picture, SkISize picture_size) = 0; diff --git a/lib/ui/ui_dart_state.cc b/lib/ui/ui_dart_state.cc index 71d02c7b73b9f..06411c4d3ce98 100644 --- a/lib/ui/ui_dart_state.cc +++ b/lib/ui/ui_dart_state.cc @@ -56,6 +56,7 @@ UIDartState::UIDartState( std::shared_ptr isolate_name_server, bool is_root_isolate, bool enable_skparagraph, + bool enable_display_list, const UIDartState::Context& context) : add_callback_(std::move(add_callback)), remove_callback_(std::move(remove_callback)), @@ -65,6 +66,7 @@ UIDartState::UIDartState( log_message_callback_(log_message_callback), isolate_name_server_(std::move(isolate_name_server)), enable_skparagraph_(enable_skparagraph), + enable_display_list_(enable_display_list), context_(std::move(context)) { AddOrRemoveTaskObserver(true /* add */); } @@ -238,4 +240,8 @@ bool UIDartState::enable_skparagraph() const { return enable_skparagraph_; } +bool UIDartState::enable_display_list() const { + return enable_display_list_; +} + } // namespace flutter diff --git a/lib/ui/ui_dart_state.h b/lib/ui/ui_dart_state.h index fb2baadb1aa2f..f79d584ad0090 100644 --- a/lib/ui/ui_dart_state.h +++ b/lib/ui/ui_dart_state.h @@ -143,6 +143,8 @@ class UIDartState : public tonic::DartState { bool enable_skparagraph() const; + bool enable_display_list() const; + template static flutter::SkiaGPUObject CreateGPUObject(sk_sp object) { if (!object) { @@ -163,6 +165,7 @@ class UIDartState : public tonic::DartState { std::shared_ptr isolate_name_server, bool is_root_isolate_, bool enable_skparagraph, + bool enable_display_list, const UIDartState::Context& context); ~UIDartState() override; @@ -189,6 +192,7 @@ class UIDartState : public tonic::DartState { LogMessageCallback log_message_callback_; const std::shared_ptr isolate_name_server_; const bool enable_skparagraph_; + const bool enable_display_list_; UIDartState::Context context_; void AddOrRemoveTaskObserver(bool add); diff --git a/runtime/dart_isolate.cc b/runtime/dart_isolate.cc index 52aa2ffb4d3ab..916ce6e72ad1c 100644 --- a/runtime/dart_isolate.cc +++ b/runtime/dart_isolate.cc @@ -311,6 +311,7 @@ DartIsolate::DartIsolate(const Settings& settings, DartVMRef::GetIsolateNameServer(), is_root_isolate, settings.enable_skparagraph, + settings.enable_display_list, std::move(context)), may_insecurely_connect_to_all_domains_( settings.may_insecurely_connect_to_all_domains), diff --git a/shell/common/rasterizer.cc b/shell/common/rasterizer.cc index 794095d308f2e..3c6ba5fbf2385 100644 --- a/shell/common/rasterizer.cc +++ b/shell/common/rasterizer.cc @@ -313,6 +313,12 @@ sk_sp Rasterizer::DoMakeRasterSnapshot( return result; } +sk_sp Rasterizer::MakeRasterSnapshot( + std::function draw_callback, + SkISize picture_size) { + return DoMakeRasterSnapshot(picture_size, draw_callback); +} + sk_sp Rasterizer::MakeRasterSnapshot(sk_sp picture, SkISize picture_size) { return DoMakeRasterSnapshot(picture_size, diff --git a/shell/common/rasterizer.h b/shell/common/rasterizer.h index b34e10b0b0d74..aa7334179e970 100644 --- a/shell/common/rasterizer.h +++ b/shell/common/rasterizer.h @@ -449,6 +449,11 @@ class Rasterizer final : public SnapshotDelegate { std::shared_ptr external_view_embedder_; bool shared_engine_block_thread_merging_ = false; + // |SnapshotDelegate| + sk_sp MakeRasterSnapshot( + std::function draw_callback, + SkISize picture_size) override; + // |SnapshotDelegate| sk_sp MakeRasterSnapshot(sk_sp picture, SkISize picture_size) override; diff --git a/shell/common/shell.cc b/shell/common/shell.cc index e423a1759689c..beea55e86736e 100644 --- a/shell/common/shell.cc +++ b/shell/common/shell.cc @@ -1690,6 +1690,9 @@ bool Shell::OnServiceProtocolEstimateRasterCacheMemory( response->AddMember("pictureBytes", raster_cache.EstimatePictureCacheByteSize(), response->GetAllocator()); + response->AddMember("displayListBytes", + raster_cache.EstimateDisplayListCacheByteSize(), + response->GetAllocator()); return true; } diff --git a/shell/common/switches.cc b/shell/common/switches.cc index 249e9c5d8dca1..d49f99bda13be 100644 --- a/shell/common/switches.cc +++ b/shell/common/switches.cc @@ -66,6 +66,8 @@ static const std::string gAllowedDartFlags[] = { "--write-service-info", "--null_assertions", "--strict_null_safety_checks", + "--enable-display-list", + "--no-enable-display-list", }; // clang-format on @@ -404,6 +406,16 @@ Settings SettingsFromCommandLine(const fml::CommandLine& command_line) { settings.dart_flags.push_back(flag); } } + if (std::find(settings.dart_flags.begin(), settings.dart_flags.end(), + "--enable-display-list") != settings.dart_flags.end()) { + FML_LOG(ERROR) << "Manually enabling display lists"; + settings.enable_display_list = true; + } else if (std::find(settings.dart_flags.begin(), settings.dart_flags.end(), + "--no-enable-display-list") != + settings.dart_flags.end()) { + FML_LOG(ERROR) << "Manually disabling display lists"; + settings.enable_display_list = false; + } #if !FLUTTER_RELEASE command_line.GetOptionValue(FlagForSwitch(Switch::LogTag), &settings.log_tag); From b681d3f30e048f5322a1bfbad70fdc95a8ed0885 Mon Sep 17 00:00:00 2001 From: Jim Graham Date: Wed, 23 Jun 2021 23:00:11 -0700 Subject: [PATCH 02/26] fix licenses and a potential source of bulk compare failures --- ci/licenses_golden/licenses_flutter | 11 +++++++++++ flow/display_list.cc | 1 + 2 files changed, 12 insertions(+) diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index 7ba3273d60c11..b3bb258f3faad 100755 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -36,6 +36,14 @@ FILE: ../../../flutter/flow/compositor_context.cc FILE: ../../../flutter/flow/compositor_context.h FILE: ../../../flutter/flow/diff_context.cc FILE: ../../../flutter/flow/diff_context.h +FILE: ../../../flutter/flow/display_list.cc +FILE: ../../../flutter/flow/display_list.h +FILE: ../../../flutter/flow/display_list_canvas.cc +FILE: ../../../flutter/flow/display_list_canvas.h +FILE: ../../../flutter/flow/display_list_canvas_unittests.cc +FILE: ../../../flutter/flow/display_list_unittests.cc +FILE: ../../../flutter/flow/display_list_utils.cc +FILE: ../../../flutter/flow/display_list_utils.h FILE: ../../../flutter/flow/embedded_view_params_unittests.cc FILE: ../../../flutter/flow/embedded_views.cc FILE: ../../../flutter/flow/embedded_views.h @@ -67,6 +75,9 @@ FILE: ../../../flutter/flow/layers/color_filter_layer_unittests.cc FILE: ../../../flutter/flow/layers/container_layer.cc FILE: ../../../flutter/flow/layers/container_layer.h FILE: ../../../flutter/flow/layers/container_layer_unittests.cc +FILE: ../../../flutter/flow/layers/display_list_layer.cc +FILE: ../../../flutter/flow/layers/display_list_layer.h +FILE: ../../../flutter/flow/layers/display_list_layer_unittests.cc FILE: ../../../flutter/flow/layers/image_filter_layer.cc FILE: ../../../flutter/flow/layers/image_filter_layer.h FILE: ../../../flutter/flow/layers/image_filter_layer_unittests.cc diff --git a/flow/display_list.cc b/flow/display_list.cc index 7055cc93a7ac0..aa5a2e9fbfd8e 100644 --- a/flow/display_list.cc +++ b/flow/display_list.cc @@ -918,6 +918,7 @@ void* DisplayListBuilder::push(size_t pod, Args&&... args) { allocated_ = (used_ + size + DL_BUILDER_PAGE) & ~(DL_BUILDER_PAGE - 1); storage_.realloc(allocated_); FML_DCHECK(storage_.get()); + memset(storage_.get() + used_, 0, allocated_ - used_); } SkASSERT(used_ + size <= allocated_); auto op = (T*)(storage_.get() + used_); From 45d4cfdfd26bd40b7d2d8b4c720322b3451282b0 Mon Sep 17 00:00:00 2001 From: Jim Graham Date: Wed, 23 Jun 2021 23:07:06 -0700 Subject: [PATCH 03/26] fix shell test expecting string output for cache stats --- shell/common/shell_unittests.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shell/common/shell_unittests.cc b/shell/common/shell_unittests.cc index 221d32f3eb223..48c27a1b77f3a 100644 --- a/shell/common/shell_unittests.cc +++ b/shell/common/shell_unittests.cc @@ -2110,7 +2110,7 @@ TEST_F(ShellTest, OnServiceProtocolEstimateRasterCacheMemoryWorks) { document.Accept(writer); std::string expected_json = "{\"type\":\"EstimateRasterCacheMemory\",\"layerBytes\":40000,\"picture" - "Bytes\":400}"; + "Bytes\":400,\"displayListBytes\":0}"; std::string actual_json = buffer.GetString(); ASSERT_EQ(actual_json, expected_json); From f3da850e0d646df1c2863ba0d1d2ef698e3640ce Mon Sep 17 00:00:00 2001 From: Jim Graham Date: Wed, 23 Jun 2021 23:16:43 -0700 Subject: [PATCH 04/26] use std::numeric_limits for min/max float values --- flow/display_list_utils.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/flow/display_list_utils.h b/flow/display_list_utils.h index 3715514e6511e..3632d86c0c2ae 100644 --- a/flow/display_list_utils.h +++ b/flow/display_list_utils.h @@ -230,10 +230,10 @@ class BoundsAccumulator { } private: - SkScalar minX_ = +MAXFLOAT; - SkScalar minY_ = +MAXFLOAT; - SkScalar maxX_ = -MAXFLOAT; - SkScalar maxY_ = -MAXFLOAT; + SkScalar minX_ = std::numeric_limits::infinity(); + SkScalar minY_ = std::numeric_limits::infinity(); + SkScalar maxX_ = -std::numeric_limits::infinity(); + SkScalar maxY_ = -std::numeric_limits::infinity(); }; // This class implements all rendering methods and computes a liberal From 921d21d532a296f2a8222869f36dffb8152f068c Mon Sep 17 00:00:00 2001 From: Jim Graham Date: Wed, 23 Jun 2021 23:21:17 -0700 Subject: [PATCH 05/26] fix windows compile errors due to structure packing --- flow/display_list.cc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/flow/display_list.cc b/flow/display_list.cc index aa5a2e9fbfd8e..02a5722c4cca8 100644 --- a/flow/display_list.cc +++ b/flow/display_list.cc @@ -349,8 +349,8 @@ struct Transform3x3Op final : DLOp { } }; -// The common data is a 4 byte header + a 4 byte common payload which -// packs evenly into 8 common bytes +// The common data is a 4 byte header + a 2 byte common payload which +// takes 6 bytes but is expanded to 8 (2 bytes unused) // SkRect is 16 more bytes, which packs efficiently into 24 bytes total // SkRRect is 52 more bytes, which rounds up to 56 bytes (4 bytes unused) // which packs into 64 bytes total @@ -362,8 +362,8 @@ struct Transform3x3Op final : DLOp { Clip##shapetype##Op(Sk##shapetype shape, bool isAA, SkClipOp op) \ : isAA(isAA), op(op), shape(shape) {} \ \ - const bool isAA : 16; \ - const SkClipOp op : 16; \ + const bool isAA : 8; \ + const SkClipOp op : 8; \ const Sk##shapetype shape; \ \ void dispatch(Dispatcher& dispatcher) const { \ From 30c0380f1b6ee4d2dd6794971f33a75ed36a5a09 Mon Sep 17 00:00:00 2001 From: Jim Graham Date: Wed, 23 Jun 2021 23:46:25 -0700 Subject: [PATCH 06/26] Fix missing save layer flags --- flow/display_list_canvas.cc | 3 +++ flow/display_list_canvas.h | 6 +++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/flow/display_list_canvas.cc b/flow/display_list_canvas.cc index 7749f7ca17d30..b11a9d5cf1a8a 100644 --- a/flow/display_list_canvas.cc +++ b/flow/display_list_canvas.cc @@ -389,6 +389,9 @@ void DisplayListCanvasRecorder::recordPaintAttributes(const SkPaint* paint, case imageRectOp: dataNeeded = imageRectMask_; break; + case saveLayerOp: + dataNeeded = saveLayerMask_; + break; default: FML_DCHECK(false); return; diff --git a/flow/display_list_canvas.h b/flow/display_list_canvas.h index d0207ae120bde..d8861184f6362 100644 --- a/flow/display_list_canvas.h +++ b/flow/display_list_canvas.h @@ -283,9 +283,9 @@ class DisplayListCanvasRecorder ditherNeeded_ | imageFilterNeeded_ | filterQualityNeeded_ | maskFilterNeeded_; static const int imageRectMask_ = imageMask_ | aaNeeded_; - static const int saveLayerFlags_ = colorNeeded_ | blendNeeded_ | - invertColorsNeeded_ | colorFilterNeeded_ | - imageFilterNeeded_; + static const int saveLayerMask_ = colorNeeded_ | blendNeeded_ | + invertColorsNeeded_ | colorFilterNeeded_ | + imageFilterNeeded_; static const SkPaint defaultPaint; From e021fefb2fd719a12a1ea253916a041e03194f88 Mon Sep 17 00:00:00 2001 From: Jim Graham Date: Thu, 24 Jun 2021 12:51:40 -0700 Subject: [PATCH 07/26] switch to enum classes and fully implement SkC::drawPicture --- flow/display_list.cc | 55 ++++++++++++++++++++++++---------- flow/display_list.h | 41 ++++++++++++++++++------- flow/display_list_canvas.cc | 54 +++++++++++++++++---------------- flow/display_list_canvas.h | 18 ++++++----- flow/display_list_unittests.cc | 10 +++++-- flow/display_list_utils.cc | 10 +++++-- flow/display_list_utils.h | 4 ++- lib/ui/painting/canvas.cc | 2 +- 8 files changed, 129 insertions(+), 65 deletions(-) diff --git a/flow/display_list.cc b/flow/display_list.cc index 02a5722c4cca8..afe066c018f4b 100644 --- a/flow/display_list.cc +++ b/flow/display_list.cc @@ -30,10 +30,10 @@ const SkSamplingOptions DisplayList::MipmapSampling = const SkSamplingOptions DisplayList::CubicSampling = SkSamplingOptions(SkCubicResampler{1 / 3.0f, 1 / 3.0f}); -enum dlCompare { - NOT_EQUAL, - BULK_COMPARE, - EQUAL, +enum class dlCompare { + kNotEqual, + kUseBulkCompare, + kEqual, }; // Assuming a 64-bit platform (most of our platforms at this time?) @@ -44,9 +44,11 @@ enum dlCompare { // of data for "free" and works best when it packs well into an 8-byte // aligned size. struct DLOp { - uint8_t type : 8; + DisplayListOpType type : 8; uint32_t size : 24; - dlCompare equals(const DLOp* other) const { return BULK_COMPARE; } + dlCompare equals(const DLOp* other) const { + return dlCompare::kUseBulkCompare; + } }; // 4 byte header + 4 byte payload packs into minimum 8 bytes @@ -672,12 +674,31 @@ DEFINE_DRAW_ATLAS_OP(AtlasColoredCulled, HAS_COLORS, HAS_CULLING) struct DrawSkPictureOp final : DLOp { static const auto kType = DisplayListOpType::DrawSkPicture; - DrawSkPictureOp(sk_sp picture) : picture(std::move(picture)) {} + DrawSkPictureOp(sk_sp picture, bool withLayer) + : withLayer(withLayer), picture(std::move(picture)) {} + + const bool withLayer; + const sk_sp picture; + + void dispatch(Dispatcher& dispatcher) const { + dispatcher.drawPicture(picture, nullptr, withLayer); + } +}; + +struct DrawSkPictureMatrixOp final : DLOp { + static const auto kType = DisplayListOpType::DrawSkPictureMatrix; + + DrawSkPictureMatrixOp(sk_sp picture, + const SkMatrix matrix, + bool withLayer) + : withLayer(withLayer), picture(std::move(picture)), matrix(matrix) {} - sk_sp picture; + const bool withLayer; + const sk_sp picture; + const SkMatrix matrix; void dispatch(Dispatcher& dispatcher) const { - dispatcher.drawPicture(picture); + dispatcher.drawPicture(picture, &matrix, withLayer); } }; @@ -835,11 +856,11 @@ static bool CompareOps(uint8_t* ptrA, return false; } switch (result) { - case NOT_EQUAL: + case dlCompare::kNotEqual: return false; - case BULK_COMPARE: + case dlCompare::kUseBulkCompare: break; - case EQUAL: + case dlCompare::kEqual: // Check if we have a backlog of bytes to bulk compare and then // reset the bulk compare pointers to the address following this op auto bulkBytes = reinterpret_cast(opA) - bulkStartA; @@ -924,7 +945,7 @@ void* DisplayListBuilder::push(size_t pod, Args&&... args) { auto op = (T*)(storage_.get() + used_); used_ += size; new (op) T{std::forward(args)...}; - op->type = (uint32_t)T::kType; + op->type = T::kType; op->size = size; opCount_++; return op + 1; @@ -1222,8 +1243,12 @@ void DisplayListBuilder::drawAtlas(const sk_sp atlas, } } -void DisplayListBuilder::drawPicture(const sk_sp picture) { - push(0, std::move(picture)); +void DisplayListBuilder::drawPicture(const sk_sp picture, + const SkMatrix* matrix, + bool withLayer) { + (matrix) + ? push(0, std::move(picture), *matrix, withLayer) + : push(0, std::move(picture), withLayer); } void DisplayListBuilder::drawDisplayList( const sk_sp display_list) { diff --git a/flow/display_list.h b/flow/display_list.h index 7d4e4063f39ea..9e5d5cc64e599 100644 --- a/flow/display_list.h +++ b/flow/display_list.h @@ -14,14 +14,8 @@ #include "third_party/skia/include/core/SkShader.h" #include "third_party/skia/include/core/SkVertices.h" -// The Flutter DisplayList mechanism is used in place of the Skia SkPicture -// mechanism. It encapsulates a sequence of rendering operations into a -// persistent list that can be replayed upon request by calling its -// dispatch() method with a Dispatcher object that responds to the various -// rendering methods encapsulated therein. The mechanism is inspired by -// the SkLiteDL class that is not directly supported by Skia, but has been -// recommended as a basis for custom display lists for a number of their -// customers. +// The Flutter DisplayList mechanism encapsulates a persistent sequence of +// rendering operations. // // This file contains the definitions for: // DisplayList: the base class that holds the information about the @@ -44,6 +38,26 @@ // and a class to compute the bounds of a DisplayList // Any class implementing Dispatcher can inherit from // these utility classes to simplify its creation +// +// The Flutter DisplayList mechanism can be used in place of the Skia +// SkPicture mechanism. The primary means of communication into and out +// of the DisplayList is through the Dispatcher virtual class which +// provides a nearly 1:1 translation between the records of the DisplayList +// to method calls. +// +// A DisplayList can be created directly using a DisplayListBuilder and +// the Dispatcher methods that it implements, or it can be created from +// a sequence of SkCanvas calls using the DisplayListCanvasRecorder class. +// +// A DisplayList can be read back by implementing the Dispatcher virtual +// methods (with help from some of the classes in the utils file) and +// passing an instance to the dispatch() method, or it can be rendered +// to Skia using a DisplayListCanvasDispatcher or simply by passing an +// SkCanvas pointer to its renderTo() method. +// +// The mechanism is inspired by the SkLiteDL class that is not directly +// supported by Skia, but has been recommended as a basis for custom +// display lists for a number of their customers. namespace flutter { @@ -121,6 +135,7 @@ namespace flutter { V(DrawAtlasColoredCulled) \ \ V(DrawSkPicture) \ + V(DrawSkPictureMatrix) \ V(DrawDisplayList) \ V(DrawTextBlob) \ /* V(DrawShadowRec) */ \ @@ -128,7 +143,7 @@ namespace flutter { V(DrawShadow) #define DL_OP_TO_ENUM_VALUE(name) name, -enum DisplayListOpType { FOR_EACH_DISPLAY_LIST_OP(DL_OP_TO_ENUM_VALUE) }; +enum class DisplayListOpType { FOR_EACH_DISPLAY_LIST_OP(DL_OP_TO_ENUM_VALUE) }; #undef DL_OP_TO_ENUM_VALUE class Dispatcher; @@ -287,7 +302,9 @@ class Dispatcher { SkBlendMode mode, const SkSamplingOptions& sampling, const SkRect* cullRect) = 0; - virtual void drawPicture(const sk_sp picture) = 0; + virtual void drawPicture(const sk_sp picture, + const SkMatrix* matrix, + bool withPaint) = 0; virtual void drawDisplayList(const sk_sp display_list) = 0; virtual void drawTextBlob(const sk_sp blob, SkScalar x, @@ -396,7 +413,9 @@ class DisplayListBuilder final : public virtual Dispatcher, public SkRefCnt { SkBlendMode mode, const SkSamplingOptions& sampling, const SkRect* cullRect) override; - void drawPicture(const sk_sp picture) override; + void drawPicture(const sk_sp picture, + const SkMatrix* matrix, + bool withSaveLayer) override; void drawDisplayList(const sk_sp display_list) override; void drawTextBlob(const sk_sp blob, SkScalar x, diff --git a/flow/display_list_canvas.cc b/flow/display_list_canvas.cc index b11a9d5cf1a8a..cf75d53e726ba 100644 --- a/flow/display_list_canvas.cc +++ b/flow/display_list_canvas.cc @@ -156,8 +156,10 @@ void DisplayListCanvasDispatcher::drawAtlas(const sk_sp atlas, canvas_->drawAtlas(atlas.get(), xform, tex, colors, count, mode, sampling, cullRect, &paint()); } -void DisplayListCanvasDispatcher::drawPicture(const sk_sp picture) { - canvas_->drawPicture(picture); +void DisplayListCanvasDispatcher::drawPicture(const sk_sp picture, + const SkMatrix* matrix, + bool withSaveLayer) { + canvas_->drawPicture(picture, matrix, withSaveLayer ? &paint() : nullptr); } void DisplayListCanvasDispatcher::drawDisplayList( const sk_sp display_list) { @@ -236,7 +238,7 @@ void DisplayListCanvasRecorder::willSave() { SkCanvas::SaveLayerStrategy DisplayListCanvasRecorder::getSaveLayerStrategy( const SaveLayerRec& rec) { if (rec.fPaint) { - recordPaintAttributes(rec.fPaint, saveLayerOp); + recordPaintAttributes(rec.fPaint, DrawType::kSaveLayerOpType); builder_->saveLayer(rec.fBounds, true); } else { builder_->saveLayer(rec.fBounds, false); @@ -248,28 +250,28 @@ void DisplayListCanvasRecorder::didRestore() { } void DisplayListCanvasRecorder::onDrawPaint(const SkPaint& paint) { - recordPaintAttributes(&paint, fillOp); + recordPaintAttributes(&paint, DrawType::kFillOpType); builder_->drawPaint(); } void DisplayListCanvasRecorder::onDrawRect(const SkRect& rect, const SkPaint& paint) { - recordPaintAttributes(&paint, drawOp); + recordPaintAttributes(&paint, DrawType::kDrawOpType); builder_->drawRect(rect); } void DisplayListCanvasRecorder::onDrawRRect(const SkRRect& rrect, const SkPaint& paint) { - recordPaintAttributes(&paint, drawOp); + recordPaintAttributes(&paint, DrawType::kDrawOpType); builder_->drawRRect(rrect); } void DisplayListCanvasRecorder::onDrawDRRect(const SkRRect& outer, const SkRRect& inner, const SkPaint& paint) { - recordPaintAttributes(&paint, drawOp); + recordPaintAttributes(&paint, DrawType::kDrawOpType); builder_->drawDRRect(outer, inner); } void DisplayListCanvasRecorder::onDrawOval(const SkRect& rect, const SkPaint& paint) { - recordPaintAttributes(&paint, drawOp); + recordPaintAttributes(&paint, DrawType::kDrawOpType); builder_->drawOval(rect); } void DisplayListCanvasRecorder::onDrawArc(const SkRect& rect, @@ -277,12 +279,12 @@ void DisplayListCanvasRecorder::onDrawArc(const SkRect& rect, SkScalar sweepAngle, bool useCenter, const SkPaint& paint) { - recordPaintAttributes(&paint, drawOp); + recordPaintAttributes(&paint, DrawType::kDrawOpType); builder_->drawArc(rect, startAngle, sweepAngle, useCenter); } void DisplayListCanvasRecorder::onDrawPath(const SkPath& path, const SkPaint& paint) { - recordPaintAttributes(&paint, drawOp); + recordPaintAttributes(&paint, DrawType::kDrawOpType); builder_->drawPath(path); } @@ -290,7 +292,7 @@ void DisplayListCanvasRecorder::onDrawPoints(SkCanvas::PointMode mode, size_t count, const SkPoint pts[], const SkPaint& paint) { - recordPaintAttributes(&paint, strokeOp); + recordPaintAttributes(&paint, DrawType::kStrokeOpType); if (mode == SkCanvas::PointMode::kLines_PointMode && count == 2) { builder_->drawLine(pts[0], pts[1]); } else { @@ -302,7 +304,7 @@ void DisplayListCanvasRecorder::onDrawPoints(SkCanvas::PointMode mode, void DisplayListCanvasRecorder::onDrawVerticesObject(const SkVertices* vertices, SkBlendMode mode, const SkPaint& paint) { - recordPaintAttributes(&paint, drawOp); + recordPaintAttributes(&paint, DrawType::kDrawOpType); builder_->drawVertices(sk_ref_sp(vertices), mode); } @@ -311,7 +313,7 @@ void DisplayListCanvasRecorder::onDrawImage2(const SkImage* image, SkScalar dy, const SkSamplingOptions& sampling, const SkPaint* paint) { - recordPaintAttributes(paint, imageOp); + recordPaintAttributes(paint, DrawType::kImageOpType); builder_->drawImage(sk_ref_sp(image), SkPoint::Make(dx, dy), sampling); } void DisplayListCanvasRecorder::onDrawImageRect2( @@ -322,7 +324,7 @@ void DisplayListCanvasRecorder::onDrawImageRect2( const SkPaint* paint, SrcRectConstraint constraint) { FML_DCHECK(constraint == SrcRectConstraint::kFast_SrcRectConstraint); - recordPaintAttributes(paint, imageRectOp); + recordPaintAttributes(paint, DrawType::kImageRectOpType); builder_->drawImageRect(sk_ref_sp(image), src, dst, sampling); } void DisplayListCanvasRecorder::onDrawImageLattice2(const SkImage* image, @@ -330,7 +332,7 @@ void DisplayListCanvasRecorder::onDrawImageLattice2(const SkImage* image, const SkRect& dst, SkFilterMode filter, const SkPaint* paint) { - recordPaintAttributes(paint, imageOp); + recordPaintAttributes(paint, DrawType::kImageOpType); builder_->drawImageLattice(sk_ref_sp(image), lattice, dst, filter); } void DisplayListCanvasRecorder::onDrawAtlas2(const SkImage* image, @@ -342,7 +344,7 @@ void DisplayListCanvasRecorder::onDrawAtlas2(const SkImage* image, const SkSamplingOptions& sampling, const SkRect* cull, const SkPaint* paint) { - recordPaintAttributes(paint, imageOp); + recordPaintAttributes(paint, DrawType::kImageOpType); builder_->drawAtlas(sk_ref_sp(image), xform, src, colors, count, mode, sampling, cull); } @@ -351,7 +353,7 @@ void DisplayListCanvasRecorder::onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y, const SkPaint& paint) { - recordPaintAttributes(&paint, drawOp); + recordPaintAttributes(&paint, DrawType::kDrawOpType); builder_->drawTextBlob(sk_ref_sp(blob), x, y); } void DisplayListCanvasRecorder::onDrawShadowRec(const SkPath& path, @@ -364,8 +366,10 @@ void DisplayListCanvasRecorder::onDrawPicture(const SkPicture* picture, const SkMatrix* matrix, const SkPaint* paint) { FML_DCHECK(matrix == nullptr); - FML_DCHECK(paint == nullptr); - builder_->drawPicture(sk_ref_sp(picture)); + if (paint) { + recordPaintAttributes(paint, DrawType::kSaveLayerOpType); + } + builder_->drawPicture(sk_ref_sp(picture), matrix, paint != nullptr); } const SkPaint DisplayListCanvasRecorder::defaultPaint; @@ -374,22 +378,22 @@ void DisplayListCanvasRecorder::recordPaintAttributes(const SkPaint* paint, DrawType type) { int dataNeeded; switch (type) { - case drawOp: + case DrawType::kDrawOpType: dataNeeded = drawMask_; break; - case fillOp: + case DrawType::kFillOpType: dataNeeded = paintMask_; break; - case strokeOp: + case DrawType::kStrokeOpType: dataNeeded = strokeMask_; break; - case imageOp: + case DrawType::kImageOpType: dataNeeded = imageMask_; break; - case imageRectOp: + case DrawType::kImageRectOpType: dataNeeded = imageRectMask_; break; - case saveLayerOp: + case DrawType::kSaveLayerOpType: dataNeeded = saveLayerMask_; break; default: diff --git a/flow/display_list_canvas.h b/flow/display_list_canvas.h index d8861184f6362..f93aa3be4b06a 100644 --- a/flow/display_list_canvas.h +++ b/flow/display_list_canvas.h @@ -98,7 +98,9 @@ class DisplayListCanvasDispatcher : public virtual Dispatcher, SkBlendMode mode, const SkSamplingOptions& sampling, const SkRect* cullRect) override; - void drawPicture(const sk_sp picture) override; + void drawPicture(const sk_sp picture, + const SkMatrix* matrix, + bool withSaveLayer) override; void drawDisplayList(const sk_sp display_list) override; void drawTextBlob(const sk_sp blob, SkScalar x, @@ -229,19 +231,19 @@ class DisplayListCanvasRecorder const SkMatrix* matrix, const SkPaint* paint) override; - enum DrawType { + enum class DrawType { // The operation will be an image operation - imageOp, + kImageOpType, // The operation will be an imageRect operation - imageRectOp, + kImageRectOpType, // The operation will be a fill or stroke depending on the paint.style - drawOp, + kDrawOpType, // The operation will be a fill (ignoring paint.style) - fillOp, + kFillOpType, // The operation will be a stroke (ignoring paint.style) - strokeOp, + kStrokeOpType, // The operation will be a saveLayer with a paint object - saveLayerOp, + kSaveLayerOpType, }; void recordPaintAttributes(const SkPaint* paint, DrawType type); diff --git a/flow/display_list_unittests.cc b/flow/display_list_unittests.cc index cfe640c340a80..0d738a96187d3 100644 --- a/flow/display_list_unittests.cc +++ b/flow/display_list_unittests.cc @@ -91,6 +91,8 @@ static const SkRRect TestInnerRRect = SkRRect::MakeRectXY(TestBounds.makeInset(5, 5), 2, 2); static const SkPath TestPath1 = SkPath::Rect(TestBounds); static const SkPath TestPath2 = SkPath::Oval(TestBounds); +static const SkMatrix TestMatrix1 = SkMatrix::Scale(2, 2); +static const SkMatrix TestMatrix2 = SkMatrix::RotateDeg(45); static sk_sp MakeTestImage(int w, int h, int checker_size) { sk_sp surface = SkSurface::MakeRasterN32Premul(w, h); @@ -488,8 +490,12 @@ std::vector allGroups = { } }, { "DrawPicture", { - {1, 16, [](DisplayListBuilder& b) {b.drawPicture(TestPicture1);}}, - {1, 16, [](DisplayListBuilder& b) {b.drawPicture(TestPicture2);}}, + {1, 16, [](DisplayListBuilder& b) {b.drawPicture(TestPicture1, nullptr, false);}}, + {1, 16, [](DisplayListBuilder& b) {b.drawPicture(TestPicture2, nullptr, false);}}, + {1, 16, [](DisplayListBuilder& b) {b.drawPicture(TestPicture1, nullptr, true);}}, + {1, 56, [](DisplayListBuilder& b) {b.drawPicture(TestPicture1, &TestMatrix1, false);}}, + {1, 56, [](DisplayListBuilder& b) {b.drawPicture(TestPicture1, &TestMatrix2, false);}}, + {1, 56, [](DisplayListBuilder& b) {b.drawPicture(TestPicture1, &TestMatrix1, true);}}, } }, { "DrawDisplayList", { diff --git a/flow/display_list_utils.cc b/flow/display_list_utils.cc index 734fa997c1a84..ea18778436a83 100644 --- a/flow/display_list_utils.cc +++ b/flow/display_list_utils.cc @@ -308,10 +308,16 @@ void DisplayListBoundsCalculator::drawAtlas(const sk_sp atlas, accumulateRect(atlasBounds.getBounds()); } } -void DisplayListBoundsCalculator::drawPicture(const sk_sp picture) { +void DisplayListBoundsCalculator::drawPicture(const sk_sp picture, + const SkMatrix* matrix, + bool withSaveLayer) { // TODO(flar) cull rect really cannot be trusted in general, but // it will work for SkPictures generated from our own PictureRecorder. - accumulateRect(picture->cullRect()); + SkRect bounds = picture->cullRect(); + if (matrix) { + matrix->mapRect(&bounds); + } + accumulateRect(bounds); } void DisplayListBoundsCalculator::drawDisplayList( const sk_sp display_list) { diff --git a/flow/display_list_utils.h b/flow/display_list_utils.h index 3632d86c0c2ae..d921ae0b4eb3b 100644 --- a/flow/display_list_utils.h +++ b/flow/display_list_utils.h @@ -296,7 +296,9 @@ class DisplayListBoundsCalculator final SkBlendMode mode, const SkSamplingOptions& sampling, const SkRect* cullRect) override; - void drawPicture(const sk_sp picture) override; + void drawPicture(const sk_sp picture, + const SkMatrix* matrix, + bool withSaveLayer) override; void drawDisplayList(const sk_sp display_list) override; void drawTextBlob(const sk_sp blob, SkScalar x, diff --git a/lib/ui/painting/canvas.cc b/lib/ui/painting/canvas.cc index 2d1c3b41c861b..0741cd39730c7 100644 --- a/lib/ui/painting/canvas.cc +++ b/lib/ui/painting/canvas.cc @@ -391,7 +391,7 @@ void Canvas::drawImageNine(const CanvasImage* image, // the recorder to record our paint attributes and record a much // simpler DrawImageNineOp record directly. display_list_recorder_->recordPaintAttributes( - paint.paint(), DisplayListCanvasRecorder::DrawType::imageOp); + paint.paint(), DisplayListCanvasRecorder::DrawType::kImageOpType); builder()->drawImageNine(image->image(), icenter, dst, filter); } else { canvas_->drawImageNine(image->image().get(), icenter, dst, filter, From 285815b2e131718bb1e36c2b343855fab675937f Mon Sep 17 00:00:00 2001 From: Jim Graham Date: Thu, 24 Jun 2021 16:46:25 -0700 Subject: [PATCH 08/26] adopt review suggestions on naming style --- flow/display_list.cc | 378 ++++++++++---------- flow/display_list.h | 42 +-- flow/display_list_canvas.cc | 136 ++++--- flow/display_list_canvas.h | 92 +++-- flow/display_list_canvas_unittests.cc | 6 +- flow/display_list_unittests.cc | 22 +- flow/display_list_utils.cc | 50 +-- flow/display_list_utils.h | 54 +-- flow/layers/display_list_layer.cc | 4 +- flow/layers/display_list_layer_unittests.cc | 2 +- flow/raster_cache.cc | 2 +- flow/testing/diff_context_test.cc | 2 +- lib/ui/painting/canvas.cc | 4 +- lib/ui/painting/picture.cc | 2 +- lib/ui/painting/picture_recorder.cc | 2 +- 15 files changed, 405 insertions(+), 393 deletions(-) diff --git a/flow/display_list.cc b/flow/display_list.cc index afe066c018f4b..4e5d9737d2e2e 100644 --- a/flow/display_list.cc +++ b/flow/display_list.cc @@ -30,9 +30,25 @@ const SkSamplingOptions DisplayList::MipmapSampling = const SkSamplingOptions DisplayList::CubicSampling = SkSamplingOptions(SkCubicResampler{1 / 3.0f, 1 / 3.0f}); -enum class dlCompare { - kNotEqual, +// Most Ops can be bulk compared using memcmp because they contain +// only numeric values or constructs that are constructed from numeric +// values. +// +// Some contain sk_sp<> references which can also be bulk compared +// to see if they are pointing to the same reference. (Note that +// two sk_sp<> that refer to the same object are themselves ==.) +// +// Only a DLOp that wants to do a deep compare needs to override the +// DLOp::equals() method and return a value of kEqual or kNotEqual. +enum class DisplayListCompare { + // The Op is deferring comparisons to a bulk memcmp performed lazily + // across all bulk-comparable ops. kUseBulkCompare, + + // The Op provided a specific equals method that spotted a difference + kNotEqual, + + // The Op provided a specific equals method that saw no differences kEqual, }; @@ -46,23 +62,23 @@ enum class dlCompare { struct DLOp { DisplayListOpType type : 8; uint32_t size : 24; - dlCompare equals(const DLOp* other) const { - return dlCompare::kUseBulkCompare; + DisplayListCompare equals(const DLOp* other) const { + return DisplayListCompare::kUseBulkCompare; } }; // 4 byte header + 4 byte payload packs into minimum 8 bytes -#define DEFINE_SET_BOOL_OP(name) \ - struct Set##name##Op final : DLOp { \ - static const auto kType = DisplayListOpType::Set##name; \ - \ - Set##name##Op(bool value) : value(value) {} \ - \ - const bool value; \ - \ - void dispatch(Dispatcher& dispatcher) const { \ - dispatcher.set##name(value); \ - } \ +#define DEFINE_SET_BOOL_OP(name) \ + struct Set##name##Op final : DLOp { \ + static const auto kType = DisplayListOpType::kSet##name; \ + \ + Set##name##Op(bool value) : value(value) {} \ + \ + const bool value; \ + \ + void dispatch(Dispatcher& dispatcher) const { \ + dispatcher.set##name(value); \ + } \ }; DEFINE_SET_BOOL_OP(AA) DEFINE_SET_BOOL_OP(Dither) @@ -70,17 +86,17 @@ DEFINE_SET_BOOL_OP(InvertColors) #undef DEFINE_SET_CLEAR_BOOL_OP // 4 byte header + 4 byte payload packs into minimum 8 bytes -#define DEFINE_SET_ENUM_OP(name) \ - struct Set##name##s##Op final : DLOp { \ - static const auto kType = DisplayListOpType::Set##name##s; \ - \ - Set##name##s##Op(SkPaint::name value) : value(value) {} \ - \ - const SkPaint::name value; \ - \ - void dispatch(Dispatcher& dispatcher) const { \ - dispatcher.set##name##s(value); \ - } \ +#define DEFINE_SET_ENUM_OP(name) \ + struct Set##name##s##Op final : DLOp { \ + static const auto kType = DisplayListOpType::kSet##name##s; \ + \ + Set##name##s##Op(SkPaint::name value) : value(value) {} \ + \ + const SkPaint::name value; \ + \ + void dispatch(Dispatcher& dispatcher) const { \ + dispatcher.set##name##s(value); \ + } \ }; DEFINE_SET_ENUM_OP(Cap) DEFINE_SET_ENUM_OP(Join) @@ -88,7 +104,7 @@ DEFINE_SET_ENUM_OP(Join) // 4 byte header + 4 byte payload packs into minimum 8 bytes struct SetDrawStyleOp final : DLOp { - static const auto kType = DisplayListOpType::SetDrawStyle; + static const auto kType = DisplayListOpType::kSetDrawStyle; SetDrawStyleOp(SkPaint::Style style) : style(style) {} @@ -100,7 +116,7 @@ struct SetDrawStyleOp final : DLOp { }; // 4 byte header + 4 byte payload packs into minimum 8 bytes struct SetStrokeWidthOp final : DLOp { - static const auto kType = DisplayListOpType::SetStrokeWidth; + static const auto kType = DisplayListOpType::kSetStrokeWidth; SetStrokeWidthOp(SkScalar width) : width(width) {} @@ -112,7 +128,7 @@ struct SetStrokeWidthOp final : DLOp { }; // 4 byte header + 4 byte payload packs into minimum 8 bytes struct SetMiterLimitOp final : DLOp { - static const auto kType = DisplayListOpType::SetMiterLimit; + static const auto kType = DisplayListOpType::kSetMiterLimit; SetMiterLimitOp(SkScalar limit) : limit(limit) {} @@ -125,7 +141,7 @@ struct SetMiterLimitOp final : DLOp { // 4 byte header + 4 byte payload packs into minimum 8 bytes struct SetColorOp final : DLOp { - static const auto kType = DisplayListOpType::SetColor; + static const auto kType = DisplayListOpType::kSetColor; SetColorOp(SkColor color) : color(color) {} @@ -135,7 +151,7 @@ struct SetColorOp final : DLOp { }; // 4 byte header + 4 byte payload packs into minimum 8 bytes struct SetBlendModeOp final : DLOp { - static const auto kType = DisplayListOpType::SetBlendMode; + static const auto kType = DisplayListOpType::kSetBlendMode; SetBlendModeOp(SkBlendMode mode) : mode(mode) {} @@ -146,7 +162,7 @@ struct SetBlendModeOp final : DLOp { // 4 byte header + 4 byte payload packs into minimum 8 bytes struct SetFilterQualityOp final : DLOp { - static const auto kType = DisplayListOpType::SetFilterQuality; + static const auto kType = DisplayListOpType::kSetFilterQuality; SetFilterQualityOp(SkFilterQuality quality) : quality(quality) {} @@ -164,7 +180,7 @@ struct SetFilterQualityOp final : DLOp { // (4 bytes unused) #define DEFINE_SET_CLEAR_SKREF_OP(name, field) \ struct Clear##name##Op final : DLOp { \ - static const auto kType = DisplayListOpType::Clear##name; \ + static const auto kType = DisplayListOpType::kClear##name; \ \ Clear##name##Op() {} \ \ @@ -173,7 +189,7 @@ struct SetFilterQualityOp final : DLOp { } \ }; \ struct Set##name##Op final : DLOp { \ - static const auto kType = DisplayListOpType::Set##name; \ + static const auto kType = DisplayListOpType::kSet##name; \ \ Set##name##Op(sk_sp field) : field(std::move(field)) {} \ \ @@ -192,17 +208,17 @@ DEFINE_SET_CLEAR_SKREF_OP(MaskFilter, filter) // 4 byte header + 4 byte payload packs into minimum 8 bytes // Note that the "blur style" is packed into the OpType to prevent // needing an additional 8 bytes for a 4-value enum. -#define DEFINE_MASK_BLUR_FILTER_OP(name, style) \ - struct SetMaskBlurFilter##name##Op final : DLOp { \ - static const auto kType = DisplayListOpType::SetMaskBlurFilter##name; \ - \ - SetMaskBlurFilter##name##Op(SkScalar sigma) : sigma(sigma) {} \ - \ - SkScalar sigma; \ - \ - void dispatch(Dispatcher& dispatcher) const { \ - dispatcher.setMaskBlurFilter(style, sigma); \ - } \ +#define DEFINE_MASK_BLUR_FILTER_OP(name, style) \ + struct SetMaskBlurFilter##name##Op final : DLOp { \ + static const auto kType = DisplayListOpType::kSetMaskBlurFilter##name; \ + \ + SetMaskBlurFilter##name##Op(SkScalar sigma) : sigma(sigma) {} \ + \ + SkScalar sigma; \ + \ + void dispatch(Dispatcher& dispatcher) const { \ + dispatcher.setMaskBlurFilter(style, sigma); \ + } \ }; DEFINE_MASK_BLUR_FILTER_OP(Normal, kNormal_SkBlurStyle) DEFINE_MASK_BLUR_FILTER_OP(Solid, kSolid_SkBlurStyle) @@ -212,7 +228,7 @@ DEFINE_MASK_BLUR_FILTER_OP(Outer, kOuter_SkBlurStyle) // 4 byte header + no payload uses minimum 8 bytes (4 bytes unused) struct SaveOp final : DLOp { - static const auto kType = DisplayListOpType::Save; + static const auto kType = DisplayListOpType::kSave; SaveOp() {} @@ -220,7 +236,7 @@ struct SaveOp final : DLOp { }; // 4 byte header + 4 byte payload packs into minimum 8 bytes struct SaveLayerOp final : DLOp { - static const auto kType = DisplayListOpType::SaveLayer; + static const auto kType = DisplayListOpType::kSaveLayer; SaveLayerOp(bool withPaint) : withPaint(withPaint) {} @@ -232,7 +248,7 @@ struct SaveLayerOp final : DLOp { }; // 4 byte header + 20 byte payload packs evenly into 24 bytes struct SaveLayerBoundsOp final : DLOp { - static const auto kType = DisplayListOpType::SaveLayerBounds; + static const auto kType = DisplayListOpType::kSaveLayerBounds; SaveLayerBoundsOp(SkRect rect, bool withPaint) : withPaint(withPaint), rect(rect) {} @@ -246,7 +262,7 @@ struct SaveLayerBoundsOp final : DLOp { }; // 4 byte header + no payload uses minimum 8 bytes (4 bytes unused) struct RestoreOp final : DLOp { - static const auto kType = DisplayListOpType::Restore; + static const auto kType = DisplayListOpType::kRestore; RestoreOp() {} @@ -256,7 +272,7 @@ struct RestoreOp final : DLOp { // 4 byte header + 8 byte payload uses 12 bytes but is rounded up to 16 bytes // (4 bytes unused) struct TranslateOp final : DLOp { - static const auto kType = DisplayListOpType::Translate; + static const auto kType = DisplayListOpType::kTranslate; TranslateOp(SkScalar tx, SkScalar ty) : tx(tx), ty(ty) {} @@ -268,7 +284,7 @@ struct TranslateOp final : DLOp { // 4 byte header + 8 byte payload uses 12 bytes but is rounded up to 16 bytes // (4 bytes unused) struct ScaleOp final : DLOp { - static const auto kType = DisplayListOpType::Scale; + static const auto kType = DisplayListOpType::kScale; ScaleOp(SkScalar sx, SkScalar sy) : sx(sx), sy(sy) {} @@ -279,7 +295,7 @@ struct ScaleOp final : DLOp { }; // 4 byte header + 4 byte payload packs into minimum 8 bytes struct RotateOp final : DLOp { - static const auto kType = DisplayListOpType::Rotate; + static const auto kType = DisplayListOpType::kRotate; RotateOp(SkScalar degrees) : degrees(degrees) {} @@ -290,7 +306,7 @@ struct RotateOp final : DLOp { // 4 byte header + 8 byte payload uses 12 bytes but is rounded up to 16 bytes // (4 bytes unused) struct SkewOp final : DLOp { - static const auto kType = DisplayListOpType::Skew; + static const auto kType = DisplayListOpType::kSkew; SkewOp(SkScalar sx, SkScalar sy) : sx(sx), sy(sy) {} @@ -302,7 +318,7 @@ struct SkewOp final : DLOp { // 4 byte header + 24 byte payload uses 28 bytes but is rounded up to 32 bytes // (4 bytes unused) struct Transform2x3Op final : DLOp { - static const auto kType = DisplayListOpType::Transform2x3; + static const auto kType = DisplayListOpType::kTransform2x3; Transform2x3Op(SkScalar mxx, SkScalar mxy, @@ -321,7 +337,7 @@ struct Transform2x3Op final : DLOp { }; // 4 byte header + 36 byte payload packs evenly into 40 bytes struct Transform3x3Op final : DLOp { - static const auto kType = DisplayListOpType::Transform3x3; + static const auto kType = DisplayListOpType::kTransform3x3; Transform3x3Op(SkScalar mxx, SkScalar mxy, @@ -359,7 +375,7 @@ struct Transform3x3Op final : DLOp { // SkPath is 16 more bytes, which packs efficiently into 24 bytes total #define DEFINE_CLIP_SHAPE_OP(shapetype) \ struct Clip##shapetype##Op final : DLOp { \ - static const auto kType = DisplayListOpType::Clip##shapetype; \ + static const auto kType = DisplayListOpType::kClip##shapetype; \ \ Clip##shapetype##Op(Sk##shapetype shape, bool isAA, SkClipOp op) \ : isAA(isAA), op(op), shape(shape) {} \ @@ -379,7 +395,7 @@ DEFINE_CLIP_SHAPE_OP(Path) // 4 byte header + no payload uses minimum 8 bytes (4 bytes unused) struct DrawPaintOp final : DLOp { - static const auto kType = DisplayListOpType::DrawPaint; + static const auto kType = DisplayListOpType::kDrawPaint; DrawPaintOp() {} @@ -388,7 +404,7 @@ struct DrawPaintOp final : DLOp { // 4 byte header + 8 byte payload uses 12 bytes but is rounded up to 16 bytes // (4 bytes unused) struct DrawColorOp final : DLOp { - static const auto kType = DisplayListOpType::DrawColor; + static const auto kType = DisplayListOpType::kDrawColor; DrawColorOp(SkColor color, SkBlendMode mode) : color(color), mode(mode) {} @@ -408,7 +424,7 @@ struct DrawColorOp final : DLOp { // (4 bytes unused) #define DEFINE_DRAW_1ARG_OP(op_name, arg_type, arg_name) \ struct Draw##op_name##Op final : DLOp { \ - static const auto kType = DisplayListOpType::Draw##op_name; \ + static const auto kType = DisplayListOpType::kDraw##op_name; \ \ Draw##op_name##Op(arg_type arg_name) : arg_name(arg_name) {} \ \ @@ -432,7 +448,7 @@ DEFINE_DRAW_1ARG_OP(Path, SkPath, path) // (4 bytes unused) #define DEFINE_DRAW_2ARG_OP(op_name, type1, name1, type2, name2) \ struct Draw##op_name##Op final : DLOp { \ - static const auto kType = DisplayListOpType::Draw##op_name; \ + static const auto kType = DisplayListOpType::kDraw##op_name; \ \ Draw##op_name##Op(type1 name1, type2 name2) \ : name1(name1), name2(name2) {} \ @@ -451,7 +467,7 @@ DEFINE_DRAW_2ARG_OP(DRRect, SkRRect, outer, SkRRect, inner) // 4 byte header + 28 byte payload packs efficiently into 32 bytes struct DrawArcOp final : DLOp { - static const auto kType = DisplayListOpType::DrawArc; + static const auto kType = DisplayListOpType::kDrawArc; DrawArcOp(SkRect bounds, SkScalar start, SkScalar sweep, bool center) : bounds(bounds), start(start), sweep(sweep), center(center) {} @@ -474,7 +490,7 @@ struct DrawArcOp final : DLOp { // 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::Draw##name; \ + static const auto kType = DisplayListOpType::kDraw##name; \ \ Draw##name##Op(uint32_t count) : count(count) {} \ \ @@ -492,7 +508,7 @@ DEFINE_DRAW_POINTS_OP(Polygon, kPolygon_PointMode); // 4 byte header + 12 byte payload packs efficiently into 16 bytes struct DrawVerticesOp final : DLOp { - static const auto kType = DisplayListOpType::DrawVertices; + static const auto kType = DisplayListOpType::kDrawVertices; DrawVerticesOp(sk_sp vertices, SkBlendMode mode) : mode(mode), vertices(std::move(vertices)) {} @@ -507,7 +523,7 @@ struct DrawVerticesOp final : DLOp { // 4 byte header + 36 byte payload packs efficiently into 40 bytes struct DrawImageOp final : DLOp { - static const auto kType = DisplayListOpType::DrawImage; + static const auto kType = DisplayListOpType::kDrawImage; DrawImageOp(const sk_sp image, const SkPoint& point, @@ -525,7 +541,7 @@ struct DrawImageOp final : DLOp { // 4 byte header + 60 byte payload packs efficiently into 64 bytes struct DrawImageRectOp final : DLOp { - static const auto kType = DisplayListOpType::DrawImageRect; + static const auto kType = DisplayListOpType::kDrawImageRect; DrawImageRectOp(const sk_sp image, const SkRect& src, @@ -545,7 +561,7 @@ struct DrawImageRectOp final : DLOp { // 4 byte header + 44 byte payload packs efficiently into 48 bytes struct DrawImageNineOp final : DLOp { - static const auto kType = DisplayListOpType::DrawImageNine; + static const auto kType = DisplayListOpType::kDrawImageNine; DrawImageNineOp(const sk_sp image, const SkIRect& center, @@ -564,7 +580,7 @@ struct DrawImageNineOp final : DLOp { }; struct DrawImageLatticeOp final : DLOp { - static const auto kType = DisplayListOpType::DrawImageLattice; + static const auto kType = DisplayListOpType::kDrawImageLattice; DrawImageLatticeOp(const sk_sp image, int xDivCount, @@ -639,7 +655,7 @@ struct DrawImageLatticeOp final : DLOp { // can be 4 unusued bytes at the end. #define DEFINE_DRAW_ATLAS_OP(name, colors, cull) \ struct Draw##name##Op final : DLOp { \ - static const auto kType = DisplayListOpType::Draw##name; \ + static const auto kType = DisplayListOpType::kDraw##name; \ \ Draw##name##Op(DRAW_ATLAS_##cull##_ARGS) : DRAW_ATLAS_##cull##_INIT {} \ \ @@ -672,7 +688,7 @@ DEFINE_DRAW_ATLAS_OP(AtlasColoredCulled, HAS_COLORS, HAS_CULLING) // 4 byte header + ptr aligned payload uses 12 bytes rounde up to 16 // (4 bytes unused) struct DrawSkPictureOp final : DLOp { - static const auto kType = DisplayListOpType::DrawSkPicture; + static const auto kType = DisplayListOpType::kDrawSkPicture; DrawSkPictureOp(sk_sp picture, bool withLayer) : withLayer(withLayer), picture(std::move(picture)) {} @@ -686,7 +702,7 @@ struct DrawSkPictureOp final : DLOp { }; struct DrawSkPictureMatrixOp final : DLOp { - static const auto kType = DisplayListOpType::DrawSkPictureMatrix; + static const auto kType = DisplayListOpType::kDrawSkPictureMatrix; DrawSkPictureMatrixOp(sk_sp picture, const SkMatrix matrix, @@ -705,7 +721,7 @@ struct DrawSkPictureMatrixOp final : DLOp { // 4 byte header + ptr aligned payload uses 12 bytes rounde up to 16 // (4 bytes unused) struct DrawDisplayListOp final : DLOp { - static const auto kType = DisplayListOpType::DrawDisplayList; + static const auto kType = DisplayListOpType::kDrawDisplayList; DrawDisplayListOp(const sk_sp display_list) : display_list(std::move(display_list)) {} @@ -718,7 +734,7 @@ struct DrawDisplayListOp final : DLOp { }; struct DrawTextBlobOp final : DLOp { - static const auto kType = DisplayListOpType::DrawTextBlob; + static const auto kType = DisplayListOpType::kDrawTextBlob; DrawTextBlobOp(const sk_sp blob, SkScalar x, SkScalar y) : x(x), y(y), blob(std::move(blob)) {} @@ -733,7 +749,7 @@ struct DrawTextBlobOp final : DLOp { }; // struct DrawShadowRecOp final : DLOp { -// static const auto kType = DisplayListOpType::DrawShadowRec; +// static const auto kType = DisplayListOpType::kDrawShadowRec; // // DrawShadowRecOp(const SkPath& path, const SkDrawShadowRec& rec) // : path(path), rec(rec) {} @@ -748,7 +764,7 @@ struct DrawTextBlobOp final : DLOp { // 4 byte header + 28 byte payload packs evenly into 32 bytes struct DrawShadowOp final : DLOp { - static const auto kType = DisplayListOpType::DrawShadow; + static const auto kType = DisplayListOpType::kDrawShadow; DrawShadowOp(const SkPath& path, SkColor color, @@ -766,13 +782,13 @@ struct DrawShadowOp final : DLOp { } }; -void DisplayList::computeBounds() { - DisplayListBoundsCalculator calculator(boundsCull_); - dispatch(calculator); +void DisplayList::ComputeBounds() { + DisplayListBoundsCalculator calculator(bounds_cull_); + Dispatch(calculator); bounds_ = calculator.getBounds(); } -void DisplayList::dispatch(Dispatcher& dispatcher, +void DisplayList::Dispatch(Dispatcher& dispatcher, uint8_t* ptr, uint8_t* end) const { while (ptr < end) { @@ -781,7 +797,7 @@ void DisplayList::dispatch(Dispatcher& dispatcher, FML_DCHECK(ptr <= end); switch (op->type) { #define DL_OP_DISPATCH(name) \ - case DisplayListOpType::name: \ + case DisplayListOpType::k##name: \ static_cast(op)->dispatch(dispatcher); \ break; @@ -803,7 +819,7 @@ static void DisposeOps(uint8_t* ptr, uint8_t* end) { FML_DCHECK(ptr <= end); switch (op->type) { #define DL_OP_DISPOSE(name) \ - case DisplayListOpType::name: \ + case DisplayListOpType::k##name: \ if (!std::is_trivially_destructible_v) { \ static_cast(op)->~name##Op(); \ } \ @@ -839,10 +855,10 @@ static bool CompareOps(uint8_t* ptrA, ptrB += opB->size; FML_DCHECK(ptrA <= endA); FML_DCHECK(ptrB <= endB); - dlCompare result; + DisplayListCompare result; switch (opA->type) { #define DL_OP_EQUALS(name) \ - case DisplayListOpType::name: \ + case DisplayListOpType::k##name: \ result = static_cast(opA)->equals( \ static_cast(opB)); \ break; @@ -856,11 +872,11 @@ static bool CompareOps(uint8_t* ptrA, return false; } switch (result) { - case dlCompare::kNotEqual: + case DisplayListCompare::kNotEqual: return false; - case dlCompare::kUseBulkCompare: + case DisplayListCompare::kUseBulkCompare: break; - case dlCompare::kEqual: + case DisplayListCompare::kEqual: // Check if we have a backlog of bytes to bulk compare and then // reset the bulk compare pointers to the address following this op auto bulkBytes = reinterpret_cast(opA) - bulkStartA; @@ -886,28 +902,28 @@ static bool CompareOps(uint8_t* ptrA, return true; } -void DisplayList::renderTo(SkCanvas* canvas) const { +void DisplayList::RenderTo(SkCanvas* canvas) const { DisplayListCanvasDispatcher dispatcher(canvas); - dispatch(dispatcher); + Dispatch(dispatcher); } -bool DisplayList::equals(const DisplayList& other) const { +bool DisplayList::Equals(const DisplayList& other) const { return CompareOps(ptr_, ptr_ + used_, other.ptr_, other.ptr_ + other.used_); } DisplayList::DisplayList(uint8_t* ptr, size_t used, - int opCount, + int op_count, const SkRect& cull) : ptr_(ptr), used_(used), - opCount_(opCount), + op_count_(op_count), bounds_({0, 0, -1, -1}), - boundsCull_(cull) { + bounds_cull_(cull) { static std::atomic nextID{1}; do { - uniqueID_ = nextID.fetch_add(+1, std::memory_order_relaxed); - } while (uniqueID_ == 0); + unique_id_ = nextID.fetch_add(+1, std::memory_order_relaxed); + } while (unique_id_ == 0); } DisplayList::~DisplayList() { @@ -917,21 +933,21 @@ DisplayList::~DisplayList() { #define DL_BUILDER_PAGE 4096 // copy_v(dst, src,n, src,n, ...) copies any number of typed srcs into dst. -static void copy_v(void* dst) {} +static void CopyV(void* dst) {} template -static void copy_v(void* dst, const S* src, int n, Rest&&... rest) { - SkASSERTF(((uintptr_t)dst & (alignof(S) - 1)) == 0, - "Expected %p to be aligned for at least %zu bytes.", dst, - alignof(S)); +static void CopyV(void* dst, const S* src, int n, Rest&&... rest) { + FML_DCHECK(((uintptr_t)dst & (alignof(S) - 1)) == 0) + << "Expected " << dst << " to be aligned for at least " << alignof(S) + << " bytes."; sk_careful_memcpy(dst, src, n * sizeof(S)); - copy_v(SkTAddOffset(dst, n * sizeof(S)), std::forward(rest)...); + CopyV(SkTAddOffset(dst, n * sizeof(S)), std::forward(rest)...); } template -void* DisplayListBuilder::push(size_t pod, Args&&... args) { +void* DisplayListBuilder::Push(size_t pod, Args&&... args) { size_t size = SkAlignPtr(sizeof(T) + pod); - SkASSERT(size < (1 << 24)); + FML_DCHECK(size < (1 << 24)); if (used_ + size > allocated_) { static_assert(SkIsPow2(DL_BUILDER_PAGE), "This math needs updating for non-pow2."); @@ -941,23 +957,23 @@ void* DisplayListBuilder::push(size_t pod, Args&&... args) { FML_DCHECK(storage_.get()); memset(storage_.get() + used_, 0, allocated_ - used_); } - SkASSERT(used_ + size <= allocated_); + FML_DCHECK(used_ + size <= allocated_); auto op = (T*)(storage_.get() + used_); used_ += size; new (op) T{std::forward(args)...}; op->type = T::kType; op->size = size; - opCount_++; + op_count_++; return op + 1; } -sk_sp DisplayListBuilder::build() { - while (saveLevel_ > 0) { +sk_sp DisplayListBuilder::Build() { + while (save_level_ > 0) { restore(); } size_t used = used_; - int count = opCount_; - used_ = allocated_ = opCount_ = 0; + int count = op_count_; + used_ = allocated_ = op_count_ = 0; storage_.realloc(used); return sk_sp( new DisplayList(storage_.release(), used, count, cull_)); @@ -973,101 +989,101 @@ DisplayListBuilder::~DisplayListBuilder() { } void DisplayListBuilder::setAA(bool aa) { - push(0, aa); + Push(0, aa); } void DisplayListBuilder::setDither(bool dither) { - push(0, dither); + Push(0, dither); } void DisplayListBuilder::setInvertColors(bool invert) { - push(0, invert); + Push(0, invert); } void DisplayListBuilder::setCaps(SkPaint::Cap cap) { - push(0, cap); + Push(0, cap); } void DisplayListBuilder::setJoins(SkPaint::Join join) { - push(0, join); + Push(0, join); } void DisplayListBuilder::setDrawStyle(SkPaint::Style style) { - push(0, style); + Push(0, style); } void DisplayListBuilder::setStrokeWidth(SkScalar width) { - push(0, width); + Push(0, width); } void DisplayListBuilder::setMiterLimit(SkScalar limit) { - push(0, limit); + Push(0, limit); } void DisplayListBuilder::setColor(SkColor color) { - push(0, color); + Push(0, color); } void DisplayListBuilder::setBlendMode(SkBlendMode mode) { - push(0, mode); + Push(0, mode); } void DisplayListBuilder::setFilterQuality(SkFilterQuality quality) { - push(0, quality); + Push(0, quality); } void DisplayListBuilder::setShader(sk_sp shader) { shader // - ? push(0, std::move(shader)) - : push(0); + ? Push(0, std::move(shader)) + : Push(0); } void DisplayListBuilder::setImageFilter(sk_sp filter) { filter // - ? push(0, std::move(filter)) - : push(0); + ? Push(0, std::move(filter)) + : Push(0); } void DisplayListBuilder::setColorFilter(sk_sp filter) { filter // - ? push(0, std::move(filter)) - : push(0); + ? Push(0, std::move(filter)) + : Push(0); } void DisplayListBuilder::setMaskFilter(sk_sp filter) { - push(0, std::move(filter)); + Push(0, std::move(filter)); } void DisplayListBuilder::setMaskBlurFilter(SkBlurStyle style, SkScalar sigma) { switch (style) { case kNormal_SkBlurStyle: - push(0, sigma); + Push(0, sigma); break; case kSolid_SkBlurStyle: - push(0, sigma); + Push(0, sigma); break; case kOuter_SkBlurStyle: - push(0, sigma); + Push(0, sigma); break; case kInner_SkBlurStyle: - push(0, sigma); + Push(0, sigma); break; } } void DisplayListBuilder::save() { - saveLevel_++; - push(0); + save_level_++; + Push(0); } void DisplayListBuilder::restore() { - if (saveLevel_ > 0) { - push(0); - saveLevel_--; + if (save_level_ > 0) { + Push(0); + save_level_--; } } void DisplayListBuilder::saveLayer(const SkRect* bounds, bool withPaint) { - saveLevel_++; + save_level_++; bounds // - ? push(0, *bounds, withPaint) - : push(0, withPaint); + ? Push(0, *bounds, withPaint) + : Push(0, withPaint); } void DisplayListBuilder::translate(SkScalar tx, SkScalar ty) { - push(0, tx, ty); + Push(0, tx, ty); } void DisplayListBuilder::scale(SkScalar sx, SkScalar sy) { - push(0, sx, sy); + Push(0, sx, sy); } void DisplayListBuilder::rotate(SkScalar degrees) { - push(0, degrees); + Push(0, degrees); } void DisplayListBuilder::skew(SkScalar sx, SkScalar sy) { - push(0, sx, sy); + Push(0, sx, sy); } void DisplayListBuilder::transform2x3(SkScalar mxx, SkScalar mxy, @@ -1075,7 +1091,7 @@ void DisplayListBuilder::transform2x3(SkScalar mxx, SkScalar myx, SkScalar myy, SkScalar myt) { - push(0, mxx, mxy, mxt, myx, myy, myt); + Push(0, mxx, mxy, mxt, myx, myy, myt); } void DisplayListBuilder::transform3x3(SkScalar mxx, SkScalar mxy, @@ -1086,13 +1102,13 @@ void DisplayListBuilder::transform3x3(SkScalar mxx, SkScalar px, SkScalar py, SkScalar pt) { - push(0, mxx, mxy, mxt, myx, myy, myt, px, py, pt); + Push(0, mxx, mxy, mxt, myx, myy, myt, px, py, pt); } void DisplayListBuilder::clipRect(const SkRect& rect, bool isAA, SkClipOp clip_op) { - push(0, rect, isAA, clip_op); + Push(0, rect, isAA, clip_op); } void DisplayListBuilder::clipRRect(const SkRRect& rrect, bool isAA, @@ -1100,32 +1116,32 @@ void DisplayListBuilder::clipRRect(const SkRRect& rrect, if (rrect.isRect()) { clipRect(rrect.rect(), isAA, clip_op); } else { - push(0, rrect, isAA, clip_op); + Push(0, rrect, isAA, clip_op); } } void DisplayListBuilder::clipPath(const SkPath& path, bool isAA, SkClipOp clip_op) { - push(0, path, isAA, clip_op); + Push(0, path, isAA, clip_op); } void DisplayListBuilder::drawPaint() { - push(0); + Push(0); } void DisplayListBuilder::drawColor(SkColor color, SkBlendMode mode) { - push(0, color, mode); + Push(0, color, mode); } void DisplayListBuilder::drawLine(const SkPoint& p0, const SkPoint& p1) { - push(0, p0, p1); + Push(0, p0, p1); } void DisplayListBuilder::drawRect(const SkRect& rect) { - push(0, rect); + Push(0, rect); } void DisplayListBuilder::drawOval(const SkRect& bounds) { - push(0, bounds); + Push(0, bounds); } void DisplayListBuilder::drawCircle(const SkPoint& center, SkScalar radius) { - push(0, center, radius); + Push(0, center, radius); } void DisplayListBuilder::drawRRect(const SkRRect& rrect) { if (rrect.isRect()) { @@ -1133,22 +1149,22 @@ void DisplayListBuilder::drawRRect(const SkRRect& rrect) { } else if (rrect.isOval()) { drawOval(rrect.rect()); } else { - push(0, rrect); + Push(0, rrect); } } void DisplayListBuilder::drawDRRect(const SkRRect& outer, const SkRRect& inner) { - push(0, outer, inner); + Push(0, outer, inner); } void DisplayListBuilder::drawPath(const SkPath& path) { - push(0, path); + Push(0, path); } void DisplayListBuilder::drawArc(const SkRect& bounds, SkScalar start, SkScalar sweep, bool useCenter) { - push(0, bounds, start, sweep, useCenter); + Push(0, bounds, start, sweep, useCenter); } void DisplayListBuilder::drawPoints(SkCanvas::PointMode mode, uint32_t count, @@ -1158,41 +1174,41 @@ void DisplayListBuilder::drawPoints(SkCanvas::PointMode mode, int bytes = count * sizeof(SkPoint); switch (mode) { case SkCanvas::PointMode::kPoints_PointMode: - data_ptr = push(bytes, count); + data_ptr = Push(bytes, count); break; case SkCanvas::PointMode::kLines_PointMode: - data_ptr = push(bytes, count); + data_ptr = Push(bytes, count); break; case SkCanvas::PointMode::kPolygon_PointMode: - data_ptr = push(bytes, count); + data_ptr = Push(bytes, count); break; default: FML_DCHECK(false); return; } - copy_v(data_ptr, pts, count); + CopyV(data_ptr, pts, count); } void DisplayListBuilder::drawVertices(const sk_sp vertices, SkBlendMode mode) { - push(0, std::move(vertices), mode); + Push(0, std::move(vertices), mode); } void DisplayListBuilder::drawImage(const sk_sp image, const SkPoint point, const SkSamplingOptions& sampling) { - push(0, std::move(image), point, sampling); + Push(0, std::move(image), point, sampling); } void DisplayListBuilder::drawImageRect(const sk_sp image, const SkRect& src, const SkRect& dst, const SkSamplingOptions& sampling) { - push(0, std::move(image), src, dst, sampling); + Push(0, std::move(image), src, dst, sampling); } void DisplayListBuilder::drawImageNine(const sk_sp image, const SkIRect& center, const SkRect& dst, SkFilterMode filter) { - push(0, std::move(image), center, dst, filter); + Push(0, std::move(image), center, dst, filter); } void DisplayListBuilder::drawImageLattice(const sk_sp image, const SkCanvas::Lattice& lattice, @@ -1204,12 +1220,12 @@ void DisplayListBuilder::drawImageLattice(const sk_sp image, size_t bytes = (xDivCount + yDivCount) * sizeof(int) + cellCount * (sizeof(SkColor) + sizeof(SkCanvas::Lattice::RectType)); - SkASSERT(lattice.fBounds); - void* pod = this->push(bytes, std::move(image), xDivCount, + FML_DCHECK(lattice.fBounds); + void* pod = this->Push(bytes, std::move(image), xDivCount, yDivCount, cellCount, *lattice.fBounds, dst, filter); - copy_v(pod, lattice.fXDivs, xDivCount, lattice.fYDivs, yDivCount, - lattice.fColors, cellCount, lattice.fRectTypes, cellCount); + CopyV(pod, lattice.fXDivs, xDivCount, lattice.fYDivs, yDivCount, + lattice.fColors, cellCount, lattice.fRectTypes, cellCount); } void DisplayListBuilder::drawAtlas(const sk_sp atlas, const SkRSXform xform[], @@ -1224,50 +1240,50 @@ void DisplayListBuilder::drawAtlas(const sk_sp atlas, if (colors) { bytes += count * sizeof(SkColor); if (cullRect) { - data_ptr = push(bytes, std::move(atlas), count, + data_ptr = Push(bytes, std::move(atlas), count, mode, sampling, *cullRect); } else { - data_ptr = push(bytes, std::move(atlas), count, mode, + data_ptr = Push(bytes, std::move(atlas), count, mode, sampling); } - copy_v(data_ptr, xform, count, tex, count, colors, count); + CopyV(data_ptr, xform, count, tex, count, colors, count); } else { if (cullRect) { - data_ptr = push(bytes, std::move(atlas), count, mode, + data_ptr = Push(bytes, std::move(atlas), count, mode, sampling, *cullRect); } else { data_ptr = - push(bytes, std::move(atlas), count, mode, sampling); + Push(bytes, std::move(atlas), count, mode, sampling); } - copy_v(data_ptr, xform, count, tex, count); + CopyV(data_ptr, xform, count, tex, count); } } void DisplayListBuilder::drawPicture(const sk_sp picture, const SkMatrix* matrix, bool withLayer) { - (matrix) - ? push(0, std::move(picture), *matrix, withLayer) - : push(0, std::move(picture), withLayer); + matrix // + ? Push(0, std::move(picture), *matrix, withLayer) + : Push(0, std::move(picture), withLayer); } void DisplayListBuilder::drawDisplayList( const sk_sp display_list) { - push(0, std::move(display_list)); + Push(0, std::move(display_list)); } void DisplayListBuilder::drawTextBlob(const sk_sp blob, SkScalar x, SkScalar y) { - push(0, std::move(blob), x, y); + Push(0, std::move(blob), x, y); } // void DisplayListBuilder::drawShadowRec(const SkPath& path, // const SkDrawShadowRec& rec) { -// push(0, path, rec); +// Push(0, path, rec); // } void DisplayListBuilder::drawShadow(const SkPath& path, const SkColor color, const SkScalar elevation, bool occludes) { - push(0, path, color, elevation, occludes); + Push(0, path, color, elevation, occludes); } } // namespace flutter diff --git a/flow/display_list.h b/flow/display_list.h index 9e5d5cc64e599..0380147c88696 100644 --- a/flow/display_list.h +++ b/flow/display_list.h @@ -142,7 +142,7 @@ namespace flutter { \ V(DrawShadow) -#define DL_OP_TO_ENUM_VALUE(name) name, +#define DL_OP_TO_ENUM_VALUE(name) k##name, enum class DisplayListOpType { FOR_EACH_DISPLAY_LIST_OP(DL_OP_TO_ENUM_VALUE) }; #undef DL_OP_TO_ENUM_VALUE @@ -162,47 +162,47 @@ class DisplayList : public SkRefCnt { DisplayList() : ptr_(nullptr), used_(0), - opCount_(0), - uniqueID_(0), + op_count_(0), + unique_id_(0), bounds_({0, 0, 0, 0}), - boundsCull_({0, 0, 0, 0}) {} + bounds_cull_({0, 0, 0, 0}) {} ~DisplayList(); - void dispatch(Dispatcher& ctx) const { dispatch(ctx, ptr_, ptr_ + used_); } + void Dispatch(Dispatcher& ctx) const { Dispatch(ctx, ptr_, ptr_ + used_); } - void renderTo(SkCanvas* canvas) const; + void RenderTo(SkCanvas* canvas) const; size_t bytes() const { return used_; } - int opCount() const { return opCount_; } - uint32_t uniqueID() const { return uniqueID_; } + int opCount() const { return op_count_; } + uint32_t uniqueID() const { return unique_id_; } const SkRect& bounds() { if (bounds_.width() < 0.0) { - // computeBounds() will leave the variable with a + // ComputeBounds() will leave the variable with a // non-negative width and height - computeBounds(); + ComputeBounds(); } return bounds_; } - bool equals(const DisplayList& other) const; + bool Equals(const DisplayList& other) const; private: - DisplayList(uint8_t* ptr, size_t used, int opCount, const SkRect& cullRect); + DisplayList(uint8_t* ptr, size_t used, int op_count, const SkRect& cull_rect); uint8_t* ptr_; size_t used_; - int opCount_; + int op_count_; - uint32_t uniqueID_; + uint32_t unique_id_; SkRect bounds_; // Only used for drawPaint() and drawColor() - SkRect boundsCull_; + SkRect bounds_cull_; - void computeBounds(); - void dispatch(Dispatcher& ctx, uint8_t* ptr, uint8_t* end) const; + void ComputeBounds(); + void Dispatch(Dispatcher& ctx, uint8_t* ptr, uint8_t* end) const; friend class DisplayListBuilder; }; @@ -426,19 +426,19 @@ class DisplayListBuilder final : public virtual Dispatcher, public SkRefCnt { const SkScalar elevation, bool occludes) override; - sk_sp build(); + sk_sp Build(); private: SkAutoTMalloc storage_; size_t used_ = 0; size_t allocated_ = 0; - int opCount_ = 0; - int saveLevel_ = 0; + int op_count_ = 0; + int save_level_ = 0; SkRect cull_; template - void* push(size_t extra, Args&&... args); + void* Push(size_t extra, Args&&... args); }; } // namespace flutter diff --git a/flow/display_list_canvas.cc b/flow/display_list_canvas.cc index cf75d53e726ba..421c00db78354 100644 --- a/flow/display_list_canvas.cc +++ b/flow/display_list_canvas.cc @@ -166,7 +166,7 @@ void DisplayListCanvasDispatcher::drawDisplayList( int save_count = canvas_->save(); { DisplayListCanvasDispatcher dispatcher(canvas_); - display_list->dispatch(dispatcher); + display_list->Dispatch(dispatcher); } canvas_->restoreToCount(save_count); } @@ -191,8 +191,8 @@ DisplayListCanvasRecorder::DisplayListCanvasRecorder(const SkRect& bounds) : SkCanvasVirtualEnforcer(bounds.width(), bounds.height()), builder_(sk_make_sp(bounds)) {} -sk_sp DisplayListCanvasRecorder::build() { - sk_sp display_list = builder_->build(); +sk_sp DisplayListCanvasRecorder::Build() { + sk_sp display_list = builder_->Build(); builder_.reset(); return display_list; } @@ -238,7 +238,7 @@ void DisplayListCanvasRecorder::willSave() { SkCanvas::SaveLayerStrategy DisplayListCanvasRecorder::getSaveLayerStrategy( const SaveLayerRec& rec) { if (rec.fPaint) { - recordPaintAttributes(rec.fPaint, DrawType::kSaveLayerOpType); + RecordPaintAttributes(rec.fPaint, DrawType::kSaveLayerOpType); builder_->saveLayer(rec.fBounds, true); } else { builder_->saveLayer(rec.fBounds, false); @@ -250,28 +250,28 @@ void DisplayListCanvasRecorder::didRestore() { } void DisplayListCanvasRecorder::onDrawPaint(const SkPaint& paint) { - recordPaintAttributes(&paint, DrawType::kFillOpType); + RecordPaintAttributes(&paint, DrawType::kFillOpType); builder_->drawPaint(); } void DisplayListCanvasRecorder::onDrawRect(const SkRect& rect, const SkPaint& paint) { - recordPaintAttributes(&paint, DrawType::kDrawOpType); + RecordPaintAttributes(&paint, DrawType::kDrawOpType); builder_->drawRect(rect); } void DisplayListCanvasRecorder::onDrawRRect(const SkRRect& rrect, const SkPaint& paint) { - recordPaintAttributes(&paint, DrawType::kDrawOpType); + RecordPaintAttributes(&paint, DrawType::kDrawOpType); builder_->drawRRect(rrect); } void DisplayListCanvasRecorder::onDrawDRRect(const SkRRect& outer, const SkRRect& inner, const SkPaint& paint) { - recordPaintAttributes(&paint, DrawType::kDrawOpType); + RecordPaintAttributes(&paint, DrawType::kDrawOpType); builder_->drawDRRect(outer, inner); } void DisplayListCanvasRecorder::onDrawOval(const SkRect& rect, const SkPaint& paint) { - recordPaintAttributes(&paint, DrawType::kDrawOpType); + RecordPaintAttributes(&paint, DrawType::kDrawOpType); builder_->drawOval(rect); } void DisplayListCanvasRecorder::onDrawArc(const SkRect& rect, @@ -279,12 +279,12 @@ void DisplayListCanvasRecorder::onDrawArc(const SkRect& rect, SkScalar sweepAngle, bool useCenter, const SkPaint& paint) { - recordPaintAttributes(&paint, DrawType::kDrawOpType); + RecordPaintAttributes(&paint, DrawType::kDrawOpType); builder_->drawArc(rect, startAngle, sweepAngle, useCenter); } void DisplayListCanvasRecorder::onDrawPath(const SkPath& path, const SkPaint& paint) { - recordPaintAttributes(&paint, DrawType::kDrawOpType); + RecordPaintAttributes(&paint, DrawType::kDrawOpType); builder_->drawPath(path); } @@ -292,7 +292,7 @@ void DisplayListCanvasRecorder::onDrawPoints(SkCanvas::PointMode mode, size_t count, const SkPoint pts[], const SkPaint& paint) { - recordPaintAttributes(&paint, DrawType::kStrokeOpType); + RecordPaintAttributes(&paint, DrawType::kStrokeOpType); if (mode == SkCanvas::PointMode::kLines_PointMode && count == 2) { builder_->drawLine(pts[0], pts[1]); } else { @@ -304,7 +304,7 @@ void DisplayListCanvasRecorder::onDrawPoints(SkCanvas::PointMode mode, void DisplayListCanvasRecorder::onDrawVerticesObject(const SkVertices* vertices, SkBlendMode mode, const SkPaint& paint) { - recordPaintAttributes(&paint, DrawType::kDrawOpType); + RecordPaintAttributes(&paint, DrawType::kDrawOpType); builder_->drawVertices(sk_ref_sp(vertices), mode); } @@ -313,7 +313,7 @@ void DisplayListCanvasRecorder::onDrawImage2(const SkImage* image, SkScalar dy, const SkSamplingOptions& sampling, const SkPaint* paint) { - recordPaintAttributes(paint, DrawType::kImageOpType); + RecordPaintAttributes(paint, DrawType::kImageOpType); builder_->drawImage(sk_ref_sp(image), SkPoint::Make(dx, dy), sampling); } void DisplayListCanvasRecorder::onDrawImageRect2( @@ -324,7 +324,7 @@ void DisplayListCanvasRecorder::onDrawImageRect2( const SkPaint* paint, SrcRectConstraint constraint) { FML_DCHECK(constraint == SrcRectConstraint::kFast_SrcRectConstraint); - recordPaintAttributes(paint, DrawType::kImageRectOpType); + RecordPaintAttributes(paint, DrawType::kImageRectOpType); builder_->drawImageRect(sk_ref_sp(image), src, dst, sampling); } void DisplayListCanvasRecorder::onDrawImageLattice2(const SkImage* image, @@ -332,7 +332,7 @@ void DisplayListCanvasRecorder::onDrawImageLattice2(const SkImage* image, const SkRect& dst, SkFilterMode filter, const SkPaint* paint) { - recordPaintAttributes(paint, DrawType::kImageOpType); + RecordPaintAttributes(paint, DrawType::kImageOpType); builder_->drawImageLattice(sk_ref_sp(image), lattice, dst, filter); } void DisplayListCanvasRecorder::onDrawAtlas2(const SkImage* image, @@ -344,7 +344,7 @@ void DisplayListCanvasRecorder::onDrawAtlas2(const SkImage* image, const SkSamplingOptions& sampling, const SkRect* cull, const SkPaint* paint) { - recordPaintAttributes(paint, DrawType::kImageOpType); + RecordPaintAttributes(paint, DrawType::kImageOpType); builder_->drawAtlas(sk_ref_sp(image), xform, src, colors, count, mode, sampling, cull); } @@ -353,7 +353,7 @@ void DisplayListCanvasRecorder::onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y, const SkPaint& paint) { - recordPaintAttributes(&paint, DrawType::kDrawOpType); + RecordPaintAttributes(&paint, DrawType::kDrawOpType); builder_->drawTextBlob(sk_ref_sp(blob), x, y); } void DisplayListCanvasRecorder::onDrawShadowRec(const SkPath& path, @@ -367,55 +367,54 @@ void DisplayListCanvasRecorder::onDrawPicture(const SkPicture* picture, const SkPaint* paint) { FML_DCHECK(matrix == nullptr); if (paint) { - recordPaintAttributes(paint, DrawType::kSaveLayerOpType); + RecordPaintAttributes(paint, DrawType::kSaveLayerOpType); } builder_->drawPicture(sk_ref_sp(picture), matrix, paint != nullptr); } -const SkPaint DisplayListCanvasRecorder::defaultPaint; - -void DisplayListCanvasRecorder::recordPaintAttributes(const SkPaint* paint, +void DisplayListCanvasRecorder::RecordPaintAttributes(const SkPaint* paint, DrawType type) { int dataNeeded; switch (type) { case DrawType::kDrawOpType: - dataNeeded = drawMask_; + dataNeeded = kDrawMask_; break; case DrawType::kFillOpType: - dataNeeded = paintMask_; + dataNeeded = kPaintMask_; break; case DrawType::kStrokeOpType: - dataNeeded = strokeMask_; + dataNeeded = kStrokeMask_; break; case DrawType::kImageOpType: - dataNeeded = imageMask_; + dataNeeded = kImageMask_; break; case DrawType::kImageRectOpType: - dataNeeded = imageRectMask_; + dataNeeded = kImageRectMask_; break; case DrawType::kSaveLayerOpType: - dataNeeded = saveLayerMask_; + dataNeeded = kSaveLayerMask_; break; default: FML_DCHECK(false); return; } if (paint == nullptr) { - paint = &defaultPaint; + paint = new SkPaint(); } - if ((dataNeeded & aaNeeded_) != 0 && currentAA_ != paint->isAntiAlias()) { - builder_->setAA(currentAA_ = paint->isAntiAlias()); + if ((dataNeeded & kAaNeeded_) != 0 && current_aa_ != paint->isAntiAlias()) { + builder_->setAA(current_aa_ = paint->isAntiAlias()); } - if ((dataNeeded & ditherNeeded_) != 0 && - currentDither_ != paint->isDither()) { - builder_->setDither(currentDither_ = paint->isDither()); + if ((dataNeeded & kDitherNeeded_) != 0 && + current_dither_ != paint->isDither()) { + builder_->setDither(current_dither_ = paint->isDither()); } - if ((dataNeeded & colorNeeded_) != 0 && currentColor_ != paint->getColor()) { - builder_->setColor(currentColor_ = paint->getColor()); + if ((dataNeeded & kColorNeeded_) != 0 && + current_color_ != paint->getColor()) { + builder_->setColor(current_color_ = paint->getColor()); } - if ((dataNeeded & blendNeeded_) != 0 && - currentBlendMode_ != paint->getBlendMode()) { - builder_->setBlendMode(currentBlendMode_ = paint->getBlendMode()); + if ((dataNeeded & kBlendNeeded_) != 0 && + current_blend_ != paint->getBlendMode()) { + builder_->setBlendMode(current_blend_ = paint->getBlendMode()); } // invert colors is a Flutter::Paint thing, not an SkPaint thing // if ((dataNeeded & invertColorsNeeded_) != 0 && @@ -425,51 +424,50 @@ void DisplayListCanvasRecorder::recordPaintAttributes(const SkPaint* paint, // ? _CanvasOp.setInvertColors // : _CanvasOp.clearInvertColors, 0); // } - if ((dataNeeded & paintStyleNeeded_) != 0) { - if (currentPaintStyle_ != paint->getStyle()) { + if ((dataNeeded & kPaintStyleNeeded_) != 0) { + if (current_style_ != paint->getStyle()) { FML_DCHECK(paint->getStyle() != SkPaint::kStrokeAndFill_Style); - builder_->setDrawStyle(currentPaintStyle_ = paint->getStyle()); + builder_->setDrawStyle(current_style_ = paint->getStyle()); } - if (currentPaintStyle_ == SkPaint::Style::kStroke_Style) { - dataNeeded |= strokeStyleNeeded_; + if (current_style_ == SkPaint::Style::kStroke_Style) { + dataNeeded |= kStrokeStyleNeeded_; } } - if ((dataNeeded & strokeStyleNeeded_) != 0) { - if (currentStrokeWidth_ != paint->getStrokeWidth()) { - builder_->setStrokeWidth(currentStrokeWidth_ = paint->getStrokeWidth()); + if ((dataNeeded & kStrokeStyleNeeded_) != 0) { + if (current_stroke_width_ != paint->getStrokeWidth()) { + builder_->setStrokeWidth(current_stroke_width_ = paint->getStrokeWidth()); } - if (currentStrokeCap_ != paint->getStrokeCap()) { - builder_->setCaps(currentStrokeCap_ = paint->getStrokeCap()); + if (current_cap_ != paint->getStrokeCap()) { + builder_->setCaps(current_cap_ = paint->getStrokeCap()); } - if (currentStrokeJoin_ != paint->getStrokeJoin()) { - builder_->setJoins(currentStrokeJoin_ = paint->getStrokeJoin()); + if (current_join_ != paint->getStrokeJoin()) { + builder_->setJoins(current_join_ = paint->getStrokeJoin()); } - if (currentMiterLimit_ != paint->getStrokeMiter()) { - builder_->setMiterLimit(currentMiterLimit_ = paint->getStrokeMiter()); + if (current_miter_limit_ != paint->getStrokeMiter()) { + builder_->setMiterLimit(current_miter_limit_ = paint->getStrokeMiter()); } } - if ((dataNeeded & filterQualityNeeded_) != 0 && - currentFilterQuality_ != paint->getFilterQuality()) { - builder_->setFilterQuality(currentFilterQuality_ = - paint->getFilterQuality()); + if ((dataNeeded & kFilterQualityNeeded_) != 0 && + current_fq_ != paint->getFilterQuality()) { + builder_->setFilterQuality(current_fq_ = paint->getFilterQuality()); } - if ((dataNeeded & shaderNeeded_) != 0 && - currentShader_.get() != paint->getShader()) { - builder_->setShader(currentShader_ = sk_ref_sp(paint->getShader())); + if ((dataNeeded & kShaderNeeded_) != 0 && + current_shader_.get() != paint->getShader()) { + builder_->setShader(current_shader_ = sk_ref_sp(paint->getShader())); } - if ((dataNeeded & colorFilterNeeded_) != 0 && - currentColorFilter_.get() != paint->getColorFilter()) { - builder_->setColorFilter(currentColorFilter_ = + if ((dataNeeded & kColorFilterNeeded_) != 0 && + current_color_filter_.get() != paint->getColorFilter()) { + builder_->setColorFilter(current_color_filter_ = sk_ref_sp(paint->getColorFilter())); } - if ((dataNeeded & imageFilterNeeded_) != 0 && - currentImageFilter_.get() != paint->getImageFilter()) { - builder_->setImageFilter(currentImageFilter_ = + if ((dataNeeded & kImageFilterNeeded_) != 0 && + current_image_filter_.get() != paint->getImageFilter()) { + builder_->setImageFilter(current_image_filter_ = sk_ref_sp(paint->getImageFilter())); } - if ((dataNeeded & maskFilterNeeded_) != 0 && - currentMaskFilter_.get() != paint->getMaskFilter()) { - builder_->setMaskFilter(currentMaskFilter_ = + if ((dataNeeded & kMaskFilterNeeded_) != 0 && + current_mask_filter_.get() != paint->getMaskFilter()) { + builder_->setMaskFilter(current_mask_filter_ = sk_ref_sp(paint->getMaskFilter())); } } diff --git a/flow/display_list_canvas.h b/flow/display_list_canvas.h index f93aa3be4b06a..7fd14d1cf3081 100644 --- a/flow/display_list_canvas.h +++ b/flow/display_list_canvas.h @@ -124,7 +124,7 @@ class DisplayListCanvasRecorder const sk_sp builder() { return builder_; } - sk_sp build(); + sk_sp Build(); void didConcat44(const SkM44&) override; void didSetM44(const SkM44&) override { FML_DCHECK(false); } @@ -246,7 +246,7 @@ class DisplayListCanvasRecorder kSaveLayerOpType, }; - void recordPaintAttributes(const SkPaint* paint, DrawType type); + void RecordPaintAttributes(const SkPaint* paint, DrawType type); private: sk_sp builder_; @@ -254,18 +254,18 @@ class DisplayListCanvasRecorder // Mask bits for the various attributes that might be needed for a given // operation. // clang-format off - static const int aaNeeded_ = 1 << 0; - static const int colorNeeded_ = 1 << 1; - static const int blendNeeded_ = 1 << 2; - static const int invertColorsNeeded_ = 1 << 3; - static const int filterQualityNeeded_ = 1 << 4; - static const int paintStyleNeeded_ = 1 << 5; - static const int strokeStyleNeeded_ = 1 << 6; - static const int shaderNeeded_ = 1 << 7; - static const int colorFilterNeeded_ = 1 << 8; - static const int imageFilterNeeded_ = 1 << 9; - static const int maskFilterNeeded_ = 1 << 10; - static const int ditherNeeded_ = 1 << 11; + static constexpr int kAaNeeded_ = 1 << 0; + static constexpr int kColorNeeded_ = 1 << 1; + static constexpr int kBlendNeeded_ = 1 << 2; + static constexpr int kInvertColorsNeeded_ = 1 << 3; + static constexpr int kFilterQualityNeeded_ = 1 << 4; + static constexpr int kPaintStyleNeeded_ = 1 << 5; + static constexpr int kStrokeStyleNeeded_ = 1 << 6; + static constexpr int kShaderNeeded_ = 1 << 7; + static constexpr int kColorFilterNeeded_ = 1 << 8; + static constexpr int kImageFilterNeeded_ = 1 << 9; + static constexpr int kMaskFilterNeeded_ = 1 << 10; + static constexpr int kDitherNeeded_ = 1 << 11; // clang-format on // Combinations of the above mask bits that are common to typical "draw" @@ -273,39 +273,37 @@ class DisplayListCanvasRecorder // Note that the strokeStyle_ is handled conditionally depending on whether // the paintStyle_ attribute value is synchronized. It can also be manually // specified for operations that will be always stroking, like [drawLine]. - static const int paintMask_ = - aaNeeded_ | colorNeeded_ | blendNeeded_ | invertColorsNeeded_ | - colorFilterNeeded_ | shaderNeeded_ | ditherNeeded_ | imageFilterNeeded_; - static const int drawMask_ = - paintMask_ | paintStyleNeeded_ | maskFilterNeeded_; - static const int strokeMask_ = - paintMask_ | strokeStyleNeeded_ | maskFilterNeeded_; - static const int imageMask_ = colorNeeded_ | blendNeeded_ | - invertColorsNeeded_ | colorFilterNeeded_ | - ditherNeeded_ | imageFilterNeeded_ | - filterQualityNeeded_ | maskFilterNeeded_; - static const int imageRectMask_ = imageMask_ | aaNeeded_; - static const int saveLayerMask_ = colorNeeded_ | blendNeeded_ | - invertColorsNeeded_ | colorFilterNeeded_ | - imageFilterNeeded_; - - static const SkPaint defaultPaint; - - bool currentAA_ = false; - bool currentDither_ = false; - SkColor currentColor_ = 0xFF000000; - SkBlendMode currentBlendMode_ = SkBlendMode::kSrcOver; - SkPaint::Style currentPaintStyle_ = SkPaint::Style::kFill_Style; - SkScalar currentStrokeWidth_ = 0.0; - SkScalar currentMiterLimit_ = 4.0; - SkPaint::Cap currentStrokeCap_ = SkPaint::Cap::kButt_Cap; - SkPaint::Join currentStrokeJoin_ = SkPaint::Join::kMiter_Join; - SkFilterQuality currentFilterQuality_ = - SkFilterQuality::kNone_SkFilterQuality; - sk_sp currentShader_; - sk_sp currentColorFilter_; - sk_sp currentImageFilter_; - sk_sp currentMaskFilter_; + static constexpr int kPaintMask_ = kAaNeeded_ | kColorNeeded_ | + kBlendNeeded_ | kInvertColorsNeeded_ | + kColorFilterNeeded_ | kShaderNeeded_ | + kDitherNeeded_ | kImageFilterNeeded_; + static constexpr int kDrawMask_ = + kPaintMask_ | kPaintStyleNeeded_ | kMaskFilterNeeded_; + static constexpr int kStrokeMask_ = + kPaintMask_ | kStrokeStyleNeeded_ | kMaskFilterNeeded_; + static constexpr int kImageMask_ = + kColorNeeded_ | kBlendNeeded_ | kInvertColorsNeeded_ | + kColorFilterNeeded_ | kDitherNeeded_ | kImageFilterNeeded_ | + kFilterQualityNeeded_ | kMaskFilterNeeded_; + static constexpr int kImageRectMask_ = kImageMask_ | kAaNeeded_; + static constexpr int kSaveLayerMask_ = + kColorNeeded_ | kBlendNeeded_ | kInvertColorsNeeded_ | + kColorFilterNeeded_ | kImageFilterNeeded_; + + bool current_aa_ = false; + bool current_dither_ = false; + SkColor current_color_ = 0xFF000000; + SkBlendMode current_blend_ = SkBlendMode::kSrcOver; + SkPaint::Style current_style_ = SkPaint::Style::kFill_Style; + SkScalar current_stroke_width_ = 0.0; + SkScalar current_miter_limit_ = 4.0; + SkPaint::Cap current_cap_ = SkPaint::Cap::kButt_Cap; + SkPaint::Join current_join_ = SkPaint::Join::kMiter_Join; + SkFilterQuality current_fq_ = SkFilterQuality::kNone_SkFilterQuality; + sk_sp current_shader_; + sk_sp current_color_filter_; + sk_sp current_image_filter_; + sk_sp current_mask_filter_; }; } // namespace flutter diff --git a/flow/display_list_canvas_unittests.cc b/flow/display_list_canvas_unittests.cc index 30cbbd44d56cb..a837dc328b1d3 100644 --- a/flow/display_list_canvas_unittests.cc +++ b/flow/display_list_canvas_unittests.cc @@ -407,7 +407,7 @@ class CanvasCompareTester { DisplayListBuilder builder(TestBounds); dl_setup(builder); dl_render(builder); - sk_sp display_list = builder.build(); + sk_sp display_list = builder.Build(); SkRect dl_bounds = display_list->bounds(); #ifdef DISPLAY_LIST_BOUNDS_ACCURACY_CHECKING if (dl_bounds != ref_bounds) { @@ -428,7 +428,7 @@ class CanvasCompareTester { // since the pixel OOB tests in the compare method do not // trigger, we will trust the DL bounds. // EXPECT_TRUE(dl_bounds.contains(ref_bounds)) << info; - display_list->renderTo(test_surface->getCanvas()); + display_list->RenderTo(test_surface->getCanvas()); compareToReference(test_surface.get(), &ref_pixels, info + " (DL render)", &dl_bounds, bg); } @@ -442,7 +442,7 @@ class CanvasCompareTester { SkPaint test_paint; cv_setup(&dl_recorder, test_paint); cv_render(&dl_recorder, test_paint); - dl_recorder.builder()->build()->renderTo(test_surface->getCanvas()); + dl_recorder.builder()->Build()->RenderTo(test_surface->getCanvas()); compareToReference(test_surface.get(), &ref_pixels, info + " (Sk->DL render)", nullptr, nullptr); } diff --git a/flow/display_list_unittests.cc b/flow/display_list_unittests.cc index 0d738a96187d3..bd8082b801126 100644 --- a/flow/display_list_unittests.cc +++ b/flow/display_list_unittests.cc @@ -144,7 +144,7 @@ static sk_sp MakeTestDisplayList(int w, int h, SkColor color) { DisplayListBuilder builder; builder.setColor(color); builder.drawRect(SkRect::MakeWH(w, h)); - return builder.build(); + return builder.Build(); } static sk_sp TestDisplayList1 = MakeTestDisplayList(20, 20, SK_ColorGREEN); @@ -160,7 +160,7 @@ struct DisplayListInvocation { sk_sp build() { DisplayListBuilder builder; invoker(builder); - return builder.build(); + return builder.Build(); } }; @@ -528,7 +528,7 @@ TEST(DisplayList, SingleOpSizes) { } TEST(DisplayList, SingleOpCompares) { - sk_sp empty = DisplayListBuilder().build(); + sk_sp empty = DisplayListBuilder().Build(); for (auto& group : allGroups) { std::vector> lists1; std::vector> lists2; @@ -537,21 +537,21 @@ TEST(DisplayList, SingleOpCompares) { lists2.push_back(group.variants[i].build()); auto desc = group.op_name + "(variant " + std::to_string(i) + " == empty)"; - ASSERT_FALSE(lists1[i]->equals(*empty)) << desc; - ASSERT_FALSE(lists2[i]->equals(*empty)) << desc; - ASSERT_FALSE(empty->equals(*lists1[i])) << desc; - ASSERT_FALSE(empty->equals(*lists2[i])) << desc; + ASSERT_FALSE(lists1[i]->Equals(*empty)) << desc; + ASSERT_FALSE(lists2[i]->Equals(*empty)) << desc; + ASSERT_FALSE(empty->Equals(*lists1[i])) << desc; + ASSERT_FALSE(empty->Equals(*lists2[i])) << desc; } for (size_t i = 0; i < lists1.size(); i++) { for (size_t j = 0; j < lists2.size(); j++) { auto desc = group.op_name + "(variant " + std::to_string(i) + " ==? variant " + std::to_string(j) + ")"; if (i == j) { - ASSERT_TRUE(lists1[i]->equals(*lists2[j])) << desc; - ASSERT_TRUE(lists2[j]->equals(*lists1[i])) << desc; + ASSERT_TRUE(lists1[i]->Equals(*lists2[j])) << desc; + ASSERT_TRUE(lists2[j]->Equals(*lists1[i])) << desc; } else { - ASSERT_FALSE(lists1[i]->equals(*lists2[j])) << desc; - ASSERT_FALSE(lists2[j]->equals(*lists1[i])) << desc; + ASSERT_FALSE(lists1[i]->Equals(*lists2[j])) << desc; + ASSERT_FALSE(lists2[j]->Equals(*lists1[i])) << desc; } } } diff --git a/flow/display_list_utils.cc b/flow/display_list_utils.cc index ea18778436a83..71f715c6368bd 100644 --- a/flow/display_list_utils.cc +++ b/flow/display_list_utils.cc @@ -179,7 +179,7 @@ void DisplayListBoundsCalculator::saveLayer(const SkRect* bounds, SaveInfo info = withPaint ? SaveLayerWithPaintInfo(this, accumulator_, matrix(), paint()) : SaveLayerInfo(accumulator_, matrix()); - savedInfos_.push_back(info); + saved_infos_.push_back(info); accumulator_ = info.save(); SkMatrixDispatchHelper::reset(); } @@ -187,27 +187,27 @@ void DisplayListBoundsCalculator::save() { SkMatrixDispatchHelper::save(); ClipBoundsDispatchHelper::save(); SaveInfo info = SaveInfo(accumulator_); - savedInfos_.push_back(info); + saved_infos_.push_back(info); accumulator_ = info.save(); } void DisplayListBoundsCalculator::restore() { - if (!savedInfos_.empty()) { + if (!saved_infos_.empty()) { SkMatrixDispatchHelper::restore(); ClipBoundsDispatchHelper::restore(); - SaveInfo info = savedInfos_.back(); - savedInfos_.pop_back(); + SaveInfo info = saved_infos_.back(); + saved_infos_.pop_back(); accumulator_ = info.restore(); } } void DisplayListBoundsCalculator::drawPaint() { - if (!boundsCull_.isEmpty()) { - baseAccumulator_.accumulate(boundsCull_); + if (!bounds_cull_.isEmpty()) { + root_accumulator_.accumulate(bounds_cull_); } } void DisplayListBoundsCalculator::drawColor(SkColor color, SkBlendMode mode) { - if (!boundsCull_.isEmpty()) { - baseAccumulator_.accumulate(boundsCull_); + if (!bounds_cull_.isEmpty()) { + root_accumulator_.accumulate(bounds_cull_); } } void DisplayListBoundsCalculator::drawLine(const SkPoint& p0, @@ -359,7 +359,7 @@ void DisplayListBoundsCalculator::accumulateRect(const SkRect& rect, matrix().mapRect(&dstRect); accumulator_->accumulate(dstRect); } else { - baseAccumulator_.accumulate(boundsCull_); + root_accumulator_.accumulate(bounds_cull_); } if (forceStroke) { setDrawStyle(SkPaint::kFill_Style); @@ -367,29 +367,29 @@ void DisplayListBoundsCalculator::accumulateRect(const SkRect& rect, } DisplayListBoundsCalculator::SaveInfo::SaveInfo(BoundsAccumulator* accumulator) - : savedAccumulator_(accumulator) {} + : saved_accumulator_(accumulator) {} BoundsAccumulator* DisplayListBoundsCalculator::SaveInfo::save() { // No need to swap out the accumulator for a normal save - return savedAccumulator_; + return saved_accumulator_; } BoundsAccumulator* DisplayListBoundsCalculator::SaveInfo::restore() { - return savedAccumulator_; + return saved_accumulator_; } DisplayListBoundsCalculator::SaveLayerInfo::SaveLayerInfo( BoundsAccumulator* accumulator, const SkMatrix& matrix) - : SaveInfo(accumulator), matrix(matrix) {} + : SaveInfo(accumulator), matrix_(matrix) {} BoundsAccumulator* DisplayListBoundsCalculator::SaveLayerInfo::save() { // Use the local layerAccumulator until restore is called and // then transform (and adjust with paint if necessary) on restore() - return &layerAccumulator_; + return &layer_accumulator_; } BoundsAccumulator* DisplayListBoundsCalculator::SaveLayerInfo::restore() { - SkRect layerBounds = layerAccumulator_.getBounds(); - matrix.mapRect(&layerBounds); - savedAccumulator_->accumulate(layerBounds); - return savedAccumulator_; + SkRect layer_bounds = layer_accumulator_.getBounds(); + matrix_.mapRect(&layer_bounds); + saved_accumulator_->accumulate(layer_bounds); + return saved_accumulator_; } DisplayListBoundsCalculator::SaveLayerWithPaintInfo::SaveLayerWithPaintInfo( @@ -403,15 +403,15 @@ DisplayListBoundsCalculator::SaveLayerWithPaintInfo::SaveLayerWithPaintInfo( BoundsAccumulator* DisplayListBoundsCalculator::SaveLayerWithPaintInfo::restore() { - SkRect layerBounds = layerAccumulator_.getBounds(); + SkRect layer_bounds = layer_accumulator_.getBounds(); if (paint_.canComputeFastBounds()) { - layerBounds = paint_.computeFastBounds(layerBounds, &layerBounds); - matrix.mapRect(&layerBounds); - savedAccumulator_->accumulate(layerBounds); + layer_bounds = paint_.computeFastBounds(layer_bounds, &layer_bounds); + matrix_.mapRect(&layer_bounds); + saved_accumulator_->accumulate(layer_bounds); } else { - calculator_->baseAccumulator_.accumulate(calculator_->boundsCull_); + calculator_->root_accumulator_.accumulate(calculator_->bounds_cull_); } - return savedAccumulator_; + return saved_accumulator_; } } // namespace flutter diff --git a/flow/display_list_utils.h b/flow/display_list_utils.h index d921ae0b4eb3b..51b7187705f42 100644 --- a/flow/display_list_utils.h +++ b/flow/display_list_utils.h @@ -204,14 +204,14 @@ class BoundsAccumulator { public: void accumulate(const SkPoint& p) { accumulate(p.fX, p.fY); } void accumulate(SkScalar x, SkScalar y) { - if (minX_ > x) - minX_ = x; - if (minY_ > y) - minY_ = y; - if (maxX_ < x) - maxX_ = x; - if (maxY_ < y) - maxY_ = y; + if (min_x_ > x) + min_x_ = x; + if (min_y_ > y) + min_y_ = y; + if (max_x_ < x) + max_x_ = x; + if (max_y_ < y) + max_y_ = y; } void accumulate(const SkRect& r) { if (r.fLeft <= r.fRight && r.fTop <= r.fBottom) { @@ -220,20 +220,20 @@ class BoundsAccumulator { } } - bool isEmpty() const { return minX_ >= maxX_ || minY_ >= maxY_; } - bool isNotEmpty() const { return minX_ < maxX_ && minY_ < maxY_; } + bool isEmpty() const { return min_x_ >= max_x_ || min_y_ >= max_y_; } + bool isNotEmpty() const { return min_x_ < max_x_ && min_y_ < max_y_; } SkRect getBounds() const { - return (maxX_ > minX_ && maxY_ > minY_) - ? SkRect::MakeLTRB(minX_, minY_, maxX_, maxY_) + return (max_x_ > min_x_ && max_y_ > min_y_) + ? SkRect::MakeLTRB(min_x_, min_y_, max_x_, max_y_) : SkRect::MakeEmpty(); } private: - SkScalar minX_ = std::numeric_limits::infinity(); - SkScalar minY_ = std::numeric_limits::infinity(); - SkScalar maxX_ = -std::numeric_limits::infinity(); - SkScalar maxY_ = -std::numeric_limits::infinity(); + SkScalar min_x_ = std::numeric_limits::infinity(); + SkScalar min_y_ = std::numeric_limits::infinity(); + SkScalar max_x_ = -std::numeric_limits::infinity(); + SkScalar max_y_ = -std::numeric_limits::infinity(); }; // This class implements all rendering methods and computes a liberal @@ -248,8 +248,8 @@ class DisplayListBoundsCalculator final // DisplayList dispatcher method calls. Since 2 of the method calls // have no intrinsic size because they render to the entire available, // the |cullRect| provides a bounds for them to include. - DisplayListBoundsCalculator(const SkRect& cullRect = SkRect::MakeEmpty()) - : accumulator_(&baseAccumulator_), boundsCull_(cullRect) {} + DisplayListBoundsCalculator(const SkRect& cull_rect = SkRect::MakeEmpty()) + : accumulator_(&root_accumulator_), bounds_cull_(cull_rect) {} void saveLayer(const SkRect* bounds, bool withPaint) override; void save() override; @@ -317,8 +317,8 @@ class DisplayListBoundsCalculator final // Only used for drawColor and drawPaint and paint objects that // cannot support fast bounds. - SkRect boundsCull_; - BoundsAccumulator baseAccumulator_; + SkRect bounds_cull_; + BoundsAccumulator root_accumulator_; class SaveInfo { public: @@ -329,7 +329,7 @@ class DisplayListBoundsCalculator final virtual BoundsAccumulator* restore(); protected: - BoundsAccumulator* savedAccumulator_; + BoundsAccumulator* saved_accumulator_; }; class SaveLayerInfo : public SaveInfo { @@ -341,16 +341,16 @@ class DisplayListBoundsCalculator final BoundsAccumulator* restore() override; protected: - BoundsAccumulator layerAccumulator_; - const SkMatrix matrix; + BoundsAccumulator layer_accumulator_; + const SkMatrix matrix_; }; class SaveLayerWithPaintInfo : public SaveLayerInfo { public: SaveLayerWithPaintInfo(DisplayListBoundsCalculator* calculator, BoundsAccumulator* accumulator, - const SkMatrix& saveMatrix, - const SkPaint& savePaint); + const SkMatrix& save_matrix, + const SkPaint& save_paint); virtual ~SaveLayerWithPaintInfo() = default; BoundsAccumulator* restore() override; @@ -361,9 +361,9 @@ class DisplayListBoundsCalculator final SkPaint paint_; }; - std::vector savedInfos_; + std::vector saved_infos_; - void accumulateRect(const SkRect& rect, bool forceStroke = false); + void accumulateRect(const SkRect& rect, bool force_stroke = false); }; } // namespace flutter diff --git a/flow/layers/display_list_layer.cc b/flow/layers/display_list_layer.cc index e27dee3c9431f..5bea153ad223c 100644 --- a/flow/layers/display_list_layer.cc +++ b/flow/layers/display_list_layer.cc @@ -69,7 +69,7 @@ bool DisplayListLayer::Compare(DiffContext::Statistics& statistics, statistics.AddDeepComparePicture(); - auto res = dl1->equals(*dl2); + auto res = dl1->Equals(*dl2); if (res) { statistics.AddDifferentInstanceButEqualPicture(); } else { @@ -124,7 +124,7 @@ void DisplayListLayer::Paint(PaintContext& context) const { return; } - display_list()->renderTo(context.leaf_nodes_canvas); + display_list()->RenderTo(context.leaf_nodes_canvas); } } // namespace flutter diff --git a/flow/layers/display_list_layer_unittests.cc b/flow/layers/display_list_layer_unittests.cc index 1ccd0f291c791..199c04882f385 100644 --- a/flow/layers/display_list_layer_unittests.cc +++ b/flow/layers/display_list_layer_unittests.cc @@ -70,7 +70,7 @@ TEST_F(DisplayListLayerTest, SimpleDisplayList) { const SkRect picture_bounds = SkRect::MakeLTRB(5.0f, 6.0f, 20.5f, 21.5f); DisplayListBuilder builder; builder.drawRect(picture_bounds); - auto display_list = builder.build(); + auto display_list = builder.Build(); auto layer = std::make_shared(layer_offset, display_list, false, false); diff --git a/flow/raster_cache.cc b/flow/raster_cache.cc index bb2f67fe40d07..6cba6b1921861 100644 --- a/flow/raster_cache.cc +++ b/flow/raster_cache.cc @@ -190,7 +190,7 @@ std::unique_ptr RasterCache::RasterizeDisplayList( bool checkerboard) const { return Rasterize(context, ctm, dst_color_space, checkerboard, display_list->bounds(), - [=](SkCanvas* canvas) { display_list->renderTo(canvas); }); + [=](SkCanvas* canvas) { display_list->RenderTo(canvas); }); } void RasterCache::Prepare(PrerollContext* context, diff --git a/flow/testing/diff_context_test.cc b/flow/testing/diff_context_test.cc index ee6c69216c192..74f26f780c659 100644 --- a/flow/testing/diff_context_test.cc +++ b/flow/testing/diff_context_test.cc @@ -47,7 +47,7 @@ sk_sp DiffContextTest::CreateDisplayList(const SkRect& bounds, DisplayListBuilder builder; builder.setColor(color); builder.drawRect(bounds); - return builder.build(); + return builder.Build(); } std::shared_ptr DiffContextTest::CreateDisplayListLayer( diff --git a/lib/ui/painting/canvas.cc b/lib/ui/painting/canvas.cc index 0741cd39730c7..6c7581bc01b78 100644 --- a/lib/ui/painting/canvas.cc +++ b/lib/ui/painting/canvas.cc @@ -390,7 +390,7 @@ void Canvas::drawImageNine(const CanvasImage* image, // pass along. For simplicity, we will bypass the canvas and ask // the recorder to record our paint attributes and record a much // simpler DrawImageNineOp record directly. - display_list_recorder_->recordPaintAttributes( + display_list_recorder_->RecordPaintAttributes( paint.paint(), DisplayListCanvasRecorder::DrawType::kImageOpType); builder()->drawImageNine(image->image(), icenter, dst, filter); } else { @@ -414,7 +414,7 @@ void Canvas::drawPicture(Picture* picture) { if (display_list_recorder_) { builder()->drawDisplayList(picture->display_list()); } else { - picture->display_list()->renderTo(canvas_); + picture->display_list()->RenderTo(canvas_); } } else { FML_DCHECK(false); diff --git a/lib/ui/painting/picture.cc b/lib/ui/painting/picture.cc index 661058e3b0fdd..2789d2eec6720 100644 --- a/lib/ui/painting/picture.cc +++ b/lib/ui/painting/picture.cc @@ -59,7 +59,7 @@ Dart_Handle Picture::toImage(uint32_t width, if (display_list_) { return RasterizeToImage( [display_list = display_list_.get()](SkCanvas* canvas) { - display_list->renderTo(canvas); + display_list->RenderTo(canvas); }, width, height, raw_image_callback); } else { diff --git a/lib/ui/painting/picture_recorder.cc b/lib/ui/painting/picture_recorder.cc index 1e0cf02e8f6aa..ed6f88cfeda78 100644 --- a/lib/ui/painting/picture_recorder.cc +++ b/lib/ui/painting/picture_recorder.cc @@ -56,7 +56,7 @@ fml::RefPtr PictureRecorder::endRecording(Dart_Handle dart_picture) { fml::RefPtr picture; if (display_list_recorder_) { - picture = Picture::Create(dart_picture, display_list_recorder_->build()); + picture = Picture::Create(dart_picture, display_list_recorder_->Build()); display_list_recorder_ = nullptr; } else { picture = Picture::Create( From 3a39b553d966a805959f4c9aa6432a490681cce5 Mon Sep 17 00:00:00 2001 From: Jim Graham Date: Thu, 24 Jun 2021 17:14:50 -0700 Subject: [PATCH 09/26] add alignment pragmas to rein in Windows x64 compiler wasted bytes --- flow/display_list.cc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/flow/display_list.cc b/flow/display_list.cc index 4e5d9737d2e2e..ff78c356b5030 100644 --- a/flow/display_list.cc +++ b/flow/display_list.cc @@ -52,6 +52,8 @@ enum class DisplayListCompare { kEqual, }; +#pragma pack(push, DLOp_Alignment, 8) + // Assuming a 64-bit platform (most of our platforms at this time?) // // Struct allocation in the DL memory is aligned to a void* boundary @@ -782,6 +784,8 @@ struct DrawShadowOp final : DLOp { } }; +#pragma pack(pop, DLOp_Alignment) + void DisplayList::ComputeBounds() { DisplayListBoundsCalculator calculator(bounds_cull_); Dispatch(calculator); From 5c0e31f45e7983a5ffeb0ee4397e5fc79908539a Mon Sep 17 00:00:00 2001 From: Jim Graham Date: Thu, 24 Jun 2021 17:18:46 -0700 Subject: [PATCH 10/26] remove the ColorFilter workaround from unit test now that Skia bug is fixed --- flow/display_list_canvas_unittests.cc | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/flow/display_list_canvas_unittests.cc b/flow/display_list_canvas_unittests.cc index a837dc328b1d3..cf0b888998b49 100644 --- a/flow/display_list_canvas_unittests.cc +++ b/flow/display_list_canvas_unittests.cc @@ -39,8 +39,6 @@ constexpr SkRect TestBounds = SkRect::MakeWH(TestWidth, TestHeight); constexpr SkRect RenderBounds = SkRect::MakeLTRB(RenderLeft, RenderTop, RenderRight, RenderBottom); -static bool skipTheColorFilter = false; - class CanvasCompareTester { public: typedef const std::function CvRenderer; @@ -123,10 +121,7 @@ class CanvasCompareTester { ASSERT_TRUE(filter->unique()) << "ImageFilter Cleanup"; } - if (skipTheColorFilter) { - // drawVertices + ColorFilter outputs nothing on the CPU renderer - FML_LOG(ERROR) << "Skipping the ColorFilter test"; - } else { + { constexpr float rotate_color_matrix[20] = { 0, 1, 0, 0, 0, // 0, 0, 1, 0, 0, // @@ -754,7 +749,6 @@ TEST(DisplayListCanvas, DrawPointsAsPolygon) { } TEST(DisplayListCanvas, DrawVerticesWithColors) { - skipTheColorFilter = true; const SkPoint pts[3] = { SkPoint::Make(RenderCenterX, RenderTop), SkPoint::Make(RenderLeft, RenderBottom), @@ -770,13 +764,10 @@ TEST(DisplayListCanvas, DrawVerticesWithColors) { [=](DisplayListBuilder& builder) { // builder.drawVertices(vertices, SkBlendMode::kSrcOver); }); - // Since there are no ASSERT macros above here, this line will execute - skipTheColorFilter = false; ASSERT_TRUE(vertices->unique()); } TEST(DisplayListCanvas, DrawVerticesWithImage) { - skipTheColorFilter = true; const SkPoint pts[3] = { SkPoint::Make(RenderCenterX, RenderTop), SkPoint::Make(RenderLeft, RenderBottom), @@ -800,8 +791,6 @@ TEST(DisplayListCanvas, DrawVerticesWithImage) { builder.setShader(shader); builder.drawVertices(vertices, SkBlendMode::kSrcOver); }); - // Since there are no ASSERT macros above here, this line will execute - skipTheColorFilter = false; ASSERT_TRUE(vertices->unique()); ASSERT_TRUE(shader->unique()); } From 3e34f1a375fc3336fadd6080a4a09505c234d51d Mon Sep 17 00:00:00 2001 From: Jim Graham Date: Fri, 25 Jun 2021 13:23:24 -0700 Subject: [PATCH 11/26] naming style for DLOp fields --- flow/display_list.cc | 95 ++++++++++++++++++++++---------------------- 1 file changed, 48 insertions(+), 47 deletions(-) diff --git a/flow/display_list.cc b/flow/display_list.cc index ff78c356b5030..309d2d09ab5b1 100644 --- a/flow/display_list.cc +++ b/flow/display_list.cc @@ -240,26 +240,26 @@ struct SaveOp final : DLOp { struct SaveLayerOp final : DLOp { static const auto kType = DisplayListOpType::kSaveLayer; - SaveLayerOp(bool withPaint) : withPaint(withPaint) {} + SaveLayerOp(bool with_paint) : with_paint(with_paint) {} - bool withPaint; + bool with_paint; void dispatch(Dispatcher& dispatcher) const { - dispatcher.saveLayer(nullptr, withPaint); + dispatcher.saveLayer(nullptr, with_paint); } }; // 4 byte header + 20 byte payload packs evenly into 24 bytes struct SaveLayerBoundsOp final : DLOp { static const auto kType = DisplayListOpType::kSaveLayerBounds; - SaveLayerBoundsOp(SkRect rect, bool withPaint) - : withPaint(withPaint), rect(rect) {} + SaveLayerBoundsOp(SkRect rect, bool with_paint) + : with_paint(with_paint), rect(rect) {} - bool withPaint; + bool with_paint; const SkRect rect; void dispatch(Dispatcher& dispatcher) const { - dispatcher.saveLayer(&rect, withPaint); + dispatcher.saveLayer(&rect, with_paint); } }; // 4 byte header + no payload uses minimum 8 bytes (4 bytes unused) @@ -375,20 +375,20 @@ struct Transform3x3Op final : DLOp { // SkRRect is 52 more bytes, which rounds up to 56 bytes (4 bytes unused) // which packs into 64 bytes total // SkPath is 16 more bytes, which packs efficiently into 24 bytes total -#define DEFINE_CLIP_SHAPE_OP(shapetype) \ - struct Clip##shapetype##Op final : DLOp { \ - static const auto kType = DisplayListOpType::kClip##shapetype; \ - \ - Clip##shapetype##Op(Sk##shapetype shape, bool isAA, SkClipOp op) \ - : isAA(isAA), op(op), shape(shape) {} \ - \ - const bool isAA : 8; \ - const SkClipOp op : 8; \ - const Sk##shapetype shape; \ - \ - void dispatch(Dispatcher& dispatcher) const { \ - dispatcher.clip##shapetype(shape, isAA, op); \ - } \ +#define DEFINE_CLIP_SHAPE_OP(shapetype) \ + struct Clip##shapetype##Op final : DLOp { \ + static const auto kType = DisplayListOpType::kClip##shapetype; \ + \ + Clip##shapetype##Op(Sk##shapetype shape, bool is_aa, SkClipOp op) \ + : is_aa(is_aa), op(op), shape(shape) {} \ + \ + const bool is_aa : 8; \ + const SkClipOp op : 8; \ + const Sk##shapetype shape; \ + \ + void dispatch(Dispatcher& dispatcher) const { \ + dispatcher.clip##shapetype(shape, is_aa, op); \ + } \ }; DEFINE_CLIP_SHAPE_OP(Rect) DEFINE_CLIP_SHAPE_OP(RRect) @@ -585,23 +585,23 @@ struct DrawImageLatticeOp final : DLOp { static const auto kType = DisplayListOpType::kDrawImageLattice; DrawImageLatticeOp(const sk_sp image, - int xDivCount, - int yDivCount, - int cellCount, + int x_count, + int y_count, + int cell_count, const SkIRect& src, const SkRect& dst, SkFilterMode filter) - : xDivCount(xDivCount), - yDivCount(yDivCount), - cellCount(cellCount), + : x_count(x_count), + y_count(y_count), + cell_count(cell_count), src(src), dst(dst), filter(filter), image(std::move(image)) {} - const int xDivCount; - const int yDivCount; - const int cellCount; + const int x_count; + const int y_count; + const int cell_count; const SkIRect src; const SkRect dst; const SkFilterMode filter; @@ -609,16 +609,17 @@ struct DrawImageLatticeOp final : DLOp { void dispatch(Dispatcher& dispatcher) const { const int* xDivs = reinterpret_cast(this + 1); - const int* yDivs = reinterpret_cast(xDivs + xDivCount); + const int* yDivs = reinterpret_cast(xDivs + x_count); const SkColor* colors = - (cellCount == 0) ? nullptr - : reinterpret_cast(yDivs + yDivCount); - const SkCanvas::Lattice::RectType* rTypes = - (cellCount == 0) ? nullptr - : reinterpret_cast( - colors + cellCount); + (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, rTypes, xDivCount, yDivCount, &src, colors}, dst, + image, {xDivs, yDivs, types, x_count, y_count, &src, colors}, dst, filter); } }; @@ -692,14 +693,14 @@ DEFINE_DRAW_ATLAS_OP(AtlasColoredCulled, HAS_COLORS, HAS_CULLING) struct DrawSkPictureOp final : DLOp { static const auto kType = DisplayListOpType::kDrawSkPicture; - DrawSkPictureOp(sk_sp picture, bool withLayer) - : withLayer(withLayer), picture(std::move(picture)) {} + DrawSkPictureOp(sk_sp picture, bool with_layer) + : with_layer(with_layer), picture(std::move(picture)) {} - const bool withLayer; + const bool with_layer; const sk_sp picture; void dispatch(Dispatcher& dispatcher) const { - dispatcher.drawPicture(picture, nullptr, withLayer); + dispatcher.drawPicture(picture, nullptr, with_layer); } }; @@ -708,15 +709,15 @@ struct DrawSkPictureMatrixOp final : DLOp { DrawSkPictureMatrixOp(sk_sp picture, const SkMatrix matrix, - bool withLayer) - : withLayer(withLayer), picture(std::move(picture)), matrix(matrix) {} + bool with_layer) + : with_layer(with_layer), picture(std::move(picture)), matrix(matrix) {} - const bool withLayer; + const bool with_layer; const sk_sp picture; const SkMatrix matrix; void dispatch(Dispatcher& dispatcher) const { - dispatcher.drawPicture(picture, &matrix, withLayer); + dispatcher.drawPicture(picture, &matrix, with_layer); } }; @@ -936,7 +937,7 @@ DisplayList::~DisplayList() { #define DL_BUILDER_PAGE 4096 -// copy_v(dst, src,n, src,n, ...) copies any number of typed srcs into dst. +// CopyV(dst, src,n, src,n, ...) copies any number of typed srcs into dst. static void CopyV(void* dst) {} template From 8b3f915770b942364fb2dfe8500905897129ddb6 Mon Sep 17 00:00:00 2001 From: Jim Graham Date: Fri, 25 Jun 2021 13:34:58 -0700 Subject: [PATCH 12/26] switch to EXPECT for byte count test to see how many tests fail on Windows --- flow/display_list_unittests.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flow/display_list_unittests.cc b/flow/display_list_unittests.cc index bd8082b801126..64ac88d4d1c8e 100644 --- a/flow/display_list_unittests.cc +++ b/flow/display_list_unittests.cc @@ -522,7 +522,7 @@ TEST(DisplayList, SingleOpSizes) { sk_sp dl = invocation.build(); auto desc = group.op_name + "(variant " + std::to_string(i) + ")"; ASSERT_EQ(dl->opCount(), invocation.opCount) << desc; - ASSERT_EQ(dl->bytes(), invocation.byteCount) << desc; + EXPECT_EQ(dl->bytes(), invocation.byteCount) << desc; } } } From 90a7b90be3f6b8790089f9f5ad1a3442e6246621 Mon Sep 17 00:00:00 2001 From: Jim Graham Date: Fri, 25 Jun 2021 14:44:35 -0700 Subject: [PATCH 13/26] implement ImageRect constraint and pack ClipOp better for Windows (hopefully) --- flow/display_list.cc | 108 ++++++++++++++++++++------------- flow/display_list.h | 26 +++++--- flow/display_list_canvas.cc | 18 +++--- flow/display_list_canvas.h | 3 +- flow/display_list_unittests.cc | 3 + flow/display_list_utils.cc | 3 +- flow/display_list_utils.h | 3 +- 7 files changed, 100 insertions(+), 64 deletions(-) diff --git a/flow/display_list.cc b/flow/display_list.cc index 309d2d09ab5b1..6f4bbc05ed80e 100644 --- a/flow/display_list.cc +++ b/flow/display_list.cc @@ -85,7 +85,7 @@ struct DLOp { DEFINE_SET_BOOL_OP(AA) DEFINE_SET_BOOL_OP(Dither) DEFINE_SET_BOOL_OP(InvertColors) -#undef DEFINE_SET_CLEAR_BOOL_OP +#undef DEFINE_SET_BOOL_OP // 4 byte header + 4 byte payload packs into minimum 8 bytes #define DEFINE_SET_ENUM_OP(name) \ @@ -369,30 +369,36 @@ struct Transform3x3Op final : DLOp { } }; -// The common data is a 4 byte header + a 2 byte common payload which -// takes 6 bytes but is expanded to 8 (2 bytes unused) +// 4 byte header + 4 byte common payload packs into minimum 8 bytes // SkRect is 16 more bytes, which packs efficiently into 24 bytes total // SkRRect is 52 more bytes, which rounds up to 56 bytes (4 bytes unused) // which packs into 64 bytes total // SkPath is 16 more bytes, which packs efficiently into 24 bytes total -#define DEFINE_CLIP_SHAPE_OP(shapetype) \ - struct Clip##shapetype##Op final : DLOp { \ - static const auto kType = DisplayListOpType::kClip##shapetype; \ - \ - Clip##shapetype##Op(Sk##shapetype shape, bool is_aa, SkClipOp op) \ - : is_aa(is_aa), op(op), shape(shape) {} \ - \ - const bool is_aa : 8; \ - const SkClipOp op : 8; \ - const Sk##shapetype shape; \ - \ - void dispatch(Dispatcher& dispatcher) const { \ - dispatcher.clip##shapetype(shape, is_aa, op); \ - } \ +// +// We could pack the clip_op and the bool both into the free 4 bytes after +// the header, but the Windows compiler keeps wanting to expand that +// 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 { \ + static const auto kType = DisplayListOpType::kClip##clipop##shapetype; \ + \ + Clip##clipop##shapetype##Op(Sk##shapetype shape, bool is_aa) \ + : is_aa(is_aa), shape(shape) {} \ + \ + const bool is_aa; \ + const Sk##shapetype shape; \ + \ + void dispatch(Dispatcher& dispatcher) const { \ + dispatcher.clip##shapetype(shape, is_aa, SkClipOp::k##clipop); \ + } \ }; -DEFINE_CLIP_SHAPE_OP(Rect) -DEFINE_CLIP_SHAPE_OP(RRect) -DEFINE_CLIP_SHAPE_OP(Path) +DEFINE_CLIP_SHAPE_OP(Rect, Intersect) +DEFINE_CLIP_SHAPE_OP(RRect, Intersect) +DEFINE_CLIP_SHAPE_OP(Path, Intersect) +DEFINE_CLIP_SHAPE_OP(Rect, Difference) +DEFINE_CLIP_SHAPE_OP(RRect, Difference) +DEFINE_CLIP_SHAPE_OP(Path, Difference) #undef DEFINE_CLIP_SHAPE_OP // 4 byte header + no payload uses minimum 8 bytes (4 bytes unused) @@ -542,24 +548,31 @@ struct DrawImageOp final : DLOp { }; // 4 byte header + 60 byte payload packs efficiently into 64 bytes -struct DrawImageRectOp final : DLOp { - static const auto kType = DisplayListOpType::kDrawImageRect; - - DrawImageRectOp(const sk_sp image, - const SkRect& src, - const SkRect& dst, - const SkSamplingOptions& sampling) - : src(src), dst(dst), sampling(sampling), image(std::move(image)) {} - - const SkRect src; - const SkRect dst; - const SkSamplingOptions sampling; - const sk_sp image; - - void dispatch(Dispatcher& dispatcher) const { - dispatcher.drawImageRect(image, src, dst, sampling); - } -}; +// +// The constraint could be stored in the struct, but it would not pack +// efficiently so 2 variants are defined instead. +#define DEFINE_DRAW_IMAGE_RECT_OP(name, constraint) \ + struct Draw##name##Op final : DLOp { \ + static const auto kType = DisplayListOpType::kDraw##name; \ + \ + Draw##name##Op(const sk_sp image, \ + const SkRect& src, \ + const SkRect& dst, \ + const SkSamplingOptions& sampling) \ + : src(src), dst(dst), sampling(sampling), image(std::move(image)) {} \ + \ + const SkRect src; \ + const SkRect dst; \ + const SkSamplingOptions sampling; \ + const sk_sp image; \ + \ + void dispatch(Dispatcher& dispatcher) const { \ + dispatcher.drawImageRect(image, src, dst, sampling, constraint); \ + } \ + }; +DEFINE_DRAW_IMAGE_RECT_OP(ImageRectStrict, SkCanvas::kStrict_SrcRectConstraint) +DEFINE_DRAW_IMAGE_RECT_OP(ImageRectFast, SkCanvas::kFast_SrcRectConstraint) +#undef DEFINE_DRAW_IMAGE_RECT_OP // 4 byte header + 44 byte payload packs efficiently into 48 bytes struct DrawImageNineOp final : DLOp { @@ -1113,7 +1126,9 @@ void DisplayListBuilder::transform3x3(SkScalar mxx, void DisplayListBuilder::clipRect(const SkRect& rect, bool isAA, SkClipOp clip_op) { - Push(0, rect, isAA, clip_op); + clip_op == SkClipOp::kIntersect // + ? Push(0, rect, isAA) + : Push(0, rect, isAA); } void DisplayListBuilder::clipRRect(const SkRRect& rrect, bool isAA, @@ -1121,13 +1136,17 @@ void DisplayListBuilder::clipRRect(const SkRRect& rrect, if (rrect.isRect()) { clipRect(rrect.rect(), isAA, clip_op); } else { - Push(0, rrect, isAA, clip_op); + clip_op == SkClipOp::kIntersect // + ? Push(0, rrect, isAA) + : Push(0, rrect, isAA); } } void DisplayListBuilder::clipPath(const SkPath& path, bool isAA, SkClipOp clip_op) { - Push(0, path, isAA, clip_op); + clip_op == SkClipOp::kIntersect // + ? Push(0, path, isAA) + : Push(0, path, isAA); } void DisplayListBuilder::drawPaint() { @@ -1206,8 +1225,11 @@ void DisplayListBuilder::drawImage(const sk_sp image, void DisplayListBuilder::drawImageRect(const sk_sp image, const SkRect& src, const SkRect& dst, - const SkSamplingOptions& sampling) { - Push(0, std::move(image), src, dst, sampling); + const SkSamplingOptions& sampling, + SkCanvas::SrcRectConstraint constraint) { + constraint == SkCanvas::kFast_SrcRectConstraint // + ? Push(0, std::move(image), src, dst, sampling) + : Push(0, std::move(image), src, dst, sampling); } void DisplayListBuilder::drawImageNine(const sk_sp image, const SkIRect& center, diff --git a/flow/display_list.h b/flow/display_list.h index 0380147c88696..df97f6bf12752 100644 --- a/flow/display_list.h +++ b/flow/display_list.h @@ -104,9 +104,12 @@ namespace flutter { V(Transform2x3) \ V(Transform3x3) \ \ - V(ClipRect) \ - V(ClipRRect) \ - V(ClipPath) \ + V(ClipIntersectRect) \ + V(ClipIntersectRRect) \ + V(ClipIntersectPath) \ + V(ClipDifferenceRect) \ + V(ClipDifferenceRRect) \ + V(ClipDifferencePath) \ \ V(DrawPaint) \ V(DrawColor) \ @@ -126,7 +129,8 @@ namespace flutter { V(DrawVertices) \ \ V(DrawImage) \ - V(DrawImageRect) \ + V(DrawImageRectStrict) \ + V(DrawImageRectFast) \ V(DrawImageNine) \ V(DrawImageLattice) \ V(DrawAtlas) \ @@ -285,7 +289,8 @@ class Dispatcher { virtual void drawImageRect(const sk_sp image, const SkRect& src, const SkRect& dst, - const SkSamplingOptions& sampling) = 0; + const SkSamplingOptions& sampling, + SkCanvas::SrcRectConstraint constraint) = 0; virtual void drawImageNine(const sk_sp image, const SkIRect& center, const SkRect& dst, @@ -393,10 +398,13 @@ class DisplayListBuilder final : public virtual Dispatcher, public SkRefCnt { void drawImage(const sk_sp image, const SkPoint point, const SkSamplingOptions& sampling) override; - void drawImageRect(const sk_sp image, - const SkRect& src, - const SkRect& dst, - const SkSamplingOptions& sampling) override; + void drawImageRect( + const sk_sp image, + const SkRect& src, + const SkRect& dst, + const SkSamplingOptions& sampling, + SkCanvas::SrcRectConstraint constraint = + SkCanvas::SrcRectConstraint::kFast_SrcRectConstraint) override; void drawImageNine(const sk_sp image, const SkIRect& center, const SkRect& dst, diff --git a/flow/display_list_canvas.cc b/flow/display_list_canvas.cc index 421c00db78354..0b59da6cd1c1b 100644 --- a/flow/display_list_canvas.cc +++ b/flow/display_list_canvas.cc @@ -66,12 +66,12 @@ void DisplayListCanvasDispatcher::clipRect(const SkRect& rect, void DisplayListCanvasDispatcher::clipRRect(const SkRRect& rrect, bool isAA, SkClipOp clip_op) { - canvas_->clipRRect(rrect, isAA); + canvas_->clipRRect(rrect, clip_op, isAA); } void DisplayListCanvasDispatcher::clipPath(const SkPath& path, bool isAA, SkClipOp clip_op) { - canvas_->clipPath(path, isAA); + canvas_->clipPath(path, clip_op, isAA); } void DisplayListCanvasDispatcher::drawPaint() { @@ -128,9 +128,9 @@ void DisplayListCanvasDispatcher::drawImageRect( const sk_sp image, const SkRect& src, const SkRect& dst, - const SkSamplingOptions& sampling) { - canvas_->drawImageRect(image, src, dst, sampling, &paint(), - SkCanvas::kFast_SrcRectConstraint); + const SkSamplingOptions& sampling, + SkCanvas::SrcRectConstraint constraint) { + canvas_->drawImageRect(image, src, dst, sampling, &paint(), constraint); } void DisplayListCanvasDispatcher::drawImageNine(const sk_sp image, const SkIRect& center, @@ -297,6 +297,9 @@ void DisplayListCanvasRecorder::onDrawPoints(SkCanvas::PointMode mode, builder_->drawLine(pts[0], pts[1]); } else { uint32_t count32 = static_cast(count); + // TODO(flar): depending on the mode we could break it down into + // multiple calls to drawPoints, but how much do we really want + // to support more than a couple billion points? FML_DCHECK(count32 == count); builder_->drawPoints(mode, count32, pts); } @@ -323,9 +326,8 @@ void DisplayListCanvasRecorder::onDrawImageRect2( const SkSamplingOptions& sampling, const SkPaint* paint, SrcRectConstraint constraint) { - FML_DCHECK(constraint == SrcRectConstraint::kFast_SrcRectConstraint); RecordPaintAttributes(paint, DrawType::kImageRectOpType); - builder_->drawImageRect(sk_ref_sp(image), src, dst, sampling); + builder_->drawImageRect(sk_ref_sp(image), src, dst, sampling, constraint); } void DisplayListCanvasRecorder::onDrawImageLattice2(const SkImage* image, const Lattice& lattice, @@ -365,7 +367,6 @@ void DisplayListCanvasRecorder::onDrawShadowRec(const SkPath& path, void DisplayListCanvasRecorder::onDrawPicture(const SkPicture* picture, const SkMatrix* matrix, const SkPaint* paint) { - FML_DCHECK(matrix == nullptr); if (paint) { RecordPaintAttributes(paint, DrawType::kSaveLayerOpType); } @@ -426,7 +427,6 @@ void DisplayListCanvasRecorder::RecordPaintAttributes(const SkPaint* paint, // } if ((dataNeeded & kPaintStyleNeeded_) != 0) { if (current_style_ != paint->getStyle()) { - FML_DCHECK(paint->getStyle() != SkPaint::kStrokeAndFill_Style); builder_->setDrawStyle(current_style_ = paint->getStyle()); } if (current_style_ == SkPaint::Style::kStroke_Style) { diff --git a/flow/display_list_canvas.h b/flow/display_list_canvas.h index 7fd14d1cf3081..3b93a0609b61e 100644 --- a/flow/display_list_canvas.h +++ b/flow/display_list_canvas.h @@ -81,7 +81,8 @@ class DisplayListCanvasDispatcher : public virtual Dispatcher, void drawImageRect(const sk_sp image, const SkRect& src, const SkRect& dst, - const SkSamplingOptions& sampling) override; + const SkSamplingOptions& sampling, + SkCanvas::SrcRectConstraint constraint) override; void drawImageNine(const sk_sp image, const SkIRect& center, const SkRect& dst, diff --git a/flow/display_list_unittests.cc b/flow/display_list_unittests.cc index 64ac88d4d1c8e..fb00038749658 100644 --- a/flow/display_list_unittests.cc +++ b/flow/display_list_unittests.cc @@ -418,6 +418,9 @@ std::vector allGroups = { { "DrawImageRect", { {1, 64, [](DisplayListBuilder& b) {b.drawImageRect(TestImage1, {10, 10, 20, 20}, {10, 10, 40, 40}, DisplayList::NearestSampling);}}, + {1, 64, [](DisplayListBuilder& b) {b.drawImageRect(TestImage1, {10, 10, 20, 20}, {10, 10, 40, 40}, + DisplayList::NearestSampling, + SkCanvas::SrcRectConstraint::kStrict_SrcRectConstraint);}}, {1, 64, [](DisplayListBuilder& b) {b.drawImageRect(TestImage1, {10, 10, 25, 20}, {10, 10, 40, 40}, DisplayList::NearestSampling);}}, {1, 64, [](DisplayListBuilder& b) {b.drawImageRect(TestImage1, {10, 10, 20, 20}, {10, 10, 45, 40}, diff --git a/flow/display_list_utils.cc b/flow/display_list_utils.cc index 71f715c6368bd..3abe58c84a2ed 100644 --- a/flow/display_list_utils.cc +++ b/flow/display_list_utils.cc @@ -271,7 +271,8 @@ void DisplayListBoundsCalculator::drawImageRect( const sk_sp image, const SkRect& src, const SkRect& dst, - const SkSamplingOptions& sampling) { + const SkSamplingOptions& sampling, + SkCanvas::SrcRectConstraint constraint) { accumulateRect(dst); } void DisplayListBoundsCalculator::drawImageNine(const sk_sp image, diff --git a/flow/display_list_utils.h b/flow/display_list_utils.h index 51b7187705f42..914eb4b7c8bb1 100644 --- a/flow/display_list_utils.h +++ b/flow/display_list_utils.h @@ -279,7 +279,8 @@ class DisplayListBoundsCalculator final void drawImageRect(const sk_sp image, const SkRect& src, const SkRect& dst, - const SkSamplingOptions& sampling) override; + const SkSamplingOptions& sampling, + SkCanvas::SrcRectConstraint constraint) override; void drawImageNine(const sk_sp image, const SkIRect& center, const SkRect& dst, From 3535b78ddc9f4fd75643d0c859070f98fdf28002 Mon Sep 17 00:00:00 2001 From: Jim Graham Date: Fri, 25 Jun 2021 16:00:33 -0700 Subject: [PATCH 14/26] add unit testing of DrawTextBlob --- flow/display_list.cc | 2 ++ flow/display_list_unittests.cc | 25 ++++++++++++++++++++++--- 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/flow/display_list.cc b/flow/display_list.cc index 6f4bbc05ed80e..0925c61bfd466 100644 --- a/flow/display_list.cc +++ b/flow/display_list.cc @@ -749,6 +749,8 @@ 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 { static const auto kType = DisplayListOpType::kDrawTextBlob; diff --git a/flow/display_list_unittests.cc b/flow/display_list_unittests.cc index fb00038749658..719c50471f893 100644 --- a/flow/display_list_unittests.cc +++ b/flow/display_list_unittests.cc @@ -12,6 +12,7 @@ #include "third_party/skia/include/core/SkRRect.h" #include "third_party/skia/include/core/SkRSXform.h" #include "third_party/skia/include/core/SkSurface.h" +#include "third_party/skia/include/core/SkTextBlob.h" #include "third_party/skia/include/core/SkVertices.h" #include "third_party/skia/include/effects/SkGradientShader.h" #include "third_party/skia/include/effects/SkImageFilters.h" @@ -151,6 +152,17 @@ static sk_sp TestDisplayList1 = static sk_sp TestDisplayList2 = MakeTestDisplayList(25, 25, SK_ColorBLUE); +static sk_sp MakeTextBlob(std::string string) { + return SkTextBlob::MakeFromText(string.c_str(), string.size(), SkFont(), + SkTextEncoding::kUTF8); +} +static sk_sp TestBlob1 = MakeTextBlob("TestBlob1"); +static sk_sp TestBlob2 = MakeTextBlob("TestBlob2"); + +// --------------- +// Test Suite data +// --------------- + typedef const std::function DlInvoker; struct DisplayListInvocation { @@ -444,7 +456,7 @@ std::vector allGroups = { SkFilterMode::kNearest);}}, } }, - // TODO(flar): Skipping DrawLattice for now + // TODO(flar): Skipping DrawLattice for now, Flutter does not use it { "DrawAtlas", { {1, 40 + 32 + 32, [](DisplayListBuilder& b) { static SkRSXform xforms[] = { {1, 0, 0, 0}, {0, 1, 0, 0} }; @@ -506,8 +518,15 @@ std::vector allGroups = { {1, 16, [](DisplayListBuilder& b) {b.drawDisplayList(TestDisplayList2);}}, } }, - // TODO(flar): Skipping DrawTextBlob for now - // TODO(flar): Skipping DrawShadowRec for now + { "DrawTextBlob", { + {1, 24, [](DisplayListBuilder& b) {b.drawTextBlob(TestBlob1, 10, 10);}}, + {1, 24, [](DisplayListBuilder& b) {b.drawTextBlob(TestBlob1, 20, 10);}}, + {1, 24, [](DisplayListBuilder& b) {b.drawTextBlob(TestBlob1, 10, 20);}}, + {1, 24, [](DisplayListBuilder& b) {b.drawTextBlob(TestBlob2, 10, 10);}}, + } + }, + // TODO(flar): Skipping DrawShadowRec for now, Flutter does not use it + // and Skia does not yet expose the require definitions { "DrawShadow", { {1, 32, [](DisplayListBuilder& b) {b.drawShadow(TestPath1, SK_ColorGREEN, 1.0, false);}}, {1, 32, [](DisplayListBuilder& b) {b.drawShadow(TestPath2, SK_ColorGREEN, 1.0, false);}}, From b8399f34562a7d00d97d85931363f873999ae91c Mon Sep 17 00:00:00 2001 From: Jim Graham Date: Fri, 25 Jun 2021 23:20:02 -0700 Subject: [PATCH 15/26] more style guide name changes and more testing of dl.Equals --- flow/display_list.cc | 85 +++++- flow/display_list.h | 4 +- flow/display_list_unittests.cc | 461 ++++++++++++++++++------------ flow/layers/display_list_layer.cc | 4 +- flow/raster_cache.cc | 6 +- 5 files changed, 352 insertions(+), 208 deletions(-) diff --git a/flow/display_list.cc b/flow/display_list.cc index 0925c61bfd466..07115d595ff1b 100644 --- a/flow/display_list.cc +++ b/flow/display_list.cc @@ -64,6 +64,7 @@ enum class DisplayListCompare { struct DLOp { DisplayListOpType type : 8; uint32_t size : 24; + DisplayListCompare equals(const DLOp* other) const { return DisplayListCompare::kUseBulkCompare; } @@ -395,12 +396,34 @@ struct Transform3x3Op final : DLOp { }; DEFINE_CLIP_SHAPE_OP(Rect, Intersect) DEFINE_CLIP_SHAPE_OP(RRect, Intersect) -DEFINE_CLIP_SHAPE_OP(Path, Intersect) DEFINE_CLIP_SHAPE_OP(Rect, Difference) DEFINE_CLIP_SHAPE_OP(RRect, Difference) -DEFINE_CLIP_SHAPE_OP(Path, Difference) #undef DEFINE_CLIP_SHAPE_OP +#define DEFINE_CLIP_PATH_OP(clipop) \ + struct Clip##clipop##PathOp final : DLOp { \ + static const auto kType = DisplayListOpType::kClip##clipop##Path; \ + \ + Clip##clipop##PathOp(SkPath path, bool is_aa) \ + : is_aa(is_aa), path(path) {} \ + \ + const bool is_aa; \ + const SkPath path; \ + \ + void dispatch(Dispatcher& dispatcher) const { \ + dispatcher.clipPath(path, is_aa, SkClipOp::k##clipop); \ + } \ + \ + DisplayListCompare equals(const Clip##clipop##PathOp* other) const { \ + return is_aa == other->is_aa && path == other->path \ + ? DisplayListCompare::kEqual \ + : DisplayListCompare::kNotEqual; \ + } \ + }; +DEFINE_CLIP_PATH_OP(Intersect) +DEFINE_CLIP_PATH_OP(Difference) +#undef DEFINE_CLIP_PATH_OP + // 4 byte header + no payload uses minimum 8 bytes (4 bytes unused) struct DrawPaintOp final : DLOp { static const auto kType = DisplayListOpType::kDrawPaint; @@ -427,9 +450,8 @@ struct DrawColorOp final : DLOp { // The common data is a 4 byte header with an unused 4 bytes // SkRect is 16 more bytes, using 20 bytes which rounds up to 24 bytes total // (4 bytes unused) +// SkOval is same as SkRect // SkRRect is 52 more bytes, which packs efficiently into 56 bytes total -// SkPath is 16 more bytes, using 20 bytes which rounds up to 24 bytes total -// (4 bytes unused) #define DEFINE_DRAW_1ARG_OP(op_name, arg_type, arg_name) \ struct Draw##op_name##Op final : DLOp { \ static const auto kType = DisplayListOpType::kDraw##op_name; \ @@ -445,9 +467,25 @@ struct DrawColorOp final : DLOp { DEFINE_DRAW_1ARG_OP(Rect, SkRect, rect) DEFINE_DRAW_1ARG_OP(Oval, SkRect, oval) DEFINE_DRAW_1ARG_OP(RRect, SkRRect, rrect) -DEFINE_DRAW_1ARG_OP(Path, SkPath, path) #undef DEFINE_DRAW_1ARG_OP +// 4 byte header + 16 byte payload uses 20 bytes but is rounded up to 24 bytes +// (4 bytes unused) +struct DrawPathOp final : DLOp { + static const auto kType = DisplayListOpType::kDrawPath; + + DrawPathOp(SkPath path) : path(path) {} + + const SkPath path; + + void dispatch(Dispatcher& dispatcher) const { dispatcher.drawPath(path); } + + DisplayListCompare equals(const DrawPathOp* other) const { + return path == other->path ? DisplayListCompare::kEqual + : DisplayListCompare::kNotEqual; + } +}; + // The common data is a 4 byte header with an unused 4 bytes // 2 x SkPoint is 16 more bytes, using 20 bytes rounding up to 24 bytes total // (4 bytes unused) @@ -1126,29 +1164,46 @@ void DisplayListBuilder::transform3x3(SkScalar mxx, } void DisplayListBuilder::clipRect(const SkRect& rect, - bool isAA, + bool is_aa, SkClipOp clip_op) { clip_op == SkClipOp::kIntersect // - ? Push(0, rect, isAA) - : Push(0, rect, isAA); + ? Push(0, rect, is_aa) + : Push(0, rect, is_aa); } void DisplayListBuilder::clipRRect(const SkRRect& rrect, - bool isAA, + bool is_aa, SkClipOp clip_op) { if (rrect.isRect()) { - clipRect(rrect.rect(), isAA, clip_op); + clipRect(rrect.rect(), is_aa, clip_op); } else { clip_op == SkClipOp::kIntersect // - ? Push(0, rrect, isAA) - : Push(0, rrect, isAA); + ? Push(0, rrect, is_aa) + : Push(0, rrect, is_aa); } } void DisplayListBuilder::clipPath(const SkPath& path, - bool isAA, + bool is_aa, SkClipOp clip_op) { + if (!path.isInverseFillType()) { + SkRect rect; + if (path.isRect(&rect)) { + this->clipRect(rect, is_aa, clip_op); + return; + } + SkRRect rrect; + if (path.isOval(&rect)) { + rrect.setOval(rect); + this->clipRRect(rrect, is_aa, clip_op); + return; + } + if (path.isRRect(&rrect)) { + this->clipRRect(rrect, is_aa, clip_op); + return; + } + } clip_op == SkClipOp::kIntersect // - ? Push(0, path, isAA) - : Push(0, path, isAA); + ? Push(0, path, is_aa) + : Push(0, path, is_aa); } void DisplayListBuilder::drawPaint() { diff --git a/flow/display_list.h b/flow/display_list.h index df97f6bf12752..eddb5ce63b6fa 100644 --- a/flow/display_list.h +++ b/flow/display_list.h @@ -178,8 +178,8 @@ class DisplayList : public SkRefCnt { void RenderTo(SkCanvas* canvas) const; size_t bytes() const { return used_; } - int opCount() const { return op_count_; } - uint32_t uniqueID() const { return unique_id_; } + int op_count() const { return op_count_; } + uint32_t unique_id() const { return unique_id_; } const SkRect& bounds() { if (bounds_.width() < 0.0) { diff --git a/flow/display_list_unittests.cc b/flow/display_list_unittests.cc index 719c50471f893..17d0c6fe5978d 100644 --- a/flow/display_list_unittests.cc +++ b/flow/display_list_unittests.cc @@ -88,10 +88,17 @@ static const sk_sp TestMaskFilter = SkMaskFilter::MakeBlur(kNormal_SkBlurStyle, 5.0); constexpr SkRect TestBounds = SkRect::MakeLTRB(10, 10, 50, 60); static const SkRRect TestRRect = SkRRect::MakeRectXY(TestBounds, 5, 5); +static const SkRRect TestRRectRect = SkRRect::MakeRect(TestBounds); static const SkRRect TestInnerRRect = SkRRect::MakeRectXY(TestBounds.makeInset(5, 5), 2, 2); -static const SkPath TestPath1 = SkPath::Rect(TestBounds); -static const SkPath TestPath2 = SkPath::Oval(TestBounds); +static const SkPath TestPathRect = SkPath::Rect(TestBounds); +static const SkPath TestPathOval = SkPath::Oval(TestBounds); +static const SkPath TestPath1 = + SkPath::Polygon({{0, 0}, {10, 10}, {10, 0}, {0, 10}}, true); +static const SkPath TestPath2 = + SkPath::Polygon({{0, 0}, {10, 10}, {0, 10}, {10, 0}}, true); +static const SkPath TestPath3 = + SkPath::Polygon({{0, 0}, {10, 10}, {10, 0}, {0, 10}}, false); static const SkMatrix TestMatrix1 = SkMatrix::Scale(2, 2); static const SkMatrix TestMatrix2 = SkMatrix::RotateDeg(45); @@ -166,10 +173,18 @@ static sk_sp TestBlob2 = MakeTextBlob("TestBlob2"); typedef const std::function DlInvoker; struct DisplayListInvocation { - int opCount; - size_t byteCount; + int op_count; + size_t byte_count; + + // in some cases, running the sequence through an SkCanvas will result + // in fewer ops/bytes. Attribute invocations are recorded in an SkPaint + // and not forwarded on, and SkCanvas culls unused save/restore/transforms. + int sk_op_count; + size_t sk_byte_count; + DlInvoker invoker; - sk_sp build() { + + sk_sp Build() { DisplayListBuilder builder; invoker(builder); return builder.Build(); @@ -183,319 +198,344 @@ struct DisplayListInvocationGroup { std::vector allGroups = { { "SetAA", { - {1, 8, [](DisplayListBuilder& b) {b.setAA(false);}}, - {1, 8, [](DisplayListBuilder& b) {b.setAA(true);}}, + {1, 8, 0, 0, [](DisplayListBuilder& b) {b.setAA(false);}}, + {1, 8, 0, 0, [](DisplayListBuilder& b) {b.setAA(true);}}, } }, { "SetDither", { - {1, 8, [](DisplayListBuilder& b) {b.setDither(false);}}, - {1, 8, [](DisplayListBuilder& b) {b.setDither(true);}}, + {1, 8, 0, 0, [](DisplayListBuilder& b) {b.setDither(false);}}, + {1, 8, 0, 0, [](DisplayListBuilder& b) {b.setDither(true);}}, } }, { "SetInvertColors", { - {1, 8, [](DisplayListBuilder& b) {b.setInvertColors(false);}}, - {1, 8, [](DisplayListBuilder& b) {b.setInvertColors(true);}}, + {1, 8, 0, 0, [](DisplayListBuilder& b) {b.setInvertColors(false);}}, + {1, 8, 0, 0, [](DisplayListBuilder& b) {b.setInvertColors(true);}}, } }, { "SetStrokeCap", { - {1, 8, [](DisplayListBuilder& b) {b.setCaps(SkPaint::kButt_Cap);}}, - {1, 8, [](DisplayListBuilder& b) {b.setCaps(SkPaint::kRound_Cap);}}, - {1, 8, [](DisplayListBuilder& b) {b.setCaps(SkPaint::kSquare_Cap);}}, + {1, 8, 0, 0, [](DisplayListBuilder& b) {b.setCaps(SkPaint::kButt_Cap);}}, + {1, 8, 0, 0, [](DisplayListBuilder& b) {b.setCaps(SkPaint::kRound_Cap);}}, + {1, 8, 0, 0, [](DisplayListBuilder& b) {b.setCaps(SkPaint::kSquare_Cap);}}, } }, { "SetStrokeJoin", { - {1, 8, [](DisplayListBuilder& b) {b.setJoins(SkPaint::kBevel_Join);}}, - {1, 8, [](DisplayListBuilder& b) {b.setJoins(SkPaint::kRound_Join);}}, - {1, 8, [](DisplayListBuilder& b) {b.setJoins(SkPaint::kMiter_Join);}}, + {1, 8, 0, 0, [](DisplayListBuilder& b) {b.setJoins(SkPaint::kBevel_Join);}}, + {1, 8, 0, 0, [](DisplayListBuilder& b) {b.setJoins(SkPaint::kRound_Join);}}, + {1, 8, 0, 0, [](DisplayListBuilder& b) {b.setJoins(SkPaint::kMiter_Join);}}, } }, { "SetDrawStyle", { - {1, 8, [](DisplayListBuilder& b) {b.setDrawStyle(SkPaint::kFill_Style);}}, - {1, 8, [](DisplayListBuilder& b) {b.setDrawStyle(SkPaint::kStroke_Style);}}, + {1, 8, 0, 0, [](DisplayListBuilder& b) {b.setDrawStyle(SkPaint::kFill_Style);}}, + {1, 8, 0, 0, [](DisplayListBuilder& b) {b.setDrawStyle(SkPaint::kStroke_Style);}}, } }, { "SetStrokeWidth", { - {1, 8, [](DisplayListBuilder& b) {b.setStrokeWidth(0.0);}}, - {1, 8, [](DisplayListBuilder& b) {b.setStrokeWidth(5.0);}}, + {1, 8, 0, 0, [](DisplayListBuilder& b) {b.setStrokeWidth(0.0);}}, + {1, 8, 0, 0, [](DisplayListBuilder& b) {b.setStrokeWidth(5.0);}}, } }, { "SetMiterLimit", { - {1, 8, [](DisplayListBuilder& b) {b.setMiterLimit(0.0);}}, - {1, 8, [](DisplayListBuilder& b) {b.setMiterLimit(5.0);}}, + {1, 8, 0, 0, [](DisplayListBuilder& b) {b.setMiterLimit(0.0);}}, + {1, 8, 0, 0, [](DisplayListBuilder& b) {b.setMiterLimit(5.0);}}, } }, { "SetColor", { - {1, 8, [](DisplayListBuilder& b) {b.setColor(SK_ColorGREEN);}}, - {1, 8, [](DisplayListBuilder& b) {b.setColor(SK_ColorBLUE);}}, + {1, 8, 0, 0, [](DisplayListBuilder& b) {b.setColor(SK_ColorGREEN);}}, + {1, 8, 0, 0, [](DisplayListBuilder& b) {b.setColor(SK_ColorBLUE);}}, } }, { "SetBlendMode", { - {1, 8, [](DisplayListBuilder& b) {b.setBlendMode(SkBlendMode::kSrcIn);}}, - {1, 8, [](DisplayListBuilder& b) {b.setBlendMode(SkBlendMode::kDstIn);}}, + {1, 8, 0, 0, [](DisplayListBuilder& b) {b.setBlendMode(SkBlendMode::kSrcIn);}}, + {1, 8, 0, 0, [](DisplayListBuilder& b) {b.setBlendMode(SkBlendMode::kDstIn);}}, } }, { "SetFilterQuality", { - {1, 8, [](DisplayListBuilder& b) {b.setFilterQuality(kNone_SkFilterQuality);}}, - {1, 8, [](DisplayListBuilder& b) {b.setFilterQuality(kLow_SkFilterQuality);}}, - {1, 8, [](DisplayListBuilder& b) {b.setFilterQuality(kMedium_SkFilterQuality);}}, - {1, 8, [](DisplayListBuilder& b) {b.setFilterQuality(kHigh_SkFilterQuality);}}, + {1, 8, 0, 0, [](DisplayListBuilder& b) {b.setFilterQuality(kNone_SkFilterQuality);}}, + {1, 8, 0, 0, [](DisplayListBuilder& b) {b.setFilterQuality(kLow_SkFilterQuality);}}, + {1, 8, 0, 0, [](DisplayListBuilder& b) {b.setFilterQuality(kMedium_SkFilterQuality);}}, + {1, 8, 0, 0, [](DisplayListBuilder& b) {b.setFilterQuality(kHigh_SkFilterQuality);}}, } }, { "SetShader", { - {1, 8, [](DisplayListBuilder& b) {b.setShader(nullptr);}}, - {1, 16, [](DisplayListBuilder& b) {b.setShader(TestShader1);}}, - {1, 16, [](DisplayListBuilder& b) {b.setShader(TestShader2);}}, - {1, 16, [](DisplayListBuilder& b) {b.setShader(TestShader3);}}, + {1, 8, 0, 0, [](DisplayListBuilder& b) {b.setShader(nullptr);}}, + {1, 16, 0, 0, [](DisplayListBuilder& b) {b.setShader(TestShader1);}}, + {1, 16, 0, 0, [](DisplayListBuilder& b) {b.setShader(TestShader2);}}, + {1, 16, 0, 0, [](DisplayListBuilder& b) {b.setShader(TestShader3);}}, } }, { "SetImageFilter", { - {1, 8, [](DisplayListBuilder& b) {b.setImageFilter(nullptr);}}, - {1, 16, [](DisplayListBuilder& b) {b.setImageFilter(TestImageFilter);}}, + {1, 8, 0, 0, [](DisplayListBuilder& b) {b.setImageFilter(nullptr);}}, + {1, 16, 0, 0, [](DisplayListBuilder& b) {b.setImageFilter(TestImageFilter);}}, } }, { "SetColorFilter", { - {1, 8, [](DisplayListBuilder& b) {b.setColorFilter(nullptr);}}, - {1, 16, [](DisplayListBuilder& b) {b.setColorFilter(TestColorFilter);}}, + {1, 8, 0, 0, [](DisplayListBuilder& b) {b.setColorFilter(nullptr);}}, + {1, 16, 0, 0, [](DisplayListBuilder& b) {b.setColorFilter(TestColorFilter);}}, } }, { "SetMaskFilter", { - {1, 16, [](DisplayListBuilder& b) {b.setMaskFilter(nullptr);}}, - {1, 16, [](DisplayListBuilder& b) {b.setMaskFilter(TestMaskFilter);}}, - {1, 8, [](DisplayListBuilder& b) {b.setMaskBlurFilter(kNormal_SkBlurStyle, 3.0);}}, - {1, 8, [](DisplayListBuilder& b) {b.setMaskBlurFilter(kNormal_SkBlurStyle, 5.0);}}, - {1, 8, [](DisplayListBuilder& b) {b.setMaskBlurFilter(kSolid_SkBlurStyle, 3.0);}}, - {1, 8, [](DisplayListBuilder& b) {b.setMaskBlurFilter(kInner_SkBlurStyle, 3.0);}}, - {1, 8, [](DisplayListBuilder& b) {b.setMaskBlurFilter(kOuter_SkBlurStyle, 3.0);}}, + {1, 16, 0, 0, [](DisplayListBuilder& b) {b.setMaskFilter(nullptr);}}, + {1, 16, 0, 0, [](DisplayListBuilder& b) {b.setMaskFilter(TestMaskFilter);}}, + {1, 8, 0, 0, [](DisplayListBuilder& b) {b.setMaskBlurFilter(kNormal_SkBlurStyle, 3.0);}}, + {1, 8, 0, 0, [](DisplayListBuilder& b) {b.setMaskBlurFilter(kNormal_SkBlurStyle, 5.0);}}, + {1, 8, 0, 0, [](DisplayListBuilder& b) {b.setMaskBlurFilter(kSolid_SkBlurStyle, 3.0);}}, + {1, 8, 0, 0, [](DisplayListBuilder& b) {b.setMaskBlurFilter(kInner_SkBlurStyle, 3.0);}}, + {1, 8, 0, 0, [](DisplayListBuilder& b) {b.setMaskBlurFilter(kOuter_SkBlurStyle, 3.0);}}, } }, { "Save(Layer)+Restore", { - {2, 16, [](DisplayListBuilder& b) {b.save(); b.restore();}}, - {2, 16, [](DisplayListBuilder& b) {b.saveLayer(nullptr, false); b.restore(); }}, - {2, 16, [](DisplayListBuilder& b) {b.saveLayer(nullptr, true); b.restore(); }}, - {2, 32, [](DisplayListBuilder& b) {b.saveLayer(&TestBounds, false); b.restore(); }}, - {2, 32, [](DisplayListBuilder& b) {b.saveLayer(&TestBounds, true); b.restore(); }}, + // cv.save/restore are ignored if there are no draw calls between them + {2, 16, 0, 0, [](DisplayListBuilder& b) {b.save(); b.restore();}}, + {2, 16, 2, 16, [](DisplayListBuilder& b) {b.saveLayer(nullptr, false); b.restore(); }}, + {2, 16, 2, 16, [](DisplayListBuilder& b) {b.saveLayer(nullptr, true); b.restore(); }}, + {2, 32, 2, 32, [](DisplayListBuilder& b) {b.saveLayer(&TestBounds, false); b.restore(); }}, + {2, 32, 2, 32, [](DisplayListBuilder& b) {b.saveLayer(&TestBounds, true); b.restore(); }}, } }, { "Translate", { - {1, 16, [](DisplayListBuilder& b) {b.translate(0, 0);}}, - {1, 16, [](DisplayListBuilder& b) {b.translate(5, 15);}}, + // cv.translate(0, 0) is ignored + {1, 16, 0, 0, [](DisplayListBuilder& b) {b.translate(0, 0);}}, + {1, 16, 1, 16, [](DisplayListBuilder& b) {b.translate(10, 10);}}, + {1, 16, 1, 16, [](DisplayListBuilder& b) {b.translate(10, 15);}}, + {1, 16, 1, 16, [](DisplayListBuilder& b) {b.translate(15, 10);}}, } }, { "Scale", { - {1, 16, [](DisplayListBuilder& b) {b.scale(1, 1);}}, - {1, 16, [](DisplayListBuilder& b) {b.scale(2, 3);}}, + // cv.scale(1, 1) is ignored + {1, 16, 0, 0, [](DisplayListBuilder& b) {b.scale(1, 1);}}, + {1, 16, 1, 16, [](DisplayListBuilder& b) {b.scale(2, 2);}}, + {1, 16, 1, 16, [](DisplayListBuilder& b) {b.scale(2, 3);}}, + {1, 16, 1, 16, [](DisplayListBuilder& b) {b.scale(3, 2);}}, } }, { "Rotate", { - {1, 8, [](DisplayListBuilder& b) {b.rotate(0);}}, - {1, 8, [](DisplayListBuilder& b) {b.rotate(45);}}, + // cv.rotate(0) is ignored, otherwise expressed as concat(rotmatrix) + {1, 8, 0, 0, [](DisplayListBuilder& b) {b.rotate(0);}}, + {1, 8, 1, 32, [](DisplayListBuilder& b) {b.rotate(30);}}, + {1, 8, 1, 32, [](DisplayListBuilder& b) {b.rotate(45);}}, } }, { "Skew", { - {1, 16, [](DisplayListBuilder& b) {b.skew(0, 0);}}, - {1, 16, [](DisplayListBuilder& b) {b.skew(0.1, 0.2);}}, + // cv.skew(0, 0) is ignored, otherwise expressed as concat(skewmatrix) + {1, 16, 0, 0, [](DisplayListBuilder& b) {b.skew(0, 0);}}, + {1, 16, 1, 32, [](DisplayListBuilder& b) {b.skew(0.1, 0.1);}}, + {1, 16, 1, 32, [](DisplayListBuilder& b) {b.skew(0.1, 0.2);}}, + {1, 16, 1, 32, [](DisplayListBuilder& b) {b.skew(0.2, 0.1);}}, } }, { "Transform2x3", { - {1, 32, [](DisplayListBuilder& b) {b.transform2x3(1, 0, 0, 0, 1, 0);}}, - {1, 32, [](DisplayListBuilder& b) {b.transform2x3(0, 1, 12, 1, 0, 33);}}, + // cv.transform(identity) is ignored + {1, 32, 0, 0, [](DisplayListBuilder& b) {b.transform2x3(1, 0, 0, 0, 1, 0);}}, + {1, 32, 1, 32, [](DisplayListBuilder& b) {b.transform2x3(0, 1, 12, 1, 0, 33);}}, } }, { "Transform3x3", { - {1, 40, [](DisplayListBuilder& b) {b.transform3x3(1, 0, 0, 0, 1, 0, 0, 0, 1);}}, - {1, 40, [](DisplayListBuilder& b) {b.transform3x3(0, 1, 12, 1, 0, 33, 0, 0, 12);}}, + // cv.transform(identity) is ignored + {1, 40, 0, 0, [](DisplayListBuilder& b) {b.transform3x3(1, 0, 0, 0, 1, 0, 0, 0, 1);}}, + {1, 40, 1, 40, [](DisplayListBuilder& b) {b.transform3x3(0, 1, 12, 1, 0, 33, 0, 0, 12);}}, } }, { "ClipRect", { - {1, 24, [](DisplayListBuilder& b) {b.clipRect(TestBounds, true, SkClipOp::kIntersect);}}, - {1, 24, [](DisplayListBuilder& b) {b.clipRect(TestBounds.makeOffset(1, 1), - true, SkClipOp::kIntersect);}}, - {1, 24, [](DisplayListBuilder& b) {b.clipRect(TestBounds, false, SkClipOp::kIntersect);}}, - {1, 24, [](DisplayListBuilder& b) {b.clipRect(TestBounds, true, SkClipOp::kDifference);}}, - {1, 24, [](DisplayListBuilder& b) {b.clipRect(TestBounds, false, SkClipOp::kDifference);}}, + {1, 24, 1, 24, [](DisplayListBuilder& b) {b.clipRect(TestBounds, true, SkClipOp::kIntersect);}}, + {1, 24, 1, 24, [](DisplayListBuilder& b) {b.clipRect(TestBounds.makeOffset(1, 1), + true, SkClipOp::kIntersect);}}, + {1, 24, 1, 24, [](DisplayListBuilder& b) {b.clipRect(TestBounds, false, SkClipOp::kIntersect);}}, + {1, 24, 1, 24, [](DisplayListBuilder& b) {b.clipRect(TestBounds, true, SkClipOp::kDifference);}}, + {1, 24, 1, 24, [](DisplayListBuilder& b) {b.clipRect(TestBounds, false, SkClipOp::kDifference);}}, } }, { "ClipRRect", { - {1, 64, [](DisplayListBuilder& b) {b.clipRRect(TestRRect, true, SkClipOp::kIntersect);}}, - {1, 64, [](DisplayListBuilder& b) {b.clipRRect(TestRRect.makeOffset(1, 1), - true, SkClipOp::kIntersect);}}, - {1, 64, [](DisplayListBuilder& b) {b.clipRRect(TestRRect, false, SkClipOp::kIntersect);}}, - {1, 64, [](DisplayListBuilder& b) {b.clipRRect(TestRRect, true, SkClipOp::kDifference);}}, - {1, 64, [](DisplayListBuilder& b) {b.clipRRect(TestRRect, false, SkClipOp::kDifference);}}, + {1, 64, 1, 64, [](DisplayListBuilder& b) {b.clipRRect(TestRRect, true, SkClipOp::kIntersect);}}, + {1, 64, 1, 64, [](DisplayListBuilder& b) {b.clipRRect(TestRRect.makeOffset(1, 1), + true, SkClipOp::kIntersect);}}, + {1, 64, 1, 64, [](DisplayListBuilder& b) {b.clipRRect(TestRRect, false, SkClipOp::kIntersect);}}, + {1, 64, 1, 64, [](DisplayListBuilder& b) {b.clipRRect(TestRRect, true, SkClipOp::kDifference);}}, + {1, 64, 1, 64, [](DisplayListBuilder& b) {b.clipRRect(TestRRect, false, SkClipOp::kDifference);}}, } }, { "ClipPath", { - {1, 24, [](DisplayListBuilder& b) {b.clipPath(TestPath1, true, SkClipOp::kIntersect);}}, - {1, 24, [](DisplayListBuilder& b) {b.clipPath(TestPath2, true, SkClipOp::kIntersect);}}, - {1, 24, [](DisplayListBuilder& b) {b.clipPath(TestPath1, false, SkClipOp::kIntersect);}}, - {1, 24, [](DisplayListBuilder& b) {b.clipPath(TestPath1, true, SkClipOp::kDifference);}}, - {1, 24, [](DisplayListBuilder& b) {b.clipPath(TestPath1, false, SkClipOp::kDifference);}}, + {1, 24, 1, 24, [](DisplayListBuilder& b) {b.clipPath(TestPath1, true, SkClipOp::kIntersect);}}, + {1, 24, 1, 24, [](DisplayListBuilder& b) {b.clipPath(TestPath2, true, SkClipOp::kIntersect);}}, + {1, 24, 1, 24, [](DisplayListBuilder& b) {b.clipPath(TestPath3, true, SkClipOp::kIntersect);}}, + {1, 24, 1, 24, [](DisplayListBuilder& b) {b.clipPath(TestPath1, false, SkClipOp::kIntersect);}}, + {1, 24, 1, 24, [](DisplayListBuilder& b) {b.clipPath(TestPath1, true, SkClipOp::kDifference);}}, + {1, 24, 1, 24, [](DisplayListBuilder& b) {b.clipPath(TestPath1, false, SkClipOp::kDifference);}}, + // clipPath(rect) becomes clipRect + {1, 24, 1, 24, [](DisplayListBuilder& b) {b.clipPath(TestPathRect, true, SkClipOp::kIntersect);}}, + // clipPath(oval) becomes clipRRect + {1, 64, 1, 64, [](DisplayListBuilder& b) {b.clipPath(TestPathOval, true, SkClipOp::kIntersect);}}, } }, { "DrawPaint", { - {1, 8, [](DisplayListBuilder& b) {b.drawPaint();}}, + {1, 8, 1, 8, [](DisplayListBuilder& b) {b.drawPaint();}}, } }, { "DrawColor", { - {1, 16, [](DisplayListBuilder& b) {b.drawColor(SK_ColorBLUE, SkBlendMode::kSrcIn);}}, - {1, 16, [](DisplayListBuilder& b) {b.drawColor(SK_ColorBLUE, SkBlendMode::kDstIn);}}, - {1, 16, [](DisplayListBuilder& b) {b.drawColor(SK_ColorCYAN, SkBlendMode::kSrcIn);}}, + // cv.drawColor becomes cv.drawPaint(paint) + {1, 16, 3, 24, [](DisplayListBuilder& b) {b.drawColor(SK_ColorBLUE, SkBlendMode::kSrcIn);}}, + {1, 16, 3, 24, [](DisplayListBuilder& b) {b.drawColor(SK_ColorBLUE, SkBlendMode::kDstIn);}}, + {1, 16, 3, 24, [](DisplayListBuilder& b) {b.drawColor(SK_ColorCYAN, SkBlendMode::kSrcIn);}}, } }, { "DrawLine", { - {1, 24, [](DisplayListBuilder& b) {b.drawLine({0, 0}, {10, 10});}}, - {1, 24, [](DisplayListBuilder& b) {b.drawLine({0, 1}, {10, 10});}}, - {1, 24, [](DisplayListBuilder& b) {b.drawLine({0, 0}, {20, 10});}}, - {1, 24, [](DisplayListBuilder& b) {b.drawLine({0, 0}, {10, 20});}}, + {1, 24, 1, 24, [](DisplayListBuilder& b) {b.drawLine({0, 0}, {10, 10});}}, + {1, 24, 1, 24, [](DisplayListBuilder& b) {b.drawLine({0, 1}, {10, 10});}}, + {1, 24, 1, 24, [](DisplayListBuilder& b) {b.drawLine({0, 0}, {20, 10});}}, + {1, 24, 1, 24, [](DisplayListBuilder& b) {b.drawLine({0, 0}, {10, 20});}}, } }, { "DrawRect", { - {1, 24, [](DisplayListBuilder& b) {b.drawRect({0, 0, 10, 10});}}, - {1, 24, [](DisplayListBuilder& b) {b.drawRect({0, 1, 10, 10});}}, - {1, 24, [](DisplayListBuilder& b) {b.drawRect({0, 0, 20, 10});}}, - {1, 24, [](DisplayListBuilder& b) {b.drawRect({0, 0, 10, 20});}}, + {1, 24, 1, 24, [](DisplayListBuilder& b) {b.drawRect({0, 0, 10, 10});}}, + {1, 24, 1, 24, [](DisplayListBuilder& b) {b.drawRect({0, 1, 10, 10});}}, + {1, 24, 1, 24, [](DisplayListBuilder& b) {b.drawRect({0, 0, 20, 10});}}, + {1, 24, 1, 24, [](DisplayListBuilder& b) {b.drawRect({0, 0, 10, 20});}}, } }, { "DrawOval", { - {1, 24, [](DisplayListBuilder& b) {b.drawOval({0, 0, 10, 10});}}, - {1, 24, [](DisplayListBuilder& b) {b.drawOval({0, 1, 10, 10});}}, - {1, 24, [](DisplayListBuilder& b) {b.drawOval({0, 0, 20, 10});}}, - {1, 24, [](DisplayListBuilder& b) {b.drawOval({0, 0, 10, 20});}}, + {1, 24, 1, 24, [](DisplayListBuilder& b) {b.drawOval({0, 0, 10, 10});}}, + {1, 24, 1, 24, [](DisplayListBuilder& b) {b.drawOval({0, 1, 10, 10});}}, + {1, 24, 1, 24, [](DisplayListBuilder& b) {b.drawOval({0, 0, 20, 10});}}, + {1, 24, 1, 24, [](DisplayListBuilder& b) {b.drawOval({0, 0, 10, 20});}}, } }, { "DrawCircle", { - {1, 16, [](DisplayListBuilder& b) {b.drawCircle({0, 0}, 10);}}, - {1, 16, [](DisplayListBuilder& b) {b.drawCircle({0, 5}, 10);}}, - {1, 16, [](DisplayListBuilder& b) {b.drawCircle({0, 0}, 20);}}, + // cv.drawCircle becomes cv.drawOval + {1, 16, 1, 24, [](DisplayListBuilder& b) {b.drawCircle({0, 0}, 10);}}, + {1, 16, 1, 24, [](DisplayListBuilder& b) {b.drawCircle({0, 5}, 10);}}, + {1, 16, 1, 24, [](DisplayListBuilder& b) {b.drawCircle({0, 0}, 20);}}, } }, { "DrawRRect", { - {1, 56, [](DisplayListBuilder& b) {b.drawRRect(TestRRect);}}, - {1, 56, [](DisplayListBuilder& b) {b.drawRRect(TestRRect.makeOffset(5, 5));}}, + {1, 56, 1, 56, [](DisplayListBuilder& b) {b.drawRRect(TestRRect);}}, + {1, 56, 1, 56, [](DisplayListBuilder& b) {b.drawRRect(TestRRect.makeOffset(5, 5));}}, } }, { "DrawDRRect", { - {1, 112, [](DisplayListBuilder& b) {b.drawDRRect(TestRRect, TestInnerRRect);}}, - {1, 112, [](DisplayListBuilder& b) {b.drawDRRect(TestRRect.makeOffset(5, 5), - TestInnerRRect.makeOffset(4, 4));}}, + {1, 112, 1, 112, [](DisplayListBuilder& b) {b.drawDRRect(TestRRect, TestInnerRRect);}}, + {1, 112, 1, 112, [](DisplayListBuilder& b) {b.drawDRRect(TestRRect.makeOffset(5, 5), + TestInnerRRect.makeOffset(4, 4));}}, } }, { "DrawPath", { - {1, 24, [](DisplayListBuilder& b) {b.drawPath(TestPath1);}}, - {1, 24, [](DisplayListBuilder& b) {b.drawPath(TestPath2);}}, + {1, 24, 1, 24, [](DisplayListBuilder& b) {b.drawPath(TestPath1);}}, + {1, 24, 1, 24, [](DisplayListBuilder& b) {b.drawPath(TestPath2);}}, + {1, 24, 1, 24, [](DisplayListBuilder& b) {b.drawPath(TestPath3);}}, + {1, 24, 1, 24, [](DisplayListBuilder& b) {b.drawPath(TestPathRect);}}, + {1, 24, 1, 24, [](DisplayListBuilder& b) {b.drawPath(TestPathOval);}}, } }, { "DrawArc", { - {1, 32, [](DisplayListBuilder& b) {b.drawArc(TestBounds, 45, 270, false);}}, - {1, 32, [](DisplayListBuilder& b) {b.drawArc(TestBounds.makeOffset(1, 1), - 45, 270, false);}}, - {1, 32, [](DisplayListBuilder& b) {b.drawArc(TestBounds, 30, 270, false);}}, - {1, 32, [](DisplayListBuilder& b) {b.drawArc(TestBounds, 45, 260, false);}}, - {1, 32, [](DisplayListBuilder& b) {b.drawArc(TestBounds, 45, 270, true);}}, + {1, 32, 1, 32, [](DisplayListBuilder& b) {b.drawArc(TestBounds, 45, 270, false);}}, + {1, 32, 1, 32, [](DisplayListBuilder& b) {b.drawArc(TestBounds.makeOffset(1, 1), + 45, 270, false);}}, + {1, 32, 1, 32, [](DisplayListBuilder& b) {b.drawArc(TestBounds, 30, 270, false);}}, + {1, 32, 1, 32, [](DisplayListBuilder& b) {b.drawArc(TestBounds, 45, 260, false);}}, + {1, 32, 1, 32, [](DisplayListBuilder& b) {b.drawArc(TestBounds, 45, 270, true);}}, } }, { "DrawPoints", { - {1, 8 + TestPointCount * 8, + {1, 8 + TestPointCount * 8, 1, 8 + TestPointCount * 8, [](DisplayListBuilder& b) {b.drawPoints(SkCanvas::kPoints_PointMode, TestPointCount, TestPoints);}}, - {1, 8 + (TestPointCount - 1) * 8, + {1, 8 + (TestPointCount - 1) * 8, 1, 8 + (TestPointCount - 1) * 8, [](DisplayListBuilder& b) {b.drawPoints(SkCanvas::kPoints_PointMode, TestPointCount - 1, TestPoints);}}, - {1, 8 + TestPointCount * 8, + {1, 8 + TestPointCount * 8, 1, 8 + TestPointCount * 8, [](DisplayListBuilder& b) {b.drawPoints(SkCanvas::kLines_PointMode, TestPointCount, TestPoints);}}, - {1, 8 + TestPointCount * 8, + {1, 8 + TestPointCount * 8, 1, 8 + TestPointCount * 8, [](DisplayListBuilder& b) {b.drawPoints(SkCanvas::kPolygon_PointMode, TestPointCount, TestPoints);}}, } }, { "DrawVertices", { - {1, 16, [](DisplayListBuilder& b) {b.drawVertices(TestVertices1, SkBlendMode::kSrcIn);}}, - {1, 16, [](DisplayListBuilder& b) {b.drawVertices(TestVertices1, SkBlendMode::kDstIn);}}, - {1, 16, [](DisplayListBuilder& b) {b.drawVertices(TestVertices2, SkBlendMode::kSrcIn);}}, + {1, 16, 1, 16, [](DisplayListBuilder& b) {b.drawVertices(TestVertices1, SkBlendMode::kSrcIn);}}, + {1, 16, 1, 16, [](DisplayListBuilder& b) {b.drawVertices(TestVertices1, SkBlendMode::kDstIn);}}, + {1, 16, 1, 16, [](DisplayListBuilder& b) {b.drawVertices(TestVertices2, SkBlendMode::kSrcIn);}}, } }, { "DrawImage", { - {1, 40, [](DisplayListBuilder& b) {b.drawImage(TestImage1, {10, 10}, DisplayList::NearestSampling);}}, - {1, 40, [](DisplayListBuilder& b) {b.drawImage(TestImage1, {20, 10}, DisplayList::NearestSampling);}}, - {1, 40, [](DisplayListBuilder& b) {b.drawImage(TestImage1, {10, 20}, DisplayList::NearestSampling);}}, - {1, 40, [](DisplayListBuilder& b) {b.drawImage(TestImage1, {10, 10}, DisplayList::LinearSampling);}}, - {1, 40, [](DisplayListBuilder& b) {b.drawImage(TestImage2, {10, 10}, DisplayList::NearestSampling);}}, + {1, 40, 1, 40, [](DisplayListBuilder& b) {b.drawImage(TestImage1, {10, 10}, DisplayList::NearestSampling);}}, + {1, 40, 1, 40, [](DisplayListBuilder& b) {b.drawImage(TestImage1, {20, 10}, DisplayList::NearestSampling);}}, + {1, 40, 1, 40, [](DisplayListBuilder& b) {b.drawImage(TestImage1, {10, 20}, DisplayList::NearestSampling);}}, + {1, 40, 1, 40, [](DisplayListBuilder& b) {b.drawImage(TestImage1, {10, 10}, DisplayList::LinearSampling);}}, + {1, 40, 1, 40, [](DisplayListBuilder& b) {b.drawImage(TestImage2, {10, 10}, DisplayList::NearestSampling);}}, } }, { "DrawImageRect", { - {1, 64, [](DisplayListBuilder& b) {b.drawImageRect(TestImage1, {10, 10, 20, 20}, {10, 10, 40, 40}, - DisplayList::NearestSampling);}}, - {1, 64, [](DisplayListBuilder& b) {b.drawImageRect(TestImage1, {10, 10, 20, 20}, {10, 10, 40, 40}, - DisplayList::NearestSampling, - SkCanvas::SrcRectConstraint::kStrict_SrcRectConstraint);}}, - {1, 64, [](DisplayListBuilder& b) {b.drawImageRect(TestImage1, {10, 10, 25, 20}, {10, 10, 40, 40}, - DisplayList::NearestSampling);}}, - {1, 64, [](DisplayListBuilder& b) {b.drawImageRect(TestImage1, {10, 10, 20, 20}, {10, 10, 45, 40}, - DisplayList::NearestSampling);}}, - {1, 64, [](DisplayListBuilder& b) {b.drawImageRect(TestImage1, {10, 10, 20, 20}, {10, 10, 40, 40}, - DisplayList::LinearSampling);}}, - {1, 64, [](DisplayListBuilder& b) {b.drawImageRect(TestImage2, {10, 10, 20, 20}, {10, 10, 40, 40}, - DisplayList::NearestSampling);}}, + {1, 64, 1, 64, [](DisplayListBuilder& b) {b.drawImageRect(TestImage1, {10, 10, 20, 20}, {10, 10, 80, 80}, + DisplayList::NearestSampling);}}, + {1, 64, 1, 64, [](DisplayListBuilder& b) {b.drawImageRect(TestImage1, {10, 10, 20, 20}, {10, 10, 80, 80}, + DisplayList::NearestSampling, + SkCanvas::SrcRectConstraint::kStrict_SrcRectConstraint);}}, + {1, 64, 1, 64, [](DisplayListBuilder& b) {b.drawImageRect(TestImage1, {10, 10, 25, 20}, {10, 10, 80, 80}, + DisplayList::NearestSampling);}}, + {1, 64, 1, 64, [](DisplayListBuilder& b) {b.drawImageRect(TestImage1, {10, 10, 20, 20}, {10, 10, 85, 80}, + DisplayList::NearestSampling);}}, + {1, 64, 1, 64, [](DisplayListBuilder& b) {b.drawImageRect(TestImage1, {10, 10, 20, 20}, {10, 10, 80, 80}, + DisplayList::LinearSampling);}}, + {1, 64, 1, 64, [](DisplayListBuilder& b) {b.drawImageRect(TestImage2, {10, 10, 15, 15}, {10, 10, 80, 80}, + DisplayList::NearestSampling);}}, } }, { "DrawImageNine", { - {1, 48, [](DisplayListBuilder& b) {b.drawImageNine(TestImage1, {10, 10, 20, 20}, {10, 10, 40, 40}, - SkFilterMode::kNearest);}}, - {1, 48, [](DisplayListBuilder& b) {b.drawImageNine(TestImage1, {10, 10, 25, 20}, {10, 10, 40, 40}, - SkFilterMode::kNearest);}}, - {1, 48, [](DisplayListBuilder& b) {b.drawImageNine(TestImage1, {10, 10, 20, 20}, {10, 10, 45, 40}, - SkFilterMode::kNearest);}}, - {1, 48, [](DisplayListBuilder& b) {b.drawImageNine(TestImage1, {10, 10, 20, 20}, {10, 10, 40, 40}, - SkFilterMode::kLinear);}}, - {1, 48, [](DisplayListBuilder& b) {b.drawImageNine(TestImage2, {10, 10, 20, 20}, {10, 10, 40, 40}, - SkFilterMode::kNearest);}}, + // cv.drawImageNine => drawImageLattice + {1, 48, 1, 80, [](DisplayListBuilder& b) {b.drawImageNine(TestImage1, {10, 10, 20, 20}, {10, 10, 80, 80}, + SkFilterMode::kNearest);}}, + {1, 48, 1, 80, [](DisplayListBuilder& b) {b.drawImageNine(TestImage1, {10, 10, 25, 20}, {10, 10, 80, 80}, + SkFilterMode::kNearest);}}, + {1, 48, 1, 80, [](DisplayListBuilder& b) {b.drawImageNine(TestImage1, {10, 10, 20, 20}, {10, 10, 85, 80}, + SkFilterMode::kNearest);}}, + {1, 48, 1, 80, [](DisplayListBuilder& b) {b.drawImageNine(TestImage1, {10, 10, 20, 20}, {10, 10, 80, 80}, + SkFilterMode::kLinear);}}, + {1, 48, 1, 80, [](DisplayListBuilder& b) {b.drawImageNine(TestImage2, {10, 10, 15, 15}, {10, 10, 80, 80}, + SkFilterMode::kNearest);}}, } }, // TODO(flar): Skipping DrawLattice for now, Flutter does not use it { "DrawAtlas", { - {1, 40 + 32 + 32, [](DisplayListBuilder& b) { + {1, 40 + 32 + 32, 1, 40 + 32 + 32, [](DisplayListBuilder& b) { static SkRSXform xforms[] = { {1, 0, 0, 0}, {0, 1, 0, 0} }; static SkRect texs[] = { { 10, 10, 20, 20 }, {20, 20, 30, 30} }; b.drawAtlas(TestImage1, xforms, texs, nullptr, 2, SkBlendMode::kSrcIn, DisplayList::NearestSampling, nullptr);}}, - {1, 40 + 32 + 32, [](DisplayListBuilder& b) { + {1, 40 + 32 + 32, 1, 40 + 32 + 32, [](DisplayListBuilder& b) { static SkRSXform xforms[] = { {0, 1, 0, 0}, {0, 1, 0, 0} }; static SkRect texs[] = { { 10, 10, 20, 20 }, {20, 20, 30, 30} }; b.drawAtlas(TestImage1, xforms, texs, nullptr, 2, SkBlendMode::kSrcIn, DisplayList::NearestSampling, nullptr);}}, - {1, 40 + 32 + 32, [](DisplayListBuilder& b) { + {1, 40 + 32 + 32, 1, 40 + 32 + 32, [](DisplayListBuilder& b) { static SkRSXform xforms[] = { {1, 0, 0, 0}, {0, 1, 0, 0} }; static SkRect texs[] = { { 10, 10, 20, 20 }, {20, 25, 30, 30} }; b.drawAtlas(TestImage1, xforms, texs, nullptr, 2, SkBlendMode::kSrcIn, DisplayList::NearestSampling, nullptr);}}, - {1, 40 + 32 + 32, [](DisplayListBuilder& b) { + {1, 40 + 32 + 32, 1, 40 + 32 + 32, [](DisplayListBuilder& b) { static SkRSXform xforms[] = { {1, 0, 0, 0}, {0, 1, 0, 0} }; static SkRect texs[] = { { 10, 10, 20, 20 }, {20, 20, 30, 30} }; b.drawAtlas(TestImage1, xforms, texs, nullptr, 2, SkBlendMode::kSrcIn, DisplayList::LinearSampling, nullptr);}}, - {1, 40 + 32 + 32, [](DisplayListBuilder& b) { + {1, 40 + 32 + 32, 1, 40 + 32 + 32, [](DisplayListBuilder& b) { static SkRSXform xforms[] = { {1, 0, 0, 0}, {0, 1, 0, 0} }; static SkRect texs[] = { { 10, 10, 20, 20 }, {20, 20, 30, 30} }; b.drawAtlas(TestImage1, xforms, texs, nullptr, 2, SkBlendMode::kDstIn, DisplayList::NearestSampling, nullptr);}}, - {1, 56 + 32 + 32, [](DisplayListBuilder& b) { + {1, 56 + 32 + 32, 1, 56 + 32 + 32, [](DisplayListBuilder& b) { static SkRSXform xforms[] = { {1, 0, 0, 0}, {0, 1, 0, 0} }; static SkRect texs[] = { { 10, 10, 20, 20 }, {20, 20, 30, 30} }; static SkRect cullRect = { 0, 0, 200, 200 }; b.drawAtlas(TestImage2, xforms, texs, nullptr, 2, SkBlendMode::kSrcIn, DisplayList::NearestSampling, &cullRect);}}, - {1, 40 + 32 + 32 + 8, [](DisplayListBuilder& b) { + {1, 40 + 32 + 32 + 8, 1, 40 + 32 + 32 + 8, [](DisplayListBuilder& b) { static SkRSXform xforms[] = { {1, 0, 0, 0}, {0, 1, 0, 0} }; static SkRect texs[] = { { 10, 10, 20, 20 }, {20, 20, 30, 30} }; static SkColor colors[] = { SK_ColorBLUE, SK_ColorGREEN }; b.drawAtlas(TestImage1, xforms, texs, colors, 2, SkBlendMode::kSrcIn, DisplayList::NearestSampling, nullptr);}}, - {1, 56 + 32 + 32 + 8, [](DisplayListBuilder& b) { + {1, 56 + 32 + 32 + 8, 1, 56 + 32 + 32 + 8, [](DisplayListBuilder& b) { static SkRSXform xforms[] = { {1, 0, 0, 0}, {0, 1, 0, 0} }; static SkRect texs[] = { { 10, 10, 20, 20 }, {20, 20, 30, 30} }; static SkColor colors[] = { SK_ColorBLUE, SK_ColorGREEN }; @@ -505,34 +545,39 @@ std::vector allGroups = { } }, { "DrawPicture", { - {1, 16, [](DisplayListBuilder& b) {b.drawPicture(TestPicture1, nullptr, false);}}, - {1, 16, [](DisplayListBuilder& b) {b.drawPicture(TestPicture2, nullptr, false);}}, - {1, 16, [](DisplayListBuilder& b) {b.drawPicture(TestPicture1, nullptr, true);}}, - {1, 56, [](DisplayListBuilder& b) {b.drawPicture(TestPicture1, &TestMatrix1, false);}}, - {1, 56, [](DisplayListBuilder& b) {b.drawPicture(TestPicture1, &TestMatrix2, false);}}, - {1, 56, [](DisplayListBuilder& b) {b.drawPicture(TestPicture1, &TestMatrix1, true);}}, + // cv.drawPicture cannot be compared as SkCanvas may inline it + {1, 16, -1, 16, [](DisplayListBuilder& b) {b.drawPicture(TestPicture1, nullptr, false);}}, + {1, 16, -1, 16, [](DisplayListBuilder& b) {b.drawPicture(TestPicture2, nullptr, false);}}, + {1, 16, -1, 16, [](DisplayListBuilder& b) {b.drawPicture(TestPicture1, nullptr, true);}}, + {1, 56, -1, 56, [](DisplayListBuilder& b) {b.drawPicture(TestPicture1, &TestMatrix1, false);}}, + {1, 56, -1, 56, [](DisplayListBuilder& b) {b.drawPicture(TestPicture1, &TestMatrix2, false);}}, + {1, 56, -1, 56, [](DisplayListBuilder& b) {b.drawPicture(TestPicture1, &TestMatrix1, true);}}, } }, { "DrawDisplayList", { - {1, 16, [](DisplayListBuilder& b) {b.drawDisplayList(TestDisplayList1);}}, - {1, 16, [](DisplayListBuilder& b) {b.drawDisplayList(TestDisplayList2);}}, + // cv.drawDL does not exist + {1, 16, -1, 16, [](DisplayListBuilder& b) {b.drawDisplayList(TestDisplayList1);}}, + {1, 16, -1, 16, [](DisplayListBuilder& b) {b.drawDisplayList(TestDisplayList2);}}, } }, { "DrawTextBlob", { - {1, 24, [](DisplayListBuilder& b) {b.drawTextBlob(TestBlob1, 10, 10);}}, - {1, 24, [](DisplayListBuilder& b) {b.drawTextBlob(TestBlob1, 20, 10);}}, - {1, 24, [](DisplayListBuilder& b) {b.drawTextBlob(TestBlob1, 10, 20);}}, - {1, 24, [](DisplayListBuilder& b) {b.drawTextBlob(TestBlob2, 10, 10);}}, + {1, 24, 1, 24, [](DisplayListBuilder& b) {b.drawTextBlob(TestBlob1, 10, 10);}}, + {1, 24, 1, 24, [](DisplayListBuilder& b) {b.drawTextBlob(TestBlob1, 20, 10);}}, + {1, 24, 1, 24, [](DisplayListBuilder& b) {b.drawTextBlob(TestBlob1, 10, 20);}}, + {1, 24, 1, 24, [](DisplayListBuilder& b) {b.drawTextBlob(TestBlob2, 10, 10);}}, } }, // TODO(flar): Skipping DrawShadowRec for now, Flutter does not use it // and Skia does not yet expose the require definitions + // The -1 op counts below are to indicate to the framework not to test + // SkCanvas conversion of these ops { "DrawShadow", { - {1, 32, [](DisplayListBuilder& b) {b.drawShadow(TestPath1, SK_ColorGREEN, 1.0, false);}}, - {1, 32, [](DisplayListBuilder& b) {b.drawShadow(TestPath2, SK_ColorGREEN, 1.0, false);}}, - {1, 32, [](DisplayListBuilder& b) {b.drawShadow(TestPath1, SK_ColorBLUE, 1.0, false);}}, - {1, 32, [](DisplayListBuilder& b) {b.drawShadow(TestPath1, SK_ColorGREEN, 2.0, false);}}, - {1, 32, [](DisplayListBuilder& b) {b.drawShadow(TestPath1, SK_ColorGREEN, 1.0, true);}}, + // cv shadows are turned into an opaque ShadowRec which is not exposed + {1, 32, -1, 32, [](DisplayListBuilder& b) {b.drawShadow(TestPath1, SK_ColorGREEN, 1.0, false);}}, + {1, 32, -1, 32, [](DisplayListBuilder& b) {b.drawShadow(TestPath2, SK_ColorGREEN, 1.0, false);}}, + {1, 32, -1, 32, [](DisplayListBuilder& b) {b.drawShadow(TestPath1, SK_ColorBLUE, 1.0, false);}}, + {1, 32, -1, 32, [](DisplayListBuilder& b) {b.drawShadow(TestPath1, SK_ColorGREEN, 2.0, false);}}, + {1, 32, -1, 32, [](DisplayListBuilder& b) {b.drawShadow(TestPath1, SK_ColorGREEN, 1.0, true);}}, } }, }; @@ -541,10 +586,10 @@ TEST(DisplayList, SingleOpSizes) { for (auto& group : allGroups) { for (size_t i = 0; i < group.variants.size(); i++) { auto& invocation = group.variants[i]; - sk_sp dl = invocation.build(); - auto desc = group.op_name + "(variant " + std::to_string(i) + ")"; - ASSERT_EQ(dl->opCount(), invocation.opCount) << desc; - EXPECT_EQ(dl->bytes(), invocation.byteCount) << desc; + sk_sp dl = invocation.Build(); + auto desc = group.op_name + "(variant " + std::to_string(i + 1) + ")"; + ASSERT_EQ(dl->op_count(), invocation.op_count) << desc; + EXPECT_EQ(dl->bytes(), invocation.byte_count) << desc; } } } @@ -555,20 +600,64 @@ TEST(DisplayList, SingleOpCompares) { std::vector> lists1; std::vector> lists2; for (size_t i = 0; i < group.variants.size(); i++) { - lists1.push_back(group.variants[i].build()); - lists2.push_back(group.variants[i].build()); + lists1.push_back(group.variants[i].Build()); + lists2.push_back(group.variants[i].Build()); auto desc = - group.op_name + "(variant " + std::to_string(i) + " == empty)"; + group.op_name + "(variant " + std::to_string(i + 1) + " == empty)"; ASSERT_FALSE(lists1[i]->Equals(*empty)) << desc; ASSERT_FALSE(lists2[i]->Equals(*empty)) << desc; ASSERT_FALSE(empty->Equals(*lists1[i])) << desc; ASSERT_FALSE(empty->Equals(*lists2[i])) << desc; + + // Verify recapturing the replay of the display list is Equals() + // when dispatching directly from the DL to another builder + DisplayListBuilder builder; + lists1[i]->Dispatch(builder); + sk_sp copy = builder.Build(); + desc = group.op_name + "(variant " + std::to_string(i + 1) + " == copy)"; + ASSERT_EQ(copy->op_count(), lists1[i]->op_count()) << desc; + ASSERT_EQ(copy->bytes(), lists1[i]->bytes()) << desc; + ASSERT_EQ(copy->bounds(), lists1[i]->bounds()) << desc; + ASSERT_TRUE(copy->Equals(*lists1[i])) << desc; + ASSERT_TRUE(lists1[i]->Equals(*copy)) << desc; + + // Verify recapturing the replay of the display list is Equals() + // when translating through the SkCanvas interface + // Note that sometimes the rendering ops can be optimized out by + // SkCanvas so the transfer is not always 1:1. We control for + // this by having separate op counts and sizes for the sk results. + // Also, an sk_op_count less than 0 means "do not test this op" + // used currently because we cannot encode a DrawShadowRec and + // drawPicture/DisplayList because SkCanvas sometimes inlines + // the SkPicture and it does not understand a DisplayList. + if (group.variants[i].sk_op_count >= 0) { + DisplayListCanvasRecorder recorder(lists1[i]->bounds()); + lists1[i]->RenderTo(&recorder); + sk_sp sk_copy = recorder.Build(); + desc = group.op_name + "(variant " + std::to_string(i + 1) + + " == sk_copy)"; + EXPECT_EQ(sk_copy->op_count(), group.variants[i].sk_op_count) << desc; + EXPECT_EQ(sk_copy->bytes(), group.variants[i].sk_byte_count) << desc; + if (group.variants[i].op_count == group.variants[i].sk_op_count && + group.variants[i].byte_count == group.variants[i].sk_byte_count) { + EXPECT_EQ(sk_copy->bounds(), lists1[i]->bounds()) << desc; + EXPECT_TRUE(sk_copy->Equals(*lists1[i])) << desc; + EXPECT_TRUE(lists1[i]->Equals(*sk_copy)) << desc; + } else { + // No assertion on bounds - they could be equal, hard to tell + EXPECT_FALSE(sk_copy->Equals(*lists1[i])) << desc; + EXPECT_FALSE(lists1[i]->Equals(*sk_copy)) << desc; + } + } } for (size_t i = 0; i < lists1.size(); i++) { for (size_t j = 0; j < lists2.size(); j++) { - auto desc = group.op_name + "(variant " + std::to_string(i) + - " ==? variant " + std::to_string(j) + ")"; + auto desc = group.op_name + "(variant " + std::to_string(i + 1) + + " ==? variant " + std::to_string(j + 1) + ")"; if (i == j) { + ASSERT_EQ(lists1[i]->op_count(), lists2[j]->op_count()) << desc; + ASSERT_EQ(lists1[i]->bytes(), lists2[j]->bytes()) << desc; + ASSERT_EQ(lists1[i]->bounds(), lists2[j]->bounds()) << desc; ASSERT_TRUE(lists1[i]->Equals(*lists2[j])) << desc; ASSERT_TRUE(lists2[j]->Equals(*lists1[i])) << desc; } else { @@ -581,7 +670,7 @@ TEST(DisplayList, SingleOpCompares) { } #ifdef DL_TEST_EXPERIMENTAL -static sk_sp build(size_t g_index, size_t v_index) { +static sk_sp Build(size_t g_index, size_t v_index) { DisplayListBuilder builder; int opCount = 0; size_t byteCount = 0; @@ -595,7 +684,7 @@ static sk_sp build(size_t g_index, size_t v_index) { byteCount += invocation.numBytes; invocation.invoker(builder); } - sk_sp dl = builder.build(); + sk_sp dl = builder.Build(); std::string name; if (g_index < 0) { name = "Default"; @@ -613,16 +702,16 @@ static sk_sp build(size_t g_index, size_t v_index) { } TEST(DisplayList, OpListEquals) { - sk_sp default_dl = build(-1, -1); + sk_sp default_dl = Build(-1, -1); ASSERT_EQ(default_dl.get(), default_dl.get()); int group_count = static_cast(allGroups.size()); for (int gi = 0; gi < group_count; gi++) { - sk_sp missing_dl = build(gi, -1); + sk_sp missing_dl = Build(gi, -1); ASSERT_EQ(missing_dl.get(), missing_dl.get()); ASSERT_NE(default_dl.get(), missing_dl.get()); DisplayListInvocationGroup& group = allGroups[gi]; for (size_t vi = 0; vi < group.variants.size(); vi++) { - sk_sp variant_dl = build(gi, vi); + sk_sp variant_dl = Build(gi, vi); ASSERT_EQ(variant_dl.get(), variant_dl.get()); if (vi == 0) { ASSERT_TRUE(default_dl->equals(*variant_dl.get())); diff --git a/flow/layers/display_list_layer.cc b/flow/layers/display_list_layer.cc index 5bea153ad223c..d5f742dd4a386 100644 --- a/flow/layers/display_list_layer.cc +++ b/flow/layers/display_list_layer.cc @@ -55,8 +55,8 @@ bool DisplayListLayer::Compare(DiffContext::Statistics& statistics, statistics.AddSameInstancePicture(); return true; } - auto op_cnt_1 = dl1->opCount(); - auto op_cnt_2 = dl2->opCount(); + auto op_cnt_1 = dl1->op_count(); + auto op_cnt_2 = dl2->op_count(); if (op_cnt_1 != op_cnt_2 || dl1->bounds() != dl2->bounds()) { statistics.AddNewPicture(); return false; diff --git a/flow/raster_cache.cc b/flow/raster_cache.cc index 6cba6b1921861..d45a20591d515 100644 --- a/flow/raster_cache.cc +++ b/flow/raster_cache.cc @@ -131,7 +131,7 @@ static bool IsDisplayListWorthRasterizing(DisplayList* display_list, // TODO(abarth): We should find a better heuristic here that lets us avoid // wasting memory on trivial layers that are easy to re-rasterize every frame. - return display_list->opCount() > 5; + return display_list->op_count() > 5; } /// @note Procedure doesn't copy all closures. @@ -307,7 +307,7 @@ bool RasterCache::Prepare(GrDirectContext* context, return false; } - DisplayListRasterCacheKey cache_key(display_list->uniqueID(), + DisplayListRasterCacheKey cache_key(display_list->unique_id(), transformation_matrix); // Creates an entry, if not present prior. @@ -347,7 +347,7 @@ bool RasterCache::Draw(const SkPicture& picture, SkCanvas& canvas) const { bool RasterCache::Draw(const DisplayList& display_list, SkCanvas& canvas) const { - DisplayListRasterCacheKey cache_key(display_list.uniqueID(), + DisplayListRasterCacheKey cache_key(display_list.unique_id(), canvas.getTotalMatrix()); auto it = display_list_cache_.find(cache_key); if (it == display_list_cache_.end()) { From 5f16bcc3f60dfe62f4cc39a76d24dd7470f6abad Mon Sep 17 00:00:00 2001 From: Jim Graham Date: Sat, 26 Jun 2021 12:51:21 -0700 Subject: [PATCH 16/26] break up complicated unit tests and enable experimental DL test --- flow/display_list_unittests.cc | 113 ++++++++++++++++++++------------- 1 file changed, 70 insertions(+), 43 deletions(-) diff --git a/flow/display_list_unittests.cc b/flow/display_list_unittests.cc index 17d0c6fe5978d..fc37a955f4c9f 100644 --- a/flow/display_list_unittests.cc +++ b/flow/display_list_unittests.cc @@ -594,33 +594,42 @@ TEST(DisplayList, SingleOpSizes) { } } -TEST(DisplayList, SingleOpCompares) { +TEST(DisplayList, SingleOpDisplayListsNotEmpty) { sk_sp empty = DisplayListBuilder().Build(); for (auto& group : allGroups) { - std::vector> lists1; - std::vector> lists2; for (size_t i = 0; i < group.variants.size(); i++) { - lists1.push_back(group.variants[i].Build()); - lists2.push_back(group.variants[i].Build()); + sk_sp dl = group.variants[i].Build(); auto desc = - group.op_name + "(variant " + std::to_string(i + 1) + " == empty)"; - ASSERT_FALSE(lists1[i]->Equals(*empty)) << desc; - ASSERT_FALSE(lists2[i]->Equals(*empty)) << desc; - ASSERT_FALSE(empty->Equals(*lists1[i])) << desc; - ASSERT_FALSE(empty->Equals(*lists2[i])) << desc; + group.op_name + "(variant " + std::to_string(i + 1) + " != empty)"; + ASSERT_FALSE(dl->Equals(*empty)) << desc; + ASSERT_FALSE(empty->Equals(*dl)) << desc; + } + } +} +TEST(DisplayList, SingleOpDisplayListsRecapturedAreEqual) { + for (auto& group : allGroups) { + for (size_t i = 0; i < group.variants.size(); i++) { + sk_sp dl = group.variants[i].Build(); // Verify recapturing the replay of the display list is Equals() // when dispatching directly from the DL to another builder DisplayListBuilder builder; - lists1[i]->Dispatch(builder); + dl->Dispatch(builder); sk_sp copy = builder.Build(); - desc = group.op_name + "(variant " + std::to_string(i + 1) + " == copy)"; - ASSERT_EQ(copy->op_count(), lists1[i]->op_count()) << desc; - ASSERT_EQ(copy->bytes(), lists1[i]->bytes()) << desc; - ASSERT_EQ(copy->bounds(), lists1[i]->bounds()) << desc; - ASSERT_TRUE(copy->Equals(*lists1[i])) << desc; - ASSERT_TRUE(lists1[i]->Equals(*copy)) << desc; + auto desc = + group.op_name + "(variant " + std::to_string(i + 1) + " == copy)"; + ASSERT_EQ(copy->op_count(), dl->op_count()) << desc; + ASSERT_EQ(copy->bytes(), dl->bytes()) << desc; + ASSERT_EQ(copy->bounds(), dl->bounds()) << desc; + ASSERT_TRUE(copy->Equals(*dl)) << desc; + ASSERT_TRUE(dl->Equals(*copy)) << desc; + } + } +} +TEST(DisplayList, SingleOpDisplayListsRecapturedViaSkCanvasAreEqual) { + for (auto& group : allGroups) { + for (size_t i = 0; i < group.variants.size(); i++) { // Verify recapturing the replay of the display list is Equals() // when translating through the SkCanvas interface // Note that sometimes the rendering ops can be optimized out by @@ -631,25 +640,39 @@ TEST(DisplayList, SingleOpCompares) { // drawPicture/DisplayList because SkCanvas sometimes inlines // the SkPicture and it does not understand a DisplayList. if (group.variants[i].sk_op_count >= 0) { - DisplayListCanvasRecorder recorder(lists1[i]->bounds()); - lists1[i]->RenderTo(&recorder); + sk_sp dl = group.variants[i].Build(); + + DisplayListCanvasRecorder recorder(dl->bounds()); + dl->RenderTo(&recorder); sk_sp sk_copy = recorder.Build(); - desc = group.op_name + "(variant " + std::to_string(i + 1) + - " == sk_copy)"; + auto desc = group.op_name + "(variant " + std::to_string(i + 1) + + " == sk_copy)"; EXPECT_EQ(sk_copy->op_count(), group.variants[i].sk_op_count) << desc; EXPECT_EQ(sk_copy->bytes(), group.variants[i].sk_byte_count) << desc; if (group.variants[i].op_count == group.variants[i].sk_op_count && group.variants[i].byte_count == group.variants[i].sk_byte_count) { - EXPECT_EQ(sk_copy->bounds(), lists1[i]->bounds()) << desc; - EXPECT_TRUE(sk_copy->Equals(*lists1[i])) << desc; - EXPECT_TRUE(lists1[i]->Equals(*sk_copy)) << desc; + EXPECT_EQ(sk_copy->bounds(), dl->bounds()) << desc; + EXPECT_TRUE(sk_copy->Equals(*dl)) << desc; + EXPECT_TRUE(dl->Equals(*sk_copy)) << desc; } else { // No assertion on bounds - they could be equal, hard to tell - EXPECT_FALSE(sk_copy->Equals(*lists1[i])) << desc; - EXPECT_FALSE(lists1[i]->Equals(*sk_copy)) << desc; + EXPECT_FALSE(sk_copy->Equals(*dl)) << desc; + EXPECT_FALSE(dl->Equals(*sk_copy)) << desc; } } } + } +} + +TEST(DisplayList, SingleOpDisplayListsCompareToEachOther) { + for (auto& group : allGroups) { + std::vector> lists1; + std::vector> lists2; + for (size_t i = 0; i < group.variants.size(); i++) { + lists1.push_back(group.variants[i].Build()); + lists2.push_back(group.variants[i].Build()); + } + for (size_t i = 0; i < lists1.size(); i++) { for (size_t j = 0; j < lists2.size(); j++) { auto desc = group.op_name + "(variant " + std::to_string(i + 1) + @@ -669,19 +692,18 @@ TEST(DisplayList, SingleOpCompares) { } } -#ifdef DL_TEST_EXPERIMENTAL static sk_sp Build(size_t g_index, size_t v_index) { DisplayListBuilder builder; - int opCount = 0; - size_t byteCount = 0; + int op_count = 0; + size_t byte_count = 0; for (size_t i = 0; i < allGroups.size(); i++) { int j = (i == g_index ? v_index : 0); if (j < 0) continue; DisplayListInvocationGroup& group = allGroups[i]; DisplayListInvocation& invocation = group.variants[j]; - opCount += invocation.numOps; - byteCount += invocation.numBytes; + op_count += invocation.op_count; + byte_count += invocation.byte_count; invocation.invoker(builder); } sk_sp dl = builder.Build(); @@ -696,34 +718,39 @@ static sk_sp Build(size_t g_index, size_t v_index) { name += " variant " + std::to_string(v_index); } } - EXPECT_EQ(dl->opCount(), opCount) << name; - EXPECT_EQ(dl->bytes(), byteCount) << name; + EXPECT_EQ(dl->op_count(), op_count) << name; + EXPECT_EQ(dl->bytes(), byte_count) << name; return dl; } -TEST(DisplayList, OpListEquals) { +TEST(DisplayList, DisplayListsWithVaryingOpComparisons) { sk_sp default_dl = Build(-1, -1); - ASSERT_EQ(default_dl.get(), default_dl.get()); + ASSERT_TRUE(default_dl->Equals(*default_dl)) << "Default == itself"; int group_count = static_cast(allGroups.size()); for (int gi = 0; gi < group_count; gi++) { sk_sp missing_dl = Build(gi, -1); - ASSERT_EQ(missing_dl.get(), missing_dl.get()); - ASSERT_NE(default_dl.get(), missing_dl.get()); + auto desc = "[Group " + std::to_string(gi + 1) + " omitted]"; + ASSERT_TRUE(missing_dl->Equals(*missing_dl)) << desc << " == itself"; + ASSERT_FALSE(missing_dl->Equals(*default_dl)) << desc << " != Default"; + ASSERT_FALSE(default_dl->Equals(*missing_dl)) << "Default != " << desc; DisplayListInvocationGroup& group = allGroups[gi]; for (size_t vi = 0; vi < group.variants.size(); vi++) { + auto desc = "[Group " + std::to_string(gi + 1) + " variant " + + std::to_string(vi + 1) + "]"; sk_sp variant_dl = Build(gi, vi); - ASSERT_EQ(variant_dl.get(), variant_dl.get()); + ASSERT_TRUE(variant_dl->Equals(*variant_dl)) << desc << " == itself"; if (vi == 0) { - ASSERT_TRUE(default_dl->equals(*variant_dl.get())); - ASSERT_EQ(default_dl.get(), variant_dl.get()); + ASSERT_TRUE(variant_dl->Equals(*default_dl)) << desc << " == Default"; + ASSERT_TRUE(default_dl->Equals(*variant_dl)) << "Default == " << desc; } else { - ASSERT_NE(default_dl.get(), variant_dl.get()); + ASSERT_FALSE(variant_dl->Equals(*default_dl)) << desc << " != Default"; + ASSERT_FALSE(default_dl->Equals(*variant_dl)) << "Default != " << desc; } - ASSERT_NE(missing_dl.get(), variant_dl.get()); + ASSERT_FALSE(variant_dl->Equals(*missing_dl)) << desc << " != omitted"; + ASSERT_FALSE(missing_dl->Equals(*variant_dl)) << "omitted != " << desc; } } } -#endif // DL_TEST_EXPERIMENTAL } // namespace testing } // namespace flutter From 0b275af461365f870b1cbd4e9f5a5033371b211d Mon Sep 17 00:00:00 2001 From: Jim Graham Date: Sat, 26 Jun 2021 13:05:56 -0700 Subject: [PATCH 17/26] minor formatting and simplification of DL unit tests --- flow/display_list_unittests.cc | 65 +++++++++++++++++++--------------- 1 file changed, 37 insertions(+), 28 deletions(-) diff --git a/flow/display_list_unittests.cc b/flow/display_list_unittests.cc index fc37a955f4c9f..cb022931e8848 100644 --- a/flow/display_list_unittests.cc +++ b/flow/display_list_unittests.cc @@ -184,6 +184,10 @@ struct DisplayListInvocation { DlInvoker invoker; + bool sk_version_matches() { + return (op_count == sk_op_count && byte_count == sk_byte_count); + } + sk_sp Build() { DisplayListBuilder builder; invoker(builder); @@ -594,7 +598,7 @@ TEST(DisplayList, SingleOpSizes) { } } -TEST(DisplayList, SingleOpDisplayListsNotEmpty) { +TEST(DisplayList, SingleOpDisplayListsNotEqualEmpty) { sk_sp empty = DisplayListBuilder().Build(); for (auto& group : allGroups) { for (size_t i = 0; i < group.variants.size(); i++) { @@ -630,35 +634,38 @@ TEST(DisplayList, SingleOpDisplayListsRecapturedAreEqual) { TEST(DisplayList, SingleOpDisplayListsRecapturedViaSkCanvasAreEqual) { for (auto& group : allGroups) { for (size_t i = 0; i < group.variants.size(); i++) { - // Verify recapturing the replay of the display list is Equals() - // when translating through the SkCanvas interface + if (group.variants[i].sk_op_count < 0) { + // A negative sk_op_count means "do not test this op". + // Used mainly for these cases: + // - we cannot encode a DrawShadowRec (Skia private header) + // - SkCanvas cannot receive a DisplayList + // - SkCanvas may or may not inline an SkPicture + continue; + } + // Verify a DisplayList (re)built by "rendering" it to an + // [SkCanvas->DisplayList] recorder recaptures an equivalent + // sequence. // Note that sometimes the rendering ops can be optimized out by // SkCanvas so the transfer is not always 1:1. We control for - // this by having separate op counts and sizes for the sk results. - // Also, an sk_op_count less than 0 means "do not test this op" - // used currently because we cannot encode a DrawShadowRec and - // drawPicture/DisplayList because SkCanvas sometimes inlines - // the SkPicture and it does not understand a DisplayList. - if (group.variants[i].sk_op_count >= 0) { - sk_sp dl = group.variants[i].Build(); - - DisplayListCanvasRecorder recorder(dl->bounds()); - dl->RenderTo(&recorder); - sk_sp sk_copy = recorder.Build(); - auto desc = group.op_name + "(variant " + std::to_string(i + 1) + - " == sk_copy)"; - EXPECT_EQ(sk_copy->op_count(), group.variants[i].sk_op_count) << desc; - EXPECT_EQ(sk_copy->bytes(), group.variants[i].sk_byte_count) << desc; - if (group.variants[i].op_count == group.variants[i].sk_op_count && - group.variants[i].byte_count == group.variants[i].sk_byte_count) { - EXPECT_EQ(sk_copy->bounds(), dl->bounds()) << desc; - EXPECT_TRUE(sk_copy->Equals(*dl)) << desc; - EXPECT_TRUE(dl->Equals(*sk_copy)) << desc; - } else { - // No assertion on bounds - they could be equal, hard to tell - EXPECT_FALSE(sk_copy->Equals(*dl)) << desc; - EXPECT_FALSE(dl->Equals(*sk_copy)) << desc; - } + // this by having separate op counts and sizes for the sk results + // and changing our expectation of Equals() results accordingly. + sk_sp dl = group.variants[i].Build(); + + DisplayListCanvasRecorder recorder(dl->bounds()); + dl->RenderTo(&recorder); + sk_sp sk_copy = recorder.Build(); + auto desc = group.op_name + "[variant " + std::to_string(i + 1) + "]"; + EXPECT_EQ(sk_copy->op_count(), group.variants[i].sk_op_count) << desc; + EXPECT_EQ(sk_copy->bytes(), group.variants[i].sk_byte_count) << desc; + if (group.variants[i].sk_version_matches()) { + EXPECT_EQ(sk_copy->bounds(), dl->bounds()) << desc; + EXPECT_TRUE(dl->Equals(*sk_copy)) << desc << " == sk_copy"; + EXPECT_TRUE(sk_copy->Equals(*dl)) << "sk_copy == " << desc; + } else { + // No assertion on bounds + // they could be equal, hard to tell + EXPECT_FALSE(dl->Equals(*sk_copy)) << desc << " != sk_copy"; + EXPECT_FALSE(sk_copy->Equals(*dl)) << "sk_copy != " << desc; } } } @@ -684,6 +691,8 @@ TEST(DisplayList, SingleOpDisplayListsCompareToEachOther) { ASSERT_TRUE(lists1[i]->Equals(*lists2[j])) << desc; ASSERT_TRUE(lists2[j]->Equals(*lists1[i])) << desc; } else { + // No assertion on op/byte counts or bounds + // they may or may not be equal between variants ASSERT_FALSE(lists1[i]->Equals(*lists2[j])) << desc; ASSERT_FALSE(lists2[j]->Equals(*lists1[i])) << desc; } From d98decddbd2f72d11e70f168d6a7c8504a170e92 Mon Sep 17 00:00:00 2001 From: Jim Graham Date: Sat, 26 Jun 2021 16:19:06 -0700 Subject: [PATCH 18/26] flush out render testing matrix and fix shadow bounds --- flow/display_list_canvas_unittests.cc | 593 +++++++++++++++++++++----- flow/display_list_utils.cc | 19 +- 2 files changed, 492 insertions(+), 120 deletions(-) diff --git a/flow/display_list_canvas_unittests.cc b/flow/display_list_canvas_unittests.cc index cf0b888998b49..5efb1a77092a9 100644 --- a/flow/display_list_canvas_unittests.cc +++ b/flow/display_list_canvas_unittests.cc @@ -3,13 +3,16 @@ // found in the LICENSE file. #include "flutter/flow/display_list_canvas.h" +#include "flutter/flow/layers/physical_shape_layer.h" #include "third_party/skia/include/core/SkColor.h" #include "third_party/skia/include/core/SkImageInfo.h" #include "third_party/skia/include/core/SkPath.h" #include "third_party/skia/include/core/SkPictureRecorder.h" #include "third_party/skia/include/core/SkRRect.h" +#include "third_party/skia/include/core/SkRSXform.h" #include "third_party/skia/include/core/SkSurface.h" +#include "third_party/skia/include/core/SkTextBlob.h" #include "third_party/skia/include/core/SkVertices.h" #include "third_party/skia/include/effects/SkGradientShader.h" #include "third_party/skia/include/effects/SkImageFilters.h" @@ -41,45 +44,95 @@ constexpr SkRect RenderBounds = class CanvasCompareTester { public: + // If a test is using any shadow operations then we cannot currently + // record those in an SkCanvas and play it back into a DisplayList + // because internally the operation gets encapsulated in a Skia + // ShadowRec which is not exposed by their headers. For operations + // that use shadows, we can perform a lot of tests, but not the tests + // that require SkCanvas->DisplayList transfers. + static bool UsingShadows; + typedef const std::function CvRenderer; typedef const std::function DlRenderer; - static void renderAll(CvRenderer& cv_renderer, DlRenderer& dl_renderer) { - renderWithAttributes(cv_renderer, dl_renderer); - renderWithTransforms(cv_renderer, dl_renderer); - renderWithClips(cv_renderer, dl_renderer); + static void RenderAll(CvRenderer& cv_renderer, DlRenderer& dl_renderer) { + RenderWithAttributes(cv_renderer, dl_renderer); + RenderWithTransforms(cv_renderer, dl_renderer); + RenderWithClips(cv_renderer, dl_renderer); + } + + static void RenderNoAttributes(CvRenderer& cv_renderer, + DlRenderer& dl_renderer) { + RenderWith([=](SkCanvas*, SkPaint& p) {}, // + [=](DisplayListBuilder& d) {}, // + cv_renderer, dl_renderer, "Base Test"); + RenderWithTransforms(cv_renderer, dl_renderer); + RenderWithClips(cv_renderer, dl_renderer); } - static void renderWithAttributes(CvRenderer& cv_renderer, + static void RenderWithSaveRestore(CvRenderer& cv_renderer, + DlRenderer& dl_renderer) { + SkRect clip = SkRect::MakeLTRB(0, 0, 10, 10); + SkRect rect = SkRect::MakeLTRB(5, 5, 15, 15); + SkColor save_layer_color = SkColorSetARGB(0x7f, 0x00, 0xff, 0xff); + RenderWith( + [=](SkCanvas* cv, SkPaint& p) { + cv->save(); + cv->clipRect(clip, SkClipOp::kIntersect, false); + cv->drawRect(rect, p); + cv->restore(); + }, + [=](DisplayListBuilder& b) { + b.save(); + b.clipRect(clip, false, SkClipOp::kIntersect); + b.drawRect(rect); + b.restore(); + }, + cv_renderer, dl_renderer, "With prior save/clip/restore"); + RenderWith( + [=](SkCanvas* cv, SkPaint& p) { + SkPaint save_p; + save_p.setColor(save_layer_color); + cv->saveLayer(RenderBounds, &save_p); + cv->drawRect(rect, p); + }, + [=](DisplayListBuilder& b) { + b.setColor(save_layer_color); + b.saveLayer(&RenderBounds, true); + b.setColor(SkPaint().getColor()); + b.drawRect(rect); + }, + cv_renderer, dl_renderer, "With saveLayer"); + } + + static void RenderWithAttributes(CvRenderer& cv_renderer, DlRenderer& dl_renderer) { - renderWith([=](SkCanvas*, SkPaint& p) {}, // + RenderWith([=](SkCanvas*, SkPaint& p) {}, // [=](DisplayListBuilder& d) {}, // cv_renderer, dl_renderer, "Base Test"); - renderWith([=](SkCanvas*, SkPaint& p) { p.setAntiAlias(true); }, // + RenderWith([=](SkCanvas*, SkPaint& p) { p.setAntiAlias(true); }, // [=](DisplayListBuilder& b) { b.setAA(true); }, // cv_renderer, dl_renderer, "AA == True"); - renderWith([=](SkCanvas*, SkPaint& p) { p.setAntiAlias(false); }, // + RenderWith([=](SkCanvas*, SkPaint& p) { p.setAntiAlias(false); }, // [=](DisplayListBuilder& b) { b.setAA(false); }, // cv_renderer, dl_renderer, "AA == False"); - // Not testing setInvertColors here because there is no SkPaint version - - renderWith([=](SkCanvas*, SkPaint& p) { p.setDither(true); }, // + RenderWith([=](SkCanvas*, SkPaint& p) { p.setDither(true); }, // [=](DisplayListBuilder& b) { b.setDither(true); }, // cv_renderer, dl_renderer, "Dither == True"); - renderWith([=](SkCanvas*, SkPaint& p) { p.setDither(false); }, // + RenderWith([=](SkCanvas*, SkPaint& p) { p.setDither(false); }, // [=](DisplayListBuilder& b) { b.setDither(false); }, // cv_renderer, dl_renderer, "Dither = False"); - renderWith([=](SkCanvas*, SkPaint& p) { p.setColor(SK_ColorBLUE); }, // + RenderWith([=](SkCanvas*, SkPaint& p) { p.setColor(SK_ColorBLUE); }, // [=](DisplayListBuilder& b) { b.setColor(SK_ColorBLUE); }, // cv_renderer, dl_renderer, "Color == Blue"); - renderWith([=](SkCanvas*, SkPaint& p) { p.setColor(SK_ColorGREEN); }, // + RenderWith([=](SkCanvas*, SkPaint& p) { p.setColor(SK_ColorGREEN); }, // [=](DisplayListBuilder& b) { b.setColor(SK_ColorGREEN); }, // cv_renderer, dl_renderer, "Color == Green"); - renderWithStrokes(cv_renderer, dl_renderer); + RenderWithStrokes(cv_renderer, dl_renderer); // Not testing FilterQuality here because there is no SkPaint version @@ -88,7 +141,7 @@ class CanvasCompareTester { SkColor blendableColor = SkColorSetARGB(0x7f, 0x00, 0xff, 0xff); SkColor bg = SK_ColorWHITE; - renderWith( + RenderWith( [=](SkCanvas*, SkPaint& p) { p.setBlendMode(SkBlendMode::kSrcIn); p.setColor(blendableColor); @@ -98,7 +151,7 @@ class CanvasCompareTester { b.setColor(blendableColor); }, cv_renderer, dl_renderer, "Blend == SrcIn", &bg); - renderWith( + RenderWith( [=](SkCanvas*, SkPaint& p) { p.setBlendMode(SkBlendMode::kDstIn); p.setColor(blendableColor); @@ -114,7 +167,7 @@ class CanvasCompareTester { sk_sp filter = SkImageFilters::Blur(5.0, 5.0, SkTileMode::kDecal, nullptr, nullptr); { - renderWith([=](SkCanvas*, SkPaint& p) { p.setImageFilter(filter); }, + RenderWith([=](SkCanvas*, SkPaint& p) { p.setImageFilter(filter); }, [=](DisplayListBuilder& b) { b.setImageFilter(filter); }, cv_renderer, dl_renderer, "ImageFilter == Decal Blur 5"); } @@ -122,16 +175,24 @@ class CanvasCompareTester { } { + // clang-format off constexpr float rotate_color_matrix[20] = { - 0, 1, 0, 0, 0, // - 0, 0, 1, 0, 0, // - 1, 0, 0, 0, 0, // - 0, 0, 0, 1, 0, // + 0, 1, 0, 0, 0, + 0, 0, 1, 0, 0, + 1, 0, 0, 0, 0, + 0, 0, 0, 1, 0, + }; + constexpr float invert_color_matrix[20] = { + -1.0, 0, 0, 1.0, 0, + 0, -1.0, 0, 1.0, 0, + 0, 0, -1.0, 1.0, 0, + 1.0, 1.0, 1.0, 1.0, 0, }; + // clang-format on sk_sp filter = SkColorFilters::Matrix(rotate_color_matrix); { SkColor bg = SK_ColorWHITE; - renderWith( + RenderWith( [=](SkCanvas*, SkPaint& p) { p.setColor(SK_ColorYELLOW); p.setColorFilter(filter); @@ -143,19 +204,34 @@ class CanvasCompareTester { cv_renderer, dl_renderer, "ColorFilter == RotateRGB", &bg); } ASSERT_TRUE(filter->unique()) << "ColorFilter Cleanup"; + filter = SkColorFilters::Matrix(invert_color_matrix); + { + SkColor bg = SK_ColorWHITE; + RenderWith( + [=](SkCanvas*, SkPaint& p) { + p.setColor(SK_ColorYELLOW); + p.setColorFilter(filter); + }, + [=](DisplayListBuilder& b) { + b.setColor(SK_ColorYELLOW); + b.setInvertColors(true); + }, + cv_renderer, dl_renderer, "ColorFilter == Invert", &bg); + } + ASSERT_TRUE(filter->unique()) << "ColorFilter Cleanup"; } { sk_sp filter = SkMaskFilter::MakeBlur(kNormal_SkBlurStyle, 5.0); { - renderWith([=](SkCanvas*, SkPaint& p) { p.setMaskFilter(filter); }, + RenderWith([=](SkCanvas*, SkPaint& p) { p.setMaskFilter(filter); }, [=](DisplayListBuilder& b) { b.setMaskFilter(filter); }, cv_renderer, dl_renderer, "MaskFilter == Blur 5"); } ASSERT_TRUE(filter->unique()) << "MaskFilter Cleanup"; { - renderWith([=](SkCanvas*, SkPaint& p) { p.setMaskFilter(filter); }, + RenderWith([=](SkCanvas*, SkPaint& p) { p.setMaskFilter(filter); }, [=](DisplayListBuilder& b) { b.setMaskBlurFilter(kNormal_SkBlurStyle, 5.0); }, @@ -182,7 +258,7 @@ class CanvasCompareTester { sk_sp shader = SkGradientShader::MakeLinear( end_points, colors, stops, 3, SkTileMode::kMirror, 0, nullptr); { - renderWith([=](SkCanvas*, SkPaint& p) { p.setShader(shader); }, + RenderWith([=](SkCanvas*, SkPaint& p) { p.setShader(shader); }, [=](DisplayListBuilder& b) { b.setShader(shader); }, cv_renderer, dl_renderer, "LinearGradient GYB"); } @@ -190,18 +266,18 @@ class CanvasCompareTester { } } - static void renderWithStrokes(CvRenderer& cv_renderer, + static void RenderWithStrokes(CvRenderer& cv_renderer, DlRenderer& dl_renderer) { - renderWith( + RenderWith( [=](SkCanvas*, SkPaint& p) { p.setStyle(SkPaint::kFill_Style); }, [=](DisplayListBuilder& b) { b.setDrawStyle(SkPaint::kFill_Style); }, cv_renderer, dl_renderer, "Fill"); - renderWith( + RenderWith( [=](SkCanvas*, SkPaint& p) { p.setStyle(SkPaint::kStroke_Style); }, [=](DisplayListBuilder& b) { b.setDrawStyle(SkPaint::kStroke_Style); }, cv_renderer, dl_renderer, "Stroke + defaults"); - renderWith( + RenderWith( [=](SkCanvas*, SkPaint& p) { p.setStyle(SkPaint::kFill_Style); p.setStrokeWidth(10.0); @@ -212,7 +288,7 @@ class CanvasCompareTester { }, cv_renderer, dl_renderer, "Fill + unnecessary StrokeWidth 10"); - renderWith( + RenderWith( [=](SkCanvas*, SkPaint& p) { p.setStyle(SkPaint::kStroke_Style); p.setStrokeWidth(10.0); @@ -222,7 +298,7 @@ class CanvasCompareTester { b.setStrokeWidth(10.0); }, cv_renderer, dl_renderer, "Stroke Width 10"); - renderWith( + RenderWith( [=](SkCanvas*, SkPaint& p) { p.setStyle(SkPaint::kStroke_Style); p.setStrokeWidth(5.0); @@ -233,7 +309,7 @@ class CanvasCompareTester { }, cv_renderer, dl_renderer, "Stroke Width 5"); - renderWith( + RenderWith( [=](SkCanvas*, SkPaint& p) { p.setStyle(SkPaint::kStroke_Style); p.setStrokeWidth(5.0); @@ -245,7 +321,7 @@ class CanvasCompareTester { b.setCaps(SkPaint::kButt_Cap); }, cv_renderer, dl_renderer, "Stroke Width 5, Butt Cap"); - renderWith( + RenderWith( [=](SkCanvas*, SkPaint& p) { p.setStyle(SkPaint::kStroke_Style); p.setStrokeWidth(5.0); @@ -258,7 +334,7 @@ class CanvasCompareTester { }, cv_renderer, dl_renderer, "Stroke Width 5, Round Cap"); - renderWith( + RenderWith( [=](SkCanvas*, SkPaint& p) { p.setStyle(SkPaint::kStroke_Style); p.setStrokeWidth(5.0); @@ -270,7 +346,7 @@ class CanvasCompareTester { b.setJoins(SkPaint::kBevel_Join); }, cv_renderer, dl_renderer, "Stroke Width 5, Bevel Join"); - renderWith( + RenderWith( [=](SkCanvas*, SkPaint& p) { p.setStyle(SkPaint::kStroke_Style); p.setStrokeWidth(5.0); @@ -283,7 +359,7 @@ class CanvasCompareTester { }, cv_renderer, dl_renderer, "Stroke Width 5, Round Join"); - renderWith( + RenderWith( [=](SkCanvas*, SkPaint& p) { p.setStyle(SkPaint::kStroke_Style); p.setStrokeWidth(5.0); @@ -298,7 +374,7 @@ class CanvasCompareTester { }, cv_renderer, dl_renderer, "Stroke Width 5, Miter 100"); - renderWith( + RenderWith( [=](SkCanvas*, SkPaint& p) { p.setStyle(SkPaint::kStroke_Style); p.setStrokeWidth(5.0); @@ -314,54 +390,121 @@ class CanvasCompareTester { cv_renderer, dl_renderer, "Stroke Width 5, Miter 0"); } - static void renderWithTransforms(CvRenderer& cv_renderer, + static void RenderWithTransforms(CvRenderer& cv_renderer, DlRenderer& dl_renderer) { - renderWith([=](SkCanvas* c, SkPaint&) { c->translate(5, 10); }, // + RenderWith([=](SkCanvas* c, SkPaint&) { c->translate(5, 10); }, // [=](DisplayListBuilder& b) { b.translate(5, 10); }, // cv_renderer, dl_renderer, "Translate 5, 10"); - renderWith([=](SkCanvas* c, SkPaint&) { c->scale(0.95, 0.95); }, // + RenderWith([=](SkCanvas* c, SkPaint&) { c->scale(0.95, 0.95); }, // [=](DisplayListBuilder& b) { b.scale(0.95, 0.95); }, // cv_renderer, dl_renderer, "Scale 95%"); - renderWith([=](SkCanvas* c, SkPaint&) { c->rotate(5); }, // + RenderWith([=](SkCanvas* c, SkPaint&) { c->rotate(5); }, // [=](DisplayListBuilder& b) { b.rotate(5); }, // cv_renderer, dl_renderer, "Rotate 5 degrees"); - renderWith([=](SkCanvas* c, SkPaint&) { c->skew(0.05, 0.05); }, // + RenderWith([=](SkCanvas* c, SkPaint&) { c->skew(0.05, 0.05); }, // [=](DisplayListBuilder& b) { b.skew(0.05, 0.05); }, // cv_renderer, dl_renderer, "Skew 5%"); + { + SkMatrix tx = SkMatrix::MakeAll(1.1, 0.1, 1.05, 0.05, 1, 1, 0, 0, 1); + RenderWith([=](SkCanvas* c, SkPaint&) { c->concat(tx); }, // + [=](DisplayListBuilder& b) { + b.transform2x3(tx[0], tx[1], tx[2], // + tx[3], tx[4], tx[5]); + }, // + cv_renderer, dl_renderer, "Transform 2x3"); + } + { + SkMatrix tx = SkMatrix::MakeAll(1.1, 0.1, 1.05, 0.05, 1, 1, 0, 0, 1.01); + RenderWith([=](SkCanvas* c, SkPaint&) { c->concat(tx); }, // + [=](DisplayListBuilder& b) { + b.transform3x3(tx[0], tx[1], tx[2], // + tx[3], tx[4], tx[5], // + tx[6], tx[7], tx[8]); + }, // + cv_renderer, dl_renderer, "Transform 3x3"); + } } - static void renderWithClips(CvRenderer& cv_renderer, + static void RenderWithClips(CvRenderer& cv_renderer, DlRenderer& dl_renderer) { - renderWith( + SkRect r_clip = RenderBounds.makeInset(15.5, 15.5); + RenderWith( + [=](SkCanvas* c, SkPaint&) { + c->clipRect(r_clip, SkClipOp::kIntersect, false); + }, + [=](DisplayListBuilder& b) { + b.clipRect(r_clip, false, SkClipOp::kIntersect); + }, + cv_renderer, dl_renderer, "Hard ClipRect inset by 15.5"); + RenderWith( + [=](SkCanvas* c, SkPaint&) { + c->clipRect(r_clip, SkClipOp::kIntersect, true); + }, + [=](DisplayListBuilder& b) { + b.clipRect(r_clip, true, SkClipOp::kIntersect); + }, + cv_renderer, dl_renderer, "AA ClipRect inset by 15.5"); + RenderWith( [=](SkCanvas* c, SkPaint&) { - c->clipRect(RenderBounds.makeInset(25.5, 25.5), // - SkClipOp::kIntersect, false); + c->clipRect(r_clip, SkClipOp::kDifference, false); }, [=](DisplayListBuilder& b) { - b.clipRect(RenderBounds.makeInset(25.5, 25.5), // - false, SkClipOp::kIntersect); + b.clipRect(r_clip, false, SkClipOp::kDifference); }, - cv_renderer, dl_renderer, "Hard ClipRect inset by 25.5"); - renderWith( + cv_renderer, dl_renderer, "Hard ClipRect Diff, inset by 15.5"); + SkRRect rr_clip = SkRRect::MakeRectXY(r_clip, 1.8, 2.7); + RenderWith( [=](SkCanvas* c, SkPaint&) { - c->clipRect(RenderBounds.makeInset(25.5, 25.5), // - SkClipOp::kIntersect, true); + c->clipRRect(rr_clip, SkClipOp::kIntersect, false); }, [=](DisplayListBuilder& b) { - b.clipRect(RenderBounds.makeInset(25.5, 25.5), // - true, SkClipOp::kIntersect); + b.clipRRect(rr_clip, false, SkClipOp::kIntersect); }, - cv_renderer, dl_renderer, "AA ClipRect inset by 25.5"); - renderWith( + cv_renderer, dl_renderer, "Hard ClipRRect inset by 15.5"); + RenderWith( [=](SkCanvas* c, SkPaint&) { - c->clipRect(RenderBounds.makeInset(25.5, 25.5), // - SkClipOp::kDifference, false); + c->clipRRect(rr_clip, SkClipOp::kIntersect, true); }, [=](DisplayListBuilder& b) { - b.clipRect(RenderBounds.makeInset(25.5, 25.5), // - false, SkClipOp::kDifference); + b.clipRRect(rr_clip, true, SkClipOp::kIntersect); }, - cv_renderer, dl_renderer, "Hard ClipRect Diff, inset by 25.5"); + cv_renderer, dl_renderer, "AA ClipRRect inset by 15.5"); + RenderWith( + [=](SkCanvas* c, SkPaint&) { + c->clipRRect(rr_clip, SkClipOp::kDifference, false); + }, + [=](DisplayListBuilder& b) { + b.clipRRect(rr_clip, false, SkClipOp::kDifference); + }, + cv_renderer, dl_renderer, "Hard ClipRRect Diff, inset by 15.5"); + SkPath path_clip = SkPath(); + path_clip.setFillType(SkPathFillType::kEvenOdd); + path_clip.addRect(r_clip); + path_clip.addCircle(RenderCenterX, RenderCenterY, 1.0); + RenderWith( + [=](SkCanvas* c, SkPaint&) { + c->clipPath(path_clip, SkClipOp::kIntersect, false); + }, + [=](DisplayListBuilder& b) { + b.clipPath(path_clip, false, SkClipOp::kIntersect); + }, + cv_renderer, dl_renderer, "Hard ClipPath inset by 15.5"); + RenderWith( + [=](SkCanvas* c, SkPaint&) { + c->clipPath(path_clip, SkClipOp::kIntersect, true); + }, + [=](DisplayListBuilder& b) { + b.clipPath(path_clip, true, SkClipOp::kIntersect); + }, + cv_renderer, dl_renderer, "AA ClipPath inset by 15.5"); + RenderWith( + [=](SkCanvas* c, SkPaint&) { + c->clipPath(path_clip, SkClipOp::kDifference, false); + }, + [=](DisplayListBuilder& b) { + b.clipPath(path_clip, false, SkClipOp::kDifference); + }, + cv_renderer, dl_renderer, "Hard ClipPath Diff, inset by 15.5"); } static SkRect getSkBounds(CvRenderer& cv_setup, CvRenderer& cv_render) { @@ -374,7 +517,7 @@ class CanvasCompareTester { return recorder.finishRecordingAsPicture()->cullRect(); } - static void renderWith(CvRenderer& cv_setup, + static void RenderWith(CvRenderer& cv_setup, DlRenderer& dl_setup, CvRenderer& cv_render, DlRenderer& dl_render, @@ -428,7 +571,9 @@ class CanvasCompareTester { &dl_bounds, bg); } - { + // This test cannot work if the rendering is using shadows until + // we can access the Skia ShadowRec via public headers. + if (!UsingShadows) { // This sequence renders SkCanvas calls to a DisplayList and then // plays them back on SkCanvas to SkSurface // SkCanvas calls => DisplayList => rendering @@ -548,13 +693,21 @@ class CanvasCompareTester { } return surface->makeImageSnapshot(); } + + static sk_sp MakeTextBlob(std::string string, + SkScalar height = RenderHeight) { + SkFont font(SkTypeface::MakeDefault(), height); + return SkTextBlob::MakeFromText(string.c_str(), string.size(), font, + SkTextEncoding::kUTF8); + } }; +bool CanvasCompareTester::UsingShadows = false; const sk_sp CanvasCompareTester::testImage = CanvasCompareTester::makeTestImage(); TEST(DisplayListCanvas, DrawPaint) { - CanvasCompareTester::renderAll( + CanvasCompareTester::RenderAll( [=](SkCanvas* canvas, SkPaint& paint) { // canvas->drawPaint(paint); }, @@ -564,24 +717,7 @@ TEST(DisplayListCanvas, DrawPaint) { } TEST(DisplayListCanvas, DrawColor) { - CanvasCompareTester::renderWith( // - [=](SkCanvas*, SkPaint& p) {}, // - [=](DisplayListBuilder& b) {}, // - [=](SkCanvas* canvas, SkPaint& paint) { // - canvas->drawColor(SK_ColorMAGENTA); - }, - [=](DisplayListBuilder& builder) { // - builder.drawColor(SK_ColorMAGENTA, SkBlendMode::kSrcOver); - }, - "No SkPaint"); - CanvasCompareTester::renderWithTransforms( // - [=](SkCanvas* canvas, SkPaint& paint) { // - canvas->drawColor(SK_ColorMAGENTA); - }, - [=](DisplayListBuilder& builder) { // - builder.drawColor(SK_ColorMAGENTA, SkBlendMode::kSrcOver); - }); - CanvasCompareTester::renderWithClips( // + CanvasCompareTester::RenderNoAttributes( // [=](SkCanvas* canvas, SkPaint& paint) { // canvas->drawColor(SK_ColorMAGENTA); }, @@ -591,11 +727,11 @@ TEST(DisplayListCanvas, DrawColor) { } TEST(DisplayListCanvas, DrawLine) { - SkRect rect = RenderBounds.makeInset(20, 20); + SkRect rect = RenderBounds; SkPoint p1 = SkPoint::Make(rect.fLeft, rect.fTop); SkPoint p2 = SkPoint::Make(rect.fRight, rect.fBottom); - CanvasCompareTester::renderAll( + CanvasCompareTester::RenderAll( [=](SkCanvas* canvas, SkPaint& paint) { // canvas->drawLine(p1, p2, paint); }, @@ -605,7 +741,7 @@ TEST(DisplayListCanvas, DrawLine) { } TEST(DisplayListCanvas, DrawRect) { - CanvasCompareTester::renderAll( + CanvasCompareTester::RenderAll( [=](SkCanvas* canvas, SkPaint& paint) { // canvas->drawRect(RenderBounds, paint); }, @@ -617,7 +753,7 @@ TEST(DisplayListCanvas, DrawRect) { TEST(DisplayListCanvas, DrawOval) { SkRect rect = RenderBounds.makeInset(0, 10); - CanvasCompareTester::renderAll( + CanvasCompareTester::RenderAll( [=](SkCanvas* canvas, SkPaint& paint) { // canvas->drawOval(rect, paint); }, @@ -627,7 +763,7 @@ TEST(DisplayListCanvas, DrawOval) { } TEST(DisplayListCanvas, DrawCircle) { - CanvasCompareTester::renderAll( + CanvasCompareTester::RenderAll( [=](SkCanvas* canvas, SkPaint& paint) { // canvas->drawCircle(TestCenter, RenderRadius, paint); }, @@ -639,7 +775,7 @@ TEST(DisplayListCanvas, DrawCircle) { TEST(DisplayListCanvas, DrawRRect) { SkRRect rrect = SkRRect::MakeRectXY(RenderBounds, RenderCornerRadius, RenderCornerRadius); - CanvasCompareTester::renderAll( + CanvasCompareTester::RenderAll( [=](SkCanvas* canvas, SkPaint& paint) { // canvas->drawRRect(rrect, paint); }, @@ -654,7 +790,7 @@ TEST(DisplayListCanvas, DrawDRRect) { SkRect innerBounds = RenderBounds.makeInset(30.0, 30.0); SkRRect inner = SkRRect::MakeRectXY(innerBounds, RenderCornerRadius, RenderCornerRadius); - CanvasCompareTester::renderAll( + CanvasCompareTester::RenderAll( [=](SkCanvas* canvas, SkPaint& paint) { // canvas->drawDRRect(outer, inner, paint); }, @@ -670,7 +806,7 @@ TEST(DisplayListCanvas, DrawPath) { path.lineTo(RenderLeft, RenderCenterY); path.lineTo(RenderRight, RenderCenterY); path.lineTo(RenderLeft, RenderBottom); - CanvasCompareTester::renderAll( + CanvasCompareTester::RenderAll( [=](SkCanvas* canvas, SkPaint& paint) { // canvas->drawPath(path, paint); }, @@ -680,7 +816,7 @@ TEST(DisplayListCanvas, DrawPath) { } TEST(DisplayListCanvas, DrawArc) { - CanvasCompareTester::renderAll( + CanvasCompareTester::RenderAll( [=](SkCanvas* canvas, SkPaint& paint) { // canvas->drawArc(RenderBounds, 30, 270, false, paint); }, @@ -690,7 +826,7 @@ TEST(DisplayListCanvas, DrawArc) { } TEST(DisplayListCanvas, DrawArcCenter) { - CanvasCompareTester::renderAll( + CanvasCompareTester::RenderAll( [=](SkCanvas* canvas, SkPaint& paint) { // canvas->drawArc(RenderBounds, 30, 270, true, paint); }, @@ -700,35 +836,74 @@ TEST(DisplayListCanvas, DrawArcCenter) { } TEST(DisplayListCanvas, DrawPointsAsPoints) { + const SkScalar x0 = RenderLeft; + const SkScalar x1 = (RenderLeft + RenderCenterX) * 0.5; + const SkScalar x2 = RenderCenterX; + const SkScalar x3 = (RenderRight + RenderCenterX) * 0.5; + const SkScalar x4 = RenderRight; + + const SkScalar y0 = RenderTop; + const SkScalar y1 = (RenderTop + RenderCenterY) * 0.5; + const SkScalar y2 = RenderCenterY; + const SkScalar y3 = (RenderBottom + RenderCenterY) * 0.5; + const SkScalar y4 = RenderBottom; + + // clang-format off const SkPoint points[] = { - SkPoint::Make(RenderLeft, RenderTop), - SkPoint::Make(RenderRight, RenderBottom), - SkPoint::Make(RenderRight, RenderTop), - SkPoint::Make(RenderLeft, RenderBottom), - SkPoint::Make(RenderCenterX, RenderCenterY), + {x0, y0}, {x1, y0}, {x2, y0}, {x3, y0}, {x4, y0}, + {x0, y1}, {x1, y1}, {x2, y1}, {x3, y1}, {x4, y1}, + {x0, y2}, {x1, y2}, {x2, y2}, {x3, y2}, {x4, y2}, + {x0, y3}, {x1, y3}, {x2, y3}, {x3, y3}, {x4, y3}, + {x0, y4}, {x1, y4}, {x2, y4}, {x3, y4}, {x4, y4}, }; - CanvasCompareTester::renderAll( + // clang-format on + + const int count = sizeof(points) / sizeof(points[0]); + CanvasCompareTester::RenderAll( [=](SkCanvas* canvas, SkPaint& paint) { // - canvas->drawPoints(SkCanvas::kPoints_PointMode, 5, points, paint); + canvas->drawPoints(SkCanvas::kPoints_PointMode, count, points, paint); }, [=](DisplayListBuilder& builder) { // - builder.drawPoints(SkCanvas::kPoints_PointMode, 5, points); + builder.drawPoints(SkCanvas::kPoints_PointMode, count, points); }); } TEST(DisplayListCanvas, DrawPointsAsLines) { + const SkScalar x0 = RenderLeft; + const SkScalar x1 = (RenderLeft + RenderCenterX) * 0.5; + const SkScalar x2 = RenderCenterX; + const SkScalar x3 = (RenderRight + RenderCenterX) * 0.5; + const SkScalar x4 = RenderRight; + + const SkScalar y0 = RenderTop; + const SkScalar y1 = (RenderTop + RenderCenterY) * 0.5; + const SkScalar y2 = RenderCenterY; + const SkScalar y3 = (RenderBottom + RenderCenterY) * 0.5; + const SkScalar y4 = RenderBottom; + + // clang-format off const SkPoint points[] = { - SkPoint::Make(RenderLeft, RenderTop), - SkPoint::Make(RenderRight, RenderBottom), - SkPoint::Make(RenderRight, RenderTop), - SkPoint::Make(RenderLeft, RenderBottom), + // Diagonals + {x0, y0}, {x4, y4}, {x4, y0}, {x0, y4}, + // Inner box + {x1, y1}, {x3, y1}, + {x3, y1}, {x3, y3}, + {x3, y3}, {x1, y3}, + {x1, y3}, {x1, y1}, + // Middle crosshair + {x2, y1}, {x2, y3}, + {x1, y2}, {x3, y3}, }; - CanvasCompareTester::renderAll( + // clang-format on + + const int count = sizeof(points) / sizeof(points[0]); + ASSERT_TRUE((count & 1) == 0); + CanvasCompareTester::RenderAll( [=](SkCanvas* canvas, SkPaint& paint) { // - canvas->drawPoints(SkCanvas::kLines_PointMode, 4, points, paint); + canvas->drawPoints(SkCanvas::kLines_PointMode, count, points, paint); }, [=](DisplayListBuilder& builder) { // - builder.drawPoints(SkCanvas::kLines_PointMode, 4, points); + builder.drawPoints(SkCanvas::kLines_PointMode, count, points); }); } @@ -738,8 +913,9 @@ TEST(DisplayListCanvas, DrawPointsAsPolygon) { SkPoint::Make(RenderRight, RenderBottom), SkPoint::Make(RenderRight, RenderTop), SkPoint::Make(RenderLeft, RenderBottom), + SkPoint::Make(RenderLeft, RenderTop), }; - CanvasCompareTester::renderAll( + CanvasCompareTester::RenderAll( [=](SkCanvas* canvas, SkPaint& paint) { // canvas->drawPoints(SkCanvas::kPolygon_PointMode, 4, points, paint); }, @@ -757,7 +933,7 @@ TEST(DisplayListCanvas, DrawVerticesWithColors) { const SkColor colors[3] = {SK_ColorRED, SK_ColorBLUE, SK_ColorGREEN}; const sk_sp vertices = SkVertices::MakeCopy( SkVertices::kTriangles_VertexMode, 3, pts, nullptr, colors); - CanvasCompareTester::renderAll( + CanvasCompareTester::RenderAll( [=](SkCanvas* canvas, SkPaint& paint) { // canvas->drawVertices(vertices.get(), SkBlendMode::kSrcOver, paint); }, @@ -782,7 +958,7 @@ TEST(DisplayListCanvas, DrawVerticesWithImage) { SkVertices::kTriangles_VertexMode, 3, pts, tex, nullptr); const sk_sp shader = CanvasCompareTester::testImage->makeShader( SkTileMode::kRepeat, SkTileMode::kRepeat, SkSamplingOptions()); - CanvasCompareTester::renderAll( + CanvasCompareTester::RenderAll( [=](SkCanvas* canvas, SkPaint& paint) { // paint.setShader(shader); canvas->drawVertices(vertices.get(), SkBlendMode::kSrcOver, paint); @@ -796,7 +972,7 @@ TEST(DisplayListCanvas, DrawVerticesWithImage) { } TEST(DisplayListCanvas, DrawImageNearest) { - CanvasCompareTester::renderAll( + CanvasCompareTester::RenderAll( [=](SkCanvas* canvas, SkPaint& paint) { // canvas->drawImage(CanvasCompareTester::testImage, RenderLeft, RenderTop, DisplayList::NearestSampling, &paint); @@ -809,7 +985,7 @@ TEST(DisplayListCanvas, DrawImageNearest) { } TEST(DisplayListCanvas, DrawImageLinear) { - CanvasCompareTester::renderAll( + CanvasCompareTester::RenderAll( [=](SkCanvas* canvas, SkPaint& paint) { // canvas->drawImage(CanvasCompareTester::testImage, RenderLeft, RenderTop, DisplayList::LinearSampling, &paint); @@ -824,7 +1000,7 @@ TEST(DisplayListCanvas, DrawImageLinear) { TEST(DisplayListCanvas, DrawImageRectNearest) { SkRect src = SkRect::MakeIWH(RenderWidth, RenderHeight).makeInset(5, 5); SkRect dst = RenderBounds.makeInset(15.5, 10.5); - CanvasCompareTester::renderAll( + CanvasCompareTester::RenderAll( [=](SkCanvas* canvas, SkPaint& paint) { // canvas->drawImageRect(CanvasCompareTester::testImage, src, dst, DisplayList::NearestSampling, &paint, @@ -839,7 +1015,7 @@ TEST(DisplayListCanvas, DrawImageRectNearest) { TEST(DisplayListCanvas, DrawImageRectLinear) { SkRect src = SkRect::MakeIWH(RenderWidth, RenderHeight).makeInset(5, 5); SkRect dst = RenderBounds.makeInset(15.5, 10.5); - CanvasCompareTester::renderAll( + CanvasCompareTester::RenderAll( [=](SkCanvas* canvas, SkPaint& paint) { // canvas->drawImageRect(CanvasCompareTester::testImage, src, dst, DisplayList::LinearSampling, &paint, @@ -854,7 +1030,7 @@ TEST(DisplayListCanvas, DrawImageRectLinear) { TEST(DisplayListCanvas, DrawImageNineNearest) { SkIRect src = SkIRect::MakeWH(RenderWidth, RenderHeight).makeInset(5, 5); SkRect dst = RenderBounds.makeInset(15.5, 10.5); - CanvasCompareTester::renderAll( + CanvasCompareTester::RenderAll( [=](SkCanvas* canvas, SkPaint& paint) { // canvas->drawImageNine(CanvasCompareTester::testImage.get(), src, dst, SkFilterMode::kNearest, &paint); @@ -868,7 +1044,7 @@ TEST(DisplayListCanvas, DrawImageNineNearest) { TEST(DisplayListCanvas, DrawImageNineLinear) { SkIRect src = SkIRect::MakeWH(RenderWidth, RenderHeight).makeInset(5, 5); SkRect dst = RenderBounds.makeInset(15.5, 10.5); - CanvasCompareTester::renderAll( + CanvasCompareTester::RenderAll( [=](SkCanvas* canvas, SkPaint& paint) { // canvas->drawImageNine(CanvasCompareTester::testImage.get(), src, dst, SkFilterMode::kLinear, &paint); @@ -879,5 +1055,188 @@ TEST(DisplayListCanvas, DrawImageNineLinear) { }); } +// TODO(flar): Skipping DrawLattice for now, Flutter does not use it + +TEST(DisplayListCanvas, DrawAtlasNearest) { + const SkRSXform xform[] = { + {0.5, 0, RenderLeft, RenderRight}, + {0, 0.5, RenderCenterX, RenderCenterY}, + }; + const SkRect tex[] = { + {0, 0, RenderWidth * 0.5, RenderHeight * 0.5}, + {RenderWidth * 0.5, RenderHeight * 0.5, RenderWidth, RenderHeight}, + }; + const SkColor colors[] = { + SK_ColorBLUE, + SK_ColorGREEN, + }; + const sk_sp image = CanvasCompareTester::testImage; + CanvasCompareTester::RenderAll( + [=](SkCanvas* canvas, SkPaint& paint) { + canvas->drawAtlas(image.get(), xform, tex, colors, 2, + SkBlendMode::kSrcOver, DisplayList::NearestSampling, + nullptr, &paint); + }, + [=](DisplayListBuilder& builder) { + builder.drawAtlas(image, xform, tex, colors, 2, // + SkBlendMode::kSrcOver, DisplayList::NearestSampling, + nullptr); + }); +} + +TEST(DisplayListCanvas, DrawAtlasLinear) { + const SkRSXform xform[] = { + {0.5, 0, RenderLeft, RenderRight}, + {0, 0.5, RenderCenterX, RenderCenterY}, + }; + const SkRect tex[] = { + {0, 0, RenderWidth * 0.5, RenderHeight * 0.5}, + {RenderWidth * 0.5, RenderHeight * 0.5, RenderWidth, RenderHeight}, + }; + const SkColor colors[] = { + SK_ColorBLUE, + SK_ColorGREEN, + }; + const sk_sp image = CanvasCompareTester::testImage; + CanvasCompareTester::RenderAll( + [=](SkCanvas* canvas, SkPaint& paint) { + canvas->drawAtlas(image.get(), xform, tex, colors, 2, // + SkBlendMode::kSrcOver, DisplayList::LinearSampling, + nullptr, &paint); + }, + [=](DisplayListBuilder& builder) { + builder.drawAtlas(image, xform, tex, colors, 2, // + SkBlendMode::kSrcOver, DisplayList::LinearSampling, + nullptr); + }); +} + +TEST(DisplayListCanvas, DrawPicture) { + SkPictureRecorder recorder; + SkCanvas* cv = recorder.beginRecording(RenderBounds); + SkPaint p; + p.setStyle(SkPaint::kFill_Style); + p.setColor(SK_ColorBLUE); + cv->drawOval(RenderBounds, p); + sk_sp picture = recorder.finishRecordingAsPicture(); + CanvasCompareTester::RenderNoAttributes( + [=](SkCanvas* canvas, SkPaint& paint) { // + canvas->drawPicture(picture, nullptr, nullptr); + }, + [=](DisplayListBuilder& builder) { // + builder.drawPicture(picture, nullptr, false); + }); +} + +TEST(DisplayListCanvas, DrawPictureWithMatrix) { + SkPictureRecorder recorder; + SkCanvas* cv = recorder.beginRecording(RenderBounds); + SkPaint p; + p.setStyle(SkPaint::kFill_Style); + p.setColor(SK_ColorBLUE); + cv->drawOval(RenderBounds, p); + sk_sp picture = recorder.finishRecordingAsPicture(); + SkMatrix matrix = SkMatrix::Scale(0.95, 0.95); + CanvasCompareTester::RenderNoAttributes( + [=](SkCanvas* canvas, SkPaint& paint) { // + canvas->drawPicture(picture, &matrix, nullptr); + }, + [=](DisplayListBuilder& builder) { // + builder.drawPicture(picture, &matrix, false); + }); +} + +TEST(DisplayListCanvas, DrawPictureWithPaint) { + SkPictureRecorder recorder; + SkCanvas* cv = recorder.beginRecording(RenderBounds); + SkPaint p; + p.setStyle(SkPaint::kFill_Style); + p.setColor(SK_ColorBLUE); + cv->drawOval(RenderBounds, p); + sk_sp picture = recorder.finishRecordingAsPicture(); + CanvasCompareTester::RenderAll( + [=](SkCanvas* canvas, SkPaint& paint) { // + canvas->drawPicture(picture, nullptr, &paint); + }, + [=](DisplayListBuilder& builder) { // + builder.drawPicture(picture, nullptr, true); + }); +} + +TEST(DisplayListCanvas, DrawDisplayList) { + DisplayListBuilder builder; + builder.setDrawStyle(SkPaint::kFill_Style); + builder.setColor(SK_ColorBLUE); + builder.drawOval(RenderBounds); + sk_sp display_list = builder.Build(); + CanvasCompareTester::RenderNoAttributes( + [=](SkCanvas* canvas, SkPaint& paint) { // + display_list->RenderTo(canvas); + }, + [=](DisplayListBuilder& builder) { // + builder.drawDisplayList(display_list); + }); +} + +TEST(DisplayListCanvas, DrawTextBlob) { + sk_sp blob = CanvasCompareTester::MakeTextBlob("Test Blob"); + CanvasCompareTester::RenderNoAttributes( + [=](SkCanvas* canvas, SkPaint& paint) { // + canvas->drawTextBlob(blob, RenderLeft, RenderBottom, paint); + }, + [=](DisplayListBuilder& builder) { // + builder.drawTextBlob(blob, RenderLeft, RenderBottom); + }); +} + +// TODO(flar): Skipping DrawShadowRec for now, Flutter does not use it +// and Skia does not yet expose the require definitions + +TEST(DisplayListCanvas, DrawShadow) { + CanvasCompareTester::UsingShadows = true; + SkPath path; + path.moveTo(RenderCenterX, RenderTop); + path.lineTo(RenderRight, RenderBottom); + path.lineTo(RenderLeft, RenderCenterY); + path.lineTo(RenderRight, RenderCenterY); + path.lineTo(RenderLeft, RenderBottom); + path.close(); + const SkColor color = SK_ColorDKGRAY; + const SkScalar elevation = 10; + + CanvasCompareTester::RenderNoAttributes( + [=](SkCanvas* canvas, SkPaint& paint) { // + PhysicalShapeLayer::DrawShadow(canvas, path, color, elevation, false, + 1.0); + }, + [=](DisplayListBuilder& builder) { // + builder.drawShadow(path, color, elevation, false); + }); + CanvasCompareTester::UsingShadows = false; +} + +TEST(DisplayListCanvas, DrawOccludingShadow) { + CanvasCompareTester::UsingShadows = true; + SkPath path; + path.moveTo(RenderCenterX, RenderTop); + path.lineTo(RenderRight, RenderBottom); + path.lineTo(RenderLeft, RenderCenterY); + path.lineTo(RenderRight, RenderCenterY); + path.lineTo(RenderLeft, RenderBottom); + path.close(); + const SkColor color = SK_ColorDKGRAY; + const SkScalar elevation = 10; + + CanvasCompareTester::RenderNoAttributes( + [=](SkCanvas* canvas, SkPaint& paint) { // + PhysicalShapeLayer::DrawShadow(canvas, path, color, elevation, true, + 1.0); + }, + [=](DisplayListBuilder& builder) { // + builder.drawShadow(path, color, elevation, true); + }); + CanvasCompareTester::UsingShadows = false; +} + } // namespace testing } // namespace flutter diff --git a/flow/display_list_utils.cc b/flow/display_list_utils.cc index 3abe58c84a2ed..a3e55d03469dd 100644 --- a/flow/display_list_utils.cc +++ b/flow/display_list_utils.cc @@ -339,9 +339,22 @@ void DisplayListBoundsCalculator::drawShadow(const SkPath& path, const SkColor color, const SkScalar elevation, bool occludes) { - SkRect bounds = - PhysicalShapeLayer::ComputeShadowBounds(path.getBounds(), elevation, 1.0); - accumulateRect(bounds); + // Constants from physical_shape_layer.cc + const SkScalar kLightHeight = 600; + const SkScalar kLightRadius = 800; + + SkShadowFlags flags = occludes + ? SkShadowFlags::kTransparentOccluder_ShadowFlag + : SkShadowFlags::kNone_ShadowFlag; + const SkRect& bounds = path.getBounds(); + SkScalar shadow_x = (bounds.left() + bounds.right()) / 2; + SkScalar shadow_y = bounds.top() - 600.0f; + SkRect shadow_bounds; + SkShadowUtils::GetLocalBounds( + matrix(), path, SkPoint3::Make(0, 0, elevation), + SkPoint3::Make(shadow_x, shadow_y, kLightHeight), kLightRadius, flags, + &shadow_bounds); + accumulateRect(shadow_bounds); } void DisplayListBoundsCalculator::accumulateRect(const SkRect& rect, From 6608c5fac176283fb9970c46d38dbb088cd87fbd Mon Sep 17 00:00:00 2001 From: Jim Graham Date: Sat, 26 Jun 2021 21:59:48 -0700 Subject: [PATCH 19/26] Skip font rendering tests on Fuchsia --- flow/display_list_canvas_unittests.cc | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/flow/display_list_canvas_unittests.cc b/flow/display_list_canvas_unittests.cc index 5efb1a77092a9..4844742b55249 100644 --- a/flow/display_list_canvas_unittests.cc +++ b/flow/display_list_canvas_unittests.cc @@ -1179,6 +1179,12 @@ TEST(DisplayListCanvas, DrawDisplayList) { } TEST(DisplayListCanvas, DrawTextBlob) { + // TODO(https://github.com/flutter/flutter/issues/82202): Remove once the + // performance overlay can use Fuchsia's font manager instead of the empty + // default. +#if defined(OS_FUCHSIA) + GTEST_SKIP() << "Rendering comparisons require a valid default font manager"; +#endif // OS_FUCHSIA sk_sp blob = CanvasCompareTester::MakeTextBlob("Test Blob"); CanvasCompareTester::RenderNoAttributes( [=](SkCanvas* canvas, SkPaint& paint) { // From a160d270524ef2de47cf879da9cb39b2b7d97d27 Mon Sep 17 00:00:00 2001 From: Jim Graham Date: Sat, 26 Jun 2021 22:19:00 -0700 Subject: [PATCH 20/26] fix signed size_t comparisons causing vector overflow --- flow/display_list_unittests.cc | 43 +++++++++++++++++----------------- 1 file changed, 22 insertions(+), 21 deletions(-) diff --git a/flow/display_list_unittests.cc b/flow/display_list_unittests.cc index cb022931e8848..d41d78adbe140 100644 --- a/flow/display_list_unittests.cc +++ b/flow/display_list_unittests.cc @@ -673,28 +673,30 @@ TEST(DisplayList, SingleOpDisplayListsRecapturedViaSkCanvasAreEqual) { TEST(DisplayList, SingleOpDisplayListsCompareToEachOther) { for (auto& group : allGroups) { - std::vector> lists1; - std::vector> lists2; + std::vector> listsA; + std::vector> listsB; for (size_t i = 0; i < group.variants.size(); i++) { - lists1.push_back(group.variants[i].Build()); - lists2.push_back(group.variants[i].Build()); + listsA.push_back(group.variants[i].Build()); + listsB.push_back(group.variants[i].Build()); } - for (size_t i = 0; i < lists1.size(); i++) { - for (size_t j = 0; j < lists2.size(); j++) { + for (size_t i = 0; i < listsA.size(); i++) { + sk_sp listA = listsA[i]; + for (size_t j = 0; j < listsB.size(); j++) { + sk_sp listB = listsB[j]; auto desc = group.op_name + "(variant " + std::to_string(i + 1) + " ==? variant " + std::to_string(j + 1) + ")"; if (i == j) { - ASSERT_EQ(lists1[i]->op_count(), lists2[j]->op_count()) << desc; - ASSERT_EQ(lists1[i]->bytes(), lists2[j]->bytes()) << desc; - ASSERT_EQ(lists1[i]->bounds(), lists2[j]->bounds()) << desc; - ASSERT_TRUE(lists1[i]->Equals(*lists2[j])) << desc; - ASSERT_TRUE(lists2[j]->Equals(*lists1[i])) << desc; + ASSERT_EQ(listA->op_count(), listB->op_count()) << desc; + ASSERT_EQ(listA->bytes(), listB->bytes()) << desc; + ASSERT_EQ(listA->bounds(), listB->bounds()) << desc; + ASSERT_TRUE(listA->Equals(*listB)) << desc; + ASSERT_TRUE(listB->Equals(*listA)) << desc; } else { // No assertion on op/byte counts or bounds // they may or may not be equal between variants - ASSERT_FALSE(lists1[i]->Equals(*lists2[j])) << desc; - ASSERT_FALSE(lists2[j]->Equals(*lists1[i])) << desc; + ASSERT_FALSE(listA->Equals(*listB)) << desc; + ASSERT_FALSE(listB->Equals(*listA)) << desc; } } } @@ -706,10 +708,10 @@ static sk_sp Build(size_t g_index, size_t v_index) { int op_count = 0; size_t byte_count = 0; for (size_t i = 0; i < allGroups.size(); i++) { - int j = (i == g_index ? v_index : 0); - if (j < 0) - continue; DisplayListInvocationGroup& group = allGroups[i]; + size_t j = (i == g_index ? v_index : 0); + if (j >= group.variants.size()) + continue; DisplayListInvocation& invocation = group.variants[j]; op_count += invocation.op_count; byte_count += invocation.byte_count; @@ -733,16 +735,15 @@ static sk_sp Build(size_t g_index, size_t v_index) { } TEST(DisplayList, DisplayListsWithVaryingOpComparisons) { - sk_sp default_dl = Build(-1, -1); + sk_sp default_dl = Build(allGroups.size(), 0); ASSERT_TRUE(default_dl->Equals(*default_dl)) << "Default == itself"; - int group_count = static_cast(allGroups.size()); - for (int gi = 0; gi < group_count; gi++) { - sk_sp missing_dl = Build(gi, -1); + for (size_t gi = 0; gi < allGroups.size(); gi++) { + DisplayListInvocationGroup& group = allGroups[gi]; + sk_sp missing_dl = Build(gi, group.variants.size()); auto desc = "[Group " + std::to_string(gi + 1) + " omitted]"; ASSERT_TRUE(missing_dl->Equals(*missing_dl)) << desc << " == itself"; ASSERT_FALSE(missing_dl->Equals(*default_dl)) << desc << " != Default"; ASSERT_FALSE(default_dl->Equals(*missing_dl)) << "Default != " << desc; - DisplayListInvocationGroup& group = allGroups[gi]; for (size_t vi = 0; vi < group.variants.size(); vi++) { auto desc = "[Group " + std::to_string(gi + 1) + " variant " + std::to_string(vi + 1) + "]"; From b57893550d218fd377a41ccd19295bd8bac16581 Mon Sep 17 00:00:00 2001 From: Jim Graham Date: Sun, 27 Jun 2021 02:00:40 -0700 Subject: [PATCH 21/26] eliminate another unsigned size_t comparison in a unit test --- flow/display_list_unittests.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flow/display_list_unittests.cc b/flow/display_list_unittests.cc index d41d78adbe140..04a2f52b95357 100644 --- a/flow/display_list_unittests.cc +++ b/flow/display_list_unittests.cc @@ -719,7 +719,7 @@ static sk_sp Build(size_t g_index, size_t v_index) { } sk_sp dl = builder.Build(); std::string name; - if (g_index < 0) { + if (g_index >= allGroups.size()) { name = "Default"; } else { name = allGroups[g_index].op_name; From cc83cf5414980c69429aed6e9abf2828d7b80c16 Mon Sep 17 00:00:00 2001 From: Jim Graham Date: Mon, 28 Jun 2021 12:03:07 -0700 Subject: [PATCH 22/26] more review feedback --- flow/display_list.cc | 12 +++++++++--- flow/layers/display_list_layer.cc | 11 +++++++---- flow/layers/display_list_layer.h | 2 ++ flow/raster_cache.cc | 2 ++ 4 files changed, 20 insertions(+), 7 deletions(-) diff --git a/flow/display_list.cc b/flow/display_list.cc index 07115d595ff1b..848bbd1f73a4d 100644 --- a/flow/display_list.cc +++ b/flow/display_list.cc @@ -898,9 +898,9 @@ static bool CompareOps(uint8_t* ptrA, uint8_t* endA, uint8_t* ptrB, uint8_t* endB) { - if (endA - ptrA != endB - ptrB) { - return false; - } + // These conditions are checked by the caller... + FML_DCHECK((endA - ptrA) == (endB - ptrB)); + FML_DCHECK(ptrA != ptrB); uint8_t* bulkStartA = ptrA; uint8_t* bulkStartB = ptrB; while (ptrA < endA && ptrB < endB) { @@ -966,6 +966,12 @@ void DisplayList::RenderTo(SkCanvas* canvas) const { } bool DisplayList::Equals(const DisplayList& other) const { + if (used_ != other.used_ || op_count_ != other.op_count_) { + return false; + } + if (ptr_ == other.ptr_) { + return true; + } return CompareOps(ptr_, ptr_ + used_, other.ptr_, other.ptr_ + other.used_); } diff --git a/flow/layers/display_list_layer.cc b/flow/layers/display_list_layer.cc index d5f742dd4a386..bf6878c388cbe 100644 --- a/flow/layers/display_list_layer.cc +++ b/flow/layers/display_list_layer.cc @@ -55,14 +55,17 @@ bool DisplayListLayer::Compare(DiffContext::Statistics& statistics, statistics.AddSameInstancePicture(); return true; } - auto op_cnt_1 = dl1->op_count(); - auto op_cnt_2 = dl2->op_count(); - if (op_cnt_1 != op_cnt_2 || dl1->bounds() != dl2->bounds()) { + const auto op_cnt_1 = dl1->op_count(); + const auto op_cnt_2 = dl2->op_count(); + const auto op_bytes_1 = dl1->bytes(); + const auto op_bytes_2 = dl2->bytes(); + if (op_cnt_1 != op_cnt_2 || op_bytes_1 != op_bytes_2 || + dl1->bounds() != dl2->bounds()) { statistics.AddNewPicture(); return false; } - if (op_cnt_1 > 10) { + if (op_bytes_1 > kMaxBytesToCompare) { statistics.AddPictureTooComplexToCompare(); return false; } diff --git a/flow/layers/display_list_layer.h b/flow/layers/display_list_layer.h index 5376e7543c903..4760d2d200654 100644 --- a/flow/layers/display_list_layer.h +++ b/flow/layers/display_list_layer.h @@ -12,6 +12,8 @@ namespace flutter { class DisplayListLayer : public Layer { public: + static constexpr size_t kMaxBytesToCompare = 10000; + DisplayListLayer(const SkPoint& offset, sk_sp display_list, bool is_complex, diff --git a/flow/raster_cache.cc b/flow/raster_cache.cc index d45a20591d515..d1671a1c1ab13 100644 --- a/flow/raster_cache.cc +++ b/flow/raster_cache.cc @@ -56,6 +56,7 @@ static bool CanRasterizePicture(SkPicture* picture) { if (!cull_rect.isFinite()) { // Cannot attempt to rasterize into an infinitely large surface. + FML_LOG(INFO) << "Attempted to raster cache non-finite picture"; return false; } @@ -76,6 +77,7 @@ static bool CanRasterizeDisplayList(DisplayList* display_list) { if (!cull_rect.isFinite()) { // Cannot attempt to rasterize into an infinitely large surface. + FML_LOG(INFO) << "Attempted to raster cache non-finite display list"; return false; } From bd50c5c3e2a3aa3869d2aba4f0e0546e9d376676 Mon Sep 17 00:00:00 2001 From: Jim Graham Date: Tue, 29 Jun 2021 15:57:59 -0700 Subject: [PATCH 23/26] add drawImageLattice tests and more style guide renames --- flow/display_list.cc | 47 +++++++++++------- flow/display_list.h | 10 ++-- flow/display_list_canvas.cc | 28 ++++++++--- flow/display_list_canvas.h | 7 +-- flow/display_list_canvas_unittests.cc | 52 +++++++++++++++++++- flow/display_list_unittests.cc | 71 +++++++++++++++++++++++++-- flow/display_list_utils.cc | 29 ++++++----- flow/display_list_utils.h | 7 +-- 8 files changed, 199 insertions(+), 52 deletions(-) diff --git a/flow/display_list.cc b/flow/display_list.cc index 848bbd1f73a4d..6b368b80b00e4 100644 --- a/flow/display_list.cc +++ b/flow/display_list.cc @@ -55,6 +55,9 @@ enum class DisplayListCompare { #pragma pack(push, DLOp_Alignment, 8) // Assuming a 64-bit platform (most of our platforms at this time?) +// the following comments are a "worst case" assessment of how well +// these structures pack into memory. They may be packed more tightly +// on some of the 32-bit platforms that we see in older phones. // // Struct allocation in the DL memory is aligned to a void* boundary // which means that the minimum (aligned) struct size will be 8 bytes. @@ -632,6 +635,7 @@ struct DrawImageNineOp final : DLOp { } }; +// 4 byte header + 60 byte payload packs evenly into 64 bytes struct DrawImageLatticeOp final : DLOp { static const auto kType = DisplayListOpType::kDrawImageLattice; @@ -641,21 +645,24 @@ struct DrawImageLatticeOp final : DLOp { int cell_count, const SkIRect& src, const SkRect& dst, - SkFilterMode filter) - : x_count(x_count), + SkFilterMode filter, + bool with_paint) + : with_paint(with_paint), + x_count(x_count), y_count(y_count), cell_count(cell_count), + filter(filter), src(src), dst(dst), - filter(filter), image(std::move(image)) {} + const bool with_paint; const int x_count; const int y_count; const int cell_count; + const SkFilterMode filter; const SkIRect src; const SkRect dst; - const SkFilterMode filter; const sk_sp image; void dispatch(Dispatcher& dispatcher) const { @@ -671,7 +678,7 @@ struct DrawImageLatticeOp final : DLOp { cell_count); dispatcher.drawImageLattice( image, {xDivs, yDivs, types, x_count, y_count, &src, colors}, dst, - filter); + filter, with_paint); } }; @@ -739,8 +746,7 @@ DEFINE_DRAW_ATLAS_OP(AtlasColoredCulled, HAS_COLORS, HAS_CULLING) #undef DRAW_ATLAS_HAS_CULLING_FIELDS #undef DRAW_ATLAS_HAS_CULLING_P_ARG -// 4 byte header + ptr aligned payload uses 12 bytes rounde up to 16 -// (4 bytes unused) +// 4 byte header + 12 byte payload packs evenly into 16 bytes struct DrawSkPictureOp final : DLOp { static const auto kType = DisplayListOpType::kDrawSkPicture; @@ -755,6 +761,7 @@ struct DrawSkPictureOp final : DLOp { } }; +// 4 byte header + 52 byte payload packs evenly into 56 bytes struct DrawSkPictureMatrixOp final : DLOp { static const auto kType = DisplayListOpType::kDrawSkPictureMatrix; @@ -1130,11 +1137,11 @@ void DisplayListBuilder::restore() { save_level_--; } } -void DisplayListBuilder::saveLayer(const SkRect* bounds, bool withPaint) { +void DisplayListBuilder::saveLayer(const SkRect* bounds, bool with_paint) { save_level_++; bounds // - ? Push(0, *bounds, withPaint) - : Push(0, withPaint); + ? Push(0, *bounds, with_paint) + : Push(0, with_paint); } void DisplayListBuilder::translate(SkScalar tx, SkScalar ty) { @@ -1303,17 +1310,21 @@ void DisplayListBuilder::drawImageNine(const sk_sp image, void DisplayListBuilder::drawImageLattice(const sk_sp image, const SkCanvas::Lattice& lattice, const SkRect& dst, - SkFilterMode filter) { + SkFilterMode filter, + bool with_paint) { int xDivCount = lattice.fXCount; int yDivCount = lattice.fYCount; - int cellCount = lattice.fRectTypes ? (xDivCount + 1) * (yDivCount + 1) : 0; + FML_DCHECK((lattice.fRectTypes == nullptr) || (lattice.fColors != nullptr)); + int cellCount = lattice.fRectTypes && lattice.fColors + ? (xDivCount + 1) * (yDivCount + 1) + : 0; size_t bytes = (xDivCount + yDivCount) * sizeof(int) + cellCount * (sizeof(SkColor) + sizeof(SkCanvas::Lattice::RectType)); - FML_DCHECK(lattice.fBounds); + SkIRect src = lattice.fBounds ? *lattice.fBounds : image->bounds(); void* pod = this->Push(bytes, std::move(image), xDivCount, - yDivCount, cellCount, - *lattice.fBounds, dst, filter); + yDivCount, cellCount, src, dst, + filter, with_paint); CopyV(pod, lattice.fXDivs, xDivCount, lattice.fYDivs, yDivCount, lattice.fColors, cellCount, lattice.fRectTypes, cellCount); } @@ -1351,10 +1362,10 @@ void DisplayListBuilder::drawAtlas(const sk_sp atlas, void DisplayListBuilder::drawPicture(const sk_sp picture, const SkMatrix* matrix, - bool withLayer) { + bool with_layer) { matrix // - ? Push(0, std::move(picture), *matrix, withLayer) - : Push(0, std::move(picture), withLayer); + ? Push(0, std::move(picture), *matrix, with_layer) + : Push(0, std::move(picture), with_layer); } void DisplayListBuilder::drawDisplayList( const sk_sp display_list) { diff --git a/flow/display_list.h b/flow/display_list.h index eddb5ce63b6fa..8c12db1501f92 100644 --- a/flow/display_list.h +++ b/flow/display_list.h @@ -298,7 +298,8 @@ class Dispatcher { virtual void drawImageLattice(const sk_sp image, const SkCanvas::Lattice& lattice, const SkRect& dst, - SkFilterMode filter) = 0; + SkFilterMode filter, + bool with_paint) = 0; virtual void drawAtlas(const sk_sp atlas, const SkRSXform xform[], const SkRect tex[], @@ -309,7 +310,7 @@ class Dispatcher { const SkRect* cullRect) = 0; virtual void drawPicture(const sk_sp picture, const SkMatrix* matrix, - bool withPaint) = 0; + bool with_save_layer) = 0; virtual void drawDisplayList(const sk_sp display_list) = 0; virtual void drawTextBlob(const sk_sp blob, SkScalar x, @@ -412,7 +413,8 @@ class DisplayListBuilder final : public virtual Dispatcher, public SkRefCnt { void drawImageLattice(const sk_sp image, const SkCanvas::Lattice& lattice, const SkRect& dst, - SkFilterMode filter) override; + SkFilterMode filter, + bool with_paint) override; void drawAtlas(const sk_sp atlas, const SkRSXform xform[], const SkRect tex[], @@ -423,7 +425,7 @@ class DisplayListBuilder final : public virtual Dispatcher, public SkRefCnt { const SkRect* cullRect) override; void drawPicture(const sk_sp picture, const SkMatrix* matrix, - bool withSaveLayer) override; + bool with_save_layer) override; void drawDisplayList(const sk_sp display_list) override; void drawTextBlob(const sk_sp blob, SkScalar x, diff --git a/flow/display_list_canvas.cc b/flow/display_list_canvas.cc index 0b59da6cd1c1b..288a58a52db3c 100644 --- a/flow/display_list_canvas.cc +++ b/flow/display_list_canvas.cc @@ -22,8 +22,8 @@ void DisplayListCanvasDispatcher::restore() { canvas_->restore(); } void DisplayListCanvasDispatcher::saveLayer(const SkRect* bounds, - bool restoreWithPaint) { - canvas_->saveLayer(bounds, restoreWithPaint ? &paint() : nullptr); + bool restore_with_paint) { + canvas_->saveLayer(bounds, restore_with_paint ? &paint() : nullptr); } void DisplayListCanvasDispatcher::translate(SkScalar tx, SkScalar ty) { @@ -142,8 +142,10 @@ void DisplayListCanvasDispatcher::drawImageLattice( const sk_sp image, const SkCanvas::Lattice& lattice, const SkRect& dst, - SkFilterMode filter) { - canvas_->drawImageLattice(image.get(), lattice, dst, filter, &paint()); + SkFilterMode filter, + bool with_paint) { + canvas_->drawImageLattice(image.get(), lattice, dst, filter, + with_paint ? &paint() : nullptr); } void DisplayListCanvasDispatcher::drawAtlas(const sk_sp atlas, const SkRSXform xform[], @@ -158,8 +160,8 @@ void DisplayListCanvasDispatcher::drawAtlas(const sk_sp atlas, } void DisplayListCanvasDispatcher::drawPicture(const sk_sp picture, const SkMatrix* matrix, - bool withSaveLayer) { - canvas_->drawPicture(picture, matrix, withSaveLayer ? &paint() : nullptr); + bool with_save_layer) { + canvas_->drawPicture(picture, matrix, with_save_layer ? &paint() : nullptr); } void DisplayListCanvasDispatcher::drawDisplayList( const sk_sp display_list) { @@ -334,8 +336,18 @@ void DisplayListCanvasRecorder::onDrawImageLattice2(const SkImage* image, const SkRect& dst, SkFilterMode filter, const SkPaint* paint) { - RecordPaintAttributes(paint, DrawType::kImageOpType); - builder_->drawImageLattice(sk_ref_sp(image), lattice, dst, filter); + if (paint != nullptr) { + // SkCanvas will always construct a paint, + // though it is a default paint most of the time + SkPaint default_paint; + if (*paint == default_paint) { + paint = nullptr; + } else { + RecordPaintAttributes(paint, DrawType::kImageOpType); + } + } + builder_->drawImageLattice(sk_ref_sp(image), lattice, dst, filter, + paint != nullptr); } void DisplayListCanvasRecorder::onDrawAtlas2(const SkImage* image, const SkRSXform xform[], diff --git a/flow/display_list_canvas.h b/flow/display_list_canvas.h index 3b93a0609b61e..b3c84619a7462 100644 --- a/flow/display_list_canvas.h +++ b/flow/display_list_canvas.h @@ -31,7 +31,7 @@ class DisplayListCanvasDispatcher : public virtual Dispatcher, void save() override; void restore() override; - void saveLayer(const SkRect* bounds, bool restoreWithBounds) override; + void saveLayer(const SkRect* bounds, bool restore_with_paint) override; void translate(SkScalar tx, SkScalar ty) override; void scale(SkScalar sx, SkScalar sy) override; @@ -90,7 +90,8 @@ class DisplayListCanvasDispatcher : public virtual Dispatcher, void drawImageLattice(const sk_sp image, const SkCanvas::Lattice& lattice, const SkRect& dst, - SkFilterMode filter) override; + SkFilterMode filter, + bool with_paint) override; void drawAtlas(const sk_sp atlas, const SkRSXform xform[], const SkRect tex[], @@ -101,7 +102,7 @@ class DisplayListCanvasDispatcher : public virtual Dispatcher, const SkRect* cullRect) override; void drawPicture(const sk_sp picture, const SkMatrix* matrix, - bool withSaveLayer) override; + bool with_save_layer) override; void drawDisplayList(const sk_sp display_list) override; void drawTextBlob(const sk_sp blob, SkScalar x, diff --git a/flow/display_list_canvas_unittests.cc b/flow/display_list_canvas_unittests.cc index 4844742b55249..9d8d4bfcad3d9 100644 --- a/flow/display_list_canvas_unittests.cc +++ b/flow/display_list_canvas_unittests.cc @@ -1055,7 +1055,57 @@ TEST(DisplayListCanvas, DrawImageNineLinear) { }); } -// TODO(flar): Skipping DrawLattice for now, Flutter does not use it +TEST(DisplayListCanvas, DrawImageLatticeNearest) { + const SkRect dst = RenderBounds.makeInset(15.5, 10.5); + const int divX[] = { + (RenderLeft + RenderCenterX) / 2, + RenderCenterX, + (RenderRight + RenderCenterX) / 2, + }; + const int divY[] = { + (RenderTop + RenderCenterY) / 2, + RenderCenterY, + (RenderBottom + RenderCenterY) / 2, + }; + SkCanvas::Lattice lattice = { + divX, divY, nullptr, 3, 3, nullptr, nullptr, + }; + CanvasCompareTester::RenderAll( + [=](SkCanvas* canvas, SkPaint& paint) { // + canvas->drawImageLattice(CanvasCompareTester::testImage.get(), lattice, + dst, SkFilterMode::kNearest, &paint); + }, + [=](DisplayListBuilder& builder) { // + builder.drawImageLattice(CanvasCompareTester::testImage, lattice, // + dst, SkFilterMode::kNearest, true); + }); +} + +TEST(DisplayListCanvas, DrawImageLatticeLinear) { + const SkRect dst = RenderBounds.makeInset(15.5, 10.5); + const int divX[] = { + (RenderLeft + RenderCenterX) / 2, + RenderCenterX, + (RenderRight + RenderCenterX) / 2, + }; + const int divY[] = { + (RenderTop + RenderCenterY) / 2, + RenderCenterY, + (RenderBottom + RenderCenterY) / 2, + }; + SkCanvas::Lattice lattice = { + divX, divY, nullptr, 3, 3, nullptr, nullptr, + }; + CanvasCompareTester::RenderAll( + [=](SkCanvas* canvas, SkPaint& paint) { // + canvas->drawImageLattice(CanvasCompareTester::testImage.get(), lattice, + dst, SkFilterMode::kLinear, &paint); + }, + [=](DisplayListBuilder& builder) { // + builder.drawImageLattice(CanvasCompareTester::testImage, lattice, // + dst, SkFilterMode::kLinear, true); + }); +} TEST(DisplayListCanvas, DrawAtlasNearest) { const SkRSXform xform[] = { diff --git a/flow/display_list_unittests.cc b/flow/display_list_unittests.cc index 04a2f52b95357..3eeb0dc107c02 100644 --- a/flow/display_list_unittests.cc +++ b/flow/display_list_unittests.cc @@ -121,7 +121,7 @@ static sk_sp MakeTestImage(int w, int h, int checker_size) { return surface->makeImageSnapshot(); } static sk_sp TestImage1 = MakeTestImage(40, 40, 5); -static sk_sp TestImage2 = MakeTestImage(20, 20, 5); +static sk_sp TestImage2 = MakeTestImage(50, 50, 5); static sk_sp TestVertices1 = SkVertices::MakeCopy(SkVertices::kTriangles_VertexMode, @@ -136,6 +136,27 @@ static sk_sp TestVertices2 = nullptr, colors); +static constexpr int TestDivs1[] = {10, 20, 30}; +static constexpr int TestDivs2[] = {15, 20, 25}; +static constexpr int TestDivs3[] = {15, 25}; +static constexpr SkCanvas::Lattice::RectType TestRTypes[] = { + SkCanvas::Lattice::RectType::kDefault, + SkCanvas::Lattice::RectType::kTransparent, + SkCanvas::Lattice::RectType::kFixedColor, + SkCanvas::Lattice::RectType::kDefault, + SkCanvas::Lattice::RectType::kTransparent, + SkCanvas::Lattice::RectType::kFixedColor, + SkCanvas::Lattice::RectType::kDefault, + SkCanvas::Lattice::RectType::kTransparent, + SkCanvas::Lattice::RectType::kFixedColor, +}; +static constexpr SkColor TestLatticeColors[] = { + SK_ColorBLUE, SK_ColorGREEN, SK_ColorYELLOW, + SK_ColorBLUE, SK_ColorGREEN, SK_ColorYELLOW, + SK_ColorBLUE, SK_ColorGREEN, SK_ColorYELLOW, +}; +static constexpr SkIRect TestLatticeSrcRect = {1, 1, 39, 39}; + static sk_sp MakeTestPicture(int w, int h, SkColor color) { SkPictureRecorder recorder; SkCanvas* cv = recorder.beginRecording(TestBounds); @@ -487,7 +508,7 @@ std::vector allGroups = { } }, { "DrawImageNine", { - // cv.drawImageNine => drawImageLattice + // SkVanvas::drawImageNine is immediately converted to drawImageLattice {1, 48, 1, 80, [](DisplayListBuilder& b) {b.drawImageNine(TestImage1, {10, 10, 20, 20}, {10, 10, 80, 80}, SkFilterMode::kNearest);}}, {1, 48, 1, 80, [](DisplayListBuilder& b) {b.drawImageNine(TestImage1, {10, 10, 25, 20}, {10, 10, 80, 80}, @@ -500,7 +521,49 @@ std::vector allGroups = { SkFilterMode::kNearest);}}, } }, - // TODO(flar): Skipping DrawLattice for now, Flutter does not use it + { "DrawImageLattice", { + // Lattice: + // const int* fXDivs; //!< x-axis values dividing bitmap + // const int* fYDivs; //!< y-axis values dividing bitmap + // const RectType* fRectTypes; //!< array of fill types + // int fXCount; //!< number of x-coordinates + // int fYCount; //!< number of y-coordinates + // const SkIRect* fBounds; //!< source bounds to draw from + // const SkColor* fColors; //!< array of colors + // size = 64 + fXCount * 4 + fYCount * 4 + // if fColors and fRectTypes are not null, add (fXCount + 1) * (fYCount + 1) * 5 + {1, 88, 1, 88, [](DisplayListBuilder& b) {b.drawImageLattice(TestImage1, + {TestDivs1, TestDivs1, nullptr, 3, 3, nullptr, nullptr}, + {10, 10, 40, 40}, SkFilterMode::kNearest, false);}}, + {1, 88, 1, 88, [](DisplayListBuilder& b) {b.drawImageLattice(TestImage1, + {TestDivs1, TestDivs1, nullptr, 3, 3, nullptr, nullptr}, + {10, 10, 40, 45}, SkFilterMode::kNearest, false);}}, + {1, 88, 1, 88, [](DisplayListBuilder& b) {b.drawImageLattice(TestImage1, + {TestDivs2, TestDivs1, nullptr, 3, 3, nullptr, nullptr}, + {10, 10, 40, 40}, SkFilterMode::kNearest, false);}}, + // One less yDiv does not change the allocation due to 8-byte alignment + {1, 88, 1, 88, [](DisplayListBuilder& b) {b.drawImageLattice(TestImage1, + {TestDivs1, TestDivs1, nullptr, 3, 2, nullptr, nullptr}, + {10, 10, 40, 40}, SkFilterMode::kNearest, false);}}, + {1, 88, 1, 88, [](DisplayListBuilder& b) {b.drawImageLattice(TestImage1, + {TestDivs1, TestDivs1, nullptr, 3, 3, nullptr, nullptr}, + {10, 10, 40, 40}, SkFilterMode::kLinear, false);}}, + {2, 96, 2, 96, [](DisplayListBuilder& b) {b.setColor(SK_ColorMAGENTA); + b.drawImageLattice(TestImage1, + {TestDivs1, TestDivs1, nullptr, 3, 3, nullptr, nullptr}, + {10, 10, 40, 40}, SkFilterMode::kNearest, true);}}, + {1, 88, 1, 88, [](DisplayListBuilder& b) {b.drawImageLattice(TestImage2, + {TestDivs1, TestDivs1, nullptr, 3, 3, nullptr, nullptr}, + {10, 10, 40, 40}, SkFilterMode::kNearest, false);}}, + // Supplying fBounds does not change size because the Op record always includes it + {1, 88, 1, 88, [](DisplayListBuilder& b) {b.drawImageLattice(TestImage1, + {TestDivs1, TestDivs1, nullptr, 3, 3, &TestLatticeSrcRect, nullptr}, + {10, 10, 40, 40}, SkFilterMode::kNearest, false);}}, + {1, 128, 1, 128, [](DisplayListBuilder& b) {b.drawImageLattice(TestImage1, + {TestDivs3, TestDivs3, TestRTypes, 2, 2, nullptr, TestLatticeColors}, + {10, 10, 40, 40}, SkFilterMode::kNearest, false);}}, + } + }, { "DrawAtlas", { {1, 40 + 32 + 32, 1, 40 + 32 + 32, [](DisplayListBuilder& b) { static SkRSXform xforms[] = { {1, 0, 0, 0}, {0, 1, 0, 0} }; @@ -726,7 +789,7 @@ static sk_sp Build(size_t g_index, size_t v_index) { if (v_index < 0) { name += " skipped"; } else { - name += " variant " + std::to_string(v_index); + name += " variant " + std::to_string(v_index + 1); } } EXPECT_EQ(dl->op_count(), op_count) << name; diff --git a/flow/display_list_utils.cc b/flow/display_list_utils.cc index a3e55d03469dd..b4837a13f218c 100644 --- a/flow/display_list_utils.cc +++ b/flow/display_list_utils.cc @@ -173,12 +173,12 @@ void ClipBoundsDispatchHelper::restore() { } void DisplayListBoundsCalculator::saveLayer(const SkRect* bounds, - bool withPaint) { + bool with_paint) { SkMatrixDispatchHelper::save(); ClipBoundsDispatchHelper::save(); SaveInfo info = - withPaint ? SaveLayerWithPaintInfo(this, accumulator_, matrix(), paint()) - : SaveLayerInfo(accumulator_, matrix()); + with_paint ? SaveLayerWithPaintInfo(this, accumulator_, matrix(), paint()) + : SaveLayerInfo(accumulator_, matrix()); saved_infos_.push_back(info); accumulator_ = info.save(); SkMatrixDispatchHelper::reset(); @@ -285,7 +285,8 @@ void DisplayListBoundsCalculator::drawImageLattice( const sk_sp image, const SkCanvas::Lattice& lattice, const SkRect& dst, - SkFilterMode filter) { + SkFilterMode filter, + bool with_paint) { accumulateRect(dst); } void DisplayListBoundsCalculator::drawAtlas(const sk_sp atlas, @@ -310,15 +311,21 @@ void DisplayListBoundsCalculator::drawAtlas(const sk_sp atlas, } } void DisplayListBoundsCalculator::drawPicture(const sk_sp picture, - const SkMatrix* matrix, - bool withSaveLayer) { - // TODO(flar) cull rect really cannot be trusted in general, but - // it will work for SkPictures generated from our own PictureRecorder. + const SkMatrix* pic_matrix, + bool with_save_layer) { + // TODO(flar) cull rect really cannot be trusted in general, but it will + // work for SkPictures generated from our own PictureRecorder or any + // picture captured with an SkRTreeFactory or accurate bounds estimate. SkRect bounds = picture->cullRect(); - if (matrix) { - matrix->mapRect(&bounds); + if (pic_matrix) { + pic_matrix->mapRect(&bounds); + } + if (with_save_layer) { + accumulateRect(bounds); + } else { + matrix().mapRect(&bounds); + accumulator_->accumulate(bounds); } - accumulateRect(bounds); } void DisplayListBoundsCalculator::drawDisplayList( const sk_sp display_list) { diff --git a/flow/display_list_utils.h b/flow/display_list_utils.h index 914eb4b7c8bb1..4c28bb813371e 100644 --- a/flow/display_list_utils.h +++ b/flow/display_list_utils.h @@ -251,7 +251,7 @@ class DisplayListBoundsCalculator final DisplayListBoundsCalculator(const SkRect& cull_rect = SkRect::MakeEmpty()) : accumulator_(&root_accumulator_), bounds_cull_(cull_rect) {} - void saveLayer(const SkRect* bounds, bool withPaint) override; + void saveLayer(const SkRect* bounds, bool with_paint) override; void save() override; void restore() override; @@ -288,7 +288,8 @@ class DisplayListBoundsCalculator final void drawImageLattice(const sk_sp image, const SkCanvas::Lattice& lattice, const SkRect& dst, - SkFilterMode filter) override; + SkFilterMode filter, + bool with_paint) override; void drawAtlas(const sk_sp atlas, const SkRSXform xform[], const SkRect tex[], @@ -299,7 +300,7 @@ class DisplayListBoundsCalculator final const SkRect* cullRect) override; void drawPicture(const sk_sp picture, const SkMatrix* matrix, - bool withSaveLayer) override; + bool with_save_layer) override; void drawDisplayList(const sk_sp display_list) override; void drawTextBlob(const sk_sp blob, SkScalar x, From 4a79eef4c6744a8d955d1c6c76e839c9b621c00f Mon Sep 17 00:00:00 2001 From: Jim Graham Date: Wed, 30 Jun 2021 14:03:03 -0700 Subject: [PATCH 24/26] remove all ShadowRec code and leave a few comments pointing to the related Skia bug --- flow/display_list.cc | 22 ---------------------- flow/display_list.h | 4 ---- flow/display_list_canvas.cc | 12 +++--------- flow/display_list_canvas.h | 1 - flow/display_list_canvas_unittests.cc | 4 +--- flow/display_list_unittests.cc | 7 ++++--- flow/display_list_utils.cc | 10 ---------- flow/display_list_utils.h | 1 - lib/ui/painting/canvas.cc | 1 + testing/mock_canvas.cc | 1 + 10 files changed, 10 insertions(+), 53 deletions(-) diff --git a/flow/display_list.cc b/flow/display_list.cc index 6b368b80b00e4..1390a161f90e5 100644 --- a/flow/display_list.cc +++ b/flow/display_list.cc @@ -15,10 +15,6 @@ #include "third_party/skia/include/core/SkRSXform.h" #include "third_party/skia/include/core/SkTextBlob.h" -// This header file cannot be included here, but we cannot -// record calls made by the SkShadowUtils without it. -// #include "third_party/skia/src/core/SkDrawShadowInfo.h" - namespace flutter { const SkSamplingOptions DisplayList::NearestSampling = @@ -811,20 +807,6 @@ struct DrawTextBlobOp final : DLOp { } }; -// struct DrawShadowRecOp final : DLOp { -// static const auto kType = DisplayListOpType::kDrawShadowRec; -// -// DrawShadowRecOp(const SkPath& path, const SkDrawShadowRec& rec) -// : path(path), rec(rec) {} -// -// const SkPath path; -// const SkDrawShadowRec rec; -// -// void dispatch(Dispatcher& dispatcher) const { -// dispatcher.drawShadowRec(path, rec); -// } -// }; - // 4 byte header + 28 byte payload packs evenly into 32 bytes struct DrawShadowOp final : DLOp { static const auto kType = DisplayListOpType::kDrawShadow; @@ -1376,10 +1358,6 @@ void DisplayListBuilder::drawTextBlob(const sk_sp blob, SkScalar y) { Push(0, std::move(blob), x, y); } -// void DisplayListBuilder::drawShadowRec(const SkPath& path, -// const SkDrawShadowRec& rec) { -// Push(0, path, rec); -// } void DisplayListBuilder::drawShadow(const SkPath& path, const SkColor color, const SkScalar elevation, diff --git a/flow/display_list.h b/flow/display_list.h index 8c12db1501f92..7fde2a8480728 100644 --- a/flow/display_list.h +++ b/flow/display_list.h @@ -142,7 +142,6 @@ namespace flutter { V(DrawSkPictureMatrix) \ V(DrawDisplayList) \ V(DrawTextBlob) \ - /* V(DrawShadowRec) */ \ \ V(DrawShadow) @@ -315,8 +314,6 @@ class Dispatcher { virtual void drawTextBlob(const sk_sp blob, SkScalar x, SkScalar y) = 0; - // Unfortunately SkDrawShadowRec requires including an internal Skia header - // virtual void drawShadowRec(const SkPath&, const SkDrawShadowRec&) = 0; virtual void drawShadow(const SkPath& path, const SkColor color, const SkScalar elevation, @@ -430,7 +427,6 @@ class DisplayListBuilder final : public virtual Dispatcher, public SkRefCnt { void drawTextBlob(const sk_sp blob, SkScalar x, SkScalar y) override; - // void drawShadowRec(const SkPath&, const SkDrawShadowRec&) override; void drawShadow(const SkPath& path, const SkColor color, const SkScalar elevation, diff --git a/flow/display_list_canvas.cc b/flow/display_list_canvas.cc index 288a58a52db3c..c6070df8b198a 100644 --- a/flow/display_list_canvas.cc +++ b/flow/display_list_canvas.cc @@ -9,10 +9,6 @@ #include "third_party/skia/include/core/SkMaskFilter.h" #include "third_party/skia/include/core/SkTextBlob.h" -// This header file cannot be included here, but we cannot -// record calls made by the SkShadowUtils without it. -// #include "third_party/skia/src/core/SkDrawShadowInfo.h" - namespace flutter { void DisplayListCanvasDispatcher::save() { @@ -177,10 +173,6 @@ void DisplayListCanvasDispatcher::drawTextBlob(const sk_sp blob, SkScalar y) { canvas_->drawTextBlob(blob, x, y, paint()); } -// void DisplayListCanvasDispatcher::drawShadowRec(const SkPath& path, -// const SkDrawShadowRec& rec) { -// canvas_->private_draw_shadow_rec(path, rec); -// } void DisplayListCanvasDispatcher::drawShadow(const SkPath& path, const SkColor color, const SkScalar elevation, @@ -372,7 +364,9 @@ void DisplayListCanvasRecorder::onDrawTextBlob(const SkTextBlob* blob, } void DisplayListCanvasRecorder::onDrawShadowRec(const SkPath& path, const SkDrawShadowRec& rec) { - // builder_->drawShadowRec(path, rec); + // Skia does not expose the SkDrawShadowRec structure in a public + // header file so we cannot record this operation. + // See: https://bugs.chromium.org/p/skia/issues/detail?id=12125 FML_DCHECK(false); } diff --git a/flow/display_list_canvas.h b/flow/display_list_canvas.h index b3c84619a7462..da535a7d26635 100644 --- a/flow/display_list_canvas.h +++ b/flow/display_list_canvas.h @@ -107,7 +107,6 @@ class DisplayListCanvasDispatcher : public virtual Dispatcher, void drawTextBlob(const sk_sp blob, SkScalar x, SkScalar y) override; - // void drawShadowRec(const SkPath&, const SkDrawShadowRec&) override; void drawShadow(const SkPath& path, const SkColor color, const SkScalar elevation, diff --git a/flow/display_list_canvas_unittests.cc b/flow/display_list_canvas_unittests.cc index 9d8d4bfcad3d9..cd2f6ff88e620 100644 --- a/flow/display_list_canvas_unittests.cc +++ b/flow/display_list_canvas_unittests.cc @@ -50,6 +50,7 @@ class CanvasCompareTester { // ShadowRec which is not exposed by their headers. For operations // that use shadows, we can perform a lot of tests, but not the tests // that require SkCanvas->DisplayList transfers. + // See: https://bugs.chromium.org/p/skia/issues/detail?id=12125 static bool UsingShadows; typedef const std::function CvRenderer; @@ -1245,9 +1246,6 @@ TEST(DisplayListCanvas, DrawTextBlob) { }); } -// TODO(flar): Skipping DrawShadowRec for now, Flutter does not use it -// and Skia does not yet expose the require definitions - TEST(DisplayListCanvas, DrawShadow) { CanvasCompareTester::UsingShadows = true; SkPath path; diff --git a/flow/display_list_unittests.cc b/flow/display_list_unittests.cc index 3eeb0dc107c02..eae1815eb047f 100644 --- a/flow/display_list_unittests.cc +++ b/flow/display_list_unittests.cc @@ -634,10 +634,11 @@ std::vector allGroups = { {1, 24, 1, 24, [](DisplayListBuilder& b) {b.drawTextBlob(TestBlob2, 10, 10);}}, } }, - // TODO(flar): Skipping DrawShadowRec for now, Flutter does not use it - // and Skia does not yet expose the require definitions // The -1 op counts below are to indicate to the framework not to test - // SkCanvas conversion of these ops + // SkCanvas conversion of these ops as it converts the operation into a + // format that is not exposed publicly and so we cannot recapture the + // operation. + // See: https://bugs.chromium.org/p/skia/issues/detail?id=12125 { "DrawShadow", { // cv shadows are turned into an opaque ShadowRec which is not exposed {1, 32, -1, 32, [](DisplayListBuilder& b) {b.drawShadow(TestPath1, SK_ColorGREEN, 1.0, false);}}, diff --git a/flow/display_list_utils.cc b/flow/display_list_utils.cc index b4837a13f218c..474435b94c992 100644 --- a/flow/display_list_utils.cc +++ b/flow/display_list_utils.cc @@ -15,10 +15,6 @@ #include "third_party/skia/include/core/SkTextBlob.h" #include "third_party/skia/include/utils/SkShadowUtils.h" -// This header file cannot be included here, but we cannot -// record calls made by the SkShadowUtils without it. -// #include "third_party/skia/src/core/SkDrawShadowInfo.h" - namespace flutter { // clang-format off @@ -336,12 +332,6 @@ void DisplayListBoundsCalculator::drawTextBlob(const sk_sp blob, SkScalar y) { accumulateRect(blob->bounds().makeOffset(x, y)); } -// void DisplayListBoundsCalculator::drawShadowRec(const SkPath& path, -// const SkDrawShadowRec& rec) { -// SkRect bounds; -// SkDrawShadowMetrics::GetLocalBounds(path, rec, SkMatrix::I(), &bounds); -// accumulateRect(bounds, NON_GEOM); -// } void DisplayListBoundsCalculator::drawShadow(const SkPath& path, const SkColor color, const SkScalar elevation, diff --git a/flow/display_list_utils.h b/flow/display_list_utils.h index 4c28bb813371e..68b9dc56b0120 100644 --- a/flow/display_list_utils.h +++ b/flow/display_list_utils.h @@ -305,7 +305,6 @@ class DisplayListBoundsCalculator final void drawTextBlob(const sk_sp blob, SkScalar x, SkScalar y) override; - // void drawShadowRec(const SkPath&, const SkDrawShadowRec&) override; void drawShadow(const SkPath& path, const SkColor color, const SkScalar elevation, diff --git a/lib/ui/painting/canvas.cc b/lib/ui/painting/canvas.cc index 6c7581bc01b78..fee3127f06dbe 100644 --- a/lib/ui/painting/canvas.cc +++ b/lib/ui/painting/canvas.cc @@ -506,6 +506,7 @@ void Canvas::drawShadow(const CanvasPath* path, // record an operation that it injects into an SkCanvas. To prevent // that situation we bypass the canvas interface and inject the // shadow parameters directly into the underlying DisplayList. + // See: https://bugs.chromium.org/p/skia/issues/detail?id=12125 builder()->drawShadow(path->path(), color, elevation, transparentOccluder); } else { SkScalar dpr = UIDartState::Current() diff --git a/testing/mock_canvas.cc b/testing/mock_canvas.cc index 518f52c89c035..9084563973dc6 100644 --- a/testing/mock_canvas.cc +++ b/testing/mock_canvas.cc @@ -105,6 +105,7 @@ void MockCanvas::onDrawPath(const SkPath& path, const SkPaint& paint) { void MockCanvas::onDrawShadowRec(const SkPath& path, const SkDrawShadowRec& rec) { + // See: https://bugs.chromium.org/p/skia/issues/detail?id=12125 (void)rec; // Can't use b/c Skia keeps this type anonymous. draw_calls_.emplace_back(DrawCall{current_layer_, DrawShadowData{path}}); } From 0d661146d3e7552cce2bfb0c9aa6aeabd9f6c054 Mon Sep 17 00:00:00 2001 From: Jim Graham Date: Wed, 30 Jun 2021 14:53:55 -0700 Subject: [PATCH 25/26] switch DL off by default and move some EXPECT_DEATH tests inside DEBUG ifdef --- common/settings.h | 2 +- flow/layers/display_list_layer_unittests.cc | 2 +- flow/layers/picture_layer_unittests.cc | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/common/settings.h b/common/settings.h index 9fa5f76f116d4..cc496285ba771 100644 --- a/common/settings.h +++ b/common/settings.h @@ -164,7 +164,7 @@ struct Settings { bool enable_skparagraph = false; // Selects the DisplayList for storage of rendering operations. - bool enable_display_list = true; + bool enable_display_list = false; // All shells in the process share the same VM. The last shell to shutdown // should typically shut down the VM as well. However, applications depend on diff --git a/flow/layers/display_list_layer_unittests.cc b/flow/layers/display_list_layer_unittests.cc index 199c04882f385..1c6eee8f6eab1 100644 --- a/flow/layers/display_list_layer_unittests.cc +++ b/flow/layers/display_list_layer_unittests.cc @@ -52,7 +52,6 @@ TEST_F(DisplayListLayerTest, PaintingEmptyLayerDies) { EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()), "needs_painting\\(context\\)"); } -#endif TEST_F(DisplayListLayerTest, InvalidDisplayListDies) { const SkPoint layer_offset = SkPoint::Make(0.0f, 0.0f); @@ -62,6 +61,7 @@ TEST_F(DisplayListLayerTest, InvalidDisplayListDies) { // Crashes reading a nullptr. EXPECT_DEATH_IF_SUPPORTED(layer->Preroll(preroll_context(), SkMatrix()), ""); } +#endif TEST_F(DisplayListLayerTest, SimpleDisplayList) { const SkPoint layer_offset = SkPoint::Make(1.5f, -0.5f); diff --git a/flow/layers/picture_layer_unittests.cc b/flow/layers/picture_layer_unittests.cc index cdd6e6b75fc2b..dba13d117ca2b 100644 --- a/flow/layers/picture_layer_unittests.cc +++ b/flow/layers/picture_layer_unittests.cc @@ -57,7 +57,6 @@ TEST_F(PictureLayerTest, PaintingEmptyLayerDies) { EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()), "needs_painting\\(context\\)"); } -#endif TEST_F(PictureLayerTest, InvalidPictureDies) { const SkPoint layer_offset = SkPoint::Make(0.0f, 0.0f); @@ -67,6 +66,7 @@ TEST_F(PictureLayerTest, InvalidPictureDies) { // Crashes reading a nullptr. EXPECT_DEATH_IF_SUPPORTED(layer->Preroll(preroll_context(), SkMatrix()), ""); } +#endif TEST_F(PictureLayerTest, SimplePicture) { const SkPoint layer_offset = SkPoint::Make(1.5f, -0.5f); From 3755bf17f6b674fcb2046e5d22b505ebf6a86b81 Mon Sep 17 00:00:00 2001 From: Jim Graham Date: Wed, 30 Jun 2021 16:21:21 -0700 Subject: [PATCH 26/26] adjust new DL sources for recent removal of Fuchsia legacy code --- flow/layers/display_list_layer.cc | 4 ---- flow/layers/display_list_layer_unittests.cc | 2 -- 2 files changed, 6 deletions(-) diff --git a/flow/layers/display_list_layer.cc b/flow/layers/display_list_layer.cc index bf6878c388cbe..a40cc314c82ec 100644 --- a/flow/layers/display_list_layer.cc +++ b/flow/layers/display_list_layer.cc @@ -87,10 +87,6 @@ void DisplayListLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) { TRACE_EVENT0("flutter", "DisplayListLayer::Preroll"); -#if defined(LEGACY_FUCHSIA_EMBEDDER) - CheckForChildLayerBelow(context); -#endif - DisplayList* disp_list = display_list(); if (auto* cache = context->raster_cache) { diff --git a/flow/layers/display_list_layer_unittests.cc b/flow/layers/display_list_layer_unittests.cc index 1c6eee8f6eab1..ad1842b8c5a73 100644 --- a/flow/layers/display_list_layer_unittests.cc +++ b/flow/layers/display_list_layer_unittests.cc @@ -47,7 +47,6 @@ TEST_F(DisplayListLayerTest, PaintingEmptyLayerDies) { layer->Preroll(preroll_context(), SkMatrix()); EXPECT_EQ(layer->paint_bounds(), SkRect::MakeEmpty()); EXPECT_FALSE(layer->needs_painting(paint_context())); - EXPECT_FALSE(layer->needs_system_composite()); EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()), "needs_painting\\(context\\)"); @@ -79,7 +78,6 @@ TEST_F(DisplayListLayerTest, SimpleDisplayList) { picture_bounds.makeOffset(layer_offset.fX, layer_offset.fY)); EXPECT_EQ(layer->display_list(), display_list.get()); EXPECT_TRUE(layer->needs_painting(paint_context())); - EXPECT_FALSE(layer->needs_system_composite()); layer->Paint(paint_context()); auto expected_draw_calls = std::vector(