Added SkClipStack portion of new clip mask caching system

http://codereview.appspot.com/6449089/



git-svn-id: http://skia.googlecode.com/svn/trunk@4978 2bbb7eff-a529-9590-31e7-b0007b416f81
This commit is contained in:
robertphillips@google.com 2012-08-07 15:38:08 +00:00
parent e69137620a
commit 46f935002c
3 changed files with 162 additions and 15 deletions

View File

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

View File

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

View File

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