aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--src/core/SkBBoxHierarchy.h32
-rw-r--r--src/core/SkBBoxHierarchyRecord.cpp12
-rw-r--r--src/core/SkBBoxHierarchyRecord.h6
-rw-r--r--src/core/SkPicturePlayback.cpp25
-rw-r--r--src/core/SkPictureRecord.cpp44
-rw-r--r--src/core/SkPictureRecord.h1
-rw-r--r--src/core/SkPictureStateTree.cpp14
-rw-r--r--src/core/SkPictureStateTree.h11
-rw-r--r--src/core/SkRTree.cpp8
-rw-r--r--src/core/SkRTree.h2
-rw-r--r--src/core/SkTileGrid.cpp9
-rw-r--r--src/core/SkTileGrid.h2
12 files changed, 150 insertions, 16 deletions
diff --git a/src/core/SkBBoxHierarchy.h b/src/core/SkBBoxHierarchy.h
index 4c8b2ae256..9de785192a 100644
--- a/src/core/SkBBoxHierarchy.h
+++ b/src/core/SkBBoxHierarchy.h
@@ -14,6 +14,23 @@
#include "SkRefCnt.h"
/**
+ * Interface for a client class that implements utility methods needed
+ * by SkBBoxHierarchy that require intrinsic knowledge of the data
+ * object type that is stored in the bounding box hierarchy.
+ */
+class SkBBoxHierarchyClient {
+public:
+ virtual ~SkBBoxHierarchyClient() {}
+
+ /**
+ * Implements a rewind stop condition used by rewindInserts
+ * Must returns true if 'data' points to an object that should be re-wound
+ * by rewinfInserts.
+ */
+ virtual bool shouldRewind(void* data) = 0;
+};
+
+/**
* Interface for a spatial data structure that associates user data pointers with axis-aligned
* bounding boxes, and allows efficient retrieval of intersections with query rectangles.
*/
@@ -21,6 +38,8 @@ class SkBBoxHierarchy : public SkRefCnt {
public:
SK_DECLARE_INST_COUNT(SkBBoxHierarchy)
+ SkBBoxHierarchy() : fClient(NULL) {}
+
/**
* Insert a data pointer and corresponding bounding box
* @param data The data pointer, may be NULL
@@ -49,6 +68,19 @@ public:
*/
virtual int getCount() const = 0;
+ /**
+ * Rewinds all the most recently inserted data elements until an element
+ * is encountered for which client->shouldRewind(data) returns false. May
+ * not rewind elements that were inserted prior to the last call to
+ * flushDeferredInserts.
+ */
+ virtual void rewindInserts() = 0;
+
+ void setClient(SkBBoxHierarchyClient* client) { fClient = client; }
+
+protected:
+ SkBBoxHierarchyClient* fClient;
+
private:
typedef SkRefCnt INHERITED;
};
diff --git a/src/core/SkBBoxHierarchyRecord.cpp b/src/core/SkBBoxHierarchyRecord.cpp
index 16172f343d..9c02468ae1 100644
--- a/src/core/SkBBoxHierarchyRecord.cpp
+++ b/src/core/SkBBoxHierarchyRecord.cpp
@@ -8,7 +8,6 @@
#include "SkBBoxHierarchyRecord.h"
#include "SkPictureStateTree.h"
-#include "SkBBoxHierarchy.h"
SkBBoxHierarchyRecord::SkBBoxHierarchyRecord(uint32_t recordFlags,
SkBBoxHierarchy* h,
@@ -17,6 +16,7 @@ SkBBoxHierarchyRecord::SkBBoxHierarchyRecord(uint32_t recordFlags,
fStateTree = SkNEW(SkPictureStateTree);
fBoundingHierarchy = h;
fBoundingHierarchy->ref();
+ fBoundingHierarchy->setClient(this);
}
void SkBBoxHierarchyRecord::handleBBox(const SkRect& bounds) {
@@ -103,3 +103,13 @@ bool SkBBoxHierarchyRecord::clipRRect(const SkRRect& rrect,
fStateTree->appendClip(this->writeStream().size());
return INHERITED::clipRRect(rrect, op, doAntiAlias);
}
+
+bool SkBBoxHierarchyRecord::shouldRewind(void* data) {
+ // SkBBoxHierarchy::rewindInserts is called by SkPicture after the
+ // SkPicture has rewound its command stream. To match that rewind in the
+ // BBH, we rewind all draws that reference commands that were recorded
+ // past the point to which the SkPicture has rewound, which is given by
+ // writeStream().size().
+ SkPictureStateTree::Draw* draw = static_cast<SkPictureStateTree::Draw*>(data);
+ return draw->fOffset >= writeStream().size();
+}
diff --git a/src/core/SkBBoxHierarchyRecord.h b/src/core/SkBBoxHierarchyRecord.h
index 854e525961..27da3c9188 100644
--- a/src/core/SkBBoxHierarchyRecord.h
+++ b/src/core/SkBBoxHierarchyRecord.h
@@ -9,13 +9,14 @@
#ifndef SkRTreeCanvas_DEFINED
#define SkRTreeCanvas_DEFINED
+#include "SkBBoxHierarchy.h"
#include "SkBBoxRecord.h"
/**
* This records bounding box information into an SkBBoxHierarchy, and clip/transform information
* into an SkPictureStateTree to allow for efficient culling and correct playback of draws.
*/
-class SkBBoxHierarchyRecord : public SkBBoxRecord {
+class SkBBoxHierarchyRecord : public SkBBoxRecord, public SkBBoxHierarchyClient {
public:
/** This will take a ref of h */
SkBBoxHierarchyRecord(uint32_t recordFlags, SkBBoxHierarchy* h,
@@ -47,6 +48,9 @@ public:
SkRegion::Op op = SkRegion::kIntersect_Op,
bool doAntiAlias = false) SK_OVERRIDE;
+ // Implementation of the SkBBoxHierarchyClient interface
+ virtual bool shouldRewind(void* data) SK_OVERRIDE;
+
private:
typedef SkBBoxRecord INHERITED;
};
diff --git a/src/core/SkPicturePlayback.cpp b/src/core/SkPicturePlayback.cpp
index 93e5cf17bf..22125ee02b 100644
--- a/src/core/SkPicturePlayback.cpp
+++ b/src/core/SkPicturePlayback.cpp
@@ -715,23 +715,34 @@ void SkPicturePlayback::draw(SkCanvas& canvas) {
size_t curOffset = reader.offset();
uint32_t size;
DrawType op = read_op_and_size(&reader, &size);
- if (NOOP == op) {
+ size_t skipTo = 0;
+#ifdef SK_DEVELOPER
+ // TODO: once chunk sizes are in all .skps just use
+ // "curOffset + size"
+ skipTo = this->preDraw(curOffset, op);
+#endif
+ if (0 == skipTo && NOOP == op) {
// NOOPs are to be ignored - do not propagate them any further
- reader.setOffset(curOffset+size);
- continue;
+ skipTo = curOffset + size;
}
-#ifdef SK_DEVELOPER
- // TODO: once chunk sizes are in all .skps just use "curOffset + size"
- size_t skipTo = this->preDraw(curOffset, op);
if (0 != skipTo) {
+ if (it.isValid()) {
+ // If using a bounding box hierarchy, advance the state tree
+ // iterator until at or after skipTo
+ uint32_t adjustedSkipTo;
+ do {
+ adjustedSkipTo = it.draw();
+ } while (adjustedSkipTo < skipTo);
+ skipTo = adjustedSkipTo;
+ }
if (kDrawComplete == skipTo) {
break;
}
reader.setOffset(skipTo);
continue;
}
-#endif
+
switch (op) {
case CLIP_PATH: {
const SkPath& path = getPath(reader);
diff --git a/src/core/SkPictureRecord.cpp b/src/core/SkPictureRecord.cpp
index 45412fe881..38bbe33be5 100644
--- a/src/core/SkPictureRecord.cpp
+++ b/src/core/SkPictureRecord.cpp
@@ -502,20 +502,48 @@ static bool collapse_save_clip_restore(SkWriter32* writer, int32_t offset,
typedef bool (*PictureRecordOptProc)(SkWriter32* writer, int32_t offset,
SkPaintDictionary* paintDict);
+enum PictureRecordOptType {
+ kRewind_OptType, // Optimization rewinds the command stream
+ kCollapseSaveLayer_OptType, // Optimization eliminates a save/restore pair
+};
+struct PictureRecordOpt {
+ PictureRecordOptProc fProc;
+ PictureRecordOptType fType;
+};
/*
* A list of the optimizations that are tried upon seeing a restore
* TODO: add a real API for such optimizations
* Add the ability to fire optimizations on any op (not just RESTORE)
*/
-static const PictureRecordOptProc gPictureRecordOpts[] = {
- collapse_save_clip_restore,
-#ifndef SK_IGNORE_PICTURE_RECORD_SAVE_LAYER_OPT
- remove_save_layer1,
- remove_save_layer2,
-#endif
+static const PictureRecordOpt gPictureRecordOpts[] = {
+ { collapse_save_clip_restore, kRewind_OptType },
+ { remove_save_layer1, kCollapseSaveLayer_OptType },
+ { remove_save_layer2, kCollapseSaveLayer_OptType }
};
+// This is called after an optimization has been applied to the command stream
+// in order to adjust the contents and state of the bounding box hierarchy and
+// state tree to reflect the optimization.
+static void apply_optimization_to_bbh(PictureRecordOptType opt, SkPictureStateTree* stateTree,
+ SkBBoxHierarchy* boundingHierarchy) {
+ switch (opt) {
+ case kCollapseSaveLayer_OptType:
+ stateTree->saveCollapsed();
+ break;
+ case kRewind_OptType:
+ if (NULL != boundingHierarchy) {
+ boundingHierarchy->rewindInserts();
+ }
+ // Note: No need to touch the state tree for this to work correctly.
+ // Unused branches do not burden the playback, and pruning the tree
+ // would be O(N^2), so it is best to leave it alone.
+ break;
+ default:
+ SkASSERT(0);
+ }
+}
+
void SkPictureRecord::restore() {
// FIXME: SkDeferredCanvas needs to be refactored to respect
// save/restore balancing so that the following test can be
@@ -536,10 +564,12 @@ void SkPictureRecord::restore() {
uint32_t initialOffset, size;
size_t opt;
for (opt = 0; opt < SK_ARRAY_COUNT(gPictureRecordOpts); ++opt) {
- if ((*gPictureRecordOpts[opt])(&fWriter, fRestoreOffsetStack.top(), &fPaints)) {
+ if ((*gPictureRecordOpts[opt].fProc)(&fWriter, fRestoreOffsetStack.top(), &fPaints)) {
// Some optimization fired so don't add the RESTORE
size = 0;
initialOffset = fWriter.size();
+ apply_optimization_to_bbh(gPictureRecordOpts[opt].fType,
+ fStateTree, fBoundingHierarchy);
break;
}
}
diff --git a/src/core/SkPictureRecord.h b/src/core/SkPictureRecord.h
index 7a2bb8de47..88ff302b7c 100644
--- a/src/core/SkPictureRecord.h
+++ b/src/core/SkPictureRecord.h
@@ -103,6 +103,7 @@ public:
void endRecording();
private:
+ void handleOptimization(int opt);
void recordRestoreOffsetPlaceholder(SkRegion::Op);
void fillRestoreOffsetPlaceholdersForCurrentStackLevel(
uint32_t restoreOffset);
diff --git a/src/core/SkPictureStateTree.cpp b/src/core/SkPictureStateTree.cpp
index 2abed19658..9f2db25848 100644
--- a/src/core/SkPictureStateTree.cpp
+++ b/src/core/SkPictureStateTree.cpp
@@ -14,6 +14,7 @@ SK_DEFINE_INST_COUNT(SkPictureStateTree)
SkPictureStateTree::SkPictureStateTree()
: fAlloc(2048)
, fRoot(NULL)
+ , fLastRestoredNode(NULL)
, fStateStack(sizeof(Draw), 16) {
SkMatrix* identity = static_cast<SkMatrix*>(fAlloc.allocThrow(sizeof(SkMatrix)));
identity->reset();
@@ -49,7 +50,20 @@ void SkPictureStateTree::appendSaveLayer(uint32_t offset) {
fCurrentState.fNode->fFlags |= Node::kSaveLayer_Flag;
}
+void SkPictureStateTree::saveCollapsed() {
+ SkASSERT(NULL != fLastRestoredNode);
+ SkASSERT(SkToBool(fLastRestoredNode->fFlags & \
+ (Node::kSaveLayer_Flag | Node::kSave_Flag)));
+ SkASSERT(fLastRestoredNode->fParent == fCurrentState.fNode);
+ // The structure of the tree is not modified here. We just turn off
+ // the save or saveLayer flag to prevent the iterator from making state
+ // changing calls on the playback canvas when traversing a save or
+ // saveLayerNode node.
+ fLastRestoredNode->fFlags = 0;
+}
+
void SkPictureStateTree::appendRestore() {
+ fLastRestoredNode = fCurrentState.fNode;
fCurrentState = *static_cast<Draw*>(fStateStack.back());
fStateStack.pop_back();
}
diff --git a/src/core/SkPictureStateTree.h b/src/core/SkPictureStateTree.h
index 798e5526b6..7e137d8fce 100644
--- a/src/core/SkPictureStateTree.h
+++ b/src/core/SkPictureStateTree.h
@@ -63,6 +63,13 @@ public:
void appendClip(uint32_t offset);
/**
+ * Call this immediately after an appendRestore call that is associated
+ * a save or saveLayer that was removed from the command stream
+ * due to a command pattern optimization in SkPicture.
+ */
+ void saveCollapsed();
+
+ /**
* Playback helper
*/
class Iterator {
@@ -109,6 +116,10 @@ private:
SkChunkAlloc fAlloc;
Node* fRoot;
+ // Needed by saveCollapsed() because nodes do not currently store
+ // references to their children. If they did, we could just retrieve the
+ // last added child.
+ Node* fLastRestoredNode;
// The currently active state
Draw fCurrentState;
diff --git a/src/core/SkRTree.cpp b/src/core/SkRTree.cpp
index 88fc079082..d7a15d5957 100644
--- a/src/core/SkRTree.cpp
+++ b/src/core/SkRTree.cpp
@@ -434,6 +434,14 @@ int SkRTree::validateSubtree(Node* root, SkIRect bounds, bool isRoot) {
}
}
+void SkRTree::rewindInserts() {
+ SkASSERT(this->isEmpty()); // Currently only supports deferred inserts
+ while (!fDeferredInserts.isEmpty() &&
+ fClient->shouldRewind(fDeferredInserts.top().fChild.data)) {
+ fDeferredInserts.pop();
+ }
+}
+
///////////////////////////////////////////////////////////////////////////////////////////////////
static inline uint32_t get_area(const SkIRect& rect) {
diff --git a/src/core/SkRTree.h b/src/core/SkRTree.h
index 0a536677cd..2d11f28a57 100644
--- a/src/core/SkRTree.h
+++ b/src/core/SkRTree.h
@@ -87,6 +87,8 @@ public:
*/
virtual int getCount() const { return fCount; }
+ virtual void rewindInserts() SK_OVERRIDE;
+
private:
struct Node;
diff --git a/src/core/SkTileGrid.cpp b/src/core/SkTileGrid.cpp
index 7b901e9b3b..641a03179c 100644
--- a/src/core/SkTileGrid.cpp
+++ b/src/core/SkTileGrid.cpp
@@ -121,3 +121,12 @@ void SkTileGrid::clear() {
int SkTileGrid::getCount() const {
return fInsertionCount;
}
+
+void SkTileGrid::rewindInserts() {
+ SkASSERT(fClient);
+ for (int i = 0; i < fTileCount; ++i) {
+ while (!fTileData[i].isEmpty() && fClient->shouldRewind(fTileData[i].top())) {
+ fTileData[i].pop();
+ }
+ }
+}
diff --git a/src/core/SkTileGrid.h b/src/core/SkTileGrid.h
index c83a0fd468..3152fa39b3 100644
--- a/src/core/SkTileGrid.h
+++ b/src/core/SkTileGrid.h
@@ -55,6 +55,8 @@ public:
*/
virtual int getCount() const SK_OVERRIDE;
+ virtual void rewindInserts() SK_OVERRIDE;
+
// Used by search() and in SkTileGridHelper implementations
enum {
kTileFinished = -1,