aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/core
diff options
context:
space:
mode:
authorGravatar robertphillips@google.com <robertphillips@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>2014-05-07 21:31:09 +0000
committerGravatar robertphillips@google.com <robertphillips@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>2014-05-07 21:31:09 +0000
commitbeb1af2f34b5c538fc08d849b132355160b4c93f (patch)
treef54c63e2f844993197f3faf2b7c55ab468d90001 /src/core
parentc71da1f6ed3a5e1a3eb2dd860b8129e1b423f9f4 (diff)
First pass at pre-rendering saveLayers for GPU
Diffstat (limited to 'src/core')
-rw-r--r--src/core/SkPicturePlayback.cpp139
-rw-r--r--src/core/SkPicturePlayback.h66
-rw-r--r--src/core/SkPictureStateTree.cpp61
-rw-r--r--src/core/SkPictureStateTree.h25
4 files changed, 262 insertions, 29 deletions
diff --git a/src/core/SkPicturePlayback.cpp b/src/core/SkPicturePlayback.cpp
index 291774b883..94fe421633 100644
--- a/src/core/SkPicturePlayback.cpp
+++ b/src/core/SkPicturePlayback.cpp
@@ -23,6 +23,33 @@ template <typename T> int SafeCount(const T* obj) {
*/
#define SPEW_CLIP_SKIPPINGx
+SkPicturePlayback::PlaybackReplacements::ReplacementInfo*
+SkPicturePlayback::PlaybackReplacements::push() {
+ SkDEBUGCODE(this->validate());
+ return fReplacements.push();
+}
+
+void SkPicturePlayback::PlaybackReplacements::freeAll() {
+ for (int i = 0; i < fReplacements.count(); ++i) {
+ SkDELETE(fReplacements[i].fBM);
+ }
+ fReplacements.reset();
+}
+
+#ifdef SK_DEBUG
+void SkPicturePlayback::PlaybackReplacements::validate() const {
+ // Check that the ranges are monotonically increasing and non-overlapping
+ if (fReplacements.count() > 0) {
+ SkASSERT(fReplacements[0].fStart < fReplacements[0].fStop);
+
+ for (int i = 1; i < fReplacements.count(); ++i) {
+ SkASSERT(fReplacements[i].fStart < fReplacements[i].fStop);
+ SkASSERT(fReplacements[i-1].fStop < fReplacements[i].fStart);
+ }
+ }
+}
+#endif
+
SkPicturePlayback::SkPicturePlayback(const SkPicture* picture, const SkPictInfo& info)
: fPicture(picture)
, fInfo(info) {
@@ -205,6 +232,10 @@ void SkPicturePlayback::init() {
fStateTree = NULL;
fCachedActiveOps = NULL;
fCurOffset = 0;
+ fUseBBH = true;
+ fStart = 0;
+ fStop = 0;
+ fReplacements = NULL;
}
SkPicturePlayback::~SkPicturePlayback() {
@@ -744,6 +775,21 @@ private:
SkPicturePlayback* fPlayback;
};
+// TODO: Replace with hash or pass in "lastLookedUp" hint
+SkPicturePlayback::PlaybackReplacements::ReplacementInfo*
+SkPicturePlayback::PlaybackReplacements::lookupByStart(size_t start) {
+ SkDEBUGCODE(this->validate());
+ for (int i = 0; i < fReplacements.count(); ++i) {
+ if (start == fReplacements[i].fStart) {
+ return &fReplacements[i];
+ } else if (start < fReplacements[i].fStart) {
+ return NULL; // the ranges are monotonically increasing and non-overlapping
+ }
+ }
+
+ return NULL;
+}
+
void SkPicturePlayback::draw(SkCanvas& canvas, SkDrawPictureCallback* callback) {
SkAutoResetOpID aroi(this);
SkASSERT(0 == fCurOffset);
@@ -769,20 +815,24 @@ void SkPicturePlayback::draw(SkCanvas& canvas, SkDrawPictureCallback* callback)
TextContainer text;
const SkTDArray<void*>* activeOps = NULL;
- if (NULL != fStateTree && NULL != fBoundingHierarchy) {
- SkRect clipBounds;
- if (canvas.getClipBounds(&clipBounds)) {
- SkIRect query;
- clipBounds.roundOut(&query);
+ // When draw limits are enabled (i.e., 0 != fStart || 0 != fStop) the state
+ // tree isn't used to pick and choose the draw operations
+ if (0 == fStart && 0 == fStop) {
+ if (fUseBBH && NULL != fStateTree && NULL != fBoundingHierarchy) {
+ SkRect clipBounds;
+ if (canvas.getClipBounds(&clipBounds)) {
+ SkIRect query;
+ clipBounds.roundOut(&query);
+
+ const SkPicture::OperationList& activeOpsList = this->getActiveOps(query);
+ if (activeOpsList.valid()) {
+ if (0 == activeOpsList.numOps()) {
+ return; // nothing to draw
+ }
- const SkPicture::OperationList& activeOpsList = this->getActiveOps(query);
- if (activeOpsList.valid()) {
- if (0 == activeOpsList.numOps()) {
- return; // nothing to draw
+ // Since the opList is valid we know it is our derived class
+ activeOps = &((const CachedOperationList&)activeOpsList).fOps;
}
-
- // Since the opList is valid we know it is our derived class
- activeOps = &((const CachedOperationList&)activeOpsList).fOps;
}
}
}
@@ -791,6 +841,14 @@ void SkPicturePlayback::draw(SkCanvas& canvas, SkDrawPictureCallback* callback)
SkPictureStateTree::Iterator() :
fStateTree->getIterator(*activeOps, &canvas);
+ if (0 != fStart || 0 != fStop) {
+ reader.setOffset(fStart);
+ uint32_t size;
+ SkDEBUGCODE(DrawType op =) read_op_and_size(&reader, &size);
+ SkASSERT(SAVE_LAYER == op);
+ reader.setOffset(fStart+size);
+ }
+
if (it.isValid()) {
uint32_t skipTo = it.nextDraw();
if (kDrawComplete == skipTo) {
@@ -821,6 +879,60 @@ void SkPicturePlayback::draw(SkCanvas& canvas, SkDrawPictureCallback* callback)
return;
}
#endif
+ if (0 != fStart || 0 != fStop) {
+ size_t offset = reader.offset() ;
+ if (offset >= fStop) {
+ uint32_t size;
+ SkDEBUGCODE(DrawType op =) read_op_and_size(&reader, &size);
+ SkASSERT(RESTORE == op);
+ return;
+ }
+ }
+
+ if (NULL != fReplacements) {
+ // Potentially replace a block of operations with a single drawBitmap call
+ SkPicturePlayback::PlaybackReplacements::ReplacementInfo* temp =
+ fReplacements->lookupByStart(reader.offset());
+ if (NULL != temp) {
+ SkASSERT(NULL != temp->fBM);
+ SkASSERT(NULL != temp->fPaint);
+ canvas.drawBitmap(*temp->fBM, temp->fPos.fX, temp->fPos.fY, temp->fPaint);
+
+ if (it.isValid()) {
+ // This save is needed since the BBH will automatically issue
+ // a restore to balanced the saveLayer we're skipping
+ canvas.save();
+ // Note: This skipping only works if the client only issues
+ // well behaved saveLayer calls (i.e., doesn't use
+ // kMatrix_SaveFlag or kClip_SaveFlag in isolation)
+
+ // At this point we know that the PictureStateTree was aiming
+ // for some draw op within temp's saveLayer (although potentially
+ // in a separate saveLayer nested inside it).
+ // We need to skip all the operations inside temp's range
+ // along with all the associated state changes but update
+ // the state tree to the first operation outside temp's range.
+ SkASSERT(it.peekDraw() >= temp->fStart && it.peekDraw() <= temp->fStop);
+
+ while (kDrawComplete != it.peekDraw() && it.peekDraw() <= temp->fStop) {
+ it.skipDraw();
+ }
+
+ if (kDrawComplete == it.peekDraw()) {
+ break;
+ }
+
+ uint32_t skipTo = it.nextDraw();
+ reader.setOffset(skipTo);
+ } else {
+ reader.setOffset(temp->fStop);
+ uint32_t size;
+ SkDEBUGCODE(DrawType op =) read_op_and_size(&reader, &size);
+ SkASSERT(RESTORE == op);
+ }
+ continue;
+ }
+ }
#ifdef SPEW_CLIP_SKIPPING
opCount++;
@@ -915,8 +1027,7 @@ void SkPicturePlayback::draw(SkCanvas& canvas, SkDrawPictureCallback* callback)
SkRegion::Op regionOp = ClipParams_unpackRegionOp(packed);
bool doAA = ClipParams_unpackDoAA(packed);
size_t offsetToRestore = reader.readInt();
- SkASSERT(!offsetToRestore || \
- offsetToRestore >= reader.offset());
+ SkASSERT(!offsetToRestore || offsetToRestore >= reader.offset());
canvas.clipRRect(rrect, regionOp, doAA);
if (canvas.isClipEmpty() && offsetToRestore) {
#ifdef SPEW_CLIP_SKIPPING
diff --git a/src/core/SkPicturePlayback.h b/src/core/SkPicturePlayback.h
index 28fdd63277..7ac8dd8d61 100644
--- a/src/core/SkPicturePlayback.h
+++ b/src/core/SkPicturePlayback.h
@@ -5,6 +5,7 @@
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
+
#ifndef SkPicturePlayback_DEFINED
#define SkPicturePlayback_DEFINED
@@ -91,6 +92,8 @@ public:
const SkPicture::OperationList& getActiveOps(const SkIRect& queryRect);
+ void setUseBBH(bool useBBH) { fUseBBH = useBBH; }
+
void draw(SkCanvas& canvas, SkDrawPictureCallback*);
void serialize(SkWStream*, SkPicture::EncodeBitmap) const;
@@ -227,6 +230,7 @@ private: // these help us with reading/writing
private:
friend class SkPicture;
+ friend class SkGpuDevice; // for access to setDrawLimits & setReplacements
// The picture that owns this SkPicturePlayback object
const SkPicture* fPicture;
@@ -248,6 +252,68 @@ private:
SkBBoxHierarchy* fBoundingHierarchy;
SkPictureStateTree* fStateTree;
+ // Limit the opcode playback to be between the offsets 'start' and 'stop'.
+ // The opcode at 'start' should be a saveLayer while the opcode at
+ // 'stop' should be a restore. Neither of those commands will be issued.
+ // Set both start & stop to 0 to disable draw limiting
+ // Draw limiting cannot be enabled at the same time as draw replacing
+ void setDrawLimits(size_t start, size_t stop) {
+ SkASSERT(NULL == fReplacements);
+ fStart = start;
+ fStop = stop;
+ }
+
+ // PlaybackReplacements collects op ranges that can be replaced with
+ // a single drawBitmap call (using a precomputed bitmap).
+ class PlaybackReplacements {
+ public:
+ // All the operations between fStart and fStop (inclusive) will be replaced with
+ // a single drawBitmap call using fPos, fBM and fPaint.
+ // fPaint will be NULL if the picture's paint wasn't copyable
+ struct ReplacementInfo {
+ size_t fStart;
+ size_t fStop;
+ SkPoint fPos;
+ SkBitmap* fBM;
+ const SkPaint* fPaint; // Note: this object doesn't own the paint
+ };
+
+ ~PlaybackReplacements() { this->freeAll(); }
+
+ // Add a new replacement range. The replacement ranges should be
+ // sorted in increasing order and non-overlapping (esp. no nested
+ // saveLayers).
+ ReplacementInfo* push();
+
+ private:
+ friend class SkPicturePlayback; // for access to lookupByStart
+
+ // look up a replacement range by its start offset
+ ReplacementInfo* lookupByStart(size_t start);
+
+ void freeAll();
+
+ #ifdef SK_DEBUG
+ void validate() const;
+ #endif
+
+ SkTDArray<ReplacementInfo> fReplacements;
+ };
+
+
+ // Replace all the draw ops in the replacement ranges in 'replacements' with
+ // the associated drawBitmap call
+ // Draw replacing cannot be enabled at the same time as draw limiting
+ void setReplacements(PlaybackReplacements* replacements) {
+ SkASSERT(fStart == 0 && fStop == 0);
+ fReplacements = replacements;
+ }
+
+ bool fUseBBH;
+ size_t fStart;
+ size_t fStop;
+ PlaybackReplacements* fReplacements;
+
class CachedOperationList : public SkPicture::OperationList {
public:
CachedOperationList() {
diff --git a/src/core/SkPictureStateTree.cpp b/src/core/SkPictureStateTree.cpp
index f6d54ffedc..39a1a47e14 100644
--- a/src/core/SkPictureStateTree.cpp
+++ b/src/core/SkPictureStateTree.cpp
@@ -114,27 +114,60 @@ void SkPictureStateTree::Iterator::setCurrentMatrix(const SkMatrix* matrix) {
fCurrentMatrix = matrix;
}
-uint32_t SkPictureStateTree::Iterator::nextDraw() {
+uint32_t SkPictureStateTree::Iterator::peekDraw() {
+ SkASSERT(this->isValid());
+ if (fPlaybackIndex >= fDraws->count()) {
+ return kDrawComplete;
+ }
+
+ Draw* draw = static_cast<Draw*>((*fDraws)[fPlaybackIndex]);
+ return draw->fOffset;
+}
+
+uint32_t SkPictureStateTree::Iterator::skipDraw() {
SkASSERT(this->isValid());
if (fPlaybackIndex >= fDraws->count()) {
+ return this->finish();
+ }
+
+ Draw* draw = static_cast<Draw*>((*fDraws)[fPlaybackIndex]);
+
+ if (fSave) {
+ fCanvas->save();
+ fSave = false;
+ }
+
+ fNodes.rewind();
+
+ ++fPlaybackIndex;
+ return draw->fOffset;
+}
+
+uint32_t SkPictureStateTree::Iterator::finish() {
+ if (fCurrentNode->fFlags & Node::kSaveLayer_Flag) {
+ fCanvas->restore();
+ }
+
+ for (fCurrentNode = fCurrentNode->fParent; fCurrentNode;
+ fCurrentNode = fCurrentNode->fParent) {
+ // Note: we call restore() twice when both flags are set.
+ if (fCurrentNode->fFlags & Node::kSave_Flag) {
+ fCanvas->restore();
+ }
if (fCurrentNode->fFlags & Node::kSaveLayer_Flag) {
fCanvas->restore();
}
+ }
- for (fCurrentNode = fCurrentNode->fParent; fCurrentNode;
- fCurrentNode = fCurrentNode->fParent) {
- // Note: we call restore() twice when both flags are set.
- if (fCurrentNode->fFlags & Node::kSave_Flag) {
- fCanvas->restore();
- }
- if (fCurrentNode->fFlags & Node::kSaveLayer_Flag) {
- fCanvas->restore();
- }
- }
+ fCanvas->setMatrix(fPlaybackMatrix);
+ fCurrentMatrix = NULL;
+ return kDrawComplete;
+}
- fCanvas->setMatrix(fPlaybackMatrix);
- fCurrentMatrix = NULL;
- return kDrawComplete;
+uint32_t SkPictureStateTree::Iterator::nextDraw() {
+ SkASSERT(this->isValid());
+ if (fPlaybackIndex >= fDraws->count()) {
+ return this->finish();
}
Draw* draw = static_cast<Draw*>((*fDraws)[fPlaybackIndex]);
diff --git a/src/core/SkPictureStateTree.h b/src/core/SkPictureStateTree.h
index d61bf032cb..a77e09448c 100644
--- a/src/core/SkPictureStateTree.h
+++ b/src/core/SkPictureStateTree.h
@@ -74,8 +74,29 @@ public:
*/
class Iterator {
public:
- /** Returns the next offset into the picture stream, or kDrawComplete if complete. */
+ /** Returns the next op offset needed to create the drawing state
+ required by the queued up draw operation or the offset of the queued
+ up draw operation itself. In the latter case, the next draw operation
+ will move into the queued up slot.
+ It retuns kDrawComplete when done.
+ TODO: this might be better named nextOp
+ */
uint32_t nextDraw();
+ /** Peek at the currently queued up draw op's offset. Note that this can
+ be different then what 'nextDraw' would return b.c. it is
+ the offset of the next _draw_ op while 'nextDraw' can return
+ the offsets to saveLayer and clip ops while it is creating the proper
+ drawing context for the queued up draw op.
+ */
+ uint32_t peekDraw();
+ /** Stop trying to create the drawing context for the currently queued
+ up _draw_ operation and queue up the next one. This call returns
+ the offset of the skipped _draw_ operation. Obviously (since the
+ correct drawing context has not been established), the skipped
+ _draw_ operation should not be issued. Returns kDrawComplete if
+ the end of the draw operations is reached.
+ */
+ uint32_t skipDraw();
static const uint32_t kDrawComplete = SK_MaxU32;
Iterator() : fPlaybackMatrix(), fValid(false) { }
bool isValid() const { return fValid; }
@@ -111,6 +132,8 @@ public:
// Whether or not this is a valid iterator (the default public constructor sets this false)
bool fValid;
+ uint32_t finish();
+
friend class SkPictureStateTree;
};