From: mtklein Date: Thu, 7 May 2015 20:41:07 +0000 (-0700) Subject: Sketch splitting SkPicture into an interface and SkBigPicture. X-Git-Tag: accepted/tizen/5.0/unified/20181102.025319~2475 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=c92c129ff85b05a714bd1bf921c02d5e14651f8b;p=platform%2Fupstream%2FlibSkiaSharp.git 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: Review URL: https://codereview.chromium.org/1112523006 --- diff --git a/gyp/core.gypi b/gyp/core.gypi index fef6fe4..edf661b 100644 --- a/gyp/core.gypi +++ b/gyp/core.gypi @@ -22,6 +22,7 @@ '<(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', @@ -116,7 +117,6 @@ '<(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', @@ -132,6 +132,7 @@ '<(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 f382e10..1193507 100644 --- a/gyp/utils.gypi +++ b/gyp/utils.gypi @@ -79,7 +79,6 @@ '<(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 88dfdf1..7ff551b 100644 --- a/include/core/SkPicture.h +++ b/include/core/SkPicture.h @@ -5,61 +5,31 @@ * found in the LICENSE file. */ - #ifndef SkPicture_DEFINED #define SkPicture_DEFINED #include "SkImageDecoder.h" #include "SkRefCnt.h" -#include "SkTDArray.h" +#include "SkTypes.h" -#if SK_SUPPORT_GPU class GrContext; -#endif - +class SkBigPicture; 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 - The SkPicture class records the drawing commands made to a canvas, to - be played back at a later time. + 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. */ -class SK_API SkPicture : public SkNVRefCnt { +class SK_API SkPicture : public SkRefCnt { public: - // 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; + virtual ~SkPicture(); /** * Function signature defining a function that sets up an SkBitmap from encoded data. On @@ -95,8 +65,6 @@ 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 @@ -111,7 +79,6 @@ public: public: AbortCallback() {} virtual ~AbortCallback() {} - virtual bool abort() = 0; }; @@ -122,16 +89,14 @@ public: @param canvas the canvas receiving the drawing commands. @param callback a callback that allows interruption of playback */ - void playback(SkCanvas* canvas, AbortCallback* = NULL) const; - - /** 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; } + virtual void playback(SkCanvas*, AbortCallback* = NULL) const = 0; - /** Return a non-zero, unique value representing the picture. + /** Return a cull rect for this picture. + Ops recorded into this picture that attempt to draw outside the cull might not be drawn. */ + virtual SkRect cullRect() const = 0; + + /** Returns a non-zero value unique among all pictures. */ uint32_t uniqueID() const; /** @@ -140,7 +105,7 @@ public: * * TODO: Use serializer to serialize SkImages as well. */ - void serialize(SkWStream*, SkPixelSerializer* serializer = NULL) const; + void serialize(SkWStream*, SkPixelSerializer* = NULL) const; /** * Serialize to a buffer. @@ -151,7 +116,21 @@ public: * Returns true if any bitmaps may be produced when this SkPicture * is replayed. */ - bool willPlayBackBitmaps() const; + 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; /** Return true if the SkStream/Buffer represents a serialized picture, and fills out SkPictInfo. After this function returns, the data source is not @@ -164,76 +143,24 @@ 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. - */ - -#if SK_SUPPORT_GPU - bool suitableForGpuRasterization(GrContext*, const char ** = NULL) const; -#endif + /** Return true if the picture is suitable for rendering on the GPU. */ + virtual bool suitableForGpuRasterization(GrContext*, const char** whyNot = NULL) 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. - */ - int approximateOpCount() const; - - /** Return true if this picture contains text. - */ - bool hasText() const; + // Sent via SkMessageBus from destructor. + struct DeletionMessage { int32_t fUniqueID; }; // TODO: -> uint32_t? - // 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(); } } + // Returns NULL if this is not an SkBigPicture. + virtual const SkBigPicture* asSkBigPicture() const { return NULL; } - const SkPicture* const* begin() const { return fPics; } - int count() const { return fCount; } - private: - SkAutoTMalloc fPics; - int fCount; - }; +private: + // Subclass whitelist. + SkPicture(); + friend class SkBigPicture; + friend class SkEmptyPicture; + template friend class SkMiniPicture; - // Sent via SkMessageBus from destructor. - struct DeletionMessage { int32_t fUniqueID; }; + virtual int numSlowPaths() const = 0; -private: - // 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) @@ -242,65 +169,20 @@ 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. - static const uint32_t CURRENT_PICTURE_VERSION = 41; + static const uint32_t MIN_PICTURE_VERSION = 35, // Produced by Chrome M39. + 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); - - // uint32_t fRefCnt; from SkNVRefCnt - mutable uint32_t fUniqueID; - const SkRect fCullRect; - SkAutoTUnref fRecord; - SkAutoTDelete fDrawablePicts; - SkAutoTUnref fBBH; - SkAutoTUnref fAccelData; - const size_t fApproxBytesUsedBySubPictures; - - // 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; - } fAnalysis; + SkPictInfo createHeader() const; + SkPictureData* backport() const; - friend class SkPictureRecorder; // SkRecord-based constructor. - friend class GrLayerHoister; // access to fRecord - friend class ReplaceDraw; - friend class SkPictureUtils; - friend class SkRecordedDrawable; + mutable uint32_t fUniqueID; }; -SK_COMPILE_ASSERT(sizeof(SkPicture) <= 88, SkPictureSize); #endif diff --git a/include/core/SkPictureRecorder.h b/include/core/SkPictureRecorder.h index 2cf7909..a84b721 100644 --- a/include/core/SkPictureRecorder.h +++ b/include/core/SkPictureRecorder.h @@ -8,6 +8,7 @@ #ifndef SkPictureRecorder_DEFINED #define SkPictureRecorder_DEFINED +#include "../../src/core/SkMiniRecorder.h" #include "SkBBHFactory.h" #include "SkPicture.h" #include "SkRefCnt.h" @@ -102,6 +103,7 @@ 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 10607da..b65a64d 100644 --- a/include/utils/SkPictureUtils.h +++ b/include/utils/SkPictureUtils.h @@ -10,6 +10,8 @@ #include "SkPicture.h" +// TODO: remove this file? + class SK_API SkPictureUtils { public: /** @@ -18,7 +20,9 @@ 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); + static size_t ApproximateBytesUsed(const SkPicture* pict) { + return pict->approximateBytesUsed(); + } }; #endif diff --git a/src/core/SkBigPicture.cpp b/src/core/SkBigPicture.cpp new file mode 100644 index 0000000..33be192 --- /dev/null +++ b/src/core/SkBigPicture.cpp @@ -0,0 +1,167 @@ +/* + * 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 "SkPathEffect.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) + , fAnalysis(*record) + , 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); +} + +SkRect SkBigPicture::cullRect() const { return fCullRect; } +bool SkBigPicture::hasText() const { return fAnalysis.fHasText; } +bool SkBigPicture::willPlayBackBitmaps() const { return fAnalysis.fWillPlaybackBitmaps; } +int SkBigPicture::numSlowPaths() const { return fAnalysis.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; +} +bool SkBigPicture::suitableForGpuRasterization(GrContext*, const char** reason) const { + return fAnalysis.suitableForGpuRasterization(reason); +} + +int SkBigPicture::drawableCount() const { + return fDrawablePicts ? fDrawablePicts->count() : 0; +} +SkPicture const* const* SkBigPicture::drawablePicts() const { + return fDrawablePicts ? fDrawablePicts->begin() : nullptr; +} + +// Some ops have a paint, some have an optional paint. Either way, get back a pointer. +static const SkPaint* as_ptr(const SkPaint& p) { return &p; } +static const SkPaint* as_ptr(const SkRecords::Optional& p) { return p; } + +struct SkBigPicture::PathCounter { + SK_CREATE_MEMBER_DETECTOR(paint); + + PathCounter() : 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(as_ptr(op.paint)); + } + + template + SK_WHEN(!HasMember_paint, void) operator()(const T& op) { /* do nothing */ } + + int fNumSlowPathsAndDashEffects; +}; + +SkBigPicture::Analysis::Analysis(const SkRecord& record) { + SkTextHunter text; + SkBitmapHunter bitmap; + PathCounter 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); +} + +bool SkBigPicture::Analysis::suitableForGpuRasterization(const char** reason) const { + if (fNumSlowPathsAndDashEffects > 5) { + if (reason) { *reason = "Too many slow paths (either concave or dashed)."; } + return false; + } + return true; +} diff --git a/src/core/SkBigPicture.h b/src/core/SkBigPicture.h new file mode 100644 index 0000000..fb3b62c --- /dev/null +++ b/src/core/SkBigPicture.h @@ -0,0 +1,89 @@ +/* + * 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" + +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; + bool suitableForGpuRasterization(GrContext*, const char** whyNot) 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; + }; + struct PathCounter; + + int numSlowPaths() const override; + + int drawableCount() const; + SkPicture const* const* drawablePicts() const; + + const SkRect fCullRect; + const Analysis fAnalysis; + const size_t fApproxBytesUsedBySubPictures; + 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 d427fa7..0000000 --- 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 a47c3f1..3d4b269 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 0000000..fab6253 --- /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; } + bool suitableForGpuRasterization(GrContext*, const char**) const override { return true; } +}; +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); } + + // TODO: These trivial implementations are not all correct for all types. + // But I suspect these will never be called on SkMiniPictures, so assert for now. + int numSlowPaths() const override { SkASSERT(false); return 0; } + bool suitableForGpuRasterization(GrContext*, const char**) const override { + SkASSERT(false); + return true; + } + +private: + SkRect fCull; + T fOp; +}; + + +SkMiniRecorder::SkMiniRecorder() : fState(State::kEmpty) {} +SkMiniRecorder::~SkMiniRecorder() { + // We've done something wrong if no one's called detachAsPicture(). + 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 0000000..d01aeda --- /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 c014879..c568e80 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->fAnalysis; - 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 (IsValidPictInfo(info)) { + if (pInfo) { *pInfo = info; } + return true; } - - if (pInfo != NULL) { - *pInfo = info; - } - 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 (pInfo != NULL) { - *pInfo = info; + if (IsValidPictInfo(info)) { + if (pInfo) { *pInfo = info; } + return true; } - 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); @@ -437,46 +188,3 @@ void SkPicture::flatten(SkWriteBuffer& buffer) const { buffer.writeBool(false); } } - -#if SK_SUPPORT_GPU -bool SkPicture::suitableForGpuRasterization(GrContext*, const char **reason) const { - return fAnalysis.suitableForGpuRasterization(reason, 0); -} -#endif - -bool SkPicture::hasText() const { return fAnalysis.fHasText; } -bool SkPicture::willPlayBackBitmaps() const { return fAnalysis.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) - , fAnalysis(*fRecord) -{} - - -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 new file mode 100644 index 0000000..e987234 --- /dev/null +++ b/src/core/SkPictureCommon.h @@ -0,0 +1,70 @@ +/* + * 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. + +#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; } +}; + diff --git a/src/core/SkPictureRecorder.cpp b/src/core/SkPictureRecorder.cpp index 282e2c2..c110e0a 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 ba15c1b..65e6d49 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 7bbab81..5b248cf 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 0a2d43e..45283f8 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 d0a992f..b6f153d 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 319d155..6e25dd2 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 70c10d7..67e3c19 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 dacc939..6f05206 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 9518600..eee93bb 100644 --- a/src/gpu/SkGpuDevice.cpp +++ b/src/gpu/SkGpuDevice.cpp @@ -2014,9 +2014,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 89959e1..c1ea4fe 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 a8a251c..0000000 --- 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; -} diff --git a/tests/GpuLayerCacheTest.cpp b/tests/GpuLayerCacheTest.cpp index 6b3084b..efb1ec1 100644 --- a/tests/GpuLayerCacheTest.cpp +++ b/tests/GpuLayerCacheTest.cpp @@ -111,7 +111,10 @@ DEF_GPUTEST(GpuLayerCache, reporter, factory) { } SkPictureRecorder recorder; - recorder.beginRecording(1, 1); + 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()); SkAutoTUnref picture(recorder.endRecording()); GrLayerCache cache(context); diff --git a/tests/PictureTest.cpp b/tests/PictureTest.cpp index 16d98b3..cc96b91 100644 --- a/tests/PictureTest.cpp +++ b/tests/PictureTest.cpp @@ -363,9 +363,10 @@ static void test_savelayer_extraction(skiatest::Reporter* reporter) { // Now test out the SaveLayer extraction if (!SkCanvas::Internal_Private_GetIgnoreSaveLayerBounds()) { - SkPicture::AccelData::Key key = SkLayerInfo::ComputeKey(); + const SkBigPicture* bp = pict->asSkBigPicture(); + REPORTER_ASSERT(reporter, bp); - const SkPicture::AccelData* data = pict->EXPERIMENTAL_getAccelData(key); + const SkBigPicture::AccelData* data = bp->accelData(); REPORTER_ASSERT(reporter, data); const SkLayerInfo *gpuData = static_cast(data); @@ -1107,30 +1108,6 @@ 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(); @@ -1151,7 +1128,6 @@ 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) { @@ -1267,7 +1243,10 @@ DEF_TEST(Picture_SkipBBH, r) { SpoonFedBBHFactory factory(&bbh); SkPictureRecorder recorder; - recorder.beginRecording(bound, &factory); + 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()); SkAutoTUnref picture(recorder.endRecording()); SkCanvas big(640, 480), small(300, 200);