/* * Copyright 2012 Google Inc. * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #ifndef GrClipMaskCache_DEFINED #define GrClipMaskCache_DEFINED #include "GrContext.h" #include "SkClipStack.h" #include "SkTypes.h" class GrTexture; /** * The stencil buffer stores the last clip path - providing a single entry * "cache". This class provides similar functionality for AA clip paths */ class GrClipMaskCache : SkNoncopyable { public: GrClipMaskCache(); ~GrClipMaskCache() { while (!fStack.empty()) { GrClipStackFrame* temp = (GrClipStackFrame*) fStack.back(); temp->~GrClipStackFrame(); fStack.pop_back(); } } bool canReuse(int32_t clipGenID, const SkIRect& bounds) { SkASSERT(clipGenID != SkClipStack::kWideOpenGenID); SkASSERT(clipGenID != SkClipStack::kEmptyGenID); GrClipStackFrame* back = (GrClipStackFrame*) fStack.back(); // We could reuse the mask if bounds is a subset of last bounds. We'd have to communicate // an offset to the caller. if (back->fLastMask && !back->fLastMask->wasDestroyed() && back->fLastBound == bounds && back->fLastClipGenID == clipGenID) { return true; } return false; } void reset() { if (fStack.empty()) { // SkASSERT(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(); void pop() { //SkASSERT(!fStack.empty()); if (!fStack.empty()) { GrClipStackFrame* back = (GrClipStackFrame*) fStack.back(); back->~GrClipStackFrame(); fStack.pop_back(); } } int32_t getLastClipGenID() const { if (fStack.empty()) { return SkClipStack::kInvalidGenID; } return ((GrClipStackFrame*) fStack.back())->fLastClipGenID; } GrTexture* getLastMask() { if (fStack.empty()) { SkASSERT(false); return NULL; } GrClipStackFrame* back = (GrClipStackFrame*) fStack.back(); return back->fLastMask; } const GrTexture* getLastMask() const { if (fStack.empty()) { SkASSERT(false); return NULL; } GrClipStackFrame* back = (GrClipStackFrame*) fStack.back(); return back->fLastMask; } void acquireMask(int32_t clipGenID, const GrTextureDesc& desc, const SkIRect& bound) { if (fStack.empty()) { SkASSERT(false); return; } GrClipStackFrame* back = (GrClipStackFrame*) fStack.back(); back->acquireMask(fContext, clipGenID, desc, bound); } int getLastMaskWidth() const { if (fStack.empty()) { SkASSERT(false); return -1; } GrClipStackFrame* back = (GrClipStackFrame*) fStack.back(); if (NULL == back->fLastMask) { return -1; } return back->fLastMask->width(); } int getLastMaskHeight() const { if (fStack.empty()) { SkASSERT(false); return -1; } GrClipStackFrame* back = (GrClipStackFrame*) fStack.back(); if (NULL == back->fLastMask) { return -1; } return back->fLastMask->height(); } void getLastBound(SkIRect* bound) const { if (fStack.empty()) { SkASSERT(false); bound->setEmpty(); return; } GrClipStackFrame* back = (GrClipStackFrame*) fStack.back(); *bound = back->fLastBound; } void setContext(GrContext* context) { fContext = context; } GrContext* getContext() { return fContext; } // TODO: Remove this when we hold cache keys instead of refs to textures. void purgeResources() { SkDeque::F2BIter iter(fStack); for (GrClipStackFrame* frame = (GrClipStackFrame*) iter.next(); frame != NULL; frame = (GrClipStackFrame*) iter.next()) { frame->reset(); } } private: struct GrClipStackFrame { GrClipStackFrame() { this->reset(); } void acquireMask(GrContext* context, int32_t clipGenID, const GrTextureDesc& desc, const SkIRect& bound) { fLastClipGenID = clipGenID; // HACK: set the last param to true to indicate that this request is at // flush time and therefore we require a scratch texture with no pending IO operations. fLastMask.reset(context->refScratchTexture(desc, GrContext::kApprox_ScratchTexMatch, /*flushing=*/true)); fLastBound = bound; } void reset () { fLastClipGenID = SkClipStack::kInvalidGenID; GrTextureDesc desc; fLastMask.reset(NULL); fLastBound.setEmpty(); } int32_t fLastClipGenID; // The mask's width & height values are used by GrClipMaskManager to correctly scale the // texture coords for the geometry drawn with this mask. TODO: This should be a cache key // and not a hard ref to a texture. SkAutoTUnref fLastMask; // fLastBound stores the bounding box of the clip mask in clip-stack space. This rect is // used by GrClipMaskManager to position a rect and compute texture coords for the mask. SkIRect fLastBound; }; GrContext* fContext; SkDeque fStack; typedef SkNoncopyable INHERITED; }; #endif // GrClipMaskCache_DEFINED