From 15877b6eae33a9282458bdb904a6d00440eca0ec Mon Sep 17 00:00:00 2001 From: mtklein Date: Mon, 18 May 2015 13:47:17 -0700 Subject: Sketch splitting SkPicture into an interface and SkBigPicture. Adds small pictures for drawRect(), drawTextBlob(), and drawPath(). These cover about 89% of draw calls from Blink SKPs, and about 25% of draw calls from our GMs. SkPicture handles: - serialization and deserialization - unique IDs Everything else is left to the subclasses: - playback(), cullRect() - hasBitmap(), hasText(), suitableForGPU(), etc. - LayerInfo / AccelData if applicable. The time to record a 1-op picture improves a good chunk (2 mallocs to 1), and the time to record a 0-op picture greatly improves (2 mallocs to none): picture_overhead_draw: 450ns -> 350ns picture_overhead_nodraw: 300ns -> 90ns BUG=skia: Committed: https://skia.googlesource.com/skia/+/c92c129ff85b05a714bd1bf921c02d5e14651f8b Latest blink_linux_rel: http://build.chromium.org/p/tryserver.blink/builders/linux_blink_rel/builds/61248 Review URL: https://codereview.chromium.org/1112523006 --- src/core/SkBigPicture.cpp | 98 +++++++++ src/core/SkBigPicture.h | 88 +++++++++ src/core/SkLayerInfo.cpp | 15 -- src/core/SkLayerInfo.h | 18 +- src/core/SkMiniRecorder.cpp | 103 ++++++++++ src/core/SkMiniRecorder.h | 44 +++++ src/core/SkPicture.cpp | 426 +++++++--------------------------------- src/core/SkPictureCommon.h | 136 +++++++++++++ src/core/SkPictureRecorder.cpp | 55 +++--- src/core/SkRecordDraw.cpp | 14 +- src/core/SkRecordDraw.h | 3 +- src/core/SkRecorder.cpp | 36 +++- src/core/SkRecorder.h | 14 +- src/core/SkRecords.h | 12 ++ src/gpu/GrLayerHoister.cpp | 42 ++-- src/gpu/GrRecordReplaceDraw.cpp | 47 +++-- src/gpu/SkGpuDevice.cpp | 7 +- src/gpu/SkGpuDevice.h | 2 - src/utils/SkPictureUtils.cpp | 25 --- 19 files changed, 691 insertions(+), 494 deletions(-) create mode 100644 src/core/SkBigPicture.cpp create mode 100644 src/core/SkBigPicture.h delete mode 100644 src/core/SkLayerInfo.cpp create mode 100644 src/core/SkMiniRecorder.cpp create mode 100644 src/core/SkMiniRecorder.h create mode 100644 src/core/SkPictureCommon.h delete mode 100644 src/utils/SkPictureUtils.cpp (limited to 'src') diff --git a/src/core/SkBigPicture.cpp b/src/core/SkBigPicture.cpp new file mode 100644 index 0000000000..6609586ca2 --- /dev/null +++ b/src/core/SkBigPicture.cpp @@ -0,0 +1,98 @@ +/* + * Copyright 2015 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "SkBBoxHierarchy.h" +#include "SkBigPicture.h" +#include "SkPictureCommon.h" +#include "SkRecord.h" +#include "SkRecordDraw.h" + +SkBigPicture::SkBigPicture(const SkRect& cull, + SkRecord* record, + SnapshotArray* drawablePicts, + SkBBoxHierarchy* bbh, + AccelData* accelData, + size_t approxBytesUsedBySubPictures) + : fCullRect(cull) + , fApproxBytesUsedBySubPictures(approxBytesUsedBySubPictures) + , fRecord(record) // Take ownership of caller's ref. + , fDrawablePicts(drawablePicts) // Take ownership. + , fBBH(bbh) // Take ownership of caller's ref. + , fAccelData(accelData) // Take ownership of caller's ref. +{} + +void SkBigPicture::playback(SkCanvas* canvas, AbortCallback* callback) const { + SkASSERT(canvas); + + // If the query contains the whole picture, don't bother with the BBH. + SkRect clipBounds = { 0, 0, 0, 0 }; + (void)canvas->getClipBounds(&clipBounds); + const bool useBBH = !clipBounds.contains(this->cullRect()); + + SkRecordDraw(*fRecord, + canvas, + this->drawablePicts(), + nullptr, + this->drawableCount(), + useBBH ? fBBH.get() : nullptr, + callback); +} + +void SkBigPicture::partialPlayback(SkCanvas* canvas, + unsigned start, + unsigned stop, + const SkMatrix& initialCTM) const { + SkASSERT(canvas); + SkRecordPartialDraw(*fRecord, + canvas, + this->drawablePicts(), + this->drawableCount(), + start, + stop, + initialCTM); +} + +const SkBigPicture::Analysis& SkBigPicture::analysis() const { + auto create = [&]() { return SkNEW_ARGS(Analysis, (*fRecord)); }; + return *fAnalysis.get(create); +} + +SkRect SkBigPicture::cullRect() const { return fCullRect; } +bool SkBigPicture::hasText() const { return this->analysis().fHasText; } +bool SkBigPicture::willPlayBackBitmaps() const { return this->analysis().fWillPlaybackBitmaps; } +int SkBigPicture::numSlowPaths() const { return this->analysis().fNumSlowPathsAndDashEffects; } +int SkBigPicture::approximateOpCount() const { return fRecord->count(); } +size_t SkBigPicture::approximateBytesUsed() const { + size_t bytes = sizeof(*this) + fRecord->bytesUsed() + fApproxBytesUsedBySubPictures; + if (fBBH) { bytes += fBBH->bytesUsed(); } + return bytes; +} + +int SkBigPicture::drawableCount() const { + return fDrawablePicts ? fDrawablePicts->count() : 0; +} + +SkPicture const* const* SkBigPicture::drawablePicts() const { + return fDrawablePicts ? fDrawablePicts->begin() : nullptr; +} + +SkBigPicture::Analysis::Analysis(const SkRecord& record) { + SkTextHunter text; + SkBitmapHunter bitmap; + SkPathCounter path; + + bool hasText = false, hasBitmap = false; + for (unsigned i = 0; i < record.count(); i++) { + hasText = hasText || record.visit(i, text); + hasBitmap = hasBitmap || record.visit(i, bitmap); + record.visit(i, path); + } + + fHasText = hasText; + fWillPlaybackBitmaps = hasBitmap; + fNumSlowPathsAndDashEffects = SkTMin(path.fNumSlowPathsAndDashEffects, 255); +} diff --git a/src/core/SkBigPicture.h b/src/core/SkBigPicture.h new file mode 100644 index 0000000000..14e413b55a --- /dev/null +++ b/src/core/SkBigPicture.h @@ -0,0 +1,88 @@ +/* + * Copyright 2015 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkBigPicture_DEFINED +#define SkBigPicture_DEFINED + +#include "SkPicture.h" +#include "SkLazyPtr.h" + +class SkBBoxHierarchy; +class SkRecord; + +// An implementation of SkPicture supporting an arbitrary number of drawing commands. +class SkBigPicture final : public SkPicture { +public: + // AccelData provides a base class for device-specific acceleration data. + class AccelData : public SkRefCnt { }; + + // An array of refcounted const SkPicture pointers. + class SnapshotArray : ::SkNoncopyable { + public: + SnapshotArray(const SkPicture* pics[], int count) : fPics(pics), fCount(count) {} + ~SnapshotArray() { for (int i = 0; i < fCount; i++) { fPics[i]->unref(); } } + + const SkPicture* const* begin() const { return fPics; } + int count() const { return fCount; } + private: + SkAutoTMalloc fPics; + int fCount; + }; + + SkBigPicture(const SkRect& cull, + SkRecord*, // We take ownership of the caller's ref. + SnapshotArray*, // We take exclusive ownership. + SkBBoxHierarchy*, // We take ownership of the caller's ref. + AccelData*, // We take ownership of the caller's ref. + size_t approxBytesUsedBySubPictures); + + +// SkPicture overrides + void playback(SkCanvas*, AbortCallback*) const override; + SkRect cullRect() const override; + bool hasText() const override; + bool willPlayBackBitmaps() const override; + int approximateOpCount() const override; + size_t approximateBytesUsed() const override; + const SkBigPicture* asSkBigPicture() const override { return this; } + +// Used by GrLayerHoister + void partialPlayback(SkCanvas*, + unsigned start, + unsigned stop, + const SkMatrix& initialCTM) const; +// Used by GrRecordReplaceDraw + const SkBBoxHierarchy* bbh() const { return fBBH; } + const SkRecord* record() const { return fRecord; } + const AccelData* accelData() const { return fAccelData; } + +private: + struct Analysis { + explicit Analysis(const SkRecord&); + + bool suitableForGpuRasterization(const char** reason) const; + + uint8_t fNumSlowPathsAndDashEffects; + bool fWillPlaybackBitmaps : 1; + bool fHasText : 1; + }; + + int numSlowPaths() const override; + const Analysis& analysis() const; + int drawableCount() const; + SkPicture const* const* drawablePicts() const; + + const SkRect fCullRect; + const size_t fApproxBytesUsedBySubPictures; + SkLazyPtr fAnalysis; + SkAutoTUnref fRecord; + SkAutoTDelete fDrawablePicts; + SkAutoTUnref fBBH; + SkAutoTUnref fAccelData; +}; + +#endif//SkBigPicture_DEFINED diff --git a/src/core/SkLayerInfo.cpp b/src/core/SkLayerInfo.cpp deleted file mode 100644 index d427fa7c21..0000000000 --- a/src/core/SkLayerInfo.cpp +++ /dev/null @@ -1,15 +0,0 @@ -/* - * Copyright 2014 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include "SkLayerInfo.h" - -SkPicture::AccelData::Key SkLayerInfo::ComputeKey() { - static const SkPicture::AccelData::Key gGPUID = SkPicture::AccelData::GenerateDomain(); - - return gGPUID; -} - diff --git a/src/core/SkLayerInfo.h b/src/core/SkLayerInfo.h index a47c3f1199..3d4b26960c 100644 --- a/src/core/SkLayerInfo.h +++ b/src/core/SkLayerInfo.h @@ -8,12 +8,12 @@ #ifndef SkLayerInfo_DEFINED #define SkLayerInfo_DEFINED -#include "SkPicture.h" +#include "SkBigPicture.h" #include "SkTArray.h" // This class stores information about the saveLayer/restore pairs found // within an SkPicture. It is used by Ganesh to perform layer hoisting. -class SkLayerInfo : public SkPicture::AccelData { +class SkLayerInfo : public SkBigPicture::AccelData { public: // Information about a given saveLayer/restore block in an SkPicture class BlockInfo { @@ -33,12 +33,12 @@ public: SkRect fSrcBounds; // The pre-matrix begins as the identity and accumulates the transforms // of the containing SkPictures (if any). This matrix state has to be - // part of the initial matrix during replay so that it will be + // part of the initial matrix during replay so that it will be // preserved across setMatrix calls. SkMatrix fPreMat; - // The matrix state (in the leaf picture) in which this layer's draws + // The matrix state (in the leaf picture) in which this layer's draws // must occur. It will/can be overridden by setMatrix calls in the - // layer itself. It does not include the translation needed to map the + // layer itself. It does not include the translation needed to map the // layer's top-left point to the origin (which must be part of the // initial matrix). SkMatrix fLocalMat; @@ -60,7 +60,7 @@ public: int fKeySize; // # of ints }; - SkLayerInfo(Key key) : INHERITED(key) { } + SkLayerInfo() {} BlockInfo& addBlock() { return fBlocks.push_back(); } @@ -72,14 +72,10 @@ public: return fBlocks[index]; } - // We may, in the future, need to pass in the GPUDevice in order to - // incorporate the clip and matrix state into the key - static SkPicture::AccelData::Key ComputeKey(); - private: SkTArray fBlocks; - typedef SkPicture::AccelData INHERITED; + typedef SkBigPicture::AccelData INHERITED; }; #endif // SkLayerInfo_DEFINED diff --git a/src/core/SkMiniRecorder.cpp b/src/core/SkMiniRecorder.cpp new file mode 100644 index 0000000000..55dd56add7 --- /dev/null +++ b/src/core/SkMiniRecorder.cpp @@ -0,0 +1,103 @@ +/* + * Copyright 2015 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "SkCanvas.h" +#include "SkLazyPtr.h" +#include "SkMiniRecorder.h" +#include "SkPicture.h" +#include "SkPictureCommon.h" +#include "SkRecordDraw.h" +#include "SkTextBlob.h" + +using namespace SkRecords; + +class SkEmptyPicture final : public SkPicture { +public: + void playback(SkCanvas*, AbortCallback*) const override { } + + size_t approximateBytesUsed() const override { return sizeof(*this); } + int approximateOpCount() const override { return 0; } + SkRect cullRect() const override { return SkRect::MakeEmpty(); } + bool hasText() const override { return false; } + int numSlowPaths() const override { return 0; } + bool willPlayBackBitmaps() const override { return false; } +}; +SK_DECLARE_STATIC_LAZY_PTR(SkEmptyPicture, gEmptyPicture); + +template +class SkMiniPicture final : public SkPicture { +public: + SkMiniPicture(SkRect cull, T* op) : fCull(cull) { + memcpy(&fOp, op, sizeof(fOp)); // We take ownership of op's guts. + } + + void playback(SkCanvas* c, AbortCallback*) const override { + SkRecords::Draw(c, nullptr, nullptr, 0, nullptr)(fOp); + } + + size_t approximateBytesUsed() const override { return sizeof(*this); } + int approximateOpCount() const override { return 1; } + SkRect cullRect() const override { return fCull; } + bool hasText() const override { return SkTextHunter()(fOp); } + bool willPlayBackBitmaps() const override { return SkBitmapHunter()(fOp); } + int numSlowPaths() const override { + SkPathCounter counter; + counter(fOp); + return counter.fNumSlowPathsAndDashEffects; + } + +private: + SkRect fCull; + T fOp; +}; + + +SkMiniRecorder::SkMiniRecorder() : fState(State::kEmpty) {} +SkMiniRecorder::~SkMiniRecorder() { + if (fState != State::kEmpty) { + // We have internal state pending. + // Detaching then deleting a picture is an easy way to clean up. + SkDELETE(this->detachAsPicture(SkRect::MakeEmpty())); + } + SkASSERT(fState == State::kEmpty); +} + +#define TRY_TO_STORE(Type, ...) \ + if (fState != State::kEmpty) { return false; } \ + fState = State::k##Type; \ + new (fBuffer.get()) Type(__VA_ARGS__); \ + return true + +bool SkMiniRecorder::drawRect(const SkRect& rect, const SkPaint& paint) { + TRY_TO_STORE(DrawRect, paint, rect); +} + +bool SkMiniRecorder::drawPath(const SkPath& path, const SkPaint& paint) { + TRY_TO_STORE(DrawPath, paint, path); +} + +bool SkMiniRecorder::drawTextBlob(const SkTextBlob* b, SkScalar x, SkScalar y, const SkPaint& p) { + TRY_TO_STORE(DrawTextBlob, p, b, x, y); +} +#undef TRY_TO_STORE + +#define CASE(Type) \ + case State::k##Type: \ + fState = State::kEmpty; \ + return SkNEW_ARGS(SkMiniPicture, (cull, reinterpret_cast(fBuffer.get()))) + +SkPicture* SkMiniRecorder::detachAsPicture(const SkRect& cull) { + switch (fState) { + case State::kEmpty: return SkRef(gEmptyPicture.get()); + CASE(DrawPath); + CASE(DrawRect); + CASE(DrawTextBlob); + } + SkASSERT(false); + return NULL; +} +#undef CASE diff --git a/src/core/SkMiniRecorder.h b/src/core/SkMiniRecorder.h new file mode 100644 index 0000000000..d01aedacea --- /dev/null +++ b/src/core/SkMiniRecorder.h @@ -0,0 +1,44 @@ +/* + * Copyright 2015 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkMiniRecorder_DEFINED +#define SkMiniRecorder_DEFINED + +#include "SkRecords.h" +#include "SkScalar.h" +#include "SkTypes.h" +class SkCanvas; + +// Records small pictures, but only a limited subset of the canvas API, and may fail. +class SkMiniRecorder : SkNoncopyable { +public: + SkMiniRecorder(); + ~SkMiniRecorder(); + + // Try to record an op. Returns false on failure. + bool drawPath(const SkPath&, const SkPaint&); + bool drawRect(const SkRect&, const SkPaint&); + bool drawTextBlob(const SkTextBlob*, SkScalar x, SkScalar y, const SkPaint&); + + // Detach anything we've recorded as a picture, resetting this SkMiniRecorder. + SkPicture* detachAsPicture(const SkRect& cull); + +private: + enum class State { kEmpty, kDrawPath, kDrawRect, kDrawTextBlob }; + + State fState; + + template + struct Max { static const size_t val = A > B ? A : B; }; + + static const size_t kInlineStorage = Max::val>::val; + SkAlignedSStorage fBuffer; +}; + +#endif//SkMiniRecorder_DEFINED diff --git a/src/core/SkPicture.cpp b/src/core/SkPicture.cpp index 0145cbe5d7..6c1f6cb759 100644 --- a/src/core/SkPicture.cpp +++ b/src/core/SkPicture.cpp @@ -5,330 +5,107 @@ * found in the LICENSE file. */ - -#include "SkPictureFlat.h" +#include "SkAtomics.h" +#include "SkMessageBus.h" +#include "SkPicture.h" #include "SkPictureData.h" #include "SkPicturePlayback.h" #include "SkPictureRecord.h" #include "SkPictureRecorder.h" -#include "SkAtomics.h" -#include "SkBitmapDevice.h" -#include "SkCanvas.h" -#include "SkChunkAlloc.h" -#include "SkMessageBus.h" -#include "SkPaintPriv.h" -#include "SkPathEffect.h" -#include "SkPicture.h" -#include "SkRegion.h" -#include "SkShader.h" -#include "SkStream.h" -#include "SkTDArray.h" -#include "SkTLogic.h" -#include "SkTSearch.h" -#include "SkTime.h" - -#include "SkReader32.h" -#include "SkWriter32.h" -#include "SkRTree.h" - -#if SK_SUPPORT_GPU -#include "GrContext.h" -#endif - -#include "SkRecord.h" -#include "SkRecordDraw.h" -#include "SkRecordOpts.h" -#include "SkRecorder.h" - DECLARE_SKMESSAGEBUS_MESSAGE(SkPicture::DeletionMessage); -template int SafeCount(const T* obj) { - return obj ? obj->count() : 0; -} - -/////////////////////////////////////////////////////////////////////////////// - -namespace { - -// Some commands have a paint, some have an optional paint. Either way, get back a pointer. -static const SkPaint* AsPtr(const SkPaint& p) { return &p; } -static const SkPaint* AsPtr(const SkRecords::Optional& p) { return p; } - -/** SkRecords visitor to determine whether an instance may require an - "external" bitmap to rasterize. May return false positives. - Does not return true for bitmap text. - - Expected use is to determine whether images need to be decoded before - rasterizing a particular SkRecord. - */ -struct BitmapTester { - // Helpers. These create HasMember_bitmap and HasMember_paint. - SK_CREATE_MEMBER_DETECTOR(bitmap); - SK_CREATE_MEMBER_DETECTOR(paint); - +/* SkPicture impl. This handles generic responsibilities like unique IDs and serialization. */ - // Main entry for visitor: - // If the command is a DrawPicture, recurse. - // If the command has a bitmap directly, return true. - // If the command has a paint and the paint has a bitmap, return true. - // Otherwise, return false. - bool operator()(const SkRecords::DrawPicture& op) { return op.picture->willPlayBackBitmaps(); } - - template - bool operator()(const T& r) { return CheckBitmap(r); } - - - // If the command has a bitmap, of course we're going to play back bitmaps. - template - static SK_WHEN(HasMember_bitmap, bool) CheckBitmap(const T&) { return true; } - - // If not, look for one in its paint (if it has a paint). - template - static SK_WHEN(!HasMember_bitmap, bool) CheckBitmap(const T& r) { return CheckPaint(r); } - - // If we have a paint, dig down into the effects looking for a bitmap. - template - static SK_WHEN(HasMember_paint, bool) CheckPaint(const T& r) { - const SkPaint* paint = AsPtr(r.paint); - if (paint) { - const SkShader* shader = paint->getShader(); - if (shader && - shader->asABitmap(NULL, NULL, NULL) == SkShader::kDefault_BitmapType) { - return true; - } - } - return false; - } - - // If we don't have a paint, that non-paint has no bitmap. - template - static SK_WHEN(!HasMember_paint, bool) CheckPaint(const T&) { return false; } -}; - -bool WillPlaybackBitmaps(const SkRecord& record) { - BitmapTester tester; - for (unsigned i = 0; i < record.count(); i++) { - if (record.visit(i, tester)) { - return true; - } - } - return false; -} - -// SkRecord visitor to find recorded text. -struct TextHunter { - // All ops with text have that text as a char array member named "text". - SK_CREATE_MEMBER_DETECTOR(text); - bool operator()(const SkRecords::DrawPicture& op) { return op.picture->hasText(); } - template SK_WHEN(HasMember_text, bool) operator()(const T&) { return true; } - template SK_WHEN(!HasMember_text, bool) operator()(const T&) { return false; } -}; - -} // namespace - -/** SkRecords visitor to determine heuristically whether or not a SkPicture - will be performant when rasterized on the GPU. - */ -struct SkPicture::PathCounter { - SK_CREATE_MEMBER_DETECTOR(paint); - - PathCounter() : fNumSlowPathsAndDashEffects(0) {} - - // Recurse into nested pictures. - void operator()(const SkRecords::DrawPicture& op) { - const SkPicture::Analysis& analysis = op.picture->analysis(); - fNumSlowPathsAndDashEffects += analysis.fNumSlowPathsAndDashEffects; - } - - void checkPaint(const SkPaint* paint) { - if (paint && paint->getPathEffect()) { - // Initially assume it's slow. - fNumSlowPathsAndDashEffects++; - } - } - - void operator()(const SkRecords::DrawPoints& op) { - this->checkPaint(&op.paint); - const SkPathEffect* effect = op.paint.getPathEffect(); - if (effect) { - SkPathEffect::DashInfo info; - SkPathEffect::DashType dashType = effect->asADash(&info); - if (2 == op.count && SkPaint::kRound_Cap != op.paint.getStrokeCap() && - SkPathEffect::kDash_DashType == dashType && 2 == info.fCount) { - fNumSlowPathsAndDashEffects--; - } - } - } - - void operator()(const SkRecords::DrawPath& op) { - this->checkPaint(&op.paint); - if (op.paint.isAntiAlias() && !op.path.isConvex()) { - SkPaint::Style paintStyle = op.paint.getStyle(); - const SkRect& pathBounds = op.path.getBounds(); - if (SkPaint::kStroke_Style == paintStyle && - 0 == op.paint.getStrokeWidth()) { - // AA hairline concave path is not slow. - } else if (SkPaint::kFill_Style == paintStyle && pathBounds.width() < 64.f && - pathBounds.height() < 64.f && !op.path.isVolatile()) { - // AADF eligible concave path is not slow. - } else { - fNumSlowPathsAndDashEffects++; - } - } - } - - template - SK_WHEN(HasMember_paint, void) operator()(const T& op) { - this->checkPaint(AsPtr(op.paint)); - } - - template - SK_WHEN(!HasMember_paint, void) operator()(const T& op) { /* do nothing */ } - - int fNumSlowPathsAndDashEffects; -}; - -SkPicture::Analysis::Analysis(const SkRecord& record) { - fWillPlaybackBitmaps = WillPlaybackBitmaps(record); - - PathCounter counter; - for (unsigned i = 0; i < record.count(); i++) { - record.visit(i, counter); - } - fNumSlowPathsAndDashEffects = SkTMin(counter.fNumSlowPathsAndDashEffects, 255); - - fHasText = false; - TextHunter text; - for (unsigned i = 0; i < record.count(); i++) { - if (record.visit(i, text)) { - fHasText = true; - break; - } - } -} - -bool SkPicture::Analysis::suitableForGpuRasterization(const char** reason, - int sampleCount) const { - // TODO: the heuristic used here needs to be refined - static const int kNumSlowPathsTol = 6; - - bool ret = fNumSlowPathsAndDashEffects < kNumSlowPathsTol; - - if (!ret && reason) { - *reason = "Too many slow paths (either concave or dashed)."; - } - return ret; -} - -/////////////////////////////////////////////////////////////////////////////// - -int SkPicture::drawableCount() const { - return fDrawablePicts.get() ? fDrawablePicts->count() : 0; -} - -SkPicture const* const* SkPicture::drawablePicts() const { - return fDrawablePicts.get() ? fDrawablePicts->begin() : NULL; -} +SkPicture::SkPicture() : fUniqueID(0) {} SkPicture::~SkPicture() { + // TODO: move this to ~SkBigPicture() only? + // If the ID is still zero, no one has read it, so no need to send this message. uint32_t id = sk_atomic_load(&fUniqueID, sk_memory_order_relaxed); if (id != 0) { - SkPicture::DeletionMessage msg; - msg.fUniqueID = id; + SkPicture::DeletionMessage msg = { (int32_t)id }; SkMessageBus::Post(msg); } } -const SkPicture::AccelData* SkPicture::EXPERIMENTAL_getAccelData( - SkPicture::AccelData::Key key) const { - if (fAccelData.get() && fAccelData->getKey() == key) { - return fAccelData.get(); - } - return NULL; -} - -SkPicture::AccelData::Domain SkPicture::AccelData::GenerateDomain() { - static int32_t gNextID = 0; - - int32_t id = sk_atomic_inc(&gNextID); - if (id >= 1 << (8 * sizeof(Domain))) { - SK_CRASH(); +uint32_t SkPicture::uniqueID() const { + static uint32_t gNextID = 1; + uint32_t id = sk_atomic_load(&fUniqueID, sk_memory_order_relaxed); + while (id == 0) { + uint32_t next = sk_atomic_fetch_add(&gNextID, 1u); + if (sk_atomic_compare_exchange(&fUniqueID, &id, next, + sk_memory_order_relaxed, + sk_memory_order_relaxed)) { + id = next; + } else { + // sk_atomic_compare_exchange replaced id with the current value of fUniqueID. + } } - - return static_cast(id); + return id; } -/////////////////////////////////////////////////////////////////////////////// +static const char kMagic[] = { 's', 'k', 'i', 'a', 'p', 'i', 'c', 't' }; -void SkPicture::playback(SkCanvas* canvas, AbortCallback* callback) const { - SkASSERT(canvas); +SkPictInfo SkPicture::createHeader() const { + SkPictInfo info; + // Copy magic bytes at the beginning of the header + static_assert(sizeof(kMagic) == 8, ""); + static_assert(sizeof(kMagic) == sizeof(info.fMagic), ""); + memcpy(info.fMagic, kMagic, sizeof(kMagic)); - // If the query contains the whole picture, don't bother with the BBH. - SkRect clipBounds = { 0, 0, 0, 0 }; - (void)canvas->getClipBounds(&clipBounds); - const bool useBBH = !clipBounds.contains(this->cullRect()); + // Set picture info after magic bytes in the header + info.fVersion = CURRENT_PICTURE_VERSION; + info.fCullRect = this->cullRect(); + info.fFlags = SkPictInfo::kCrossProcess_Flag; + // TODO: remove this flag, since we're always float (now) + info.fFlags |= SkPictInfo::kScalarIsFloat_Flag; - SkRecordDraw(*fRecord, canvas, this->drawablePicts(), NULL, this->drawableCount(), - useBBH ? fBBH.get() : NULL, callback); + if (8 == sizeof(void*)) { + info.fFlags |= SkPictInfo::kPtrIs64Bit_Flag; + } + return info; } -/////////////////////////////////////////////////////////////////////////////// - -#include "SkStream.h" - -static const char kMagic[] = { 's', 'k', 'i', 'a', 'p', 'i', 'c', 't' }; - bool SkPicture::IsValidPictInfo(const SkPictInfo& info) { if (0 != memcmp(info.fMagic, kMagic, sizeof(kMagic))) { return false; } - - if (info.fVersion < MIN_PICTURE_VERSION || - info.fVersion > CURRENT_PICTURE_VERSION) { + if (info.fVersion < MIN_PICTURE_VERSION || info.fVersion > CURRENT_PICTURE_VERSION) { return false; } - return true; } bool SkPicture::InternalOnly_StreamIsSKP(SkStream* stream, SkPictInfo* pInfo) { - if (NULL == stream) { + if (!stream) { return false; } - // Check magic bytes. SkPictInfo info; SkASSERT(sizeof(kMagic) == sizeof(info.fMagic)); - if (!stream->read(&info.fMagic, sizeof(kMagic))) { return false; } - info.fVersion = stream->readU32(); - info.fCullRect.fLeft = stream->readScalar(); - info.fCullRect.fTop = stream->readScalar(); - info.fCullRect.fRight = stream->readScalar(); + info.fVersion = stream->readU32(); + info.fCullRect.fLeft = stream->readScalar(); + info.fCullRect.fTop = stream->readScalar(); + info.fCullRect.fRight = stream->readScalar(); info.fCullRect.fBottom = stream->readScalar(); + info.fFlags = stream->readU32(); - info.fFlags = stream->readU32(); - - if (!IsValidPictInfo(info)) { - return false; - } - - if (pInfo != NULL) { - *pInfo = info; + if (IsValidPictInfo(info)) { + if (pInfo) { *pInfo = info; } + return true; } - return true; + return false; } bool SkPicture::InternalOnly_BufferIsSKP(SkReadBuffer* buffer, SkPictInfo* pInfo) { - // Check magic bytes. SkPictInfo info; SkASSERT(sizeof(kMagic) == sizeof(info.fMagic)); - if (!buffer->readByteArray(&info.fMagic, sizeof(kMagic))) { return false; } @@ -337,32 +114,29 @@ bool SkPicture::InternalOnly_BufferIsSKP(SkReadBuffer* buffer, SkPictInfo* pInfo buffer->readRect(&info.fCullRect); info.fFlags = buffer->readUInt(); - if (!IsValidPictInfo(info)) { - return false; + if (IsValidPictInfo(info)) { + if (pInfo) { *pInfo = info; } + return true; } - - if (pInfo != NULL) { - *pInfo = info; - } - return true; + return false; } SkPicture* SkPicture::Forwardport(const SkPictInfo& info, const SkPictureData* data) { if (!data) { - return NULL; + return nullptr; } SkPicturePlayback playback(data); SkPictureRecorder r; playback.draw(r.beginRecording(SkScalarCeilToInt(info.fCullRect.width()), SkScalarCeilToInt(info.fCullRect.height())), - NULL/*no callback*/); + nullptr/*no callback*/); return r.endRecording(); } SkPicture* SkPicture::CreateFromStream(SkStream* stream, InstallPixelRefProc proc) { SkPictInfo info; if (!InternalOnly_StreamIsSKP(stream, &info) || !stream->readBool()) { - return NULL; + return nullptr; } SkAutoTDelete data(SkPictureData::CreateFromStream(stream, info, proc)); return Forwardport(info, data); @@ -371,45 +145,24 @@ SkPicture* SkPicture::CreateFromStream(SkStream* stream, InstallPixelRefProc pro SkPicture* SkPicture::CreateFromBuffer(SkReadBuffer& buffer) { SkPictInfo info; if (!InternalOnly_BufferIsSKP(&buffer, &info) || !buffer.readBool()) { - return NULL; + return nullptr; } SkAutoTDelete data(SkPictureData::CreateFromBuffer(buffer, info)); return Forwardport(info, data); } -void SkPicture::createHeader(SkPictInfo* info) const { - // Copy magic bytes at the beginning of the header - SkASSERT(sizeof(kMagic) == 8); - SkASSERT(sizeof(kMagic) == sizeof(info->fMagic)); - memcpy(info->fMagic, kMagic, sizeof(kMagic)); - - // Set picture info after magic bytes in the header - info->fVersion = CURRENT_PICTURE_VERSION; - info->fCullRect = this->cullRect(); - info->fFlags = SkPictInfo::kCrossProcess_Flag; - // TODO: remove this flag, since we're always float (now) - info->fFlags |= SkPictInfo::kScalarIsFloat_Flag; - - if (8 == sizeof(void*)) { - info->fFlags |= SkPictInfo::kPtrIs64Bit_Flag; - } -} - -// This for compatibility with serialization code only. This is not cheap. -SkPictureData* SkPicture::Backport(const SkRecord& src, const SkPictInfo& info, - SkPicture const* const drawablePicts[], int drawableCount) { +SkPictureData* SkPicture::backport() const { + SkPictInfo info = this->createHeader(); SkPictureRecord rec(SkISize::Make(info.fCullRect.width(), info.fCullRect.height()), 0/*flags*/); rec.beginRecording(); - SkRecordDraw(src, &rec, drawablePicts, NULL, drawableCount, NULL/*bbh*/, NULL/*callback*/); + this->playback(&rec); rec.endRecording(); return SkNEW_ARGS(SkPictureData, (rec, info, false/*deep copy ops?*/)); } void SkPicture::serialize(SkWStream* stream, SkPixelSerializer* pixelSerializer) const { - SkPictInfo info; - this->createHeader(&info); - SkAutoTDelete data(Backport(*fRecord, info, this->drawablePicts(), - this->drawableCount())); + SkPictInfo info = this->createHeader(); + SkAutoTDelete data(this->backport()); stream->write(&info, sizeof(info)); if (data) { @@ -421,10 +174,8 @@ void SkPicture::serialize(SkWStream* stream, SkPixelSerializer* pixelSerializer) } void SkPicture::flatten(SkWriteBuffer& buffer) const { - SkPictInfo info; - this->createHeader(&info); - SkAutoTDelete data(Backport(*fRecord, info, this->drawablePicts(), - this->drawableCount())); + SkPictInfo info = this->createHeader(); + SkAutoTDelete data(this->backport()); buffer.writeByteArray(&info.fMagic, sizeof(info.fMagic)); buffer.writeUInt(info.fVersion); @@ -438,49 +189,10 @@ void SkPicture::flatten(SkWriteBuffer& buffer) const { } } -const SkPicture::Analysis& SkPicture::analysis() const { - auto create = [&](){ return SkNEW_ARGS(Analysis, (*fRecord)); }; - return *fAnalysis.get(create); -} - -#if SK_SUPPORT_GPU -bool SkPicture::suitableForGpuRasterization(GrContext*, const char **reason) const { - return this->analysis().suitableForGpuRasterization(reason, 0); -} -#endif - -bool SkPicture::hasText() const { return this->analysis().fHasText; } -bool SkPicture::willPlayBackBitmaps() const { return this->analysis().fWillPlaybackBitmaps; } -int SkPicture::approximateOpCount() const { return fRecord->count(); } - -SkPicture::SkPicture(const SkRect& cullRect, - SkRecord* record, - SnapshotArray* drawablePicts, - SkBBoxHierarchy* bbh, - AccelData* accelData, - size_t approxBytesUsedBySubPictures) - : fUniqueID(0) - , fCullRect(cullRect) - , fRecord(record) // Take ownership of caller's ref. - , fDrawablePicts(drawablePicts) // Take ownership. - , fBBH(bbh) // Take ownership of caller's ref. - , fAccelData(accelData) // Take ownership of caller's ref. - , fApproxBytesUsedBySubPictures(approxBytesUsedBySubPictures) -{} - - -static uint32_t gNextID = 1; -uint32_t SkPicture::uniqueID() const { - uint32_t id = sk_atomic_load(&fUniqueID, sk_memory_order_relaxed); - while (id == 0) { - uint32_t next = sk_atomic_fetch_add(&gNextID, 1u); - if (sk_atomic_compare_exchange(&fUniqueID, &id, next, - sk_memory_order_relaxed, - sk_memory_order_relaxed)) { - id = next; - } else { - // sk_atomic_compare_exchange replaced id with the current value of fUniqueID. - } +bool SkPicture::suitableForGpuRasterization(GrContext*, const char** whyNot) const { + if (this->numSlowPaths() > 5) { + if (whyNot) { *whyNot = "Too many slow paths (either concave or dashed)."; } + return false; } - return id; + return true; } diff --git a/src/core/SkPictureCommon.h b/src/core/SkPictureCommon.h new file mode 100644 index 0000000000..827bf117de --- /dev/null +++ b/src/core/SkPictureCommon.h @@ -0,0 +1,136 @@ +/* + * Copyright 2015 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +// Some shared code used by both SkBigPicture and SkMiniPicture. +// SkTextHunter -- SkRecord visitor that returns true when the op draws text. +// SkBitmapHunter -- SkRecord visitor that returns true when the op draws a bitmap. +// SkPathCounter -- SkRecord visitor that counts paths that draw slowly on the GPU. + +#include "SkPathEffect.h" +#include "SkRecords.h" +#include "SkTLogic.h" + +struct SkTextHunter { + // Most ops never have text. Some always do. Subpictures know themeselves. + template bool operator()(const T&) { return false; } + bool operator()(const SkRecords::DrawPosText&) { return true; } + bool operator()(const SkRecords::DrawPosTextH&) { return true; } + bool operator()(const SkRecords::DrawText&) { return true; } + bool operator()(const SkRecords::DrawTextBlob&) { return true; } + bool operator()(const SkRecords::DrawTextOnPath&) { return true; } + bool operator()(const SkRecords::DrawPicture& op) { return op.picture->hasText(); } +}; + + +struct SkBitmapHunter { + // Helpers. These create HasMember_bitmap and HasMember_paint. + SK_CREATE_MEMBER_DETECTOR(bitmap); + SK_CREATE_MEMBER_DETECTOR(paint); + + // Some ops have a paint, some have an optional paint. Either way, get back a pointer. + static const SkPaint* AsPtr(const SkPaint& p) { return &p; } + static const SkPaint* AsPtr(const SkRecords::Optional& p) { return p; } + + // Main entry for visitor: + // If the op is a DrawPicture, recurse. + // If the op has a bitmap directly, return true. + // If the op has a paint and the paint has a bitmap, return true. + // Otherwise, return false. + bool operator()(const SkRecords::DrawPicture& op) { return op.picture->willPlayBackBitmaps(); } + + template + bool operator()(const T& r) { return CheckBitmap(r); } + + // If the op has a bitmap, of course we're going to play back bitmaps. + template + static SK_WHEN(HasMember_bitmap, bool) CheckBitmap(const T&) { return true; } + + // If not, look for one in its paint (if it has a paint). + template + static SK_WHEN(!HasMember_bitmap, bool) CheckBitmap(const T& r) { return CheckPaint(r); } + + // If we have a paint, dig down into the effects looking for a bitmap. + template + static SK_WHEN(HasMember_paint, bool) CheckPaint(const T& r) { + const SkPaint* paint = AsPtr(r.paint); + if (paint) { + const SkShader* shader = paint->getShader(); + if (shader && + shader->asABitmap(nullptr, nullptr, nullptr) == SkShader::kDefault_BitmapType) { + return true; + } + } + return false; + } + + // If we don't have a paint, that non-paint has no bitmap. + template + static SK_WHEN(!HasMember_paint, bool) CheckPaint(const T&) { return false; } +}; + +// TODO: might be nicer to have operator() return an int (the number of slow paths) ? +struct SkPathCounter { + SK_CREATE_MEMBER_DETECTOR(paint); + + // Some ops have a paint, some have an optional paint. Either way, get back a pointer. + static const SkPaint* AsPtr(const SkPaint& p) { return &p; } + static const SkPaint* AsPtr(const SkRecords::Optional& p) { return p; } + + SkPathCounter() : fNumSlowPathsAndDashEffects(0) {} + + // Recurse into nested pictures. + void operator()(const SkRecords::DrawPicture& op) { + fNumSlowPathsAndDashEffects += op.picture->numSlowPaths(); + } + + void checkPaint(const SkPaint* paint) { + if (paint && paint->getPathEffect()) { + // Initially assume it's slow. + fNumSlowPathsAndDashEffects++; + } + } + + void operator()(const SkRecords::DrawPoints& op) { + this->checkPaint(&op.paint); + const SkPathEffect* effect = op.paint.getPathEffect(); + if (effect) { + SkPathEffect::DashInfo info; + SkPathEffect::DashType dashType = effect->asADash(&info); + if (2 == op.count && SkPaint::kRound_Cap != op.paint.getStrokeCap() && + SkPathEffect::kDash_DashType == dashType && 2 == info.fCount) { + fNumSlowPathsAndDashEffects--; + } + } + } + + void operator()(const SkRecords::DrawPath& op) { + this->checkPaint(&op.paint); + if (op.paint.isAntiAlias() && !op.path.isConvex()) { + SkPaint::Style paintStyle = op.paint.getStyle(); + const SkRect& pathBounds = op.path.getBounds(); + if (SkPaint::kStroke_Style == paintStyle && + 0 == op.paint.getStrokeWidth()) { + // AA hairline concave path is not slow. + } else if (SkPaint::kFill_Style == paintStyle && pathBounds.width() < 64.f && + pathBounds.height() < 64.f && !op.path.isVolatile()) { + // AADF eligible concave path is not slow. + } else { + fNumSlowPathsAndDashEffects++; + } + } + } + + template + SK_WHEN(HasMember_paint, void) operator()(const T& op) { + this->checkPaint(AsPtr(op.paint)); + } + + template + SK_WHEN(!HasMember_paint, void) operator()(const T& op) { /* do nothing */ } + + int fNumSlowPathsAndDashEffects; +}; diff --git a/src/core/SkPictureRecorder.cpp b/src/core/SkPictureRecorder.cpp index 282e2c22dd..c110e0a2db 100644 --- a/src/core/SkPictureRecorder.cpp +++ b/src/core/SkPictureRecorder.cpp @@ -5,6 +5,7 @@ * found in the LICENSE file. */ +#include "SkBigPicture.h" #include "SkData.h" #include "SkDrawable.h" #include "SkLayerInfo.h" @@ -18,7 +19,7 @@ SkPictureRecorder::SkPictureRecorder() { fActivelyRecording = false; - fRecorder.reset(SkNEW_ARGS(SkRecorder, (nullptr, SkRect::MakeWH(0,0)))); + fRecorder.reset(SkNEW_ARGS(SkRecorder, (nullptr, SkRect::MakeWH(0,0), &fMiniRecorder))); } SkPictureRecorder::~SkPictureRecorder() {} @@ -34,8 +35,10 @@ SkCanvas* SkPictureRecorder::beginRecording(const SkRect& cullRect, SkASSERT(fBBH.get()); } - fRecord.reset(SkNEW(SkRecord)); - fRecorder->reset(fRecord.get(), cullRect); + if (!fRecord) { + fRecord.reset(SkNEW(SkRecord)); + } + fRecorder->reset(fRecord.get(), cullRect, &fMiniRecorder); fActivelyRecording = true; return this->getRecordingCanvas(); } @@ -47,19 +50,23 @@ SkCanvas* SkPictureRecorder::getRecordingCanvas() { SkPicture* SkPictureRecorder::endRecordingAsPicture() { fActivelyRecording = false; fRecorder->restoreToCount(1); // If we were missing any restores, add them now. + + if (fRecord->count() == 0) { + return fMiniRecorder.detachAsPicture(fCullRect); + } + // TODO: delay as much of this work until just before first playback? SkRecordOptimize(fRecord); SkAutoTUnref saveLayerData; if (fBBH && (fFlags & kComputeSaveLayerInfo_RecordFlag)) { - SkPicture::AccelData::Key key = SkLayerInfo::ComputeKey(); - - saveLayerData.reset(SkNEW_ARGS(SkLayerInfo, (key))); + saveLayerData.reset(SkNEW(SkLayerInfo)); } SkDrawableList* drawableList = fRecorder->getDrawableList(); - SkPicture::SnapshotArray* pictList = drawableList ? drawableList->newDrawableSnapshot() : NULL; + SkBigPicture::SnapshotArray* pictList = + drawableList ? drawableList->newDrawableSnapshot() : NULL; if (fBBH.get()) { if (saveLayerData) { @@ -77,12 +84,12 @@ SkPicture* SkPictureRecorder::endRecordingAsPicture() { for (int i = 0; pictList && i < pictList->count(); i++) { subPictureBytes += SkPictureUtils::ApproximateBytesUsed(pictList->begin()[i]); } - return SkNEW_ARGS(SkPicture, (fCullRect, - fRecord.detach(), - pictList, - fBBH.detach(), - saveLayerData.detach(), - subPictureBytes)); + return SkNEW_ARGS(SkBigPicture, (fCullRect, + fRecord.detach(), + pictList, + fBBH.detach(), + saveLayerData.detach(), + subPictureBytes)); } void SkPictureRecorder::partialReplay(SkCanvas* canvas) const { @@ -133,7 +140,7 @@ protected: } SkPicture* onNewPictureSnapshot() override { - SkPicture::SnapshotArray* pictList = NULL; + SkBigPicture::SnapshotArray* pictList = NULL; if (fDrawableList) { // TODO: should we plumb-down the BBHFactory and recordFlags from our host // PictureRecorder? @@ -143,9 +150,7 @@ protected: SkAutoTUnref saveLayerData; if (fBBH && fDoSaveLayerInfo) { - SkPicture::AccelData::Key key = SkLayerInfo::ComputeKey(); - - saveLayerData.reset(SkNEW_ARGS(SkLayerInfo, (key))); + saveLayerData.reset(SkNEW(SkLayerInfo)); SkBBoxHierarchy* bbh = NULL; // we've already computed fBBH (received in constructor) // TODO: update saveLayer info computation to reuse the already computed @@ -157,20 +162,22 @@ protected: for (int i = 0; pictList && i < pictList->count(); i++) { subPictureBytes += SkPictureUtils::ApproximateBytesUsed(pictList->begin()[i]); } - // SkPicture will take ownership of a ref on both fRecord and fBBH. + // SkBigPicture will take ownership of a ref on both fRecord and fBBH. // We're not willing to give up our ownership, so we must ref them for SkPicture. - return SkNEW_ARGS(SkPicture, (fBounds, - SkRef(fRecord.get()), - pictList, - SkSafeRef(fBBH.get()), - saveLayerData.detach(), - subPictureBytes)); + return SkNEW_ARGS(SkBigPicture, (fBounds, + SkRef(fRecord.get()), + pictList, + SkSafeRef(fBBH.get()), + saveLayerData.detach(), + subPictureBytes)); } }; SkDrawable* SkPictureRecorder::endRecordingAsDrawable() { fActivelyRecording = false; + fRecorder->flushMiniRecorder(); fRecorder->restoreToCount(1); // If we were missing any restores, add them now. + // TODO: delay as much of this work until just before first playback? SkRecordOptimize(fRecord); diff --git a/src/core/SkRecordDraw.cpp b/src/core/SkRecordDraw.cpp index ba15c1b512..65e6d490db 100644 --- a/src/core/SkRecordDraw.cpp +++ b/src/core/SkRecordDraw.cpp @@ -587,7 +587,7 @@ private: class CollectLayers : SkNoncopyable { public: CollectLayers(const SkRect& cullRect, const SkRecord& record, - const SkPicture::SnapshotArray* pictList, SkLayerInfo* accelData) + const SkBigPicture::SnapshotArray* pictList, SkLayerInfo* accelData) : fSaveLayersInStack(0) , fAccelData(accelData) , fPictList(pictList) @@ -640,10 +640,10 @@ private: void trackSaveLayersForPicture(const SkPicture* picture, const SkPaint* paint) { // For sub-pictures, we wrap their layer information within the parent // picture's rendering hierarchy - SkPicture::AccelData::Key key = SkLayerInfo::ComputeKey(); - - const SkLayerInfo* childData = - static_cast(picture->EXPERIMENTAL_getAccelData(key)); + const SkLayerInfo* childData = NULL; + if (const SkBigPicture* bp = picture->asSkBigPicture()) { + childData = static_cast(bp->accelData()); + } if (!childData) { // If the child layer hasn't been generated with saveLayer data we // assume the worst (i.e., that it does contain layers which nest @@ -774,7 +774,7 @@ private: // The op code indices of all the currently active saveLayers SkTDArray fSaveLayerOpStack; SkLayerInfo* fAccelData; - const SkPicture::SnapshotArray* fPictList; + const SkBigPicture::SnapshotArray* fPictList; SkRecords::FillBounds fFillBounds; }; @@ -793,7 +793,7 @@ void SkRecordFillBounds(const SkRect& cullRect, const SkRecord& record, SkBBoxHi } void SkRecordComputeLayers(const SkRect& cullRect, const SkRecord& record, - const SkPicture::SnapshotArray* pictList, SkBBoxHierarchy* bbh, + const SkBigPicture::SnapshotArray* pictList, SkBBoxHierarchy* bbh, SkLayerInfo* data) { SkRecords::CollectLayers visitor(cullRect, record, pictList, data); diff --git a/src/core/SkRecordDraw.h b/src/core/SkRecordDraw.h index 7bbab81b5a..5b248cfbd5 100644 --- a/src/core/SkRecordDraw.h +++ b/src/core/SkRecordDraw.h @@ -9,6 +9,7 @@ #define SkRecordDraw_DEFINED #include "SkBBoxHierarchy.h" +#include "SkBigPicture.h" #include "SkCanvas.h" #include "SkMatrix.h" #include "SkRecord.h" @@ -20,7 +21,7 @@ class SkLayerInfo; void SkRecordFillBounds(const SkRect& cullRect, const SkRecord&, SkBBoxHierarchy*); void SkRecordComputeLayers(const SkRect& cullRect, const SkRecord& record, - const SkPicture::SnapshotArray*, + const SkBigPicture::SnapshotArray*, SkBBoxHierarchy* bbh, SkLayerInfo* data); // Draw an SkRecord into an SkCanvas. A convenience wrapper around SkRecords::Draw. diff --git a/src/core/SkRecorder.cpp b/src/core/SkRecorder.cpp index 0a2d43edbf..45283f8118 100644 --- a/src/core/SkRecorder.cpp +++ b/src/core/SkRecorder.cpp @@ -5,6 +5,7 @@ * found in the LICENSE file. */ +#include "SkBigPicture.h" #include "SkPatchUtils.h" #include "SkPicture.h" #include "SkPictureUtils.h" @@ -14,7 +15,7 @@ SkDrawableList::~SkDrawableList() { fArray.unrefAll(); } -SkPicture::SnapshotArray* SkDrawableList::newDrawableSnapshot() { +SkBigPicture::SnapshotArray* SkDrawableList::newDrawableSnapshot() { const int count = fArray.count(); if (0 == count) { return NULL; @@ -23,7 +24,7 @@ SkPicture::SnapshotArray* SkDrawableList::newDrawableSnapshot() { for (int i = 0; i < count; ++i) { pics[i] = fArray[i]->newPictureSnapshot(); } - return SkNEW_ARGS(SkPicture::SnapshotArray, (pics.detach(), count)); + return SkNEW_ARGS(SkBigPicture::SnapshotArray, (pics.detach(), count)); } void SkDrawableList::append(SkDrawable* drawable) { @@ -32,20 +33,23 @@ void SkDrawableList::append(SkDrawable* drawable) { /////////////////////////////////////////////////////////////////////////////////////////////// -SkRecorder::SkRecorder(SkRecord* record, int width, int height) +SkRecorder::SkRecorder(SkRecord* record, int width, int height, SkMiniRecorder* mr) : SkCanvas(SkIRect::MakeWH(width, height), SkCanvas::kConservativeRasterClip_InitFlag) , fApproxBytesUsedBySubPictures(0) - , fRecord(record) {} + , fRecord(record) + , fMiniRecorder(mr) {} -SkRecorder::SkRecorder(SkRecord* record, const SkRect& bounds) +SkRecorder::SkRecorder(SkRecord* record, const SkRect& bounds, SkMiniRecorder* mr) : SkCanvas(bounds.roundOut(), SkCanvas::kConservativeRasterClip_InitFlag) , fApproxBytesUsedBySubPictures(0) - , fRecord(record) {} + , fRecord(record) + , fMiniRecorder(mr) {} -void SkRecorder::reset(SkRecord* record, const SkRect& bounds) { +void SkRecorder::reset(SkRecord* record, const SkRect& bounds, SkMiniRecorder* mr) { this->forgetRecord(); fRecord = record; this->resetForNextPicture(bounds.roundOut()); + fMiniRecorder = mr; } void SkRecorder::forgetRecord() { @@ -55,9 +59,13 @@ void SkRecorder::forgetRecord() { } // To make appending to fRecord a little less verbose. -#define APPEND(T, ...) \ +#define APPEND(T, ...) \ + if (fMiniRecorder) { this->flushMiniRecorder(); } \ SkNEW_PLACEMENT_ARGS(fRecord->append(), SkRecords::T, (__VA_ARGS__)) +#define TRY_MINIRECORDER(method, ...) \ + if (fMiniRecorder && fMiniRecorder->method(__VA_ARGS__)) { return; } + // For methods which must call back into SkCanvas. #define INHERITED(method, ...) this->SkCanvas::method(__VA_ARGS__) @@ -125,6 +133,15 @@ char* SkRecorder::copy(const char* src) { return this->copy(src, strlen(src)+1); } +void SkRecorder::flushMiniRecorder() { + if (fMiniRecorder) { + SkMiniRecorder* mr = fMiniRecorder; + fMiniRecorder = nullptr; // Needs to happen before p->playback(this) or we loop forever. + // TODO: this can probably be done more efficiently by SkMiniRecorder if it matters. + SkAutoTUnref p(mr->detachAsPicture(SkRect::MakeEmpty())); + p->playback(this); + } +} void SkRecorder::onDrawPaint(const SkPaint& paint) { APPEND(DrawPaint, delay_copy(paint)); @@ -138,6 +155,7 @@ void SkRecorder::onDrawPoints(PointMode mode, } void SkRecorder::onDrawRect(const SkRect& rect, const SkPaint& paint) { + TRY_MINIRECORDER(drawRect, rect, paint); APPEND(DrawRect, delay_copy(paint), rect); } @@ -162,6 +180,7 @@ void SkRecorder::onDrawDrawable(SkDrawable* drawable) { } void SkRecorder::onDrawPath(const SkPath& path, const SkPaint& paint) { + TRY_MINIRECORDER(drawPath, path, paint); APPEND(DrawPath, delay_copy(paint), delay_copy(path)); } @@ -248,6 +267,7 @@ void SkRecorder::onDrawTextOnPath(const void* text, size_t byteLength, const SkP void SkRecorder::onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y, const SkPaint& paint) { + TRY_MINIRECORDER(drawTextBlob, blob, x, y, paint); APPEND(DrawTextBlob, delay_copy(paint), blob, x, y); } diff --git a/src/core/SkRecorder.h b/src/core/SkRecorder.h index d0a992fc08..b6f153dc54 100644 --- a/src/core/SkRecorder.h +++ b/src/core/SkRecorder.h @@ -8,7 +8,9 @@ #ifndef SkRecorder_DEFINED #define SkRecorder_DEFINED +#include "SkBigPicture.h" #include "SkCanvas.h" +#include "SkMiniRecorder.h" #include "SkRecord.h" #include "SkRecords.h" #include "SkTDArray.h" @@ -25,7 +27,7 @@ public: void append(SkDrawable* drawable); // Return a new or ref'd array of pictures that were snapped from our drawables. - SkPicture::SnapshotArray* newDrawableSnapshot(); + SkBigPicture::SnapshotArray* newDrawableSnapshot(); private: SkTDArray fArray; @@ -36,10 +38,10 @@ private: class SkRecorder : public SkCanvas { public: // Does not take ownership of the SkRecord. - SkRecorder(SkRecord*, int width, int height); // legacy version - SkRecorder(SkRecord*, const SkRect& bounds); + SkRecorder(SkRecord*, int width, int height, SkMiniRecorder* = nullptr); // legacy version + SkRecorder(SkRecord*, const SkRect& bounds, SkMiniRecorder* = nullptr); - void reset(SkRecord*, const SkRect& bounds); + void reset(SkRecord*, const SkRect& bounds, SkMiniRecorder* = nullptr); size_t approxBytesUsedBySubPictures() const { return fApproxBytesUsedBySubPictures; } @@ -120,6 +122,8 @@ public: SkSurface* onNewSurface(const SkImageInfo&, const SkSurfaceProps&) override { return NULL; } + void flushMiniRecorder(); + private: template T* copy(const T*); @@ -136,6 +140,8 @@ private: size_t fApproxBytesUsedBySubPictures; SkRecord* fRecord; SkAutoTDelete fDrawableList; + + SkMiniRecorder* fMiniRecorder; }; #endif//SkRecorder_DEFINED diff --git a/src/core/SkRecords.h b/src/core/SkRecords.h index 319d155054..6e25dd22e2 100644 --- a/src/core/SkRecords.h +++ b/src/core/SkRecords.h @@ -81,6 +81,7 @@ struct T { \ #define RECORD1(T, A, a) \ struct T { \ static const Type kType = T##_Type; \ + T() {} \ template \ T(Z a) : a(a) {} \ A a; \ @@ -89,6 +90,7 @@ struct T { \ #define RECORD2(T, A, a, B, b) \ struct T { \ static const Type kType = T##_Type; \ + T() {} \ template \ T(Z a, Y b) : a(a), b(b) {} \ A a; B b; \ @@ -97,6 +99,7 @@ struct T { \ #define RECORD3(T, A, a, B, b, C, c) \ struct T { \ static const Type kType = T##_Type; \ + T() {} \ template \ T(Z a, Y b, X c) : a(a), b(b), c(c) {} \ A a; B b; C c; \ @@ -105,6 +108,7 @@ struct T { \ #define RECORD4(T, A, a, B, b, C, c, D, d) \ struct T { \ static const Type kType = T##_Type; \ + T() {} \ template \ T(Z a, Y b, X c, W d) : a(a), b(b), c(c), d(d) {} \ A a; B b; C c; D d; \ @@ -113,6 +117,7 @@ struct T { \ #define RECORD5(T, A, a, B, b, C, c, D, d, E, e) \ struct T { \ static const Type kType = T##_Type; \ + T() {} \ template \ T(Z a, Y b, X c, W d, V e) : a(a), b(b), c(c), d(d), e(e) {} \ A a; B b; C c; D d; E e; \ @@ -125,6 +130,7 @@ struct T { \ template class RefBox : SkNoncopyable { public: + RefBox() {} RefBox(T* obj) : fObj(SkSafeRef(obj)) {} ~RefBox() { SkSafeUnref(fObj); } @@ -138,6 +144,7 @@ private: template class Optional : SkNoncopyable { public: + Optional() : fPtr(nullptr) {} Optional(T* ptr) : fPtr(ptr) {} ~Optional() { if (fPtr) fPtr->~T(); } @@ -167,6 +174,7 @@ private: template class PODArray { public: + PODArray() {} PODArray(T* ptr) : fPtr(ptr) {} // Default copy and assign. @@ -181,6 +189,7 @@ private: // Using this, we guarantee the immutability of all bitmaps we record. class ImmutableBitmap : SkNoncopyable { public: + ImmutableBitmap() {} explicit ImmutableBitmap(const SkBitmap& bitmap) { if (bitmap.isImmutable()) { fBitmap = bitmap; @@ -203,6 +212,7 @@ private: // SkPath::cheapComputeDirection() is similar. // Recording is a convenient time to cache these, or we can delay it to between record and playback. struct PreCachedPath : public SkPath { + PreCachedPath() {} explicit PreCachedPath(const SkPath& path) : SkPath(path) { this->updateBoundsCache(); SkPath::Direction junk; @@ -213,6 +223,7 @@ struct PreCachedPath : public SkPath { // Like SkPath::getBounds(), SkMatrix::getType() isn't thread safe unless we precache it. // This may not cover all SkMatrices used by the picture (e.g. some could be hiding in a shader). struct TypedMatrix : public SkMatrix { + TypedMatrix() {} explicit TypedMatrix(const SkMatrix& matrix) : SkMatrix(matrix) { (void)this->getType(); } @@ -227,6 +238,7 @@ RECORD3(SaveLayer, Optional, bounds, Optional, paint, SkCanvas: RECORD1(SetMatrix, TypedMatrix, matrix); struct RegionOpAndAA { + RegionOpAndAA() {} RegionOpAndAA(SkRegion::Op op, bool aa) : op(op), aa(aa) {} SkRegion::Op op : 31; // This really only needs to be 3, but there's no win today to do so. unsigned aa : 1; // MSVC won't pack an enum with an bool, so we call this an unsigned. diff --git a/src/gpu/GrLayerHoister.cpp b/src/gpu/GrLayerHoister.cpp index 70c10d7f69..67e3c19a2f 100644 --- a/src/gpu/GrLayerHoister.cpp +++ b/src/gpu/GrLayerHoister.cpp @@ -9,6 +9,7 @@ #include "GrLayerHoister.h" #include "GrRecordReplaceDraw.h" +#include "SkBigPicture.h" #include "SkCanvas.h" #include "SkDeviceImageFilterProxy.h" #include "SkDeviceProperties.h" @@ -21,7 +22,7 @@ // Create the layer information for the hoisted layer and secure the // required texture/render target resources. -static void prepare_for_hoisting(GrLayerCache* layerCache, +static void prepare_for_hoisting(GrLayerCache* layerCache, const SkPicture* topLevelPicture, const SkMatrix& initialMat, const SkLayerInfo::BlockInfo& info, @@ -74,7 +75,7 @@ static void prepare_for_hoisting(GrLayerCache* layerCache, } else { hl = recycled->append(); } - + layerCache->addUse(layer); hl->fLayer = layer; hl->fPicture = pict; @@ -129,12 +130,12 @@ void GrLayerHoister::FindLayersToAtlas(GrContext* context, } GrLayerCache* layerCache = context->getLayerCache(); - layerCache->processDeletedPictures(); - SkPicture::AccelData::Key key = SkLayerInfo::ComputeKey(); - - const SkPicture::AccelData* topLevelData = topLevelPicture->EXPERIMENTAL_getAccelData(key); + const SkBigPicture::AccelData* topLevelData = NULL; + if (const SkBigPicture* bp = topLevelPicture->asSkBigPicture()) { + topLevelData = bp->accelData(); + } if (!topLevelData) { return; } @@ -189,9 +190,10 @@ void GrLayerHoister::FindLayersToHoist(GrContext* context, layerCache->processDeletedPictures(); - SkPicture::AccelData::Key key = SkLayerInfo::ComputeKey(); - - const SkPicture::AccelData* topLevelData = topLevelPicture->EXPERIMENTAL_getAccelData(key); + const SkBigPicture::AccelData* topLevelData = NULL; + if (const SkBigPicture* bp = topLevelPicture->asSkBigPicture()) { + topLevelData = bp->accelData(); + } if (!topLevelData) { return; } @@ -239,7 +241,11 @@ void GrLayerHoister::DrawLayersToAtlas(GrContext* context, for (int i = 0; i < atlased.count(); ++i) { const GrCachedLayer* layer = atlased[i].fLayer; - const SkPicture* pict = atlased[i].fPicture; + const SkBigPicture* pict = atlased[i].fPicture->asSkBigPicture(); + if (!pict) { + // TODO: can we assume / assert this? + continue; + } const SkIPoint offset = SkIPoint::Make(layer->srcIR().fLeft, layer->srcIR().fTop); SkDEBUGCODE(const SkPaint* layerPaint = layer->paint();) @@ -265,10 +271,7 @@ void GrLayerHoister::DrawLayersToAtlas(GrContext* context, atlasCanvas->setMatrix(initialCTM); atlasCanvas->concat(atlased[i].fLocalMat); - SkRecordPartialDraw(*pict->fRecord.get(), atlasCanvas, - pict->drawablePicts(), pict->drawableCount(), - layer->start() + 1, layer->stop(), initialCTM); - + pict->partialPlayback(atlasCanvas, layer->start() + 1, layer->stop(), initialCTM); atlasCanvas->restore(); } @@ -328,7 +331,11 @@ void GrLayerHoister::FilterLayer(GrContext* context, void GrLayerHoister::DrawLayers(GrContext* context, const SkTDArray& layers) { for (int i = 0; i < layers.count(); ++i) { GrCachedLayer* layer = layers[i].fLayer; - const SkPicture* pict = layers[i].fPicture; + const SkBigPicture* pict = layers[i].fPicture->asSkBigPicture(); + if (!pict) { + // TODO: can we assume / assert this? + continue; + } const SkIPoint offset = SkIPoint::Make(layer->srcIR().fLeft, layer->srcIR().fTop); // Each non-atlased layer has its own GrTexture @@ -353,10 +360,7 @@ void GrLayerHoister::DrawLayers(GrContext* context, const SkTDArraysetMatrix(initialCTM); layerCanvas->concat(layers[i].fLocalMat); - SkRecordPartialDraw(*pict->fRecord.get(), layerCanvas, - pict->drawablePicts(), pict->drawableCount(), - layer->start()+1, layer->stop(), initialCTM); - + pict->partialPlayback(layerCanvas, layer->start()+1, layer->stop(), initialCTM); layerCanvas->flush(); if (layer->filter()) { diff --git a/src/gpu/GrRecordReplaceDraw.cpp b/src/gpu/GrRecordReplaceDraw.cpp index dacc939e62..6f05206dcc 100644 --- a/src/gpu/GrRecordReplaceDraw.cpp +++ b/src/gpu/GrRecordReplaceDraw.cpp @@ -8,6 +8,7 @@ #include "GrContext.h" #include "GrLayerCache.h" #include "GrRecordReplaceDraw.h" +#include "SkBigPicture.h" #include "SkCanvasPriv.h" #include "SkGrPixelRef.h" #include "SkImage.h" @@ -45,7 +46,7 @@ static inline void draw_replacement_bitmap(GrCachedLayer* layer, SkCanvas* canva canvas->drawBitmapRectToRect(bm, &src, dst, layer->paint()); canvas->restore(); } else { - canvas->drawSprite(bm, + canvas->drawSprite(bm, layer->srcIR().fLeft + layer->offset().fX, layer->srcIR().fTop + layer->offset().fY, layer->paint()); @@ -59,7 +60,7 @@ public: ReplaceDraw(SkCanvas* canvas, GrLayerCache* layerCache, SkPicture const* const drawablePicts[], int drawableCount, const SkPicture* topLevelPicture, - const SkPicture* picture, + const SkBigPicture* picture, const SkMatrix& initialMatrix, SkPicture::AbortCallback* callback, const unsigned* opIndices, int numIndices) @@ -76,8 +77,8 @@ public: } int draw() { - const SkBBoxHierarchy* bbh = fPicture->fBBH.get(); - const SkRecord* record = fPicture->fRecord.get(); + const SkBBoxHierarchy* bbh = fPicture->bbh(); + const SkRecord* record = fPicture->record(); if (NULL == record) { return 0; } @@ -135,13 +136,17 @@ public: SkAutoCanvasMatrixPaint acmp(fCanvas, &dp.matrix, dp.paint, dp.picture->cullRect()); - // Draw sub-pictures with the same replacement list but a different picture - ReplaceDraw draw(fCanvas, fLayerCache, - this->drawablePicts(), this->drawableCount(), - fTopLevelPicture, dp.picture, fInitialMatrix, fCallback, - fOpIndexStack.begin(), fOpIndexStack.count()); - - fNumReplaced += draw.draw(); + if (const SkBigPicture* bp = dp.picture->asSkBigPicture()) { + // Draw sub-pictures with the same replacement list but a different picture + ReplaceDraw draw(fCanvas, fLayerCache, + this->drawablePicts(), this->drawableCount(), + fTopLevelPicture, bp, fInitialMatrix, fCallback, + fOpIndexStack.begin(), fOpIndexStack.count()); + fNumReplaced += draw.draw(); + } else { + // TODO: can we assume / assert this doesn't happen? + dp.picture->playback(fCanvas, fCallback); + } fOpIndexStack.pop(); } @@ -168,7 +173,7 @@ public: draw_replacement_bitmap(layer, fCanvas); - if (fPicture->fBBH.get()) { + if (fPicture->bbh()) { while (fOps[fIndex] < layer->stop()) { ++fIndex; } @@ -190,7 +195,7 @@ private: SkCanvas* fCanvas; GrLayerCache* fLayerCache; const SkPicture* fTopLevelPicture; - const SkPicture* fPicture; + const SkBigPicture* fPicture; const SkMatrix fInitialMatrix; SkPicture::AbortCallback* fCallback; @@ -211,9 +216,15 @@ int GrRecordReplaceDraw(const SkPicture* picture, SkPicture::AbortCallback* callback) { SkAutoCanvasRestore saveRestore(canvas, true /*save now, restore at exit*/); - // TODO: drawablePicts? - ReplaceDraw draw(canvas, layerCache, NULL, 0, - picture, picture, - initialMatrix, callback, NULL, 0); - return draw.draw(); + if (const SkBigPicture* bp = picture->asSkBigPicture()) { + // TODO: drawablePicts? + ReplaceDraw draw(canvas, layerCache, NULL, 0, + bp, bp, + initialMatrix, callback, NULL, 0); + return draw.draw(); + } else { + // TODO: can we assume / assert this doesn't happen? + picture->playback(canvas, callback); + return 0; + } } diff --git a/src/gpu/SkGpuDevice.cpp b/src/gpu/SkGpuDevice.cpp index 7b363635e2..25f6fd370f 100644 --- a/src/gpu/SkGpuDevice.cpp +++ b/src/gpu/SkGpuDevice.cpp @@ -1999,9 +1999,10 @@ bool SkGpuDevice::EXPERIMENTAL_drawPicture(SkCanvas* mainCanvas, const SkPicture return false; } - SkPicture::AccelData::Key key = SkLayerInfo::ComputeKey(); - - const SkPicture::AccelData* data = mainPicture->EXPERIMENTAL_getAccelData(key); + const SkBigPicture::AccelData* data = NULL; + if (const SkBigPicture* bp = mainPicture->asSkBigPicture()) { + data = bp->accelData(); + } if (!data) { return false; } diff --git a/src/gpu/SkGpuDevice.h b/src/gpu/SkGpuDevice.h index 89959e1f9c..c1ea4fe873 100644 --- a/src/gpu/SkGpuDevice.h +++ b/src/gpu/SkGpuDevice.h @@ -217,8 +217,6 @@ private: bool drawDashLine(const SkPoint pts[2], const SkPaint& paint); - static SkPicture::AccelData::Key ComputeAccelDataKey(); - static GrRenderTarget* CreateRenderTarget(GrContext*, SkSurface::Budgeted, const SkImageInfo&, int sampleCount); diff --git a/src/utils/SkPictureUtils.cpp b/src/utils/SkPictureUtils.cpp deleted file mode 100644 index a8a251c927..0000000000 --- a/src/utils/SkPictureUtils.cpp +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright 2012 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include "SkBBoxHierarchy.h" -#include "SkCanvas.h" -#include "SkData.h" -#include "SkPictureUtils.h" -#include "SkRecord.h" -#include "SkShader.h" - -size_t SkPictureUtils::ApproximateBytesUsed(const SkPicture* pict) { - size_t byteCount = sizeof(*pict); - - byteCount += pict->fRecord->bytesUsed(); - if (pict->fBBH.get()) { - byteCount += pict->fBBH->bytesUsed(); - } - byteCount += pict->fApproxBytesUsedBySubPictures; - - return byteCount; -} -- cgit v1.2.3