Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(301)

Unified Diff: cc/paint/paint_op_buffer.cc

Issue 2768143002: Back PaintRecord with PaintOpBuffer instead of SkPicture (Closed)
Patch Set: more const casting Created 3 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « cc/paint/paint_op_buffer.h ('k') | cc/paint/paint_op_buffer_unittest.cc » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: cc/paint/paint_op_buffer.cc
diff --git a/cc/paint/paint_op_buffer.cc b/cc/paint/paint_op_buffer.cc
new file mode 100644
index 0000000000000000000000000000000000000000..b10c2a8cd38b066b572332ae785a74c84118adf3
--- /dev/null
+++ b/cc/paint/paint_op_buffer.cc
@@ -0,0 +1,579 @@
+// Copyright 2017 The Chromium 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 "cc/paint/paint_op_buffer.h"
+
+#include "cc/paint/display_item_list.h"
+#include "cc/paint/paint_record.h"
+#include "third_party/skia/include/core/SkAnnotation.h"
+
+namespace cc {
+
+#define TYPES(M) \
+ M(AnnotateOp) \
+ M(ClipPathOp) \
+ M(ClipRectOp) \
+ M(ClipRRectOp) \
+ M(ConcatOp) \
+ M(DrawArcOp) \
+ M(DrawCircleOp) \
+ M(DrawColorOp) \
+ M(DrawDisplayItemListOp) \
+ M(DrawDRRectOp) \
+ M(DrawImageOp) \
+ M(DrawImageRectOp) \
+ M(DrawIRectOp) \
+ M(DrawLineOp) \
+ M(DrawOvalOp) \
+ M(DrawPathOp) \
+ M(DrawPosTextOp) \
+ M(DrawRecordOp) \
+ M(DrawRectOp) \
+ M(DrawRRectOp) \
+ M(DrawTextOp) \
+ M(DrawTextBlobOp) \
+ M(NoopOp) \
+ M(RestoreOp) \
+ M(RotateOp) \
+ M(SaveOp) \
+ M(SaveLayerOp) \
+ M(SaveLayerAlphaOp) \
+ M(ScaleOp) \
+ M(SetMatrixOp) \
+ M(TranslateOp)
+
+// Helper template to share common code for RasterWithAlpha when paint ops
+// have or don't have PaintFlags.
+template <typename T, bool HasFlags>
+struct Rasterizer {
+ static void Raster(const T* op,
+ SkCanvas* canvas,
+ const SkMatrix& original_ctm) {
+ // Paint ops with kHasPaintFlags need to declare RasterWithPaintFlags
+ // otherwise, the paint op needs its own Raster function. Without its
+ // own, this becomes an infinite loop as PaintOp::Raster calls itself.
+ static_assert(
+ !std::is_same<decltype(&PaintOp::Raster), decltype(&T::Raster)>::value,
+ "No Raster function");
+
+ op->Raster(canvas);
+ }
+ static void RasterWithAlpha(const T* op, SkCanvas* canvas, uint8_t alpha) {
+ DCHECK(T::kIsDrawOp);
+ // TODO(enne): is it ok to just drop the bounds here?
+ canvas->saveLayerAlpha(nullptr, alpha);
+ op->Raster(canvas);
+ canvas->restore();
+ }
+};
+
+template <typename T>
+struct Rasterizer<T, true> {
+ static void Raster(const T* op,
+ SkCanvas* canvas,
+ const SkMatrix& original_ctm) {
+ op->RasterWithFlags(canvas, op->flags);
+ }
+ static void RasterWithAlpha(const T* op, SkCanvas* canvas, uint8_t alpha) {
+ DCHECK(T::kIsDrawOp);
+ SkMatrix unused_matrix;
+ if (alpha == 255) {
+ Raster(op, canvas, unused_matrix);
+ } else if (op->flags.SupportsFoldingAlpha()) {
+ PaintFlags flags = op->flags;
+ flags.setAlpha(SkMulDiv255Round(flags.getAlpha(), alpha));
+ op->RasterWithFlags(canvas, flags);
+ } else {
+ canvas->saveLayerAlpha(nullptr, alpha);
+ op->RasterWithFlags(canvas, op->flags);
+ canvas->restore();
+ }
+ }
+};
+
+template <>
+struct Rasterizer<SetMatrixOp, false> {
+ static void Raster(const SetMatrixOp* op,
+ SkCanvas* canvas,
+ const SkMatrix& original_ctm) {
+ op->Raster(canvas, original_ctm);
+ }
+ static void RasterWithAlpha(const SetMatrixOp* op,
+ SkCanvas* canvas,
+ uint8_t alpha) {
+ NOTREACHED();
+ }
+};
+
+template <>
+struct Rasterizer<DrawRecordOp, false> {
+ static void Raster(const DrawRecordOp* op,
+ SkCanvas* canvas,
+ const SkMatrix& original_ctm) {
+ op->Raster(canvas);
+ }
+ static void RasterWithAlpha(const DrawRecordOp* op,
+ SkCanvas* canvas,
+ uint8_t alpha) {
+ // This "looking into records" optimization is done here instead of
+ // in the PaintOpBuffer::Raster function as DisplayItemList calls
+ // into RasterWithAlpha directly.
+ if (op->record->approximateOpCount() == 1) {
+ PaintOp* single_op = op->record->GetFirstOp();
+ // RasterWithAlpha only supported for draw ops.
+ if (single_op->IsDrawOp()) {
+ single_op->RasterWithAlpha(canvas, alpha);
+ return;
+ }
+ }
+
+ canvas->saveLayerAlpha(nullptr, alpha);
+ op->Raster(canvas);
+ canvas->restore();
+ }
+};
+
+// TODO(enne): partially specialize RasterWithAlpha for draw color?
+
+static constexpr size_t kNumOpTypes =
+ static_cast<size_t>(PaintOpType::LastPaintOpType) + 1;
+
+// Verify that every op is in the TYPES macro.
+#define M(T) +1
+static_assert(kNumOpTypes == TYPES(M), "Missing op in list");
+#undef M
+
+using RasterFunction = void (*)(const PaintOp* op,
+ SkCanvas* canvas,
+ const SkMatrix& original_ctm);
+#define M(T) \
+ [](const PaintOp* op, SkCanvas* canvas, const SkMatrix& original_ctm) { \
+ Rasterizer<T, T::kHasPaintFlags>::Raster(static_cast<const T*>(op), \
+ canvas, original_ctm); \
+ },
+static const RasterFunction g_raster_functions[kNumOpTypes] = {TYPES(M)};
+#undef M
+
+using RasterAlphaFunction = void (*)(const PaintOp* op,
+ SkCanvas* canvas,
+ uint8_t alpha);
+#define M(T) \
+ T::kIsDrawOp ? \
+ [](const PaintOp* op, SkCanvas* canvas, uint8_t alpha) { \
+ Rasterizer<T, T::kHasPaintFlags>::RasterWithAlpha( \
+ static_cast<const T*>(op), canvas, alpha); \
+ } : static_cast<RasterAlphaFunction>(nullptr),
+static const RasterAlphaFunction g_raster_alpha_functions[kNumOpTypes] = {
+ TYPES(M)};
+#undef M
+
+// Most state ops (matrix, clip, save, restore) have a trivial destructor.
+// TODO(enne): evaluate if we need the nullptr optimization or if
+// we even need to differentiate trivial destructors here.
+using VoidFunction = void (*)(PaintOp* op);
+#define M(T) \
+ !std::is_trivially_destructible<T>::value \
+ ? [](PaintOp* op) { static_cast<T*>(op)->~T(); } \
+ : static_cast<VoidFunction>(nullptr),
+static const VoidFunction g_destructor_functions[kNumOpTypes] = {TYPES(M)};
+#undef M
+
+#define M(T) T::kIsDrawOp,
+static bool g_is_draw_op[kNumOpTypes] = {TYPES(M)};
+#undef M
+
+#define M(T) \
+ static_assert(sizeof(T) <= sizeof(LargestPaintOp), \
+ #T " must be no bigger than LargestPaintOp");
+TYPES(M);
+#undef M
+
+#define M(T) \
+ static_assert(ALIGNOF(T) <= PaintOpBuffer::PaintOpAlign, \
+ #T " must have alignment no bigger than PaintOpAlign");
+TYPES(M);
+#undef M
+
+#undef TYPES
+
+SkRect PaintOp::kUnsetRect = {SK_ScalarInfinity, 0, 0, 0};
+
+void AnnotateOp::Raster(SkCanvas* canvas) const {
+ switch (annotation_type) {
+ case PaintCanvas::AnnotationType::URL:
+ SkAnnotateRectWithURL(canvas, rect, data.get());
+ break;
+ case PaintCanvas::AnnotationType::LINK_TO_DESTINATION:
+ SkAnnotateLinkToDestination(canvas, rect, data.get());
+ break;
+ case PaintCanvas::AnnotationType::NAMED_DESTINATION: {
+ SkPoint point = SkPoint::Make(rect.x(), rect.y());
+ SkAnnotateNamedDestination(canvas, point, data.get());
+ break;
+ }
+ }
+}
+
+void ClipPathOp::Raster(SkCanvas* canvas) const {
+ canvas->clipPath(path, op, antialias);
+}
+
+void ClipRectOp::Raster(SkCanvas* canvas) const {
+ canvas->clipRect(rect, op, antialias);
+}
+
+void ClipRRectOp::Raster(SkCanvas* canvas) const {
+ canvas->clipRRect(rrect, op, antialias);
+}
+
+void ConcatOp::Raster(SkCanvas* canvas) const {
+ canvas->concat(matrix);
+}
+
+void DrawArcOp::RasterWithFlags(SkCanvas* canvas,
+ const PaintFlags& flags) const {
+ canvas->drawArc(oval, start_angle, sweep_angle, use_center, ToSkPaint(flags));
+}
+
+void DrawCircleOp::RasterWithFlags(SkCanvas* canvas,
+ const PaintFlags& flags) const {
+ canvas->drawCircle(cx, cy, radius, ToSkPaint(flags));
+}
+
+void DrawColorOp::Raster(SkCanvas* canvas) const {
+ canvas->drawColor(color, mode);
+}
+
+void DrawDisplayItemListOp::Raster(SkCanvas* canvas) const {
+ list->Raster(canvas, nullptr);
+}
+
+void DrawDRRectOp::RasterWithFlags(SkCanvas* canvas,
+ const PaintFlags& flags) const {
+ canvas->drawDRRect(outer, inner, ToSkPaint(flags));
+}
+
+void DrawImageOp::RasterWithFlags(SkCanvas* canvas,
+ const PaintFlags& flags) const {
+ canvas->drawImage(image.sk_image().get(), left, top, ToSkPaint(&flags));
+}
+
+void DrawImageRectOp::RasterWithFlags(SkCanvas* canvas,
+ const PaintFlags& flags) const {
+ // TODO(enne): Probably PaintCanvas should just use the skia enum directly.
+ SkCanvas::SrcRectConstraint skconstraint =
+ static_cast<SkCanvas::SrcRectConstraint>(constraint);
+ canvas->drawImageRect(image.sk_image().get(), src, dst, ToSkPaint(&flags),
+ skconstraint);
+}
+
+void DrawIRectOp::RasterWithFlags(SkCanvas* canvas,
+ const PaintFlags& flags) const {
+ canvas->drawIRect(rect, ToSkPaint(flags));
+}
+
+void DrawLineOp::RasterWithFlags(SkCanvas* canvas,
+ const PaintFlags& flags) const {
+ canvas->drawLine(x0, y0, x1, y1, ToSkPaint(flags));
+}
+
+void DrawOvalOp::RasterWithFlags(SkCanvas* canvas,
+ const PaintFlags& flags) const {
+ canvas->drawOval(oval, ToSkPaint(flags));
+}
+
+void DrawPathOp::RasterWithFlags(SkCanvas* canvas,
+ const PaintFlags& flags) const {
+ canvas->drawPath(path, ToSkPaint(flags));
+}
+
+void DrawPosTextOp::RasterWithFlags(SkCanvas* canvas,
+ const PaintFlags& flags) const {
+ canvas->drawPosText(GetData(), bytes, GetArray(), ToSkPaint(flags));
+}
+
+void DrawRecordOp::Raster(SkCanvas* canvas) const {
+ record->playback(canvas);
+}
+
+void DrawRectOp::RasterWithFlags(SkCanvas* canvas,
+ const PaintFlags& flags) const {
+ canvas->drawRect(rect, ToSkPaint(flags));
+}
+
+void DrawRRectOp::RasterWithFlags(SkCanvas* canvas,
+ const PaintFlags& flags) const {
+ canvas->drawRRect(rrect, ToSkPaint(flags));
+}
+
+void DrawTextOp::RasterWithFlags(SkCanvas* canvas,
+ const PaintFlags& flags) const {
+ canvas->drawText(GetData(), bytes, x, y, ToSkPaint(flags));
+}
+
+void DrawTextBlobOp::RasterWithFlags(SkCanvas* canvas,
+ const PaintFlags& flags) const {
+ canvas->drawTextBlob(blob.get(), x, y, ToSkPaint(flags));
+}
+
+void RestoreOp::Raster(SkCanvas* canvas) const {
+ canvas->restore();
+}
+
+void RotateOp::Raster(SkCanvas* canvas) const {
+ canvas->rotate(degrees);
+}
+
+void SaveOp::Raster(SkCanvas* canvas) const {
+ canvas->save();
+}
+
+void SaveLayerOp::RasterWithFlags(SkCanvas* canvas,
+ const PaintFlags& flags) const {
+ // See PaintOp::kUnsetRect
+ bool unset = bounds.left() == SK_ScalarInfinity;
+
+ canvas->saveLayer(unset ? nullptr : &bounds, ToSkPaint(&flags));
+}
+
+void SaveLayerAlphaOp::Raster(SkCanvas* canvas) const {
+ // See PaintOp::kUnsetRect
+ bool unset = bounds.left() == SK_ScalarInfinity;
+ canvas->saveLayerAlpha(unset ? nullptr : &bounds, alpha);
+}
+
+void ScaleOp::Raster(SkCanvas* canvas) const {
+ canvas->scale(sx, sy);
+}
+
+void SetMatrixOp::Raster(SkCanvas* canvas, const SkMatrix& original_ctm) const {
+ canvas->setMatrix(SkMatrix::Concat(original_ctm, matrix));
+}
+
+void TranslateOp::Raster(SkCanvas* canvas) const {
+ canvas->translate(dx, dy);
+}
+
+bool PaintOp::IsDrawOp() const {
+ return g_is_draw_op[type];
+}
+
+void PaintOp::Raster(SkCanvas* canvas, const SkMatrix& original_ctm) const {
+ g_raster_functions[type](this, canvas, original_ctm);
+}
+
+void PaintOp::RasterWithAlpha(SkCanvas* canvas, uint8_t alpha) const {
+ g_raster_alpha_functions[type](this, canvas, alpha);
+}
+
+int ClipPathOp::CountSlowPaths() const {
+ return antialias && !path.isConvex() ? 1 : 0;
+}
+
+int DrawLineOp::CountSlowPaths() const {
+ if (const SkPathEffect* effect = flags.getPathEffect()) {
+ SkPathEffect::DashInfo info;
+ SkPathEffect::DashType dashType = effect->asADash(&info);
+ if (flags.getStrokeCap() != PaintFlags::kRound_Cap &&
+ dashType == SkPathEffect::kDash_DashType && info.fCount == 2) {
+ // The PaintFlags will count this as 1, so uncount that here as
+ // this kind of line is special cased and not slow.
+ return -1;
+ }
+ }
+ return 0;
+}
+
+int DrawPathOp::CountSlowPaths() const {
+ // This logic is copied from SkPathCounter instead of attempting to expose
+ // that from Skia.
+ if (!flags.isAntiAlias() || path.isConvex())
+ return 0;
+
+ PaintFlags::Style paintStyle = flags.getStyle();
+ const SkRect& pathBounds = path.getBounds();
+ if (paintStyle == PaintFlags::kStroke_Style && flags.getStrokeWidth() == 0) {
+ // AA hairline concave path is not slow.
+ return 0;
+ } else if (paintStyle == PaintFlags::kFill_Style &&
+ pathBounds.width() < 64.f && pathBounds.height() < 64.f &&
+ !path.isVolatile()) {
+ // AADF eligible concave path is not slow.
+ return 0;
+ } else {
+ return 1;
+ }
+}
+
+AnnotateOp::AnnotateOp(PaintCanvas::AnnotationType annotation_type,
+ const SkRect& rect,
+ sk_sp<SkData> data)
+ : annotation_type(annotation_type), rect(rect), data(std::move(data)) {}
+
+AnnotateOp::~AnnotateOp() = default;
+
+DrawDisplayItemListOp::DrawDisplayItemListOp(
+ scoped_refptr<DisplayItemList> list)
+ : list(list) {}
+
+size_t DrawDisplayItemListOp::AdditionalBytesUsed() const {
+ return list->ApproximateMemoryUsage();
+}
+
+DrawDisplayItemListOp::DrawDisplayItemListOp(const DrawDisplayItemListOp& op) =
+ default;
+
+DrawDisplayItemListOp& DrawDisplayItemListOp::operator=(
+ const DrawDisplayItemListOp& op) = default;
+
+DrawDisplayItemListOp::~DrawDisplayItemListOp() = default;
+
+DrawImageOp::DrawImageOp(const PaintImage& image,
+ SkScalar left,
+ SkScalar top,
+ const PaintFlags* flags)
+ : image(image),
+ left(left),
+ top(top),
+ flags(flags ? *flags : PaintFlags()) {}
+
+DrawImageOp::~DrawImageOp() = default;
+
+DrawImageRectOp::DrawImageRectOp(const PaintImage& image,
+ const SkRect& src,
+ const SkRect& dst,
+ const PaintFlags* flags,
+ PaintCanvas::SrcRectConstraint constraint)
+ : image(image),
+ flags(flags ? *flags : PaintFlags()),
+ src(src),
+ dst(dst),
+ constraint(constraint) {}
+
+DrawImageRectOp::~DrawImageRectOp() = default;
+
+DrawPosTextOp::DrawPosTextOp(size_t bytes,
+ size_t count,
+ const PaintFlags& flags)
+ : PaintOpWithArray(bytes, count), flags(flags) {}
+
+DrawPosTextOp::~DrawPosTextOp() = default;
+
+DrawRecordOp::DrawRecordOp(sk_sp<const PaintRecord> record)
+ : record(std::move(record)) {}
+
+DrawRecordOp::~DrawRecordOp() = default;
+
+size_t DrawRecordOp::AdditionalBytesUsed() const {
+ return record->approximateBytesUsed();
+}
+
+DrawTextBlobOp::DrawTextBlobOp(sk_sp<SkTextBlob> blob,
+ SkScalar x,
+ SkScalar y,
+ const PaintFlags& flags)
+ : blob(std::move(blob)), x(x), y(y), flags(flags) {}
+
+DrawTextBlobOp::~DrawTextBlobOp() = default;
+
+PaintOpBuffer::PaintOpBuffer() : cull_rect_(SkRect::MakeEmpty()) {}
+
+PaintOpBuffer::PaintOpBuffer(const SkRect& cull_rect) : cull_rect_(cull_rect) {}
+
+PaintOpBuffer::~PaintOpBuffer() {
+ Reset();
+}
+
+void PaintOpBuffer::Reset() {
+ for (auto* op : Iterator(this)) {
+ auto func = g_destructor_functions[op->type];
+ if (func)
+ func(op);
+ }
+
+ // Leave data_ allocated, reserved_ unchanged.
+ used_ = 0;
+ op_count_ = 0;
+ num_slow_paths_ = 0;
+}
+
+void PaintOpBuffer::playback(SkCanvas* canvas) const {
+ // TODO(enne): a PaintRecord that contains a SetMatrix assumes that the
+ // SetMatrix is local to that PaintRecord itself. Said differently, if you
+ // translate(x, y), then draw a paint record with a SetMatrix(identity),
+ // the translation should be preserved instead of clobbering the top level
+ // transform. This could probably be done more efficiently.
+ SkMatrix original = canvas->getTotalMatrix();
+
+ for (Iterator iter(this); iter; ++iter) {
+ // Optimize out save/restores or save/draw/restore that can be a single
+ // draw. See also: similar code in SkRecordOpts and cc's DisplayItemList.
+ // TODO(enne): consider making this recursive?
+ const PaintOp* op = *iter;
+ if (op->GetType() == PaintOpType::SaveLayerAlpha) {
+ const PaintOp* second = iter.peek1();
+ if (second) {
+ if (second->GetType() == PaintOpType::Restore) {
+ ++iter;
+ continue;
+ }
+ if (second->IsDrawOp()) {
+ const PaintOp* third = iter.peek2();
+ if (third && third->GetType() == PaintOpType::Restore) {
+ const SaveLayerAlphaOp* save_op =
+ static_cast<const SaveLayerAlphaOp*>(op);
+ second->RasterWithAlpha(canvas, save_op->alpha);
+ ++iter;
+ ++iter;
+ continue;
+ }
+ }
+ }
+ }
+ // TODO(enne): skip SaveLayer followed by restore with nothing in
+ // between, however SaveLayer with image filters on it (or maybe
+ // other PaintFlags options) are not a noop. Figure out what these
+ // are so we can skip them correctly.
+
+ op->Raster(canvas, original);
+ }
+}
+
+void PaintOpBuffer::playback(SkCanvas* canvas,
+ SkPicture::AbortCallback* callback) const {
+ // The abort callback is only used for analysis, in general, so
+ // this playback code can be more straightforward and not do the
+ // optimizations in the other function.
+ if (!callback) {
+ playback(canvas);
+ return;
+ }
+
+ SkMatrix original = canvas->getTotalMatrix();
+
+ // TODO(enne): ideally callers would just iterate themselves and we
+ // can remove the entire notion of an abort callback.
+ for (auto* op : Iterator(this)) {
+ op->Raster(canvas, original);
+ if (callback && callback->abort())
+ return;
+ }
+}
+
+void PaintOpBuffer::ReallocBuffer(size_t new_size) {
+ DCHECK_GE(new_size, used_);
+ std::unique_ptr<char, base::AlignedFreeDeleter> new_data(
+ static_cast<char*>(base::AlignedAlloc(new_size, PaintOpAlign)));
+ memcpy(new_data.get(), data_.get(), used_);
+ data_ = std::move(new_data);
+ reserved_ = new_size;
+}
+
+void PaintOpBuffer::ShrinkToFit() {
+ if (!used_ || used_ == reserved_)
+ return;
+ ReallocBuffer(used_);
+}
+
+} // namespace cc
« no previous file with comments | « cc/paint/paint_op_buffer.h ('k') | cc/paint/paint_op_buffer_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698