aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar robertphillips@google.com <robertphillips@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>2012-08-07 15:38:08 +0000
committerGravatar robertphillips@google.com <robertphillips@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>2012-08-07 15:38:08 +0000
commit46f935002c2b25331e552520dc7b1a912e12dfdc (patch)
tree87a2f22fa0bffc779a68d45a39ba24115d6341a2
parente69137620ab0b5b40d230318c8e11b822f63cb9d (diff)
Added SkClipStack portion of new clip mask caching system
-rw-r--r--include/core/SkClipStack.h51
-rw-r--r--src/core/SkClipStack.cpp107
-rw-r--r--src/gpu/SkGpuDevice.cpp19
3 files changed, 162 insertions, 15 deletions
diff --git a/include/core/SkClipStack.h b/include/core/SkClipStack.h
index 4c79c2f824..077c4b7eea 100644
--- a/include/core/SkClipStack.h
+++ b/include/core/SkClipStack.h
@@ -10,6 +10,7 @@
#include "SkDeque.h"
#include "SkRegion.h"
+#include "SkTDArray.h"
struct SkRect;
class SkPath;
@@ -76,6 +77,29 @@ public:
*/
bool isWideOpen() const;
+ /**
+ * Add a callback function that will be called whenever a clip state
+ * is no longer viable. This will occur whenever restore
+ * is called or when a clipDevRect or clipDevPath call updates the
+ * clip within an existing save/restore state. Each clip state is
+ * represented by a unique generation ID.
+ */
+ typedef void (*PFPurgeClipCB)(int genID, void* data);
+ void addPurgeClipCallback(PFPurgeClipCB callback, void* data) const;
+
+ /**
+ * Remove a callback added earlier via addPurgeClipCallback
+ */
+ void removePurgeClipCallback(PFPurgeClipCB callback, void* data) const;
+
+ /**
+ * The generation ID has three reserved values to indicate special
+ * (potentially ignoreable) cases
+ */
+ static const int32_t kInvalidGenID = 0;
+ static const int32_t kEmptyGenID = 1; // no pixels writeable
+ static const int32_t kWideOpenGenID = 2; // all pixels writeable
+
private:
struct Rec;
@@ -198,6 +222,33 @@ private:
SkDeque fDeque;
int fSaveCount;
+
+ // Generation ID for the clip stack. This is incremented for each
+ // clipDevRect and clipDevPath call. 0 is reserved to indicate an
+ // invalid ID.
+ static int32_t gGenID;
+
+ struct ClipCallbackData {
+ PFPurgeClipCB fCallback;
+ void* fData;
+
+ friend bool operator==(const ClipCallbackData& a,
+ const ClipCallbackData& b) {
+ return a.fCallback == b.fCallback && a.fData == b.fData;
+ }
+ };
+
+ mutable SkTDArray<ClipCallbackData> fCallbackData;
+
+ /**
+ * Invoke all the purge callbacks passing in rec's generation ID.
+ */
+ void purgeClip(Rec* rec);
+
+ /**
+ * Return the next unique generation ID.
+ */
+ static int32_t GetNextGenID();
};
#endif
diff --git a/src/core/SkClipStack.cpp b/src/core/SkClipStack.cpp
index 38856e586a..52b12cf779 100644
--- a/src/core/SkClipStack.cpp
+++ b/src/core/SkClipStack.cpp
@@ -7,9 +7,14 @@
*/
#include "SkClipStack.h"
#include "SkPath.h"
+#include "SkThread.h"
+
#include <new>
+// 0-2 are reserved for invalid, empty & wide-open
+int32_t SkClipStack::gGenID = 3;
+
struct SkClipStack::Rec {
enum State {
kEmpty_State,
@@ -41,7 +46,11 @@ struct SkClipStack::Rec {
SkRect fFiniteBound;
bool fIsIntersectionOfRects;
- Rec(int saveCount, const SkRect& rect, SkRegion::Op op, bool doAA) : fRect(rect) {
+ int fGenID;
+
+ Rec(int saveCount, const SkRect& rect, SkRegion::Op op, bool doAA)
+ : fRect(rect)
+ , fGenID(kInvalidGenID) {
fSaveCount = saveCount;
fOp = op;
fState = kRect_State;
@@ -49,7 +58,9 @@ struct SkClipStack::Rec {
// bounding box members are updated in a following updateBound call
}
- Rec(int saveCount, const SkPath& path, SkRegion::Op op, bool doAA) : fPath(path) {
+ Rec(int saveCount, const SkPath& path, SkRegion::Op op, bool doAA)
+ : fPath(path)
+ , fGenID(kInvalidGenID) {
fRect.setEmpty();
fSaveCount = saveCount;
fOp = op;
@@ -63,11 +74,22 @@ struct SkClipStack::Rec {
fFiniteBound.setEmpty();
fFiniteBoundType = kNormal_BoundsType;
fIsIntersectionOfRects = false;
+ fGenID = kEmptyGenID;
+ }
+
+ void checkEmpty() {
+ SkASSERT(fFiniteBound.isEmpty());
+ SkASSERT(kNormal_BoundsType == fFiniteBoundType);
+ SkASSERT(!fIsIntersectionOfRects);
+ SkASSERT(kEmptyGenID == fGenID);
}
bool operator==(const Rec& b) const {
- if (fSaveCount != b.fSaveCount || fOp != b.fOp || fState != b.fState ||
- fDoAA != b.fDoAA) {
+ if (fSaveCount != b.fSaveCount ||
+ fGenID != b.fGenID ||
+ fOp != b.fOp ||
+ fState != b.fState ||
+ fDoAA != b.fDoAA) {
return false;
}
switch (fState) {
@@ -95,6 +117,8 @@ struct SkClipStack::Rec {
SkRegion::kIntersect_Op == op)) {
return true;
}
+ // Only clips within the same save/restore frame (as captured by
+ // the save count) can be merged
return fSaveCount == saveCount &&
SkRegion::kIntersect_Op == op &&
(SkRegion::kIntersect_Op == fOp || SkRegion::kReplace_Op == fOp);
@@ -408,21 +432,27 @@ struct SkClipStack::Rec {
}
};
-SkClipStack::SkClipStack() : fDeque(sizeof(Rec)) {
- fSaveCount = 0;
+
+SkClipStack::SkClipStack()
+ : fDeque(sizeof(Rec))
+ , fSaveCount(0) {
}
SkClipStack::SkClipStack(const SkClipStack& b) : fDeque(sizeof(Rec)) {
*this = b;
}
-SkClipStack::SkClipStack(const SkRect& r) : fDeque(sizeof(Rec)) {
+SkClipStack::SkClipStack(const SkRect& r)
+ : fDeque(sizeof(Rec))
+ , fSaveCount(0) {
if (!r.isEmpty()) {
this->clipDevRect(r, SkRegion::kReplace_Op, false);
}
}
-SkClipStack::SkClipStack(const SkIRect& r) : fDeque(sizeof(Rec)) {
+SkClipStack::SkClipStack(const SkIRect& r)
+ : fDeque(sizeof(Rec))
+ , fSaveCount(0) {
if (!r.isEmpty()) {
SkRect temp;
temp.set(r);
@@ -452,7 +482,8 @@ SkClipStack& SkClipStack::operator=(const SkClipStack& b) {
}
bool SkClipStack::operator==(const SkClipStack& b) const {
- if (fSaveCount != b.fSaveCount || fDeque.count() != b.fDeque.count()) {
+ if (fSaveCount != b.fSaveCount ||
+ fDeque.count() != b.fDeque.count()) {
return false;
}
SkDeque::F2BIter myIter(fDeque);
@@ -493,6 +524,7 @@ void SkClipStack::restore() {
if (rec->fSaveCount <= fSaveCount) {
break;
}
+ this->purgeClip(rec);
rec->~Rec();
fDeque.pop_back();
}
@@ -524,18 +556,19 @@ void SkClipStack::getBounds(SkRect* canvFiniteBound,
void SkClipStack::clipDevRect(const SkRect& rect, SkRegion::Op op, bool doAA) {
+ int32_t genID = GetNextGenID();
+
SkDeque::Iter iter(fDeque, SkDeque::Iter::kBack_IterStart);
Rec* rec = (Rec*) iter.prev();
if (rec && rec->canBeIntersectedInPlace(fSaveCount, op)) {
switch (rec->fState) {
case Rec::kEmpty_State:
- SkASSERT(rec->fFiniteBound.isEmpty());
- SkASSERT(kNormal_BoundsType == rec->fFiniteBoundType);
- SkASSERT(!rec->fIsIntersectionOfRects);
+ rec->checkEmpty();
return;
case Rec::kRect_State:
if (rec->rectRectIntersectAllowed(rect, doAA)) {
+ this->purgeClip(rec);
if (!rec->fRect.intersect(rect)) {
rec->setEmpty();
return;
@@ -544,11 +577,13 @@ void SkClipStack::clipDevRect(const SkRect& rect, SkRegion::Op op, bool doAA) {
rec->fDoAA = doAA;
Rec* prev = (Rec*) iter.prev();
rec->updateBound(prev);
+ rec->fGenID = genID;
return;
}
break;
case Rec::kPath_State:
if (!SkRect::Intersects(rec->fPath.getBounds(), rect)) {
+ this->purgeClip(rec);
rec->setEmpty();
return;
}
@@ -557,6 +592,11 @@ void SkClipStack::clipDevRect(const SkRect& rect, SkRegion::Op op, bool doAA) {
}
new (fDeque.push_back()) Rec(fSaveCount, rect, op, doAA);
((Rec*) fDeque.back())->updateBound(rec);
+ ((Rec*) fDeque.back())->fGenID = genID;
+
+ if (rec && rec->fSaveCount == fSaveCount) {
+ this->purgeClip(rec);
+ }
}
void SkClipStack::clipDevPath(const SkPath& path, SkRegion::Op op, bool doAA) {
@@ -564,23 +604,26 @@ void SkClipStack::clipDevPath(const SkPath& path, SkRegion::Op op, bool doAA) {
if (path.isRect(&alt)) {
return this->clipDevRect(alt, op, doAA);
}
+
+ int32_t genID = GetNextGenID();
+
Rec* rec = (Rec*)fDeque.back();
if (rec && rec->canBeIntersectedInPlace(fSaveCount, op)) {
const SkRect& pathBounds = path.getBounds();
switch (rec->fState) {
case Rec::kEmpty_State:
- SkASSERT(rec->fFiniteBound.isEmpty());
- SkASSERT(kNormal_BoundsType == rec->fFiniteBoundType);
- SkASSERT(!rec->fIsIntersectionOfRects);
+ rec->checkEmpty();
return;
case Rec::kRect_State:
if (!SkRect::Intersects(rec->fRect, pathBounds)) {
+ this->purgeClip(rec);
rec->setEmpty();
return;
}
break;
case Rec::kPath_State:
if (!SkRect::Intersects(rec->fPath.getBounds(), pathBounds)) {
+ this->purgeClip(rec);
rec->setEmpty();
return;
}
@@ -589,6 +632,11 @@ void SkClipStack::clipDevPath(const SkPath& path, SkRegion::Op op, bool doAA) {
}
new (fDeque.push_back()) Rec(fSaveCount, path, op, doAA);
((Rec*) fDeque.back())->updateBound(rec);
+ ((Rec*) fDeque.back())->fGenID = genID;
+
+ if (rec && rec->fSaveCount == fSaveCount) {
+ this->purgeClip(rec);
+ }
}
bool SkClipStack::isWideOpen() const {
@@ -736,3 +784,32 @@ void SkClipStack::getConservativeBounds(int offsetX,
devBounds->setEmpty();
}
}
+
+void SkClipStack::addPurgeClipCallback(PFPurgeClipCB callback, void* data) const {
+ ClipCallbackData temp = { callback, data };
+ fCallbackData.append(1, &temp);
+}
+
+void SkClipStack::removePurgeClipCallback(PFPurgeClipCB callback, void* data) const {
+ ClipCallbackData temp = { callback, data };
+ int index = fCallbackData.find(temp);
+ if (index >= 0) {
+ fCallbackData.removeShuffle(index);
+ }
+}
+
+// The clip state represented by 'rec' will never be used again. Purge it.
+void SkClipStack::purgeClip(Rec* rec) {
+ SkASSERT(NULL != rec);
+
+ for (int i = 0; i < fCallbackData.count(); ++i) {
+ (*fCallbackData[i].fCallback)(rec->fGenID, fCallbackData[i].fData);
+ }
+
+ // Invalidate rec's gen ID so handlers can detect already handled records
+ rec->fGenID = kInvalidGenID;
+}
+
+int32_t SkClipStack::GetNextGenID() {
+ return sk_atomic_inc(&gGenID);
+}
diff --git a/src/gpu/SkGpuDevice.cpp b/src/gpu/SkGpuDevice.cpp
index 9ed1ccbdf0..592cdfc6e6 100644
--- a/src/gpu/SkGpuDevice.cpp
+++ b/src/gpu/SkGpuDevice.cpp
@@ -345,16 +345,35 @@ void SkGpuDevice::writePixels(const SkBitmap& bitmap, int x, int y,
config, bitmap.getPixels(), bitmap.rowBytes());
}
+namespace {
+void purgeClipCB(int genID, void* data) {
+ GrContext* context = (GrContext*) data;
+
+ if (SkClipStack::kInvalidGenID == genID ||
+ SkClipStack::kEmptyGenID == genID ||
+ SkClipStack::kWideOpenGenID == genID) {
+ // none of these cases will have a cached clip mask
+ return;
+ }
+
+}
+};
+
void SkGpuDevice::onAttachToCanvas(SkCanvas* canvas) {
INHERITED::onAttachToCanvas(canvas);
// Canvas promises that this ptr is valid until onDetachFromCanvas is called
fClipData.fClipStack = canvas->getClipStack();
+
+ fClipData.fClipStack->addPurgeClipCallback(purgeClipCB, fContext);
}
void SkGpuDevice::onDetachFromCanvas() {
INHERITED::onDetachFromCanvas();
+ // TODO: iterate through the clip stack and clean up any cached clip masks
+ fClipData.fClipStack->removePurgeClipCallback(purgeClipCB, fContext);
+
fClipData.fClipStack = NULL;
}