aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--gyp/tests.gyp1
-rw-r--r--src/gpu/GrClipMaskManager.cpp2
-rw-r--r--src/gpu/GrClipMaskManager.h222
-rw-r--r--src/gpu/GrSoftwarePathRenderer.cpp1
-rw-r--r--tests/ClipCacheTest.cpp152
5 files changed, 344 insertions, 34 deletions
diff --git a/gyp/tests.gyp b/gyp/tests.gyp
index 58039ba6c7..e7d5724cda 100644
--- a/gyp/tests.gyp
+++ b/gyp/tests.gyp
@@ -23,6 +23,7 @@
'../tests/BlurTest.cpp',
'../tests/CanvasTest.cpp',
'../tests/ClampRangeTest.cpp',
+ '../tests/ClipCacheTest.cpp',
'../tests/ClipCubicTest.cpp',
'../tests/ClipStackTest.cpp',
'../tests/ClipperTest.cpp',
diff --git a/src/gpu/GrClipMaskManager.cpp b/src/gpu/GrClipMaskManager.cpp
index 1a892d80dc..f8c43b0491 100644
--- a/src/gpu/GrClipMaskManager.cpp
+++ b/src/gpu/GrClipMaskManager.cpp
@@ -396,7 +396,7 @@ bool GrClipMaskManager::createAlphaClipMask(GrGpu* gpu,
if (fAACache.canReuse(clipIn, rt->width(), rt->height())) {
*result = fAACache.getLastMask();
- *resultBounds = fAACache.getLastBound();
+ fAACache.getLastBound(resultBounds);
return true;
}
diff --git a/src/gpu/GrClipMaskManager.h b/src/gpu/GrClipMaskManager.h
index f827948202..65205679d5 100644
--- a/src/gpu/GrClipMaskManager.h
+++ b/src/gpu/GrClipMaskManager.h
@@ -15,6 +15,7 @@
#include "GrClip.h"
#include "SkRefCnt.h"
#include "GrTexture.h"
+#include "SkDeque.h"
class GrGpu;
class GrPathRenderer;
@@ -42,22 +43,34 @@ struct ScissoringSettings {
*/
class GrClipMaskCache : public GrNoncopyable {
public:
- GrClipMaskCache() {
- reset();
+ GrClipMaskCache()
+ : fStack(sizeof(GrClipStackFrame)) {
+ // We need an initial frame to capture the clip state prior to
+ // any pushes
+ new (fStack.push_back()) GrClipStackFrame();
}
- void reset () {
- fLastWidth = -1;
- fLastHeight = -1;
- fLastClip.setEmpty();
- fLastMask.reset(NULL);
- fLastBound.MakeEmpty();
+ ~GrClipMaskCache() {
+
+ while (!fStack.empty()) {
+ GrClipStackFrame* temp = (GrClipStackFrame*) fStack.back();
+ temp->~GrClipStackFrame();
+ fStack.pop_back();
+ }
}
bool canReuse(const GrClip& clip, int width, int height) {
- if (fLastWidth >= width &&
- fLastHeight >= height &&
- clip == fLastClip) {
+
+ if (fStack.empty()) {
+ GrAssert(false);
+ return false;
+ }
+
+ GrClipStackFrame* back = (GrClipStackFrame*) fStack.back();
+
+ if (back->fLastWidth >= width &&
+ back->fLastHeight >= height &&
+ clip == back->fLastClip) {
return true;
}
@@ -67,61 +80,204 @@ public:
void set(const GrClip& clip, int width, int height,
GrTexture* mask, const GrRect& bound) {
- fLastWidth = width;
- fLastHeight = height;
- fLastClip = clip;
+ if (fStack.empty()) {
+ GrAssert(false);
+ return;
+ }
+
+ GrClipStackFrame* back = (GrClipStackFrame*) fStack.back();
+
+ back->fLastWidth = width;
+ back->fLastHeight = height;
+ back->fLastClip = clip;
SkSafeRef(mask);
- fLastMask.reset(mask);
- fLastBound = bound;
+ back->fLastMask.reset(mask);
+ back->fLastBound = bound;
+ }
+
+ void reset() {
+ if (fStack.empty()) {
+ GrAssert(false);
+ return;
+ }
+
+ GrClipStackFrame* back = (GrClipStackFrame*) fStack.back();
+
+ back->reset();
+ }
+
+ /**
+ * After a "push" the clip state is entirely open. Currently, the
+ * entire clip stack will be re-rendered into a new clip mask.
+ * TODO: can we take advantage of the nested nature of the clips to
+ * reduce the mask creation cost?
+ */
+ void push() {
+ new (fStack.push_back()) GrClipStackFrame();
+ }
+
+ void pop() {
+ GrAssert(!fStack.empty());
+
+ if (!fStack.empty()) {
+ GrClipStackFrame* back = (GrClipStackFrame*) fStack.back();
+
+ back->~GrClipStackFrame();
+ fStack.pop_back();
+ }
}
int getLastWidth() const {
- return fLastWidth;
+
+ if (fStack.empty()) {
+ GrAssert(false);
+ return -1;
+ }
+
+ GrClipStackFrame* back = (GrClipStackFrame*) fStack.back();
+
+ return back->fLastWidth;
}
int getLastHeight() const {
- return fLastHeight;
+
+ if (fStack.empty()) {
+ GrAssert(false);
+ return -1;
+ }
+
+ GrClipStackFrame* back = (GrClipStackFrame*) fStack.back();
+
+ return back->fLastHeight;
}
- const GrClip& getLastClip() const {
- return fLastClip;
+ void getLastClip(GrClip* clip) const {
+
+ if (fStack.empty()) {
+ GrAssert(false);
+ clip->setEmpty();
+ return;
+ }
+
+ GrClipStackFrame* back = (GrClipStackFrame*) fStack.back();
+
+ *clip = back->fLastClip;
}
GrTexture* getLastMask() {
- return fLastMask.get();
+
+ if (fStack.empty()) {
+ GrAssert(false);
+ return NULL;
+ }
+
+ GrClipStackFrame* back = (GrClipStackFrame*) fStack.back();
+
+ return back->fLastMask.get();
+ }
+
+ const GrTexture* getLastMask() const {
+
+ if (fStack.empty()) {
+ GrAssert(false);
+ return NULL;
+ }
+
+ GrClipStackFrame* back = (GrClipStackFrame*) fStack.back();
+
+ return back->fLastMask.get();
}
GrTexture* detachLastMask() {
- return fLastMask.detach();
+
+ if (fStack.empty()) {
+ GrAssert(false);
+ return NULL;
+ }
+
+ GrClipStackFrame* back = (GrClipStackFrame*) fStack.back();
+
+ return back->fLastMask.detach();
}
int getLastMaskWidth() const {
- if (NULL == fLastMask.get()) {
+
+ if (fStack.empty()) {
+ GrAssert(false);
+ return -1;
+ }
+
+ GrClipStackFrame* back = (GrClipStackFrame*) fStack.back();
+
+ if (NULL == back->fLastMask.get()) {
return -1;
}
- return fLastMask.get()->width();
+ return back->fLastMask.get()->width();
}
int getLastMaskHeight() const {
- if (NULL == fLastMask.get()) {
+
+ if (fStack.empty()) {
+ GrAssert(false);
return -1;
}
- return fLastMask.get()->height();
+ GrClipStackFrame* back = (GrClipStackFrame*) fStack.back();
+
+ if (NULL == back->fLastMask.get()) {
+ return -1;
+ }
+
+ return back->fLastMask.get()->height();
}
- const GrRect& getLastBound() const {
- return fLastBound;
+ void getLastBound(GrRect* bound) const {
+
+ if (fStack.empty()) {
+ GrAssert(false);
+ bound->setEmpty();
+ return;
+ }
+
+ GrClipStackFrame* back = (GrClipStackFrame*) fStack.back();
+
+ *bound = back->fLastBound;
}
protected:
private:
- int fLastWidth;
- int fLastHeight;
- GrClip fLastClip;
- SkAutoTUnref<GrTexture> fLastMask;
- GrRect fLastBound;
+ struct GrClipStackFrame {
+
+ GrClipStackFrame() {
+ reset();
+ }
+
+ void reset () {
+ fLastWidth = -1;
+ fLastHeight = -1;
+ fLastClip.setEmpty();
+ fLastMask.reset(NULL);
+ fLastBound.setEmpty();
+ }
+
+ // fLastWidth & fLastHeight store the render target size used when
+ // creating the mask. They factor into the reuse decision (in canReuse)
+ // TODO: We should probably use the mask's width & height rather than
+ // the render target's width & height for reuse decisions
+ int fLastWidth;
+ int fLastHeight;
+ GrClip fLastClip;
+ // The mask's width & height values are used in setupDrawStateAAClip to
+ // correctly scale the uvs for geometry drawn with this mask
+ SkAutoTUnref<GrTexture> fLastMask;
+ // fLastBound stores the bounding box of the clip mask in canvas
+ // space. The left and top fields are used to offset the uvs for
+ // geometry drawn with this mask (in setupDrawStateAAClip)
+ GrRect fLastBound;
+ };
+
+ SkDeque fStack;
typedef GrNoncopyable INHERITED;
};
diff --git a/src/gpu/GrSoftwarePathRenderer.cpp b/src/gpu/GrSoftwarePathRenderer.cpp
index cbec9653de..22e4a2641a 100644
--- a/src/gpu/GrSoftwarePathRenderer.cpp
+++ b/src/gpu/GrSoftwarePathRenderer.cpp
@@ -232,6 +232,7 @@ bool GrSoftwarePathRenderer::onDrawPath(const SkPath& path,
// rendering (kGlyphMaskStage in GrBatchedTextContext)
kPathMaskStage = GrPaint::kTotalStages,
};
+ GrAssert(NULL == target->drawState()->getTexture(kPathMaskStage));
target->drawState()->setTexture(kPathMaskStage, texture);
target->drawState()->sampler(kPathMaskStage)->reset();
GrScalar w = GrIntToScalar(pathBounds.width());
diff --git a/tests/ClipCacheTest.cpp b/tests/ClipCacheTest.cpp
new file mode 100644
index 0000000000..dbcb4e2994
--- /dev/null
+++ b/tests/ClipCacheTest.cpp
@@ -0,0 +1,152 @@
+/*
+ * 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 "Test.h"
+#include "SkGpuDevice.h"
+#include "../../src/gpu/GrClipMaskManager.h"
+
+static const int X_SIZE = 12;
+static const int Y_SIZE = 12;
+
+////////////////////////////////////////////////////////////////////////////////
+static GrTexture* createTexture(GrContext* context) {
+ unsigned char textureData[X_SIZE][Y_SIZE];
+
+ memset(textureData, 0, X_SIZE * Y_SIZE);
+
+ GrTextureDesc desc;
+
+ // let Skia know we will be using this texture as a render target
+ desc.fFlags = kRenderTarget_GrTextureFlagBit;
+ // it is a single channel texture
+ desc.fConfig = kAlpha_8_GrPixelConfig;
+ desc.fWidth = X_SIZE;
+ desc.fHeight = Y_SIZE;
+ desc.fSampleCnt = 0;
+
+ // We are initializing the texture with zeros here
+ GrTexture* texture = context->createUncachedTexture(desc, textureData, 0);
+ if (!texture) {
+ return NULL;
+ }
+
+ return texture;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// verify that the top state of the stack matches the passed in state
+static void check_state(skiatest::Reporter* reporter,
+ const GrClipMaskCache& cache,
+ int width,
+ int height,
+ const GrClip& clip,
+ GrTexture* mask,
+ const GrRect& bound) {
+ REPORTER_ASSERT(reporter, width == cache.getLastWidth());
+ REPORTER_ASSERT(reporter, height == cache.getLastHeight());
+
+ GrClip cacheClip;
+ cache.getLastClip(&cacheClip);
+ REPORTER_ASSERT(reporter, clip == cacheClip);
+
+ REPORTER_ASSERT(reporter, mask == cache.getLastMask());
+
+ GrRect cacheBound;
+ cache.getLastBound(&cacheBound);
+ REPORTER_ASSERT(reporter, bound == cacheBound);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// basic test of the cache's base functionality:
+// push, pop, set, canReuse & getters
+static void test_cache(skiatest::Reporter* reporter, GrContext* context) {
+
+ GrClipMaskCache cache;
+
+ GrClip emptyClip;
+ emptyClip.setEmpty();
+
+ GrRect emptyBound;
+ emptyBound.setEmpty();
+
+ // check initial state
+ check_state(reporter, cache, -1, -1, emptyClip, NULL, emptyBound);
+
+ // set the current state
+ GrRect bound1;
+ bound1.set(0, 0, 100, 100);
+
+ GrClip clip1;
+ clip1.setFromRect(bound1);
+
+ SkAutoTUnref<GrTexture> texture(createTexture(context));
+
+ cache.set(clip1, 128, 128, texture.get(), bound1);
+
+ // check that the set took
+ check_state(reporter, cache, 128, 128, clip1, texture.get(), bound1);
+ REPORTER_ASSERT(reporter, 2 == texture.get()->getRefCnt());
+
+ // push the state
+ cache.push();
+
+ // verify that the pushed state is initially empty
+ check_state(reporter, cache, -1, -1, emptyClip, NULL, emptyBound);
+ REPORTER_ASSERT(reporter, 2 == texture.get()->getRefCnt());
+
+ // modify the new state
+ GrRect bound2;
+ bound2.set(-10, -10, 10, 10);
+
+ GrClip clip2;
+ clip2.setEmpty();
+ clip2.setFromRect(bound2);
+
+ cache.set(clip2, 10, 10, texture.get(), bound2);
+
+ // check that the changes took
+ check_state(reporter, cache, 10, 10, clip2, texture.get(), bound2);
+ REPORTER_ASSERT(reporter, 3 == texture.get()->getRefCnt());
+
+ // check to make sure canReuse works
+ REPORTER_ASSERT(reporter, cache.canReuse(clip2, 10, 10));
+ REPORTER_ASSERT(reporter, !cache.canReuse(clip1, 10, 10));
+
+ // pop the state
+ cache.pop();
+
+ // verify that the old state is restored
+ check_state(reporter, cache, 128, 128, clip1, texture.get(), bound1);
+ REPORTER_ASSERT(reporter, 2 == texture.get()->getRefCnt());
+
+ // manually clear the state
+ cache.reset();
+
+ // verify it is now empty
+ check_state(reporter, cache, -1, -1, emptyClip, NULL, emptyBound);
+ REPORTER_ASSERT(reporter, 1 == texture.get()->getRefCnt());
+
+ // pop again - so there is no state
+ cache.pop();
+
+#if !defined(SK_DEBUG)
+ // verify that the getters don't crash
+ // only do in release since it generates asserts in debug
+ check_state(reporter, cache, -1, -1, emptyClip, NULL, emptyBound);
+#endif
+ REPORTER_ASSERT(reporter, 1 == texture.get()->getRefCnt());
+}
+
+////////////////////////////////////////////////////////////////////////////////
+static void TestClipCache(skiatest::Reporter* reporter, GrContext* context) {
+
+ test_cache(reporter, context);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+#include "TestClassDef.h"
+DEFINE_GPUTESTCLASS("ClipCache", ClipCacheTestClass, TestClipCache)