diff --git a/gyp/core.gypi b/gyp/core.gypi index b99f51c2ad..f6a2ce2ca7 100644 --- a/gyp/core.gypi +++ b/gyp/core.gypi @@ -23,7 +23,6 @@ '<(skia_src_path)/core/SkAntiRun.h', '<(skia_src_path)/core/SkBBHFactory.cpp', '<(skia_src_path)/core/SkBBoxHierarchy.h', - '<(skia_src_path)/core/SkBigPicture.cpp', '<(skia_src_path)/core/SkBitmap.cpp', '<(skia_src_path)/core/SkBitmapCache.cpp', '<(skia_src_path)/core/SkBitmapDevice.cpp', @@ -118,6 +117,7 @@ '<(skia_src_path)/core/SkImageInfo.cpp', '<(skia_src_path)/core/SkImageGenerator.cpp', '<(skia_src_path)/core/SkLayerInfo.h', + '<(skia_src_path)/core/SkLayerInfo.cpp', '<(skia_src_path)/core/SkLocalMatrixShader.cpp', '<(skia_src_path)/core/SkLineClipper.cpp', '<(skia_src_path)/core/SkMallocPixelRef.cpp', @@ -133,7 +133,6 @@ '<(skia_src_path)/core/SkMessageBus.h', '<(skia_src_path)/core/SkMetaData.cpp', '<(skia_src_path)/core/SkMipMap.cpp', - '<(skia_src_path)/core/SkMiniRecorder.cpp', '<(skia_src_path)/core/SkMultiPictureDraw.cpp', '<(skia_src_path)/core/SkPackBits.cpp', '<(skia_src_path)/core/SkPaint.cpp', diff --git a/gyp/utils.gypi b/gyp/utils.gypi index 1193507215..f382e1028f 100644 --- a/gyp/utils.gypi +++ b/gyp/utils.gypi @@ -79,6 +79,7 @@ '<(skia_src_path)/utils/SkParse.cpp', '<(skia_src_path)/utils/SkParseColor.cpp', '<(skia_src_path)/utils/SkParsePath.cpp', + '<(skia_src_path)/utils/SkPictureUtils.cpp', '<(skia_src_path)/utils/SkPatchGrid.cpp', '<(skia_src_path)/utils/SkPatchGrid.h', '<(skia_src_path)/utils/SkPatchUtils.cpp', diff --git a/include/core/SkPicture.h b/include/core/SkPicture.h index 0f671a3682..9a2b65b5c5 100644 --- a/include/core/SkPicture.h +++ b/include/core/SkPicture.h @@ -5,32 +5,62 @@ * found in the LICENSE file. */ + #ifndef SkPicture_DEFINED #define SkPicture_DEFINED #include "SkImageDecoder.h" #include "SkLazyPtr.h" #include "SkRefCnt.h" -#include "SkTypes.h" +#include "SkTDArray.h" +#if SK_SUPPORT_GPU class GrContext; -class SkBigPicture; +#endif + class SkBitmap; +class SkBBoxHierarchy; class SkCanvas; +class SkData; class SkPictureData; class SkPixelSerializer; class SkStream; class SkWStream; + struct SkPictInfo; +class SkRecord; + +namespace SkRecords { + class CollectLayers; +}; + /** \class SkPicture - An SkPicture records drawing commands made to a canvas to be played back at a later time. - This base class handles serialization and a few other miscellany. + The SkPicture class records the drawing commands made to a canvas, to + be played back at a later time. */ -class SK_API SkPicture : public SkRefCnt { +class SK_API SkPicture : public SkNVRefCnt { public: - virtual ~SkPicture(); + // AccelData provides a base class for device-specific acceleration data. + class AccelData : public SkRefCnt { + public: + typedef uint8_t Domain; + typedef uint32_t Key; + + AccelData(Key key) : fKey(key) { } + + const Key& getKey() const { return fKey; } + + // This entry point allows user's to get a unique domain prefix + // for their keys + static Domain GenerateDomain(); + private: + Key fKey; + }; + + /** PRIVATE / EXPERIMENTAL -- do not call */ + const AccelData* EXPERIMENTAL_getAccelData(AccelData::Key) const; /** * Function signature defining a function that sets up an SkBitmap from encoded data. On @@ -66,6 +96,8 @@ public: */ static SkPicture* CreateFromBuffer(SkReadBuffer&); + ~SkPicture(); + /** * Subclasses of this can be passed to playback(). During the playback * of the picture, this callback will periodically be invoked. If its @@ -80,6 +112,7 @@ public: public: AbortCallback() {} virtual ~AbortCallback() {} + virtual bool abort() = 0; }; @@ -90,14 +123,16 @@ public: @param canvas the canvas receiving the drawing commands. @param callback a callback that allows interruption of playback */ - virtual void playback(SkCanvas*, AbortCallback* = NULL) const = 0; + void playback(SkCanvas* canvas, AbortCallback* = NULL) const; - /** Return a cull rect for this picture. - Ops recorded into this picture that attempt to draw outside the cull might not be drawn. + /** Return the cull rect used when creating this picture: { 0, 0, cullWidth, cullHeight }. + It does not necessarily reflect the bounds of what has been recorded into the picture. + @return the cull rect used to create this picture + */ + SkRect cullRect() const { return fCullRect; } + + /** Return a non-zero, unique value representing the picture. */ - virtual SkRect cullRect() const = 0; - - /** Returns a non-zero value unique among all pictures. */ uint32_t uniqueID() const; /** @@ -106,7 +141,7 @@ public: * * TODO: Use serializer to serialize SkImages as well. */ - void serialize(SkWStream*, SkPixelSerializer* = NULL) const; + void serialize(SkWStream*, SkPixelSerializer* serializer = NULL) const; /** * Serialize to a buffer. @@ -117,21 +152,7 @@ public: * Returns true if any bitmaps may be produced when this SkPicture * is replayed. */ - virtual bool willPlayBackBitmaps() const = 0; - - /** Return the approximate number of operations in this picture. This - * number may be greater or less than the number of SkCanvas calls - * recorded: some calls may be recorded as more than one operation, or some - * calls may be optimized away. - */ - virtual int approximateOpCount() const = 0; - - /** Return true if this picture contains text. - */ - virtual bool hasText() const = 0; - - /** Returns the approximate byte size of this picture, not including large ref'd objects. */ - virtual size_t approximateBytesUsed() const = 0; + bool willPlayBackBitmaps() const; /** Return true if the SkStream/Buffer represents a serialized picture, and fills out SkPictInfo. After this function returns, the data source is not @@ -144,25 +165,76 @@ public: static bool InternalOnly_StreamIsSKP(SkStream*, SkPictInfo*); static bool InternalOnly_BufferIsSKP(SkReadBuffer*, SkPictInfo*); - /** Return true if the picture is suitable for rendering on the GPU. */ - bool suitableForGpuRasterization(GrContext*, const char** whyNot = NULL) const; + /** Return true if the picture is suitable for rendering on the GPU. + */ + +#if SK_SUPPORT_GPU + bool suitableForGpuRasterization(GrContext*, const char ** = NULL) const; +#endif + + /** Return the approximate number of operations in this picture. This + * number may be greater or less than the number of SkCanvas calls + * recorded: some calls may be recorded as more than one operation, or some + * calls may be optimized away. + */ + int approximateOpCount() const; + + /** Return true if this picture contains text. + */ + bool hasText() const; + + // 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; + }; // Sent via SkMessageBus from destructor. - struct DeletionMessage { int32_t fUniqueID; }; // TODO: -> uint32_t? - - // Returns NULL if this is not an SkBigPicture. - virtual const SkBigPicture* asSkBigPicture() const { return NULL; } + struct DeletionMessage { int32_t fUniqueID; }; private: - // Subclass whitelist. - SkPicture(); - friend class SkBigPicture; - friend class SkEmptyPicture; - template friend class SkMiniPicture; - - virtual int numSlowPaths() const = 0; - friend struct SkPathCounter; - + // V2 : adds SkPixelRef's generation ID. + // V3 : PictInfo tag at beginning, and EOF tag at the end + // V4 : move SkPictInfo to be the header + // V5 : don't read/write FunctionPtr on cross-process (we can detect that) + // V6 : added serialization of SkPath's bounds (and packed its flags tighter) + // V7 : changed drawBitmapRect(IRect) to drawBitmapRectToRect(Rect) + // V8 : Add an option for encoding bitmaps + // V9 : Allow the reader and writer of an SKP disagree on whether to support + // SK_SUPPORT_HINTING_SCALE_FACTOR + // V10: add drawRRect, drawOval, clipRRect + // V11: modify how readBitmap and writeBitmap store their info. + // V12: add conics to SkPath, use new SkPathRef flattening + // V13: add flag to drawBitmapRectToRect + // parameterize blurs by sigma rather than radius + // V14: Add flags word to PathRef serialization + // V15: Remove A1 bitmap config (and renumber remaining configs) + // V16: Move SkPath's isOval flag to SkPathRef + // V17: SkPixelRef now writes SkImageInfo + // V18: SkBitmap now records x,y for its pixelref origin, instead of offset. + // V19: encode matrices and regions into the ops stream + // V20: added bool to SkPictureImageFilter's serialization (to allow SkPicture serialization) + // V21: add pushCull, popCull + // V22: SK_PICT_FACTORY_TAG's size is now the chunk size in bytes + // V23: SkPaint::FilterLevel became a real enum + // V24: SkTwoPointConicalGradient now has fFlipped flag for gradient flipping + // V25: SkDashPathEffect now only writes phase and interval array when flattening + // V26: Removed boolean from SkColorShader for inheriting color from SkPaint. + // V27: Remove SkUnitMapper from gradients (and skia). + // V28: No longer call bitmap::flatten inside SkWriteBuffer::writeBitmap. + // V29: Removed SaveFlags parameter from save(). + // V30: Remove redundant SkMatrix from SkLocalMatrixShader. + // V31: Add a serialized UniqueID to SkImageFilter. + // V32: Removed SkPaintOptionsAndroid from SkPaint + // V33: Serialize only public API of effects. + // V34: Add SkTextBlob serialization. // V35: Store SkRect (rather then width & height) in header // V36: Remove (obsolete) alphatype from SkColorTable // V37: Added shadow only option to SkDropShadowImageFilter (last version to record CLEAR) @@ -171,20 +243,67 @@ private: // V40: Remove UniqueID serialization from SkImageFilter. // V41: Added serialization of SkBitmapSource's filterQuality parameter + // Note: If the picture version needs to be increased then please follow the + // steps to generate new SKPs in (only accessible to Googlers): http://goo.gl/qATVcw + // Only SKPs within the min/current picture version range (inclusive) can be read. - static const uint32_t MIN_PICTURE_VERSION = 35, // Produced by Chrome M39. - CURRENT_PICTURE_VERSION = 41; + static const uint32_t MIN_PICTURE_VERSION = 35; // Produced by Chrome M39. + static const uint32_t CURRENT_PICTURE_VERSION = 41; static_assert(MIN_PICTURE_VERSION <= 41, "Remove kFontFileName and related code from SkFontDescriptor.cpp."); + void createHeader(SkPictInfo* info) const; static bool IsValidPictInfo(const SkPictInfo& info); + + // Takes ownership of the (optional) SnapshotArray. + // For performance, we take ownership of the caller's refs on the SkRecord, BBH, and AccelData. + SkPicture(const SkRect& cullRect, + SkRecord*, + SnapshotArray*, + SkBBoxHierarchy*, + AccelData*, + size_t approxBytesUsedBySubPictures); + static SkPicture* Forwardport(const SkPictInfo&, const SkPictureData*); + static SkPictureData* Backport(const SkRecord&, const SkPictInfo&, + SkPicture const* const drawablePics[], int drawableCount); - SkPictInfo createHeader() const; - SkPictureData* backport() const; + // uint32_t fRefCnt; from SkNVRefCnt + mutable uint32_t fUniqueID; + const SkRect fCullRect; + SkAutoTUnref fRecord; + SkAutoTDelete fDrawablePicts; + SkAutoTUnref fBBH; + SkAutoTUnref fAccelData; + const size_t fApproxBytesUsedBySubPictures; - mutable uint32_t fUniqueID; + // helpers for fDrawablePicts + int drawableCount() const; + // will return NULL if drawableCount() returns 0 + SkPicture const* const* drawablePicts() const; + + struct PathCounter; + + struct Analysis { + Analysis() {} // Only used by SkPictureData codepath. + explicit Analysis(const SkRecord&); + + bool suitableForGpuRasterization(const char** reason, int sampleCount) const; + + uint8_t fNumSlowPathsAndDashEffects; + bool fWillPlaybackBitmaps : 1; + bool fHasText : 1; + }; + SkLazyPtr fAnalysis; + const Analysis& analysis() const; + + friend class SkPictureRecorder; // SkRecord-based constructor. + friend class GrLayerHoister; // access to fRecord + friend class ReplaceDraw; + friend class SkPictureUtils; + friend class SkRecordedDrawable; }; +SK_COMPILE_ASSERT(sizeof(SkPicture) <= 88, SkPictureSize); #endif diff --git a/include/core/SkPictureRecorder.h b/include/core/SkPictureRecorder.h index a84b721e44..2cf7909cf3 100644 --- a/include/core/SkPictureRecorder.h +++ b/include/core/SkPictureRecorder.h @@ -8,7 +8,6 @@ #ifndef SkPictureRecorder_DEFINED #define SkPictureRecorder_DEFINED -#include "../../src/core/SkMiniRecorder.h" #include "SkBBHFactory.h" #include "SkPicture.h" #include "SkRefCnt.h" @@ -103,7 +102,6 @@ private: SkAutoTUnref fBBH; SkAutoTUnref fRecorder; SkAutoTUnref fRecord; - SkMiniRecorder fMiniRecorder; typedef SkNoncopyable INHERITED; }; diff --git a/include/utils/SkPictureUtils.h b/include/utils/SkPictureUtils.h index b65a64d579..10607da9c6 100644 --- a/include/utils/SkPictureUtils.h +++ b/include/utils/SkPictureUtils.h @@ -10,8 +10,6 @@ #include "SkPicture.h" -// TODO: remove this file? - class SK_API SkPictureUtils { public: /** @@ -20,9 +18,7 @@ public: * includes nested SkPictures, but does not include large objects that * SkRecord holds a reference to (e.g. paths, or pixels backing bitmaps). */ - static size_t ApproximateBytesUsed(const SkPicture* pict) { - return pict->approximateBytesUsed(); - } + static size_t ApproximateBytesUsed(const SkPicture* pict); }; #endif diff --git a/src/core/SkBigPicture.cpp b/src/core/SkBigPicture.cpp deleted file mode 100644 index 6609586ca2..0000000000 --- a/src/core/SkBigPicture.cpp +++ /dev/null @@ -1,98 +0,0 @@ -/* - * 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 deleted file mode 100644 index 14e413b55a..0000000000 --- a/src/core/SkBigPicture.h +++ /dev/null @@ -1,88 +0,0 @@ -/* - * 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 new file mode 100644 index 0000000000..d427fa7c21 --- /dev/null +++ b/src/core/SkLayerInfo.cpp @@ -0,0 +1,15 @@ +/* + * 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 3d4b26960c..a47c3f1199 100644 --- a/src/core/SkLayerInfo.h +++ b/src/core/SkLayerInfo.h @@ -8,12 +8,12 @@ #ifndef SkLayerInfo_DEFINED #define SkLayerInfo_DEFINED -#include "SkBigPicture.h" +#include "SkPicture.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 SkBigPicture::AccelData { +class SkLayerInfo : public SkPicture::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() {} + SkLayerInfo(Key key) : INHERITED(key) { } BlockInfo& addBlock() { return fBlocks.push_back(); } @@ -72,10 +72,14 @@ 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 SkBigPicture::AccelData INHERITED; + typedef SkPicture::AccelData INHERITED; }; #endif // SkLayerInfo_DEFINED diff --git a/src/core/SkMiniRecorder.cpp b/src/core/SkMiniRecorder.cpp deleted file mode 100644 index 55dd56add7..0000000000 --- a/src/core/SkMiniRecorder.cpp +++ /dev/null @@ -1,103 +0,0 @@ -/* - * 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 deleted file mode 100644 index d01aedacea..0000000000 --- a/src/core/SkMiniRecorder.h +++ /dev/null @@ -1,44 +0,0 @@ -/* - * 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 6c1f6cb759..0145cbe5d7 100644 --- a/src/core/SkPicture.cpp +++ b/src/core/SkPicture.cpp @@ -5,107 +5,330 @@ * found in the LICENSE file. */ -#include "SkAtomics.h" -#include "SkMessageBus.h" -#include "SkPicture.h" + +#include "SkPictureFlat.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); -/* SkPicture impl. This handles generic responsibilities like unique IDs and serialization. */ +template int SafeCount(const T* obj) { + return obj ? obj->count() : 0; +} -SkPicture::SkPicture() : fUniqueID(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); + + + // 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() { - // 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 = { (int32_t)id }; + SkPicture::DeletionMessage msg; + msg.fUniqueID = id; SkMessageBus::Post(msg); } } -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. - } +const SkPicture::AccelData* SkPicture::EXPERIMENTAL_getAccelData( + SkPicture::AccelData::Key key) const { + if (fAccelData.get() && fAccelData->getKey() == key) { + return fAccelData.get(); } - return id; + 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(); + } + + return static_cast(id); +} + +/////////////////////////////////////////////////////////////////////////////// + +void SkPicture::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(), NULL, this->drawableCount(), + useBBH ? fBBH.get() : NULL, callback); +} + +/////////////////////////////////////////////////////////////////////////////// + +#include "SkStream.h" + static const char kMagic[] = { 's', 'k', 'i', 'a', 'p', 'i', 'c', 't' }; -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)); - - // 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; - } - return info; -} - 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 (!stream) { + if (NULL == 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(); - if (IsValidPictInfo(info)) { - if (pInfo) { *pInfo = info; } - return true; + info.fFlags = stream->readU32(); + + if (!IsValidPictInfo(info)) { + return false; } - return false; + + if (pInfo != NULL) { + *pInfo = info; + } + return true; } 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; } @@ -114,29 +337,32 @@ bool SkPicture::InternalOnly_BufferIsSKP(SkReadBuffer* buffer, SkPictInfo* pInfo buffer->readRect(&info.fCullRect); info.fFlags = buffer->readUInt(); - if (IsValidPictInfo(info)) { - if (pInfo) { *pInfo = info; } - return true; + if (!IsValidPictInfo(info)) { + return false; } - return false; + + if (pInfo != NULL) { + *pInfo = info; + } + return true; } SkPicture* SkPicture::Forwardport(const SkPictInfo& info, const SkPictureData* data) { if (!data) { - return nullptr; + return NULL; } SkPicturePlayback playback(data); SkPictureRecorder r; playback.draw(r.beginRecording(SkScalarCeilToInt(info.fCullRect.width()), SkScalarCeilToInt(info.fCullRect.height())), - nullptr/*no callback*/); + NULL/*no callback*/); return r.endRecording(); } SkPicture* SkPicture::CreateFromStream(SkStream* stream, InstallPixelRefProc proc) { SkPictInfo info; if (!InternalOnly_StreamIsSKP(stream, &info) || !stream->readBool()) { - return nullptr; + return NULL; } SkAutoTDelete data(SkPictureData::CreateFromStream(stream, info, proc)); return Forwardport(info, data); @@ -145,24 +371,45 @@ SkPicture* SkPicture::CreateFromStream(SkStream* stream, InstallPixelRefProc pro SkPicture* SkPicture::CreateFromBuffer(SkReadBuffer& buffer) { SkPictInfo info; if (!InternalOnly_BufferIsSKP(&buffer, &info) || !buffer.readBool()) { - return nullptr; + return NULL; } SkAutoTDelete data(SkPictureData::CreateFromBuffer(buffer, info)); return Forwardport(info, data); } -SkPictureData* SkPicture::backport() const { - SkPictInfo info = this->createHeader(); +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) { SkPictureRecord rec(SkISize::Make(info.fCullRect.width(), info.fCullRect.height()), 0/*flags*/); rec.beginRecording(); - this->playback(&rec); + SkRecordDraw(src, &rec, drawablePicts, NULL, drawableCount, NULL/*bbh*/, NULL/*callback*/); rec.endRecording(); return SkNEW_ARGS(SkPictureData, (rec, info, false/*deep copy ops?*/)); } void SkPicture::serialize(SkWStream* stream, SkPixelSerializer* pixelSerializer) const { - SkPictInfo info = this->createHeader(); - SkAutoTDelete data(this->backport()); + SkPictInfo info; + this->createHeader(&info); + SkAutoTDelete data(Backport(*fRecord, info, this->drawablePicts(), + this->drawableCount())); stream->write(&info, sizeof(info)); if (data) { @@ -174,8 +421,10 @@ void SkPicture::serialize(SkWStream* stream, SkPixelSerializer* pixelSerializer) } void SkPicture::flatten(SkWriteBuffer& buffer) const { - SkPictInfo info = this->createHeader(); - SkAutoTDelete data(this->backport()); + SkPictInfo info; + this->createHeader(&info); + SkAutoTDelete data(Backport(*fRecord, info, this->drawablePicts(), + this->drawableCount())); buffer.writeByteArray(&info.fMagic, sizeof(info.fMagic)); buffer.writeUInt(info.fVersion); @@ -189,10 +438,49 @@ void SkPicture::flatten(SkWriteBuffer& buffer) const { } } -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 true; +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. + } + } + return id; } diff --git a/src/core/SkPictureCommon.h b/src/core/SkPictureCommon.h deleted file mode 100644 index 827bf117de..0000000000 --- a/src/core/SkPictureCommon.h +++ /dev/null @@ -1,136 +0,0 @@ -/* - * 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 c110e0a2db..282e2c22dd 100644 --- a/src/core/SkPictureRecorder.cpp +++ b/src/core/SkPictureRecorder.cpp @@ -5,7 +5,6 @@ * found in the LICENSE file. */ -#include "SkBigPicture.h" #include "SkData.h" #include "SkDrawable.h" #include "SkLayerInfo.h" @@ -19,7 +18,7 @@ SkPictureRecorder::SkPictureRecorder() { fActivelyRecording = false; - fRecorder.reset(SkNEW_ARGS(SkRecorder, (nullptr, SkRect::MakeWH(0,0), &fMiniRecorder))); + fRecorder.reset(SkNEW_ARGS(SkRecorder, (nullptr, SkRect::MakeWH(0,0)))); } SkPictureRecorder::~SkPictureRecorder() {} @@ -35,10 +34,8 @@ SkCanvas* SkPictureRecorder::beginRecording(const SkRect& cullRect, SkASSERT(fBBH.get()); } - if (!fRecord) { - fRecord.reset(SkNEW(SkRecord)); - } - fRecorder->reset(fRecord.get(), cullRect, &fMiniRecorder); + fRecord.reset(SkNEW(SkRecord)); + fRecorder->reset(fRecord.get(), cullRect); fActivelyRecording = true; return this->getRecordingCanvas(); } @@ -50,23 +47,19 @@ 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)) { - saveLayerData.reset(SkNEW(SkLayerInfo)); + SkPicture::AccelData::Key key = SkLayerInfo::ComputeKey(); + + saveLayerData.reset(SkNEW_ARGS(SkLayerInfo, (key))); } SkDrawableList* drawableList = fRecorder->getDrawableList(); - SkBigPicture::SnapshotArray* pictList = - drawableList ? drawableList->newDrawableSnapshot() : NULL; + SkPicture::SnapshotArray* pictList = drawableList ? drawableList->newDrawableSnapshot() : NULL; if (fBBH.get()) { if (saveLayerData) { @@ -84,12 +77,12 @@ SkPicture* SkPictureRecorder::endRecordingAsPicture() { for (int i = 0; pictList && i < pictList->count(); i++) { subPictureBytes += SkPictureUtils::ApproximateBytesUsed(pictList->begin()[i]); } - return SkNEW_ARGS(SkBigPicture, (fCullRect, - fRecord.detach(), - pictList, - fBBH.detach(), - saveLayerData.detach(), - subPictureBytes)); + return SkNEW_ARGS(SkPicture, (fCullRect, + fRecord.detach(), + pictList, + fBBH.detach(), + saveLayerData.detach(), + subPictureBytes)); } void SkPictureRecorder::partialReplay(SkCanvas* canvas) const { @@ -140,7 +133,7 @@ protected: } SkPicture* onNewPictureSnapshot() override { - SkBigPicture::SnapshotArray* pictList = NULL; + SkPicture::SnapshotArray* pictList = NULL; if (fDrawableList) { // TODO: should we plumb-down the BBHFactory and recordFlags from our host // PictureRecorder? @@ -150,7 +143,9 @@ protected: SkAutoTUnref saveLayerData; if (fBBH && fDoSaveLayerInfo) { - saveLayerData.reset(SkNEW(SkLayerInfo)); + SkPicture::AccelData::Key key = SkLayerInfo::ComputeKey(); + + saveLayerData.reset(SkNEW_ARGS(SkLayerInfo, (key))); SkBBoxHierarchy* bbh = NULL; // we've already computed fBBH (received in constructor) // TODO: update saveLayer info computation to reuse the already computed @@ -162,22 +157,20 @@ protected: for (int i = 0; pictList && i < pictList->count(); i++) { subPictureBytes += SkPictureUtils::ApproximateBytesUsed(pictList->begin()[i]); } - // SkBigPicture will take ownership of a ref on both fRecord and fBBH. + // SkPicture 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(SkBigPicture, (fBounds, - SkRef(fRecord.get()), - pictList, - SkSafeRef(fBBH.get()), - saveLayerData.detach(), - subPictureBytes)); + return SkNEW_ARGS(SkPicture, (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 65e6d490db..ba15c1b512 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 SkBigPicture::SnapshotArray* pictList, SkLayerInfo* accelData) + const SkPicture::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 - const SkLayerInfo* childData = NULL; - if (const SkBigPicture* bp = picture->asSkBigPicture()) { - childData = static_cast(bp->accelData()); - } + SkPicture::AccelData::Key key = SkLayerInfo::ComputeKey(); + + const SkLayerInfo* childData = + static_cast(picture->EXPERIMENTAL_getAccelData(key)); 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 SkBigPicture::SnapshotArray* fPictList; + const SkPicture::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 SkBigPicture::SnapshotArray* pictList, SkBBoxHierarchy* bbh, + const SkPicture::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 5b248cfbd5..7bbab81b5a 100644 --- a/src/core/SkRecordDraw.h +++ b/src/core/SkRecordDraw.h @@ -9,7 +9,6 @@ #define SkRecordDraw_DEFINED #include "SkBBoxHierarchy.h" -#include "SkBigPicture.h" #include "SkCanvas.h" #include "SkMatrix.h" #include "SkRecord.h" @@ -21,7 +20,7 @@ class SkLayerInfo; void SkRecordFillBounds(const SkRect& cullRect, const SkRecord&, SkBBoxHierarchy*); void SkRecordComputeLayers(const SkRect& cullRect, const SkRecord& record, - const SkBigPicture::SnapshotArray*, + const SkPicture::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 45283f8118..0a2d43edbf 100644 --- a/src/core/SkRecorder.cpp +++ b/src/core/SkRecorder.cpp @@ -5,7 +5,6 @@ * found in the LICENSE file. */ -#include "SkBigPicture.h" #include "SkPatchUtils.h" #include "SkPicture.h" #include "SkPictureUtils.h" @@ -15,7 +14,7 @@ SkDrawableList::~SkDrawableList() { fArray.unrefAll(); } -SkBigPicture::SnapshotArray* SkDrawableList::newDrawableSnapshot() { +SkPicture::SnapshotArray* SkDrawableList::newDrawableSnapshot() { const int count = fArray.count(); if (0 == count) { return NULL; @@ -24,7 +23,7 @@ SkBigPicture::SnapshotArray* SkDrawableList::newDrawableSnapshot() { for (int i = 0; i < count; ++i) { pics[i] = fArray[i]->newPictureSnapshot(); } - return SkNEW_ARGS(SkBigPicture::SnapshotArray, (pics.detach(), count)); + return SkNEW_ARGS(SkPicture::SnapshotArray, (pics.detach(), count)); } void SkDrawableList::append(SkDrawable* drawable) { @@ -33,23 +32,20 @@ void SkDrawableList::append(SkDrawable* drawable) { /////////////////////////////////////////////////////////////////////////////////////////////// -SkRecorder::SkRecorder(SkRecord* record, int width, int height, SkMiniRecorder* mr) +SkRecorder::SkRecorder(SkRecord* record, int width, int height) : SkCanvas(SkIRect::MakeWH(width, height), SkCanvas::kConservativeRasterClip_InitFlag) , fApproxBytesUsedBySubPictures(0) - , fRecord(record) - , fMiniRecorder(mr) {} + , fRecord(record) {} -SkRecorder::SkRecorder(SkRecord* record, const SkRect& bounds, SkMiniRecorder* mr) +SkRecorder::SkRecorder(SkRecord* record, const SkRect& bounds) : SkCanvas(bounds.roundOut(), SkCanvas::kConservativeRasterClip_InitFlag) , fApproxBytesUsedBySubPictures(0) - , fRecord(record) - , fMiniRecorder(mr) {} + , fRecord(record) {} -void SkRecorder::reset(SkRecord* record, const SkRect& bounds, SkMiniRecorder* mr) { +void SkRecorder::reset(SkRecord* record, const SkRect& bounds) { this->forgetRecord(); fRecord = record; this->resetForNextPicture(bounds.roundOut()); - fMiniRecorder = mr; } void SkRecorder::forgetRecord() { @@ -59,13 +55,9 @@ void SkRecorder::forgetRecord() { } // To make appending to fRecord a little less verbose. -#define APPEND(T, ...) \ - if (fMiniRecorder) { this->flushMiniRecorder(); } \ +#define APPEND(T, ...) \ 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__) @@ -133,15 +125,6 @@ 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)); @@ -155,7 +138,6 @@ 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); } @@ -180,7 +162,6 @@ 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)); } @@ -267,7 +248,6 @@ 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 b6f153dc54..d0a992fc08 100644 --- a/src/core/SkRecorder.h +++ b/src/core/SkRecorder.h @@ -8,9 +8,7 @@ #ifndef SkRecorder_DEFINED #define SkRecorder_DEFINED -#include "SkBigPicture.h" #include "SkCanvas.h" -#include "SkMiniRecorder.h" #include "SkRecord.h" #include "SkRecords.h" #include "SkTDArray.h" @@ -27,7 +25,7 @@ public: void append(SkDrawable* drawable); // Return a new or ref'd array of pictures that were snapped from our drawables. - SkBigPicture::SnapshotArray* newDrawableSnapshot(); + SkPicture::SnapshotArray* newDrawableSnapshot(); private: SkTDArray fArray; @@ -38,10 +36,10 @@ private: class SkRecorder : public SkCanvas { public: // Does not take ownership of the SkRecord. - SkRecorder(SkRecord*, int width, int height, SkMiniRecorder* = nullptr); // legacy version - SkRecorder(SkRecord*, const SkRect& bounds, SkMiniRecorder* = nullptr); + SkRecorder(SkRecord*, int width, int height); // legacy version + SkRecorder(SkRecord*, const SkRect& bounds); - void reset(SkRecord*, const SkRect& bounds, SkMiniRecorder* = nullptr); + void reset(SkRecord*, const SkRect& bounds); size_t approxBytesUsedBySubPictures() const { return fApproxBytesUsedBySubPictures; } @@ -122,8 +120,6 @@ public: SkSurface* onNewSurface(const SkImageInfo&, const SkSurfaceProps&) override { return NULL; } - void flushMiniRecorder(); - private: template T* copy(const T*); @@ -140,8 +136,6 @@ 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 6e25dd22e2..319d155054 100644 --- a/src/core/SkRecords.h +++ b/src/core/SkRecords.h @@ -81,7 +81,6 @@ struct T { \ #define RECORD1(T, A, a) \ struct T { \ static const Type kType = T##_Type; \ - T() {} \ template \ T(Z a) : a(a) {} \ A a; \ @@ -90,7 +89,6 @@ 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; \ @@ -99,7 +97,6 @@ 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; \ @@ -108,7 +105,6 @@ 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; \ @@ -117,7 +113,6 @@ 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; \ @@ -130,7 +125,6 @@ struct T { \ template class RefBox : SkNoncopyable { public: - RefBox() {} RefBox(T* obj) : fObj(SkSafeRef(obj)) {} ~RefBox() { SkSafeUnref(fObj); } @@ -144,7 +138,6 @@ private: template class Optional : SkNoncopyable { public: - Optional() : fPtr(nullptr) {} Optional(T* ptr) : fPtr(ptr) {} ~Optional() { if (fPtr) fPtr->~T(); } @@ -174,7 +167,6 @@ private: template class PODArray { public: - PODArray() {} PODArray(T* ptr) : fPtr(ptr) {} // Default copy and assign. @@ -189,7 +181,6 @@ 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; @@ -212,7 +203,6 @@ 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; @@ -223,7 +213,6 @@ 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(); } @@ -238,7 +227,6 @@ 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 67e3c19a2f..70c10d7f69 100644 --- a/src/gpu/GrLayerHoister.cpp +++ b/src/gpu/GrLayerHoister.cpp @@ -9,7 +9,6 @@ #include "GrLayerHoister.h" #include "GrRecordReplaceDraw.h" -#include "SkBigPicture.h" #include "SkCanvas.h" #include "SkDeviceImageFilterProxy.h" #include "SkDeviceProperties.h" @@ -22,7 +21,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, @@ -75,7 +74,7 @@ static void prepare_for_hoisting(GrLayerCache* layerCache, } else { hl = recycled->append(); } - + layerCache->addUse(layer); hl->fLayer = layer; hl->fPicture = pict; @@ -130,12 +129,12 @@ void GrLayerHoister::FindLayersToAtlas(GrContext* context, } GrLayerCache* layerCache = context->getLayerCache(); + layerCache->processDeletedPictures(); - const SkBigPicture::AccelData* topLevelData = NULL; - if (const SkBigPicture* bp = topLevelPicture->asSkBigPicture()) { - topLevelData = bp->accelData(); - } + SkPicture::AccelData::Key key = SkLayerInfo::ComputeKey(); + + const SkPicture::AccelData* topLevelData = topLevelPicture->EXPERIMENTAL_getAccelData(key); if (!topLevelData) { return; } @@ -190,10 +189,9 @@ void GrLayerHoister::FindLayersToHoist(GrContext* context, layerCache->processDeletedPictures(); - const SkBigPicture::AccelData* topLevelData = NULL; - if (const SkBigPicture* bp = topLevelPicture->asSkBigPicture()) { - topLevelData = bp->accelData(); - } + SkPicture::AccelData::Key key = SkLayerInfo::ComputeKey(); + + const SkPicture::AccelData* topLevelData = topLevelPicture->EXPERIMENTAL_getAccelData(key); if (!topLevelData) { return; } @@ -241,11 +239,7 @@ void GrLayerHoister::DrawLayersToAtlas(GrContext* context, for (int i = 0; i < atlased.count(); ++i) { const GrCachedLayer* layer = atlased[i].fLayer; - const SkBigPicture* pict = atlased[i].fPicture->asSkBigPicture(); - if (!pict) { - // TODO: can we assume / assert this? - continue; - } + const SkPicture* pict = atlased[i].fPicture; const SkIPoint offset = SkIPoint::Make(layer->srcIR().fLeft, layer->srcIR().fTop); SkDEBUGCODE(const SkPaint* layerPaint = layer->paint();) @@ -271,7 +265,10 @@ void GrLayerHoister::DrawLayersToAtlas(GrContext* context, atlasCanvas->setMatrix(initialCTM); atlasCanvas->concat(atlased[i].fLocalMat); - pict->partialPlayback(atlasCanvas, layer->start() + 1, layer->stop(), initialCTM); + SkRecordPartialDraw(*pict->fRecord.get(), atlasCanvas, + pict->drawablePicts(), pict->drawableCount(), + layer->start() + 1, layer->stop(), initialCTM); + atlasCanvas->restore(); } @@ -331,11 +328,7 @@ 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 SkBigPicture* pict = layers[i].fPicture->asSkBigPicture(); - if (!pict) { - // TODO: can we assume / assert this? - continue; - } + const SkPicture* pict = layers[i].fPicture; const SkIPoint offset = SkIPoint::Make(layer->srcIR().fLeft, layer->srcIR().fTop); // Each non-atlased layer has its own GrTexture @@ -360,7 +353,10 @@ void GrLayerHoister::DrawLayers(GrContext* context, const SkTDArraysetMatrix(initialCTM); layerCanvas->concat(layers[i].fLocalMat); - pict->partialPlayback(layerCanvas, layer->start()+1, layer->stop(), initialCTM); + SkRecordPartialDraw(*pict->fRecord.get(), layerCanvas, + pict->drawablePicts(), pict->drawableCount(), + layer->start()+1, layer->stop(), initialCTM); + layerCanvas->flush(); if (layer->filter()) { diff --git a/src/gpu/GrRecordReplaceDraw.cpp b/src/gpu/GrRecordReplaceDraw.cpp index 6f05206dcc..dacc939e62 100644 --- a/src/gpu/GrRecordReplaceDraw.cpp +++ b/src/gpu/GrRecordReplaceDraw.cpp @@ -8,7 +8,6 @@ #include "GrContext.h" #include "GrLayerCache.h" #include "GrRecordReplaceDraw.h" -#include "SkBigPicture.h" #include "SkCanvasPriv.h" #include "SkGrPixelRef.h" #include "SkImage.h" @@ -46,7 +45,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()); @@ -60,7 +59,7 @@ public: ReplaceDraw(SkCanvas* canvas, GrLayerCache* layerCache, SkPicture const* const drawablePicts[], int drawableCount, const SkPicture* topLevelPicture, - const SkBigPicture* picture, + const SkPicture* picture, const SkMatrix& initialMatrix, SkPicture::AbortCallback* callback, const unsigned* opIndices, int numIndices) @@ -77,8 +76,8 @@ public: } int draw() { - const SkBBoxHierarchy* bbh = fPicture->bbh(); - const SkRecord* record = fPicture->record(); + const SkBBoxHierarchy* bbh = fPicture->fBBH.get(); + const SkRecord* record = fPicture->fRecord.get(); if (NULL == record) { return 0; } @@ -136,17 +135,13 @@ public: SkAutoCanvasMatrixPaint acmp(fCanvas, &dp.matrix, dp.paint, dp.picture->cullRect()); - 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); - } + // 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(); fOpIndexStack.pop(); } @@ -173,7 +168,7 @@ public: draw_replacement_bitmap(layer, fCanvas); - if (fPicture->bbh()) { + if (fPicture->fBBH.get()) { while (fOps[fIndex] < layer->stop()) { ++fIndex; } @@ -195,7 +190,7 @@ private: SkCanvas* fCanvas; GrLayerCache* fLayerCache; const SkPicture* fTopLevelPicture; - const SkBigPicture* fPicture; + const SkPicture* fPicture; const SkMatrix fInitialMatrix; SkPicture::AbortCallback* fCallback; @@ -216,15 +211,9 @@ int GrRecordReplaceDraw(const SkPicture* picture, SkPicture::AbortCallback* callback) { SkAutoCanvasRestore saveRestore(canvas, true /*save now, restore at exit*/); - 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; - } + // TODO: drawablePicts? + ReplaceDraw draw(canvas, layerCache, NULL, 0, + picture, picture, + initialMatrix, callback, NULL, 0); + return draw.draw(); } diff --git a/src/gpu/SkGpuDevice.cpp b/src/gpu/SkGpuDevice.cpp index 25f6fd370f..7b363635e2 100644 --- a/src/gpu/SkGpuDevice.cpp +++ b/src/gpu/SkGpuDevice.cpp @@ -1999,10 +1999,9 @@ bool SkGpuDevice::EXPERIMENTAL_drawPicture(SkCanvas* mainCanvas, const SkPicture return false; } - const SkBigPicture::AccelData* data = NULL; - if (const SkBigPicture* bp = mainPicture->asSkBigPicture()) { - data = bp->accelData(); - } + SkPicture::AccelData::Key key = SkLayerInfo::ComputeKey(); + + const SkPicture::AccelData* data = mainPicture->EXPERIMENTAL_getAccelData(key); if (!data) { return false; } diff --git a/src/gpu/SkGpuDevice.h b/src/gpu/SkGpuDevice.h index c1ea4fe873..89959e1f9c 100644 --- a/src/gpu/SkGpuDevice.h +++ b/src/gpu/SkGpuDevice.h @@ -217,6 +217,8 @@ 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 new file mode 100644 index 0000000000..a8a251c927 --- /dev/null +++ b/src/utils/SkPictureUtils.cpp @@ -0,0 +1,25 @@ +/* + * 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; +} diff --git a/tests/GpuLayerCacheTest.cpp b/tests/GpuLayerCacheTest.cpp index efb1ec1c20..6b3084b641 100644 --- a/tests/GpuLayerCacheTest.cpp +++ b/tests/GpuLayerCacheTest.cpp @@ -111,10 +111,7 @@ DEF_GPUTEST(GpuLayerCache, reporter, factory) { } SkPictureRecorder recorder; - SkCanvas* c = recorder.beginRecording(1, 1); - // Draw something, anything, to prevent an empty-picture optimization, - // which is a singleton and never purged. - c->drawRect(SkRect::MakeWH(1,1), SkPaint()); + recorder.beginRecording(1, 1); SkAutoTUnref picture(recorder.endRecording()); GrLayerCache cache(context); diff --git a/tests/PictureTest.cpp b/tests/PictureTest.cpp index 0ccef3f0a9..16d98b3245 100644 --- a/tests/PictureTest.cpp +++ b/tests/PictureTest.cpp @@ -23,7 +23,6 @@ #include "SkPictureUtils.h" #include "SkPixelRef.h" #include "SkPixelSerializer.h" -#include "SkMiniRecorder.h" #include "SkRRect.h" #include "SkRandom.h" #include "SkRecord.h" @@ -364,10 +363,9 @@ static void test_savelayer_extraction(skiatest::Reporter* reporter) { // Now test out the SaveLayer extraction if (!SkCanvas::Internal_Private_GetIgnoreSaveLayerBounds()) { - const SkBigPicture* bp = pict->asSkBigPicture(); - REPORTER_ASSERT(reporter, bp); + SkPicture::AccelData::Key key = SkLayerInfo::ComputeKey(); - const SkBigPicture::AccelData* data = bp->accelData(); + const SkPicture::AccelData* data = pict->EXPERIMENTAL_getAccelData(key); REPORTER_ASSERT(reporter, data); const SkLayerInfo *gpuData = static_cast(data); @@ -1109,6 +1107,30 @@ static void test_gen_id(skiatest::Reporter* reporter) { REPORTER_ASSERT(reporter, hasData->uniqueID() != empty->uniqueID()); } +static void test_bytes_used(skiatest::Reporter* reporter) { + SkPictureRecorder recorder; + + recorder.beginRecording(0, 0); + SkAutoTUnref empty(recorder.endRecording()); + + // Sanity check to make sure we aren't under-measuring. + REPORTER_ASSERT(reporter, SkPictureUtils::ApproximateBytesUsed(empty.get()) >= + sizeof(SkPicture) + sizeof(SkRecord)); + + // Protect against any unintentional bloat. + size_t approxUsed = SkPictureUtils::ApproximateBytesUsed(empty.get()); + REPORTER_ASSERT(reporter, approxUsed <= 432); + + // Sanity check of nested SkPictures. + SkPictureRecorder r2; + r2.beginRecording(0, 0); + r2.getRecordingCanvas()->drawPicture(empty.get()); + SkAutoTUnref nested(r2.endRecording()); + + REPORTER_ASSERT(reporter, SkPictureUtils::ApproximateBytesUsed(nested.get()) >= + SkPictureUtils::ApproximateBytesUsed(empty.get())); +} + DEF_TEST(Picture, reporter) { #ifdef SK_DEBUG test_deleting_empty_picture(); @@ -1129,6 +1151,7 @@ DEF_TEST(Picture, reporter) { test_hierarchical(reporter); test_gen_id(reporter); test_savelayer_extraction(reporter); + test_bytes_used(reporter); } static void draw_bitmaps(const SkBitmap bitmap, SkCanvas* canvas) { @@ -1244,10 +1267,7 @@ DEF_TEST(Picture_SkipBBH, r) { SpoonFedBBHFactory factory(&bbh); SkPictureRecorder recorder; - SkCanvas* c = recorder.beginRecording(bound, &factory); - // Record a few ops so we don't hit a small- or empty- picture optimization. - c->drawRect(bound, SkPaint()); - c->drawRect(bound, SkPaint()); + recorder.beginRecording(bound, &factory); SkAutoTUnref picture(recorder.endRecording()); SkCanvas big(640, 480), small(300, 200); @@ -1303,13 +1323,3 @@ DEF_TEST(Picture_getRecordingCanvas, r) { REPORTER_ASSERT(r, !rec.getRecordingCanvas()); } } - -DEF_TEST(MiniRecorderLeftHanging, r) { - // Any shader or other ref-counted effect will do just fine here. - SkPaint paint; - paint.setShader(SkShader::CreateColorShader(SK_ColorRED))->unref(); - - SkMiniRecorder rec; - REPORTER_ASSERT(r, rec.drawRect(SkRect::MakeWH(20,30), paint)); - // Don't call rec.detachPicture(). Test succeeds by not asserting or leaking the shader. -}