diff options
-rw-r--r-- | gyp/tests.gyp | 1 | ||||
-rw-r--r-- | src/gpu/GrClipMaskManager.cpp | 2 | ||||
-rw-r--r-- | src/gpu/GrClipMaskManager.h | 222 | ||||
-rw-r--r-- | src/gpu/GrSoftwarePathRenderer.cpp | 1 | ||||
-rw-r--r-- | tests/ClipCacheTest.cpp | 152 |
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) |