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
This commit is contained in:
mtklein 2015-05-07 13:41:07 -07:00 committed by Commit bot
parent 2fbd4068bd
commit c92c129ff8
26 changed files with 845 additions and 778 deletions

View File

@ -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',

View File

@ -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',

View File

@ -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<SkPicture> {
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;
virtual void playback(SkCanvas*, AbortCallback* = NULL) const = 0;
/** Return the cull rect used when creating this picture: { 0, 0, cullWidth, cullHeight }.
It does not necessarily reflect the bounds of what has been recorded into the picture.
@return the cull rect used to create this picture
*/
SkRect cullRect() const { return fCullRect; }
/** Return a non-zero, unique value representing the picture.
/** 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 the approximate number of operations in this picture. This
* number may be greater or less than the number of SkCanvas calls
* recorded: some calls may be recorded as more than one operation, or some
* calls may be optimized away.
*/
int approximateOpCount() const;
/** Return true if this picture contains text.
*/
bool hasText() const;
// An array of refcounted const SkPicture pointers.
class SnapshotArray : ::SkNoncopyable {
public:
SnapshotArray(const SkPicture* pics[], int count) : fPics(pics), fCount(count) {}
~SnapshotArray() { for (int i = 0; i < fCount; i++) { fPics[i]->unref(); } }
const SkPicture* const* begin() const { return fPics; }
int count() const { return fCount; }
private:
SkAutoTMalloc<const SkPicture*> fPics;
int fCount;
};
/** Return true if the picture is suitable for rendering on the GPU. */
virtual bool suitableForGpuRasterization(GrContext*, const char** whyNot = NULL) const = 0;
// Sent via SkMessageBus from destructor.
struct DeletionMessage { int32_t fUniqueID; };
struct DeletionMessage { int32_t fUniqueID; }; // TODO: -> uint32_t?
// Returns NULL if this is not an SkBigPicture.
virtual const SkBigPicture* asSkBigPicture() const { return NULL; }
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.
// Subclass whitelist.
SkPicture();
friend class SkBigPicture;
friend class SkEmptyPicture;
template <typename> friend class SkMiniPicture;
virtual int numSlowPaths() const = 0;
// 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<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;
SkPictInfo createHeader() const;
SkPictureData* backport() const;
// 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;
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

View File

@ -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;
};

View File

@ -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

167
src/core/SkBigPicture.cpp Normal file
View File

@ -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<SkPaint>& 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 <typename T>
SK_WHEN(HasMember_paint<T>, void) operator()(const T& op) {
this->checkPaint(as_ptr(op.paint));
}
template <typename T>
SK_WHEN(!HasMember_paint<T>, 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<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);
}
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;
}

89
src/core/SkBigPicture.h Normal file
View File

@ -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<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;
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<const SkRecord> fRecord;
SkAutoTDelete<const SnapshotArray> fDrawablePicts;
SkAutoTUnref<const SkBBoxHierarchy> fBBH;
SkAutoTUnref<const AccelData> fAccelData;
};
#endif//SkBigPicture_DEFINED

View File

@ -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;
}

View File

@ -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

103
src/core/SkMiniRecorder.cpp Normal file
View File

@ -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 <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); }
// 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<Type>, (cull, reinterpret_cast<Type*>(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

44
src/core/SkMiniRecorder.h Normal file
View File

@ -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

View File

@ -5,468 +5,33 @@
* 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;
}
/* SkPicture impl. This handles generic responsibilities like unique IDs and serialization. */
///////////////////////////////////////////////////////////////////////////////
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);
// 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->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 <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();
}
return static_cast<Domain>(id);
}
///////////////////////////////////////////////////////////////////////////////
void SkPicture::playback(SkCanvas* canvas, AbortCallback* callback) const {
SkASSERT(canvas);
// If the query contains the whole picture, don't bother with the BBH.
SkRect clipBounds = { 0, 0, 0, 0 };
(void)canvas->getClipBounds(&clipBounds);
const bool useBBH = !clipBounds.contains(this->cullRect());
SkRecordDraw(*fRecord, canvas, this->drawablePicts(), NULL, this->drawableCount(),
useBBH ? fBBH.get() : NULL, callback);
}
///////////////////////////////////////////////////////////////////////////////
#include "SkStream.h"
static const char kMagic[] = { 's', 'k', 'i', 'a', 'p', 'i', 'c', 't' };
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) {
return false;
}
return true;
}
bool SkPicture::InternalOnly_StreamIsSKP(SkStream* stream, SkPictInfo* pInfo) {
if (NULL == stream) {
return false;
}
// Check magic bytes.
SkPictInfo info;
SkASSERT(sizeof(kMagic) == sizeof(info.fMagic));
if (!stream->read(&info.fMagic, sizeof(kMagic))) {
return false;
}
info.fVersion = stream->readU32();
info.fCullRect.fLeft = stream->readScalar();
info.fCullRect.fTop = stream->readScalar();
info.fCullRect.fRight = stream->readScalar();
info.fCullRect.fBottom = stream->readScalar();
info.fFlags = stream->readU32();
if (!IsValidPictInfo(info)) {
return false;
}
if (pInfo != NULL) {
*pInfo = info;
}
return true;
}
bool SkPicture::InternalOnly_BufferIsSKP(SkReadBuffer* buffer, SkPictInfo* pInfo) {
// Check magic bytes.
SkPictInfo info;
SkASSERT(sizeof(kMagic) == sizeof(info.fMagic));
if (!buffer->readByteArray(&info.fMagic, sizeof(kMagic))) {
return false;
}
info.fVersion = buffer->readUInt();
buffer->readRect(&info.fCullRect);
info.fFlags = buffer->readUInt();
if (!IsValidPictInfo(info)) {
return false;
}
if (pInfo != NULL) {
*pInfo = info;
}
return true;
}
SkPicture* SkPicture::Forwardport(const SkPictInfo& info, const SkPictureData* data) {
if (!data) {
return NULL;
}
SkPicturePlayback playback(data);
SkPictureRecorder r;
playback.draw(r.beginRecording(SkScalarCeilToInt(info.fCullRect.width()),
SkScalarCeilToInt(info.fCullRect.height())),
NULL/*no callback*/);
return r.endRecording();
}
SkPicture* SkPicture::CreateFromStream(SkStream* stream, InstallPixelRefProc proc) {
SkPictInfo info;
if (!InternalOnly_StreamIsSKP(stream, &info) || !stream->readBool()) {
return NULL;
}
SkAutoTDelete<SkPictureData> data(SkPictureData::CreateFromStream(stream, info, proc));
return Forwardport(info, data);
}
SkPicture* SkPicture::CreateFromBuffer(SkReadBuffer& buffer) {
SkPictInfo info;
if (!InternalOnly_BufferIsSKP(&buffer, &info) || !buffer.readBool()) {
return NULL;
}
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) {
SkPictureRecord rec(SkISize::Make(info.fCullRect.width(), info.fCullRect.height()), 0/*flags*/);
rec.beginRecording();
SkRecordDraw(src, &rec, drawablePicts, NULL, drawableCount, NULL/*bbh*/, NULL/*callback*/);
rec.endRecording();
return SkNEW_ARGS(SkPictureData, (rec, info, false/*deep copy ops?*/));
}
void SkPicture::serialize(SkWStream* stream, SkPixelSerializer* pixelSerializer) const {
SkPictInfo info;
this->createHeader(&info);
SkAutoTDelete<SkPictureData> data(Backport(*fRecord, info, this->drawablePicts(),
this->drawableCount()));
stream->write(&info, sizeof(info));
if (data) {
stream->writeBool(true);
data->serialize(stream, pixelSerializer);
} else {
stream->writeBool(false);
}
}
void SkPicture::flatten(SkWriteBuffer& buffer) const {
SkPictInfo info;
this->createHeader(&info);
SkAutoTDelete<SkPictureData> data(Backport(*fRecord, info, this->drawablePicts(),
this->drawableCount()));
buffer.writeByteArray(&info.fMagic, sizeof(info.fMagic));
buffer.writeUInt(info.fVersion);
buffer.writeRect(info.fCullRect);
buffer.writeUInt(info.fFlags);
if (data) {
buffer.writeBool(true);
data->flatten(buffer);
} else {
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 {
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);
@ -480,3 +45,146 @@ uint32_t SkPicture::uniqueID() const {
}
return id;
}
static const char kMagic[] = { 's', 'k', 'i', 'a', 'p', 'i', 'c', 't' };
SkPictInfo SkPicture::createHeader() const {
SkPictInfo info;
// Copy magic bytes at the beginning of the header
static_assert(sizeof(kMagic) == 8, "");
static_assert(sizeof(kMagic) == sizeof(info.fMagic), "");
memcpy(info.fMagic, kMagic, sizeof(kMagic));
// Set picture info after magic bytes in the header
info.fVersion = CURRENT_PICTURE_VERSION;
info.fCullRect = this->cullRect();
info.fFlags = SkPictInfo::kCrossProcess_Flag;
// TODO: remove this flag, since we're always float (now)
info.fFlags |= SkPictInfo::kScalarIsFloat_Flag;
if (8 == sizeof(void*)) {
info.fFlags |= SkPictInfo::kPtrIs64Bit_Flag;
}
return info;
}
bool SkPicture::IsValidPictInfo(const SkPictInfo& info) {
if (0 != memcmp(info.fMagic, kMagic, sizeof(kMagic))) {
return false;
}
if (info.fVersion < MIN_PICTURE_VERSION || info.fVersion > CURRENT_PICTURE_VERSION) {
return false;
}
return true;
}
bool SkPicture::InternalOnly_StreamIsSKP(SkStream* stream, SkPictInfo* pInfo) {
if (!stream) {
return false;
}
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.fCullRect.fBottom = stream->readScalar();
info.fFlags = stream->readU32();
if (IsValidPictInfo(info)) {
if (pInfo) { *pInfo = info; }
return true;
}
return false;
}
bool SkPicture::InternalOnly_BufferIsSKP(SkReadBuffer* buffer, SkPictInfo* pInfo) {
SkPictInfo info;
SkASSERT(sizeof(kMagic) == sizeof(info.fMagic));
if (!buffer->readByteArray(&info.fMagic, sizeof(kMagic))) {
return false;
}
info.fVersion = buffer->readUInt();
buffer->readRect(&info.fCullRect);
info.fFlags = buffer->readUInt();
if (IsValidPictInfo(info)) {
if (pInfo) { *pInfo = info; }
return true;
}
return false;
}
SkPicture* SkPicture::Forwardport(const SkPictInfo& info, const SkPictureData* data) {
if (!data) {
return nullptr;
}
SkPicturePlayback playback(data);
SkPictureRecorder r;
playback.draw(r.beginRecording(SkScalarCeilToInt(info.fCullRect.width()),
SkScalarCeilToInt(info.fCullRect.height())),
nullptr/*no callback*/);
return r.endRecording();
}
SkPicture* SkPicture::CreateFromStream(SkStream* stream, InstallPixelRefProc proc) {
SkPictInfo info;
if (!InternalOnly_StreamIsSKP(stream, &info) || !stream->readBool()) {
return nullptr;
}
SkAutoTDelete<SkPictureData> data(SkPictureData::CreateFromStream(stream, info, proc));
return Forwardport(info, data);
}
SkPicture* SkPicture::CreateFromBuffer(SkReadBuffer& buffer) {
SkPictInfo info;
if (!InternalOnly_BufferIsSKP(&buffer, &info) || !buffer.readBool()) {
return nullptr;
}
SkAutoTDelete<SkPictureData> data(SkPictureData::CreateFromBuffer(buffer, info));
return Forwardport(info, data);
}
SkPictureData* SkPicture::backport() const {
SkPictInfo info = this->createHeader();
SkPictureRecord rec(SkISize::Make(info.fCullRect.width(), info.fCullRect.height()), 0/*flags*/);
rec.beginRecording();
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();
SkAutoTDelete<SkPictureData> data(this->backport());
stream->write(&info, sizeof(info));
if (data) {
stream->writeBool(true);
data->serialize(stream, pixelSerializer);
} else {
stream->writeBool(false);
}
}
void SkPicture::flatten(SkWriteBuffer& buffer) const {
SkPictInfo info = this->createHeader();
SkAutoTDelete<SkPictureData> data(this->backport());
buffer.writeByteArray(&info.fMagic, sizeof(info.fMagic));
buffer.writeUInt(info.fVersion);
buffer.writeRect(info.fCullRect);
buffer.writeUInt(info.fFlags);
if (data) {
buffer.writeBool(true);
data->flatten(buffer);
} else {
buffer.writeBool(false);
}
}

View File

@ -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 <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; }
};

View File

@ -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);

View File

@ -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);

View File

@ -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.

View File

@ -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);
}

View File

@ -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

View File

@ -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.

View File

@ -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()) {

View File

@ -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;
}
}

View File

@ -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;
}

View File

@ -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);

View File

@ -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;
}

View File

@ -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);

View File

@ -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<const SkLayerInfo*>(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<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 +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<const SkPicture> picture(recorder.endRecording());
SkCanvas big(640, 480), small(300, 200);