aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--gyp/core.gypi3
-rw-r--r--gyp/utils.gypi1
-rw-r--r--include/core/SkPicture.h212
-rw-r--r--include/core/SkPictureRecorder.h2
-rw-r--r--include/utils/SkPictureUtils.h6
-rw-r--r--src/core/SkBigPicture.cpp98
-rw-r--r--src/core/SkBigPicture.h88
-rw-r--r--src/core/SkLayerInfo.cpp15
-rw-r--r--src/core/SkLayerInfo.h18
-rw-r--r--src/core/SkMiniRecorder.cpp104
-rw-r--r--src/core/SkMiniRecorder.h44
-rw-r--r--src/core/SkPicture.cpp426
-rw-r--r--src/core/SkPictureCommon.h136
-rw-r--r--src/core/SkPictureRecorder.cpp55
-rw-r--r--src/core/SkRecordDraw.cpp14
-rw-r--r--src/core/SkRecordDraw.h3
-rw-r--r--src/core/SkRecorder.cpp36
-rw-r--r--src/core/SkRecorder.h14
-rw-r--r--src/core/SkRecords.h12
-rw-r--r--src/gpu/GrLayerHoister.cpp42
-rw-r--r--src/gpu/GrRecordReplaceDraw.cpp47
-rw-r--r--src/gpu/SkGpuDevice.cpp7
-rw-r--r--src/gpu/SkGpuDevice.h2
-rw-r--r--src/utils/SkPictureUtils.cpp25
-rw-r--r--tests/GpuLayerCacheTest.cpp5
-rw-r--r--tests/PictureTest.cpp46
26 files changed, 771 insertions, 690 deletions
diff --git a/gyp/core.gypi b/gyp/core.gypi
index f6a2ce2ca7..b99f51c2ad 100644
--- a/gyp/core.gypi
+++ b/gyp/core.gypi
@@ -23,6 +23,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',
@@ -117,7 +118,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',
@@ -133,6 +133,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 f382e1028f..1193507215 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 9a2b65b5c5..995db4c941 100644
--- a/include/core/SkPicture.h
+++ b/include/core/SkPicture.h
@@ -5,62 +5,35 @@
* found in the LICENSE file.
*/
-
#ifndef SkPicture_DEFINED
#define SkPicture_DEFINED
#include "SkImageDecoder.h"
#include "SkLazyPtr.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.
*/
+// TODO(mtklein): logically this could be : public SkRefCnt, but that exposes
+// some ref-count adoption bugs in Blink. Keeping this using SkNVRefCnt
+// happens to stifle them for now. skia:3847
class SK_API SkPicture : public SkNVRefCnt<SkPicture> {
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
@@ -96,8 +69,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
@@ -112,7 +83,6 @@ public:
public:
AbortCallback() {}
virtual ~AbortCallback() {}
-
virtual bool abort() = 0;
};
@@ -123,16 +93,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;
/**
@@ -141,7 +109,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.
@@ -152,7 +120,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
@@ -165,76 +147,25 @@ 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 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 the picture is suitable for rendering on the GPU. */
+ bool suitableForGpuRasterization(GrContext*, const char** whyNot = NULL) 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<const SkPicture*> fPics;
- int fCount;
- };
+private:
+ // Subclass whitelist.
+ SkPicture();
+ friend class SkBigPicture;
+ friend class SkEmptyPicture;
+ template <typename> friend class SkMiniPicture;
- // Sent via SkMessageBus from destructor.
- struct DeletionMessage { int32_t fUniqueID; };
+ virtual int numSlowPaths() const = 0;
+ friend struct SkPathCounter;
-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)
@@ -243,67 +174,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 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);
- // uint32_t fRefCnt; from SkNVRefCnt<SkPicture>
- mutable uint32_t fUniqueID;
- const SkRect fCullRect;
- SkAutoTUnref<const SkRecord> fRecord;
- SkAutoTDelete<const SnapshotArray> fDrawablePicts;
- SkAutoTUnref<const SkBBoxHierarchy> fBBH;
- SkAutoTUnref<const AccelData> 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;
- };
- SkLazyPtr<Analysis> fAnalysis;
- const Analysis& analysis() const;
+ 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 2cf7909cf3..a84b721e44 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<SkBBoxHierarchy> fBBH;
SkAutoTUnref<SkRecorder> fRecorder;
SkAutoTUnref<SkRecord> fRecord;
+ SkMiniRecorder fMiniRecorder;
typedef SkNoncopyable INHERITED;
};
diff --git a/include/utils/SkPictureUtils.h b/include/utils/SkPictureUtils.h
index 10607da9c6..b65a64d579 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 0000000000..6609586ca2
--- /dev/null
+++ b/src/core/SkBigPicture.cpp
@@ -0,0 +1,98 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkBBoxHierarchy.h"
+#include "SkBigPicture.h"
+#include "SkPictureCommon.h"
+#include "SkRecord.h"
+#include "SkRecordDraw.h"
+
+SkBigPicture::SkBigPicture(const SkRect& cull,
+ SkRecord* record,
+ SnapshotArray* drawablePicts,
+ SkBBoxHierarchy* bbh,
+ AccelData* accelData,
+ size_t approxBytesUsedBySubPictures)
+ : fCullRect(cull)
+ , fApproxBytesUsedBySubPictures(approxBytesUsedBySubPictures)
+ , fRecord(record) // Take ownership of caller's ref.
+ , fDrawablePicts(drawablePicts) // Take ownership.
+ , fBBH(bbh) // Take ownership of caller's ref.
+ , fAccelData(accelData) // Take ownership of caller's ref.
+{}
+
+void SkBigPicture::playback(SkCanvas* canvas, AbortCallback* callback) const {
+ SkASSERT(canvas);
+
+ // If the query contains the whole picture, don't bother with the BBH.
+ SkRect clipBounds = { 0, 0, 0, 0 };
+ (void)canvas->getClipBounds(&clipBounds);
+ const bool useBBH = !clipBounds.contains(this->cullRect());
+
+ SkRecordDraw(*fRecord,
+ canvas,
+ this->drawablePicts(),
+ nullptr,
+ this->drawableCount(),
+ useBBH ? fBBH.get() : nullptr,
+ callback);
+}
+
+void SkBigPicture::partialPlayback(SkCanvas* canvas,
+ unsigned start,
+ unsigned stop,
+ const SkMatrix& initialCTM) const {
+ SkASSERT(canvas);
+ SkRecordPartialDraw(*fRecord,
+ canvas,
+ this->drawablePicts(),
+ this->drawableCount(),
+ start,
+ stop,
+ initialCTM);
+}
+
+const SkBigPicture::Analysis& SkBigPicture::analysis() const {
+ auto create = [&]() { return SkNEW_ARGS(Analysis, (*fRecord)); };
+ return *fAnalysis.get(create);
+}
+
+SkRect SkBigPicture::cullRect() const { return fCullRect; }
+bool SkBigPicture::hasText() const { return this->analysis().fHasText; }
+bool SkBigPicture::willPlayBackBitmaps() const { return this->analysis().fWillPlaybackBitmaps; }
+int SkBigPicture::numSlowPaths() const { return this->analysis().fNumSlowPathsAndDashEffects; }
+int SkBigPicture::approximateOpCount() const { return fRecord->count(); }
+size_t SkBigPicture::approximateBytesUsed() const {
+ size_t bytes = sizeof(*this) + fRecord->bytesUsed() + fApproxBytesUsedBySubPictures;
+ if (fBBH) { bytes += fBBH->bytesUsed(); }
+ return bytes;
+}
+
+int SkBigPicture::drawableCount() const {
+ return fDrawablePicts ? fDrawablePicts->count() : 0;
+}
+
+SkPicture const* const* SkBigPicture::drawablePicts() const {
+ return fDrawablePicts ? fDrawablePicts->begin() : nullptr;
+}
+
+SkBigPicture::Analysis::Analysis(const SkRecord& record) {
+ SkTextHunter text;
+ SkBitmapHunter bitmap;
+ SkPathCounter path;
+
+ bool hasText = false, hasBitmap = false;
+ for (unsigned i = 0; i < record.count(); i++) {
+ hasText = hasText || record.visit<bool>(i, text);
+ hasBitmap = hasBitmap || record.visit<bool>(i, bitmap);
+ record.visit<void>(i, path);
+ }
+
+ fHasText = hasText;
+ fWillPlaybackBitmaps = hasBitmap;
+ fNumSlowPathsAndDashEffects = SkTMin<int>(path.fNumSlowPathsAndDashEffects, 255);
+}
diff --git a/src/core/SkBigPicture.h b/src/core/SkBigPicture.h
new file mode 100644
index 0000000000..14e413b55a
--- /dev/null
+++ b/src/core/SkBigPicture.h
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkBigPicture_DEFINED
+#define SkBigPicture_DEFINED
+
+#include "SkPicture.h"
+#include "SkLazyPtr.h"
+
+class SkBBoxHierarchy;
+class SkRecord;
+
+// An implementation of SkPicture supporting an arbitrary number of drawing commands.
+class SkBigPicture final : public SkPicture {
+public:
+ // AccelData provides a base class for device-specific acceleration data.
+ class AccelData : public SkRefCnt { };
+
+ // An array of refcounted const SkPicture pointers.
+ class SnapshotArray : ::SkNoncopyable {
+ public:
+ SnapshotArray(const SkPicture* pics[], int count) : fPics(pics), fCount(count) {}
+ ~SnapshotArray() { for (int i = 0; i < fCount; i++) { fPics[i]->unref(); } }
+
+ const SkPicture* const* begin() const { return fPics; }
+ int count() const { return fCount; }
+ private:
+ SkAutoTMalloc<const SkPicture*> 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<const Analysis> fAnalysis;
+ SkAutoTUnref<const SkRecord> fRecord;
+ SkAutoTDelete<const SnapshotArray> fDrawablePicts;
+ SkAutoTUnref<const SkBBoxHierarchy> fBBH;
+ SkAutoTUnref<const AccelData> fAccelData;
+};
+
+#endif//SkBigPicture_DEFINED
diff --git a/src/core/SkLayerInfo.cpp b/src/core/SkLayerInfo.cpp
deleted file mode 100644
index d427fa7c21..0000000000
--- a/src/core/SkLayerInfo.cpp
+++ /dev/null
@@ -1,15 +0,0 @@
-/*
- * Copyright 2014 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#include "SkLayerInfo.h"
-
-SkPicture::AccelData::Key SkLayerInfo::ComputeKey() {
- static const SkPicture::AccelData::Key gGPUID = SkPicture::AccelData::GenerateDomain();
-
- return gGPUID;
-}
-
diff --git a/src/core/SkLayerInfo.h b/src/core/SkLayerInfo.h
index a47c3f1199..3d4b26960c 100644
--- a/src/core/SkLayerInfo.h
+++ b/src/core/SkLayerInfo.h
@@ -8,12 +8,12 @@
#ifndef SkLayerInfo_DEFINED
#define SkLayerInfo_DEFINED
-#include "SkPicture.h"
+#include "SkBigPicture.h"
#include "SkTArray.h"
// This class stores information about the saveLayer/restore pairs found
// within an SkPicture. It is used by Ganesh to perform layer hoisting.
-class SkLayerInfo : public SkPicture::AccelData {
+class SkLayerInfo : public SkBigPicture::AccelData {
public:
// Information about a given saveLayer/restore block in an SkPicture
class BlockInfo {
@@ -33,12 +33,12 @@ public:
SkRect fSrcBounds;
// The pre-matrix begins as the identity and accumulates the transforms
// of the containing SkPictures (if any). This matrix state has to be
- // part of the initial matrix during replay so that it will be
+ // part of the initial matrix during replay so that it will be
// preserved across setMatrix calls.
SkMatrix fPreMat;
- // The matrix state (in the leaf picture) in which this layer's draws
+ // The matrix state (in the leaf picture) in which this layer's draws
// must occur. It will/can be overridden by setMatrix calls in the
- // layer itself. It does not include the translation needed to map the
+ // layer itself. It does not include the translation needed to map the
// layer's top-left point to the origin (which must be part of the
// initial matrix).
SkMatrix fLocalMat;
@@ -60,7 +60,7 @@ public:
int fKeySize; // # of ints
};
- SkLayerInfo(Key key) : INHERITED(key) { }
+ SkLayerInfo() {}
BlockInfo& addBlock() { return fBlocks.push_back(); }
@@ -72,14 +72,10 @@ public:
return fBlocks[index];
}
- // We may, in the future, need to pass in the GPUDevice in order to
- // incorporate the clip and matrix state into the key
- static SkPicture::AccelData::Key ComputeKey();
-
private:
SkTArray<BlockInfo, true> fBlocks;
- typedef SkPicture::AccelData INHERITED;
+ typedef SkBigPicture::AccelData INHERITED;
};
#endif // SkLayerInfo_DEFINED
diff --git a/src/core/SkMiniRecorder.cpp b/src/core/SkMiniRecorder.cpp
new file mode 100644
index 0000000000..f851e1cac0
--- /dev/null
+++ b/src/core/SkMiniRecorder.cpp
@@ -0,0 +1,104 @@
+/*
+ * 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 "SkMiniRecorder.h"
+#include "SkPicture.h"
+#include "SkPictureCommon.h"
+#include "SkRecordDraw.h"
+#include "SkTextBlob.h"
+
+using namespace SkRecords;
+
+// SkEmptyPicture could logically be a singleton, but that plays badly with Blink's
+// Debug-only adopted() / requireAdoption() tracking in sk_ref_cnt_ext_debug.h.
+// TODO(mtklein): modify sk_ref_cnt_ext_debug.h to play better with non-new'd objects.
+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; }
+};
+
+template <typename T>
+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<Type>, (cull, reinterpret_cast<Type*>(fBuffer.get())))
+
+SkPicture* SkMiniRecorder::detachAsPicture(const SkRect& cull) {
+ switch (fState) {
+ case State::kEmpty: return SkNEW(SkEmptyPicture);
+ CASE(DrawPath);
+ CASE(DrawRect);
+ CASE(DrawTextBlob);
+ }
+ SkASSERT(false);
+ return NULL;
+}
+#undef CASE
diff --git a/src/core/SkMiniRecorder.h b/src/core/SkMiniRecorder.h
new file mode 100644
index 0000000000..d01aedacea
--- /dev/null
+++ b/src/core/SkMiniRecorder.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkMiniRecorder_DEFINED
+#define SkMiniRecorder_DEFINED
+
+#include "SkRecords.h"
+#include "SkScalar.h"
+#include "SkTypes.h"
+class SkCanvas;
+
+// Records small pictures, but only a limited subset of the canvas API, and may fail.
+class SkMiniRecorder : SkNoncopyable {
+public:
+ SkMiniRecorder();
+ ~SkMiniRecorder();
+
+ // Try to record an op. Returns false on failure.
+ bool drawPath(const SkPath&, const SkPaint&);
+ bool drawRect(const SkRect&, const SkPaint&);
+ bool drawTextBlob(const SkTextBlob*, SkScalar x, SkScalar y, const SkPaint&);
+
+ // Detach anything we've recorded as a picture, resetting this SkMiniRecorder.
+ SkPicture* detachAsPicture(const SkRect& cull);
+
+private:
+ enum class State { kEmpty, kDrawPath, kDrawRect, kDrawTextBlob };
+
+ State fState;
+
+ template <size_t A, size_t B>
+ struct Max { static const size_t val = A > B ? A : B; };
+
+ static const size_t kInlineStorage = Max<sizeof(SkRecords::DrawPath),
+ Max<sizeof(SkRecords::DrawRect),
+ sizeof(SkRecords::DrawTextBlob)>::val>::val;
+ SkAlignedSStorage<kInlineStorage> fBuffer;
+};
+
+#endif//SkMiniRecorder_DEFINED
diff --git a/src/core/SkPicture.cpp b/src/core/SkPicture.cpp
index 0145cbe5d7..6c1f6cb759 100644
--- a/src/core/SkPicture.cpp
+++ b/src/core/SkPicture.cpp
@@ -5,330 +5,107 @@
* found in the LICENSE file.
*/
-
-#include "SkPictureFlat.h"
+#include "SkAtomics.h"
+#include "SkMessageBus.h"
+#include "SkPicture.h"
#include "SkPictureData.h"
#include "SkPicturePlayback.h"
#include "SkPictureRecord.h"
#include "SkPictureRecorder.h"
-#include "SkAtomics.h"
-#include "SkBitmapDevice.h"
-#include "SkCanvas.h"
-#include "SkChunkAlloc.h"
-#include "SkMessageBus.h"
-#include "SkPaintPriv.h"
-#include "SkPathEffect.h"
-#include "SkPicture.h"
-#include "SkRegion.h"
-#include "SkShader.h"
-#include "SkStream.h"
-#include "SkTDArray.h"
-#include "SkTLogic.h"
-#include "SkTSearch.h"
-#include "SkTime.h"
-
-#include "SkReader32.h"
-#include "SkWriter32.h"
-#include "SkRTree.h"
-
-#if SK_SUPPORT_GPU
-#include "GrContext.h"
-#endif
-
-#include "SkRecord.h"
-#include "SkRecordDraw.h"
-#include "SkRecordOpts.h"
-#include "SkRecorder.h"
-
DECLARE_SKMESSAGEBUS_MESSAGE(SkPicture::DeletionMessage);
-template <typename T> 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<SkPaint>& 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 <typename T>
- bool operator()(const T& r) { return CheckBitmap(r); }
-
-
- // If the command has a bitmap, of course we're going to play back bitmaps.
- template <typename T>
- static SK_WHEN(HasMember_bitmap<T>, bool) CheckBitmap(const T&) { return true; }
-
- // If not, look for one in its paint (if it has a paint).
- template <typename T>
- static SK_WHEN(!HasMember_bitmap<T>, bool) CheckBitmap(const T& r) { return CheckPaint(r); }
-
- // If we have a paint, dig down into the effects looking for a bitmap.
- template <typename T>
- static SK_WHEN(HasMember_paint<T>, 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 <typename T>
- static SK_WHEN(!HasMember_paint<T>, bool) CheckPaint(const T&) { return false; }
-};
-
-bool WillPlaybackBitmaps(const SkRecord& record) {
- BitmapTester tester;
- for (unsigned i = 0; i < record.count(); i++) {
- if (record.visit<bool>(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 <typename T> SK_WHEN(HasMember_text<T>, bool) operator()(const T&) { return true; }
- template <typename T> SK_WHEN(!HasMember_text<T>, 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 <typename T>
- SK_WHEN(HasMember_paint<T>, void) operator()(const T& op) {
- this->checkPaint(AsPtr(op.paint));
- }
-
- template <typename T>
- SK_WHEN(!HasMember_paint<T>, 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<void>(i, counter);
- }
- fNumSlowPathsAndDashEffects = SkTMin<int>(counter.fNumSlowPathsAndDashEffects, 255);
-
- fHasText = false;
- TextHunter text;
- for (unsigned i = 0; i < record.count(); i++) {
- if (record.visit<bool>(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<SkPicture::DeletionMessage>::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<Domain>(id);
+ return id;
}
-///////////////////////////////////////////////////////////////////////////////
+static const char kMagic[] = { 's', 'k', 'i', 'a', 'p', 'i', 'c', 't' };
-void SkPicture::playback(SkCanvas* canvas, AbortCallback* callback) const {
- SkASSERT(canvas);
+SkPictInfo SkPicture::createHeader() const {
+ SkPictInfo info;
+ // Copy magic bytes at the beginning of the header
+ static_assert(sizeof(kMagic) == 8, "");
+ static_assert(sizeof(kMagic) == sizeof(info.fMagic), "");
+ memcpy(info.fMagic, kMagic, sizeof(kMagic));
- // If the query contains the whole picture, don't bother with the BBH.
- SkRect clipBounds = { 0, 0, 0, 0 };
- (void)canvas->getClipBounds(&clipBounds);
- const bool useBBH = !clipBounds.contains(this->cullRect());
+ // Set picture info after magic bytes in the header
+ info.fVersion = CURRENT_PICTURE_VERSION;
+ info.fCullRect = this->cullRect();
+ info.fFlags = SkPictInfo::kCrossProcess_Flag;
+ // TODO: remove this flag, since we're always float (now)
+ info.fFlags |= SkPictInfo::kScalarIsFloat_Flag;
- SkRecordDraw(*fRecord, canvas, this->drawablePicts(), NULL, this->drawableCount(),
- useBBH ? fBBH.get() : NULL, callback);
+ if (8 == sizeof(void*)) {
+ info.fFlags |= SkPictInfo::kPtrIs64Bit_Flag;
+ }
+ return info;
}
-///////////////////////////////////////////////////////////////////////////////
-
-#include "SkStream.h"
-
-static const char kMagic[] = { 's', 'k', 'i', 'a', 'p', 'i', 'c', 't' };
-
bool SkPicture::IsValidPictInfo(const SkPictInfo& info) {
if (0 != memcmp(info.fMagic, kMagic, sizeof(kMagic))) {
return false;
}
-
- if (info.fVersion < MIN_PICTURE_VERSION ||
- info.fVersion > CURRENT_PICTURE_VERSION) {
+ if (info.fVersion < MIN_PICTURE_VERSION || info.fVersion > CURRENT_PICTURE_VERSION) {
return false;
}
-
return true;
}
bool SkPicture::InternalOnly_StreamIsSKP(SkStream* stream, SkPictInfo* pInfo) {
- if (NULL == stream) {
+ if (!stream) {
return false;
}
- // Check magic bytes.
SkPictInfo info;
SkASSERT(sizeof(kMagic) == sizeof(info.fMagic));
-
if (!stream->read(&info.fMagic, sizeof(kMagic))) {
return false;
}
- info.fVersion = stream->readU32();
- info.fCullRect.fLeft = stream->readScalar();
- info.fCullRect.fTop = stream->readScalar();
- info.fCullRect.fRight = stream->readScalar();
+ info.fVersion = stream->readU32();
+ info.fCullRect.fLeft = stream->readScalar();
+ info.fCullRect.fTop = stream->readScalar();
+ info.fCullRect.fRight = stream->readScalar();
info.fCullRect.fBottom = stream->readScalar();
+ info.fFlags = stream->readU32();
- info.fFlags = stream->readU32();
-
- if (!IsValidPictInfo(info)) {
- return false;
- }
-
- if (pInfo != NULL) {
- *pInfo = info;
+ if (IsValidPictInfo(info)) {
+ if (pInfo) { *pInfo = info; }
+ return true;
}
- return true;
+ return false;
}
bool SkPicture::InternalOnly_BufferIsSKP(SkReadBuffer* buffer, SkPictInfo* pInfo) {
- // Check magic bytes.
SkPictInfo info;
SkASSERT(sizeof(kMagic) == sizeof(info.fMagic));
-
if (!buffer->readByteArray(&info.fMagic, sizeof(kMagic))) {
return false;
}
@@ -337,32 +114,29 @@ bool SkPicture::InternalOnly_BufferIsSKP(SkReadBuffer* buffer, SkPictInfo* pInfo
buffer->readRect(&info.fCullRect);
info.fFlags = buffer->readUInt();
- if (!IsValidPictInfo(info)) {
- return false;
+ if (IsValidPictInfo(info)) {
+ if (pInfo) { *pInfo = info; }
+ return true;
}
-
- if (pInfo != NULL) {
- *pInfo = info;
- }
- return true;
+ return false;
}
SkPicture* SkPicture::Forwardport(const SkPictInfo& info, const SkPictureData* data) {
if (!data) {
- return NULL;
+ return nullptr;
}
SkPicturePlayback playback(data);
SkPictureRecorder r;
playback.draw(r.beginRecording(SkScalarCeilToInt(info.fCullRect.width()),
SkScalarCeilToInt(info.fCullRect.height())),
- NULL/*no callback*/);
+ nullptr/*no callback*/);
return r.endRecording();
}
SkPicture* SkPicture::CreateFromStream(SkStream* stream, InstallPixelRefProc proc) {
SkPictInfo info;
if (!InternalOnly_StreamIsSKP(stream, &info) || !stream->readBool()) {
- return NULL;
+ return nullptr;
}
SkAutoTDelete<SkPictureData> 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<SkPictureData> 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<SkPictureData> data(Backport(*fRecord, info, this->drawablePicts(),
- this->drawableCount()));
+ SkPictInfo info = this->createHeader();
+ SkAutoTDelete<SkPictureData> 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<SkPictureData> data(Backport(*fRecord, info, this->drawablePicts(),
- this->drawableCount()));
+ SkPictInfo info = this->createHeader();
+ SkAutoTDelete<SkPictureData> data(this->backport());
buffer.writeByteArray(&info.fMagic, sizeof(info.fMagic));
buffer.writeUInt(info.fVersion);
@@ -438,49 +189,10 @@ void SkPicture::flatten(SkWriteBuffer& buffer) const {
}
}
-const SkPicture::Analysis& SkPicture::analysis() const {
- auto create = [&](){ return SkNEW_ARGS(Analysis, (*fRecord)); };
- return *fAnalysis.get(create);
-}
-
-#if SK_SUPPORT_GPU
-bool SkPicture::suitableForGpuRasterization(GrContext*, const char **reason) const {
- return this->analysis().suitableForGpuRasterization(reason, 0);
-}
-#endif
-
-bool SkPicture::hasText() const { return this->analysis().fHasText; }
-bool SkPicture::willPlayBackBitmaps() const { return this->analysis().fWillPlaybackBitmaps; }
-int SkPicture::approximateOpCount() const { return fRecord->count(); }
-
-SkPicture::SkPicture(const SkRect& cullRect,
- SkRecord* record,
- SnapshotArray* drawablePicts,
- SkBBoxHierarchy* bbh,
- AccelData* accelData,
- size_t approxBytesUsedBySubPictures)
- : fUniqueID(0)
- , fCullRect(cullRect)
- , fRecord(record) // Take ownership of caller's ref.
- , fDrawablePicts(drawablePicts) // Take ownership.
- , fBBH(bbh) // Take ownership of caller's ref.
- , fAccelData(accelData) // Take ownership of caller's ref.
- , fApproxBytesUsedBySubPictures(approxBytesUsedBySubPictures)
-{}
-
-
-static uint32_t gNextID = 1;
-uint32_t SkPicture::uniqueID() const {
- uint32_t id = sk_atomic_load(&fUniqueID, sk_memory_order_relaxed);
- while (id == 0) {
- uint32_t next = sk_atomic_fetch_add(&gNextID, 1u);
- if (sk_atomic_compare_exchange(&fUniqueID, &id, next,
- sk_memory_order_relaxed,
- sk_memory_order_relaxed)) {
- id = next;
- } else {
- // sk_atomic_compare_exchange replaced id with the current value of fUniqueID.
- }
+bool SkPicture::suitableForGpuRasterization(GrContext*, const char** whyNot) const {
+ if (this->numSlowPaths() > 5) {
+ if (whyNot) { *whyNot = "Too many slow paths (either concave or dashed)."; }
+ return false;
}
- return id;
+ return true;
}
diff --git a/src/core/SkPictureCommon.h b/src/core/SkPictureCommon.h
new file mode 100644
index 0000000000..827bf117de
--- /dev/null
+++ b/src/core/SkPictureCommon.h
@@ -0,0 +1,136 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+// Some shared code used by both SkBigPicture and SkMiniPicture.
+// SkTextHunter -- SkRecord visitor that returns true when the op draws text.
+// SkBitmapHunter -- SkRecord visitor that returns true when the op draws a bitmap.
+// SkPathCounter -- SkRecord visitor that counts paths that draw slowly on the GPU.
+
+#include "SkPathEffect.h"
+#include "SkRecords.h"
+#include "SkTLogic.h"
+
+struct SkTextHunter {
+ // Most ops never have text. Some always do. Subpictures know themeselves.
+ template <typename T> 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<SkPaint>& 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 <typename T>
+ bool operator()(const T& r) { return CheckBitmap(r); }
+
+ // If the op has a bitmap, of course we're going to play back bitmaps.
+ template <typename T>
+ static SK_WHEN(HasMember_bitmap<T>, bool) CheckBitmap(const T&) { return true; }
+
+ // If not, look for one in its paint (if it has a paint).
+ template <typename T>
+ static SK_WHEN(!HasMember_bitmap<T>, bool) CheckBitmap(const T& r) { return CheckPaint(r); }
+
+ // If we have a paint, dig down into the effects looking for a bitmap.
+ template <typename T>
+ static SK_WHEN(HasMember_paint<T>, 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 <typename T>
+ static SK_WHEN(!HasMember_paint<T>, 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<SkPaint>& 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 <typename T>
+ SK_WHEN(HasMember_paint<T>, void) operator()(const T& op) {
+ this->checkPaint(AsPtr(op.paint));
+ }
+
+ template <typename T>
+ SK_WHEN(!HasMember_paint<T>, void) operator()(const T& op) { /* do nothing */ }
+
+ int fNumSlowPathsAndDashEffects;
+};
diff --git a/src/core/SkPictureRecorder.cpp b/src/core/SkPictureRecorder.cpp
index 282e2c22dd..c110e0a2db 100644
--- a/src/core/SkPictureRecorder.cpp
+++ b/src/core/SkPictureRecorder.cpp
@@ -5,6 +5,7 @@
* found in the LICENSE file.
*/
+#include "SkBigPicture.h"
#include "SkData.h"
#include "SkDrawable.h"
#include "SkLayerInfo.h"
@@ -18,7 +19,7 @@
SkPictureRecorder::SkPictureRecorder() {
fActivelyRecording = false;
- fRecorder.reset(SkNEW_ARGS(SkRecorder, (nullptr, SkRect::MakeWH(0,0))));
+ fRecorder.reset(SkNEW_ARGS(SkRecorder, (nullptr, SkRect::MakeWH(0,0), &fMiniRecorder)));
}
SkPictureRecorder::~SkPictureRecorder() {}
@@ -34,8 +35,10 @@ SkCanvas* SkPictureRecorder::beginRecording(const SkRect& cullRect,
SkASSERT(fBBH.get());
}
- fRecord.reset(SkNEW(SkRecord));
- fRecorder->reset(fRecord.get(), cullRect);
+ if (!fRecord) {
+ fRecord.reset(SkNEW(SkRecord));
+ }
+ fRecorder->reset(fRecord.get(), cullRect, &fMiniRecorder);
fActivelyRecording = true;
return this->getRecordingCanvas();
}
@@ -47,19 +50,23 @@ SkCanvas* SkPictureRecorder::getRecordingCanvas() {
SkPicture* SkPictureRecorder::endRecordingAsPicture() {
fActivelyRecording = false;
fRecorder->restoreToCount(1); // If we were missing any restores, add them now.
+
+ if (fRecord->count() == 0) {
+ return fMiniRecorder.detachAsPicture(fCullRect);
+ }
+
// TODO: delay as much of this work until just before first playback?
SkRecordOptimize(fRecord);
SkAutoTUnref<SkLayerInfo> 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<SkLayerInfo> saveLayerData;
if (fBBH && fDoSaveLayerInfo) {
- SkPicture::AccelData::Key key = SkLayerInfo::ComputeKey();
-
- saveLayerData.reset(SkNEW_ARGS(SkLayerInfo, (key)));
+ saveLayerData.reset(SkNEW(SkLayerInfo));
SkBBoxHierarchy* bbh = NULL; // we've already computed fBBH (received in constructor)
// TODO: update saveLayer info computation to reuse the already computed
@@ -157,20 +162,22 @@ protected:
for (int i = 0; pictList && i < pictList->count(); i++) {
subPictureBytes += SkPictureUtils::ApproximateBytesUsed(pictList->begin()[i]);
}
- // SkPicture will take ownership of a ref on both fRecord and fBBH.
+ // SkBigPicture will take ownership of a ref on both fRecord and fBBH.
// We're not willing to give up our ownership, so we must ref them for SkPicture.
- return SkNEW_ARGS(SkPicture, (fBounds,
- SkRef(fRecord.get()),
- pictList,
- SkSafeRef(fBBH.get()),
- saveLayerData.detach(),
- subPictureBytes));
+ return SkNEW_ARGS(SkBigPicture, (fBounds,
+ SkRef(fRecord.get()),
+ pictList,
+ SkSafeRef(fBBH.get()),
+ saveLayerData.detach(),
+ subPictureBytes));
}
};
SkDrawable* SkPictureRecorder::endRecordingAsDrawable() {
fActivelyRecording = false;
+ fRecorder->flushMiniRecorder();
fRecorder->restoreToCount(1); // If we were missing any restores, add them now.
+
// TODO: delay as much of this work until just before first playback?
SkRecordOptimize(fRecord);
diff --git a/src/core/SkRecordDraw.cpp b/src/core/SkRecordDraw.cpp
index ba15c1b512..65e6d490db 100644
--- a/src/core/SkRecordDraw.cpp
+++ b/src/core/SkRecordDraw.cpp
@@ -587,7 +587,7 @@ private:
class CollectLayers : SkNoncopyable {
public:
CollectLayers(const SkRect& cullRect, const SkRecord& record,
- const SkPicture::SnapshotArray* pictList, SkLayerInfo* accelData)
+ const SkBigPicture::SnapshotArray* pictList, SkLayerInfo* accelData)
: fSaveLayersInStack(0)
, fAccelData(accelData)
, fPictList(pictList)
@@ -640,10 +640,10 @@ private:
void trackSaveLayersForPicture(const SkPicture* picture, const SkPaint* paint) {
// For sub-pictures, we wrap their layer information within the parent
// picture's rendering hierarchy
- SkPicture::AccelData::Key key = SkLayerInfo::ComputeKey();
-
- const SkLayerInfo* childData =
- static_cast<const SkLayerInfo*>(picture->EXPERIMENTAL_getAccelData(key));
+ const SkLayerInfo* childData = NULL;
+ if (const SkBigPicture* bp = picture->asSkBigPicture()) {
+ childData = static_cast<const SkLayerInfo*>(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<unsigned> fSaveLayerOpStack;
SkLayerInfo* fAccelData;
- const SkPicture::SnapshotArray* fPictList;
+ const SkBigPicture::SnapshotArray* fPictList;
SkRecords::FillBounds fFillBounds;
};
@@ -793,7 +793,7 @@ void SkRecordFillBounds(const SkRect& cullRect, const SkRecord& record, SkBBoxHi
}
void SkRecordComputeLayers(const SkRect& cullRect, const SkRecord& record,
- const SkPicture::SnapshotArray* pictList, SkBBoxHierarchy* bbh,
+ const SkBigPicture::SnapshotArray* pictList, SkBBoxHierarchy* bbh,
SkLayerInfo* data) {
SkRecords::CollectLayers visitor(cullRect, record, pictList, data);
diff --git a/src/core/SkRecordDraw.h b/src/core/SkRecordDraw.h
index 7bbab81b5a..5b248cfbd5 100644
--- a/src/core/SkRecordDraw.h
+++ b/src/core/SkRecordDraw.h
@@ -9,6 +9,7 @@
#define SkRecordDraw_DEFINED
#include "SkBBoxHierarchy.h"
+#include "SkBigPicture.h"
#include "SkCanvas.h"
#include "SkMatrix.h"
#include "SkRecord.h"
@@ -20,7 +21,7 @@ class SkLayerInfo;
void SkRecordFillBounds(const SkRect& cullRect, const SkRecord&, SkBBoxHierarchy*);
void SkRecordComputeLayers(const SkRect& cullRect, const SkRecord& record,
- const SkPicture::SnapshotArray*,
+ const SkBigPicture::SnapshotArray*,
SkBBoxHierarchy* bbh, SkLayerInfo* data);
// Draw an SkRecord into an SkCanvas. A convenience wrapper around SkRecords::Draw.
diff --git a/src/core/SkRecorder.cpp b/src/core/SkRecorder.cpp
index 0a2d43edbf..45283f8118 100644
--- a/src/core/SkRecorder.cpp
+++ b/src/core/SkRecorder.cpp
@@ -5,6 +5,7 @@
* found in the LICENSE file.
*/
+#include "SkBigPicture.h"
#include "SkPatchUtils.h"
#include "SkPicture.h"
#include "SkPictureUtils.h"
@@ -14,7 +15,7 @@ SkDrawableList::~SkDrawableList() {
fArray.unrefAll();
}
-SkPicture::SnapshotArray* SkDrawableList::newDrawableSnapshot() {
+SkBigPicture::SnapshotArray* SkDrawableList::newDrawableSnapshot() {
const int count = fArray.count();
if (0 == count) {
return NULL;
@@ -23,7 +24,7 @@ SkPicture::SnapshotArray* SkDrawableList::newDrawableSnapshot() {
for (int i = 0; i < count; ++i) {
pics[i] = fArray[i]->newPictureSnapshot();
}
- return SkNEW_ARGS(SkPicture::SnapshotArray, (pics.detach(), count));
+ return SkNEW_ARGS(SkBigPicture::SnapshotArray, (pics.detach(), count));
}
void SkDrawableList::append(SkDrawable* drawable) {
@@ -32,20 +33,23 @@ void SkDrawableList::append(SkDrawable* drawable) {
///////////////////////////////////////////////////////////////////////////////////////////////
-SkRecorder::SkRecorder(SkRecord* record, int width, int height)
+SkRecorder::SkRecorder(SkRecord* record, int width, int height, SkMiniRecorder* mr)
: SkCanvas(SkIRect::MakeWH(width, height), SkCanvas::kConservativeRasterClip_InitFlag)
, fApproxBytesUsedBySubPictures(0)
- , fRecord(record) {}
+ , fRecord(record)
+ , fMiniRecorder(mr) {}
-SkRecorder::SkRecorder(SkRecord* record, const SkRect& bounds)
+SkRecorder::SkRecorder(SkRecord* record, const SkRect& bounds, SkMiniRecorder* mr)
: SkCanvas(bounds.roundOut(), SkCanvas::kConservativeRasterClip_InitFlag)
, fApproxBytesUsedBySubPictures(0)
- , fRecord(record) {}
+ , fRecord(record)
+ , fMiniRecorder(mr) {}
-void SkRecorder::reset(SkRecord* record, const SkRect& bounds) {
+void SkRecorder::reset(SkRecord* record, const SkRect& bounds, SkMiniRecorder* mr) {
this->forgetRecord();
fRecord = record;
this->resetForNextPicture(bounds.roundOut());
+ fMiniRecorder = mr;
}
void SkRecorder::forgetRecord() {
@@ -55,9 +59,13 @@ void SkRecorder::forgetRecord() {
}
// To make appending to fRecord a little less verbose.
-#define APPEND(T, ...) \
+#define APPEND(T, ...) \
+ if (fMiniRecorder) { this->flushMiniRecorder(); } \
SkNEW_PLACEMENT_ARGS(fRecord->append<SkRecords::T>(), 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<SkPicture> p(mr->detachAsPicture(SkRect::MakeEmpty()));
+ p->playback(this);
+ }
+}
void SkRecorder::onDrawPaint(const SkPaint& paint) {
APPEND(DrawPaint, delay_copy(paint));
@@ -138,6 +155,7 @@ void SkRecorder::onDrawPoints(PointMode mode,
}
void SkRecorder::onDrawRect(const SkRect& rect, const SkPaint& paint) {
+ TRY_MINIRECORDER(drawRect, rect, paint);
APPEND(DrawRect, delay_copy(paint), rect);
}
@@ -162,6 +180,7 @@ void SkRecorder::onDrawDrawable(SkDrawable* drawable) {
}
void SkRecorder::onDrawPath(const SkPath& path, const SkPaint& paint) {
+ TRY_MINIRECORDER(drawPath, path, paint);
APPEND(DrawPath, delay_copy(paint), delay_copy(path));
}
@@ -248,6 +267,7 @@ void SkRecorder::onDrawTextOnPath(const void* text, size_t byteLength, const SkP
void SkRecorder::onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
const SkPaint& paint) {
+ TRY_MINIRECORDER(drawTextBlob, blob, x, y, paint);
APPEND(DrawTextBlob, delay_copy(paint), blob, x, y);
}
diff --git a/src/core/SkRecorder.h b/src/core/SkRecorder.h
index d0a992fc08..b6f153dc54 100644
--- a/src/core/SkRecorder.h
+++ b/src/core/SkRecorder.h
@@ -8,7 +8,9 @@
#ifndef SkRecorder_DEFINED
#define SkRecorder_DEFINED
+#include "SkBigPicture.h"
#include "SkCanvas.h"
+#include "SkMiniRecorder.h"
#include "SkRecord.h"
#include "SkRecords.h"
#include "SkTDArray.h"
@@ -25,7 +27,7 @@ public:
void append(SkDrawable* drawable);
// Return a new or ref'd array of pictures that were snapped from our drawables.
- SkPicture::SnapshotArray* newDrawableSnapshot();
+ SkBigPicture::SnapshotArray* newDrawableSnapshot();
private:
SkTDArray<SkDrawable*> 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 <typename T>
T* copy(const T*);
@@ -136,6 +140,8 @@ private:
size_t fApproxBytesUsedBySubPictures;
SkRecord* fRecord;
SkAutoTDelete<SkDrawableList> fDrawableList;
+
+ SkMiniRecorder* fMiniRecorder;
};
#endif//SkRecorder_DEFINED
diff --git a/src/core/SkRecords.h b/src/core/SkRecords.h
index 319d155054..6e25dd22e2 100644
--- a/src/core/SkRecords.h
+++ b/src/core/SkRecords.h
@@ -81,6 +81,7 @@ struct T { \
#define RECORD1(T, A, a) \
struct T { \
static const Type kType = T##_Type; \
+ T() {} \
template <typename Z> \
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 <typename Z, typename Y> \
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 <typename Z, typename Y, typename X> \
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 <typename Z, typename Y, typename X, typename W> \
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 <typename Z, typename Y, typename X, typename W, typename V> \
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 <typename T>
class RefBox : SkNoncopyable {
public:
+ RefBox() {}
RefBox(T* obj) : fObj(SkSafeRef(obj)) {}
~RefBox() { SkSafeUnref(fObj); }
@@ -138,6 +144,7 @@ private:
template <typename T>
class Optional : SkNoncopyable {
public:
+ Optional() : fPtr(nullptr) {}
Optional(T* ptr) : fPtr(ptr) {}
~Optional() { if (fPtr) fPtr->~T(); }
@@ -167,6 +174,7 @@ private:
template <typename T>
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<SkRect>, bounds, Optional<SkPaint>, paint, SkCanvas:
RECORD1(SetMatrix, TypedMatrix, matrix);
struct RegionOpAndAA {
+ RegionOpAndAA() {}
RegionOpAndAA(SkRegion::Op op, bool aa) : op(op), aa(aa) {}
SkRegion::Op op : 31; // This really only needs to be 3, but there's no win today to do so.
unsigned aa : 1; // MSVC won't pack an enum with an bool, so we call this an unsigned.
diff --git a/src/gpu/GrLayerHoister.cpp b/src/gpu/GrLayerHoister.cpp
index 70c10d7f69..67e3c19a2f 100644
--- a/src/gpu/GrLayerHoister.cpp
+++ b/src/gpu/GrLayerHoister.cpp
@@ -9,6 +9,7 @@
#include "GrLayerHoister.h"
#include "GrRecordReplaceDraw.h"
+#include "SkBigPicture.h"
#include "SkCanvas.h"
#include "SkDeviceImageFilterProxy.h"
#include "SkDeviceProperties.h"
@@ -21,7 +22,7 @@
// Create the layer information for the hoisted layer and secure the
// required texture/render target resources.
-static void prepare_for_hoisting(GrLayerCache* layerCache,
+static void prepare_for_hoisting(GrLayerCache* layerCache,
const SkPicture* topLevelPicture,
const SkMatrix& initialMat,
const SkLayerInfo::BlockInfo& info,
@@ -74,7 +75,7 @@ static void prepare_for_hoisting(GrLayerCache* layerCache,
} else {
hl = recycled->append();
}
-
+
layerCache->addUse(layer);
hl->fLayer = layer;
hl->fPicture = pict;
@@ -129,12 +130,12 @@ void GrLayerHoister::FindLayersToAtlas(GrContext* context,
}
GrLayerCache* layerCache = context->getLayerCache();
-
layerCache->processDeletedPictures();
- SkPicture::AccelData::Key key = SkLayerInfo::ComputeKey();
-
- const SkPicture::AccelData* topLevelData = topLevelPicture->EXPERIMENTAL_getAccelData(key);
+ const SkBigPicture::AccelData* topLevelData = NULL;
+ if (const SkBigPicture* bp = topLevelPicture->asSkBigPicture()) {
+ topLevelData = bp->accelData();
+ }
if (!topLevelData) {
return;
}
@@ -189,9 +190,10 @@ void GrLayerHoister::FindLayersToHoist(GrContext* context,
layerCache->processDeletedPictures();
- SkPicture::AccelData::Key key = SkLayerInfo::ComputeKey();
-
- const SkPicture::AccelData* topLevelData = topLevelPicture->EXPERIMENTAL_getAccelData(key);
+ const SkBigPicture::AccelData* topLevelData = NULL;
+ if (const SkBigPicture* bp = topLevelPicture->asSkBigPicture()) {
+ topLevelData = bp->accelData();
+ }
if (!topLevelData) {
return;
}
@@ -239,7 +241,11 @@ void GrLayerHoister::DrawLayersToAtlas(GrContext* context,
for (int i = 0; i < atlased.count(); ++i) {
const GrCachedLayer* layer = atlased[i].fLayer;
- const SkPicture* pict = atlased[i].fPicture;
+ const SkBigPicture* pict = atlased[i].fPicture->asSkBigPicture();
+ if (!pict) {
+ // TODO: can we assume / assert this?
+ continue;
+ }
const SkIPoint offset = SkIPoint::Make(layer->srcIR().fLeft, layer->srcIR().fTop);
SkDEBUGCODE(const SkPaint* layerPaint = layer->paint();)
@@ -265,10 +271,7 @@ void GrLayerHoister::DrawLayersToAtlas(GrContext* context,
atlasCanvas->setMatrix(initialCTM);
atlasCanvas->concat(atlased[i].fLocalMat);
- SkRecordPartialDraw(*pict->fRecord.get(), atlasCanvas,
- pict->drawablePicts(), pict->drawableCount(),
- layer->start() + 1, layer->stop(), initialCTM);
-
+ pict->partialPlayback(atlasCanvas, layer->start() + 1, layer->stop(), initialCTM);
atlasCanvas->restore();
}
@@ -328,7 +331,11 @@ void GrLayerHoister::FilterLayer(GrContext* context,
void GrLayerHoister::DrawLayers(GrContext* context, const SkTDArray<GrHoistedLayer>& 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 SkTDArray<GrHoistedLay
layerCanvas->setMatrix(initialCTM);
layerCanvas->concat(layers[i].fLocalMat);
- SkRecordPartialDraw(*pict->fRecord.get(), layerCanvas,
- pict->drawablePicts(), pict->drawableCount(),
- layer->start()+1, layer->stop(), initialCTM);
-
+ pict->partialPlayback(layerCanvas, layer->start()+1, layer->stop(), initialCTM);
layerCanvas->flush();
if (layer->filter()) {
diff --git a/src/gpu/GrRecordReplaceDraw.cpp b/src/gpu/GrRecordReplaceDraw.cpp
index dacc939e62..6f05206dcc 100644
--- a/src/gpu/GrRecordReplaceDraw.cpp
+++ b/src/gpu/GrRecordReplaceDraw.cpp
@@ -8,6 +8,7 @@
#include "GrContext.h"
#include "GrLayerCache.h"
#include "GrRecordReplaceDraw.h"
+#include "SkBigPicture.h"
#include "SkCanvasPriv.h"
#include "SkGrPixelRef.h"
#include "SkImage.h"
@@ -45,7 +46,7 @@ static inline void draw_replacement_bitmap(GrCachedLayer* layer, SkCanvas* canva
canvas->drawBitmapRectToRect(bm, &src, dst, layer->paint());
canvas->restore();
} else {
- canvas->drawSprite(bm,
+ canvas->drawSprite(bm,
layer->srcIR().fLeft + layer->offset().fX,
layer->srcIR().fTop + layer->offset().fY,
layer->paint());
@@ -59,7 +60,7 @@ public:
ReplaceDraw(SkCanvas* canvas, GrLayerCache* layerCache,
SkPicture const* const drawablePicts[], int drawableCount,
const SkPicture* topLevelPicture,
- const SkPicture* picture,
+ const SkBigPicture* picture,
const SkMatrix& initialMatrix,
SkPicture::AbortCallback* callback,
const unsigned* opIndices, int numIndices)
@@ -76,8 +77,8 @@ public:
}
int draw() {
- const SkBBoxHierarchy* bbh = fPicture->fBBH.get();
- const SkRecord* record = fPicture->fRecord.get();
+ const SkBBoxHierarchy* bbh = fPicture->bbh();
+ const SkRecord* record = fPicture->record();
if (NULL == record) {
return 0;
}
@@ -135,13 +136,17 @@ public:
SkAutoCanvasMatrixPaint acmp(fCanvas, &dp.matrix, dp.paint, dp.picture->cullRect());
- // Draw sub-pictures with the same replacement list but a different picture
- ReplaceDraw draw(fCanvas, fLayerCache,
- this->drawablePicts(), this->drawableCount(),
- fTopLevelPicture, dp.picture, fInitialMatrix, fCallback,
- fOpIndexStack.begin(), fOpIndexStack.count());
-
- fNumReplaced += draw.draw();
+ if (const SkBigPicture* bp = dp.picture->asSkBigPicture()) {
+ // Draw sub-pictures with the same replacement list but a different picture
+ ReplaceDraw draw(fCanvas, fLayerCache,
+ this->drawablePicts(), this->drawableCount(),
+ fTopLevelPicture, bp, fInitialMatrix, fCallback,
+ fOpIndexStack.begin(), fOpIndexStack.count());
+ fNumReplaced += draw.draw();
+ } else {
+ // TODO: can we assume / assert this doesn't happen?
+ dp.picture->playback(fCanvas, fCallback);
+ }
fOpIndexStack.pop();
}
@@ -168,7 +173,7 @@ public:
draw_replacement_bitmap(layer, fCanvas);
- if (fPicture->fBBH.get()) {
+ if (fPicture->bbh()) {
while (fOps[fIndex] < layer->stop()) {
++fIndex;
}
@@ -190,7 +195,7 @@ private:
SkCanvas* fCanvas;
GrLayerCache* fLayerCache;
const SkPicture* fTopLevelPicture;
- const SkPicture* fPicture;
+ const SkBigPicture* fPicture;
const SkMatrix fInitialMatrix;
SkPicture::AbortCallback* fCallback;
@@ -211,9 +216,15 @@ int GrRecordReplaceDraw(const SkPicture* picture,
SkPicture::AbortCallback* callback) {
SkAutoCanvasRestore saveRestore(canvas, true /*save now, restore at exit*/);
- // TODO: drawablePicts?
- ReplaceDraw draw(canvas, layerCache, NULL, 0,
- picture, picture,
- initialMatrix, callback, NULL, 0);
- return draw.draw();
+ if (const SkBigPicture* bp = picture->asSkBigPicture()) {
+ // TODO: drawablePicts?
+ ReplaceDraw draw(canvas, layerCache, NULL, 0,
+ bp, bp,
+ initialMatrix, callback, NULL, 0);
+ return draw.draw();
+ } else {
+ // TODO: can we assume / assert this doesn't happen?
+ picture->playback(canvas, callback);
+ return 0;
+ }
}
diff --git a/src/gpu/SkGpuDevice.cpp b/src/gpu/SkGpuDevice.cpp
index ecd6730118..ea998b86b0 100644
--- a/src/gpu/SkGpuDevice.cpp
+++ b/src/gpu/SkGpuDevice.cpp
@@ -1996,9 +1996,10 @@ bool SkGpuDevice::EXPERIMENTAL_drawPicture(SkCanvas* mainCanvas, const SkPicture
return false;
}
- SkPicture::AccelData::Key key = SkLayerInfo::ComputeKey();
-
- const SkPicture::AccelData* data = mainPicture->EXPERIMENTAL_getAccelData(key);
+ const SkBigPicture::AccelData* data = NULL;
+ if (const SkBigPicture* bp = mainPicture->asSkBigPicture()) {
+ data = bp->accelData();
+ }
if (!data) {
return false;
}
diff --git a/src/gpu/SkGpuDevice.h b/src/gpu/SkGpuDevice.h
index 89959e1f9c..c1ea4fe873 100644
--- a/src/gpu/SkGpuDevice.h
+++ b/src/gpu/SkGpuDevice.h
@@ -217,8 +217,6 @@ private:
bool drawDashLine(const SkPoint pts[2], const SkPaint& paint);
- static SkPicture::AccelData::Key ComputeAccelDataKey();
-
static GrRenderTarget* CreateRenderTarget(GrContext*, SkSurface::Budgeted, const SkImageInfo&,
int sampleCount);
diff --git a/src/utils/SkPictureUtils.cpp b/src/utils/SkPictureUtils.cpp
deleted file mode 100644
index a8a251c927..0000000000
--- a/src/utils/SkPictureUtils.cpp
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * Copyright 2012 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#include "SkBBoxHierarchy.h"
-#include "SkCanvas.h"
-#include "SkData.h"
-#include "SkPictureUtils.h"
-#include "SkRecord.h"
-#include "SkShader.h"
-
-size_t SkPictureUtils::ApproximateBytesUsed(const SkPicture* pict) {
- size_t byteCount = sizeof(*pict);
-
- byteCount += pict->fRecord->bytesUsed();
- if (pict->fBBH.get()) {
- byteCount += pict->fBBH->bytesUsed();
- }
- byteCount += pict->fApproxBytesUsedBySubPictures;
-
- return byteCount;
-}
diff --git a/tests/GpuLayerCacheTest.cpp b/tests/GpuLayerCacheTest.cpp
index 6b3084b641..efb1ec1c20 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<const SkPicture> picture(recorder.endRecording());
GrLayerCache cache(context);
diff --git a/tests/PictureTest.cpp b/tests/PictureTest.cpp
index 16d98b3245..0ccef3f0a9 100644
--- a/tests/PictureTest.cpp
+++ b/tests/PictureTest.cpp
@@ -23,6 +23,7 @@
#include "SkPictureUtils.h"
#include "SkPixelRef.h"
#include "SkPixelSerializer.h"
+#include "SkMiniRecorder.h"
#include "SkRRect.h"
#include "SkRandom.h"
#include "SkRecord.h"
@@ -363,9 +364,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<const SkLayerInfo*>(data);
@@ -1107,30 +1109,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<SkPicture> 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<SkPicture> 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 +1129,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 +1244,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<const SkPicture> picture(recorder.endRecording());
SkCanvas big(640, 480), small(300, 200);
@@ -1323,3 +1303,13 @@ 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.
+}