Use a single stencil buffer for a given width,height,samplecount

Review URL: http://codereview.appspot.com/4854044/


git-svn-id: http://skia.googlecode.com/svn/trunk@2061 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/gpu/src/GrContext.cpp b/gpu/src/GrContext.cpp
index bfae62e..54b2954 100644
--- a/gpu/src/GrContext.cpp
+++ b/gpu/src/GrContext.cpp
@@ -16,6 +16,7 @@
 #include "GrPathRenderer.h"
 #include "GrPathUtils.h"
 #include "GrResourceCache.h"
+#include "GrStencilBuffer.h"
 #include "GrTextStrike.h"
 #include "SkTrace.h"
 
@@ -126,9 +127,14 @@
 ////////////////////////////////////////////////////////////////////////////////
 
 enum {
-    kNPOTBit    = 0x1,
-    kFilterBit  = 0x2,
-    kScratchBit = 0x4,
+    // flags for textures
+    kNPOTBit            = 0x1,
+    kFilterBit          = 0x2,
+    kScratchBit         = 0x4,
+
+    // resource type
+    kTextureBit         = 0x8,
+    kStencilBufferBit   = 0x10
 };
 
 GrTexture* GrContext::TextureCacheEntry::texture() const {
@@ -176,8 +182,26 @@
         v[3] |= kScratchBit;
     }
 
+    v[3] |= kTextureBit;
+
     return v[3] & kNPOTBit;
 }
+
+// we should never have more than one stencil buffer with same combo of
+// (width,height,samplecount)
+void gen_stencil_key_values(int width, int height,
+                            int sampleCnt, uint32_t v[4]) {
+    v[0] = width;
+    v[1] = height;
+    v[2] = sampleCnt;
+    v[3] = kStencilBufferBit;
+}
+
+void gen_stencil_key_values(const GrStencilBuffer* sb,
+                            uint32_t v[4]) {
+    gen_stencil_key_values(sb->width(), sb->height(),
+                           sb->numSamples(), v);
+}
 }
 
 GrContext::TextureCacheEntry GrContext::findAndLockTexture(TextureKey key,
@@ -187,7 +211,34 @@
     uint32_t v[4];
     gen_texture_key_values(fGpu, sampler, key, width, height, false, v);
     GrResourceKey resourceKey(v);
-    return TextureCacheEntry(fTextureCache->findAndLock(resourceKey));
+    return TextureCacheEntry(fTextureCache->findAndLock(resourceKey,
+                                            GrResourceCache::kNested_LockType));
+}
+
+GrResourceEntry* GrContext::addAndLockStencilBuffer(GrStencilBuffer* sb) {
+    uint32_t v[4];
+    gen_stencil_key_values(sb, v);
+    GrResourceKey resourceKey(v);
+    return fTextureCache->createAndLock(resourceKey, sb);
+}
+
+GrStencilBuffer* GrContext::findStencilBuffer(int width, int height,
+                                              int sampleCnt) {
+    uint32_t v[4];
+    gen_stencil_key_values(width, height, sampleCnt, v);
+    GrResourceKey resourceKey(v);
+    GrResourceEntry* entry = fTextureCache->findAndLock(resourceKey,
+                                            GrResourceCache::kSingle_LockType);
+    if (NULL != entry) {
+        GrStencilBuffer* sb = (GrStencilBuffer*) entry->resource();
+        return sb;
+    } else {
+        return NULL;
+    }
+}
+
+void GrContext::unlockStencilBuffer(GrResourceEntry* sbEntry) {
+    fTextureCache->unlock(sbEntry);
 }
 
 static void stretchImage(void* dst,
@@ -376,7 +427,8 @@
         uint32_t v[4];
         gen_scratch_tex_key_values(fGpu, desc, v);
         GrResourceKey key(v);
-        entry = fTextureCache->findAndLock(key);
+        entry = fTextureCache->findAndLock(key,
+                                           GrResourceCache::kNested_LockType);
         // if we miss, relax the fit of the flags...
         // then try doubling width... then height.
         if (NULL != entry || kExact_ScratchTexMatch == match) {
diff --git a/gpu/src/GrGLRenderTarget.cpp b/gpu/src/GrGLRenderTarget.cpp
index 39aa332..ccfc55a 100644
--- a/gpu/src/GrGLRenderTarget.cpp
+++ b/gpu/src/GrGLRenderTarget.cpp
@@ -78,7 +78,7 @@
     fMSColorRenderbufferID  = 0;
     GrSafeUnref(fTexIDObj);
     fTexIDObj = NULL;
-    GrSafeSetNull(fStencilBuffer);
+    this->setStencilBuffer(NULL);
 }
 
 void GrGLRenderTarget::onAbandon() {
@@ -89,6 +89,6 @@
         fTexIDObj->abandon();
         fTexIDObj = NULL;
     }
-    GrSafeSetNull(fStencilBuffer);
+    this->setStencilBuffer(NULL);
 }
 
diff --git a/gpu/src/GrGLStencilBuffer.h b/gpu/src/GrGLStencilBuffer.h
index 97394e3..f55f518 100644
--- a/gpu/src/GrGLStencilBuffer.h
+++ b/gpu/src/GrGLStencilBuffer.h
@@ -25,8 +25,9 @@
 
     GrGLStencilBuffer(GrGpu* gpu, GrGLint rbid, 
                       int width, int height,
+                      int sampleCnt,
                       const Format& format) 
-        : GrStencilBuffer(gpu, width, height, format.fStencilBits)
+        : GrStencilBuffer(gpu, width, height, format.fStencilBits, sampleCnt)
         , fFormat(format)
         , fRenderbufferID(rbid) {
     }
@@ -36,7 +37,10 @@
     }
 
     virtual size_t sizeInBytes() const {
-        return this->width() * this->height() * fFormat.fTotalBits;
+        return (size_t) this->width() *
+                        this->height() *
+                        fFormat.fTotalBits *
+                        GrMax(1,this->numSamples());
     }
 
     GrGLuint renderbufferID() const {
diff --git a/gpu/src/GrGpu.cpp b/gpu/src/GrGpu.cpp
index ad297f4..53b5c03 100644
--- a/gpu/src/GrGpu.cpp
+++ b/gpu/src/GrGpu.cpp
@@ -8,8 +8,10 @@
 
 
 #include "GrGpu.h"
+
 #include "GrBufferAllocPool.h"
 #include "GrClipIterator.h"
+#include "GrContext.h"
 #include "GrIndexBuffer.h"
 #include "GrPathRenderer.h"
 #include "GrGLStencilBuffer.h"
@@ -59,7 +61,7 @@
 }
 
 GrGpu::~GrGpu() {
-    releaseResources();
+    this->releaseResources();
 }
 
 void GrGpu::abandonResources() {
@@ -155,10 +157,24 @@
 }
 
 bool GrGpu::attachStencilBufferToRenderTarget(GrRenderTarget* rt) {
-    // TODO: use a cache of stencil buffers rather than create per-rt.
-    bool ret = this->createStencilBufferForRenderTarget(rt, rt->allocatedWidth(),
-                                                        rt->allocatedHeight());
-    if (ret) {
+    GrAssert(NULL == rt->getStencilBuffer());
+    GrStencilBuffer* sb = 
+        this->getContext()->findStencilBuffer(rt->allocatedWidth(),
+                                              rt->allocatedHeight(),
+                                              rt->numSamples());
+    if (NULL != sb) {
+        rt->setStencilBuffer(sb);
+        bool attached = this->attachStencilBufferToRenderTarget(sb, rt);
+        if (!attached) {
+            rt->setStencilBuffer(NULL);
+        }
+        return attached;
+    }
+    if (this->createStencilBufferForRenderTarget(rt, rt->allocatedWidth(),
+                                                 rt->allocatedHeight())) {
+        rt->getStencilBuffer()->ref();
+        rt->getStencilBuffer()->transferToCacheAndLock();
+
         // Right now we're clearing the stencil buffer here after it is
         // attached to an RT for the first time. When we start matching
         // stencil buffers with smaller color targets this will no longer
@@ -171,8 +187,10 @@
         fCurrDrawState.fRenderTarget = rt;
         this->clearStencil();
         fCurrDrawState.fRenderTarget = oldRT;
+        return true;
+    } else {
+        return false;
     }
-    return ret;
 }
 
 GrRenderTarget* GrGpu::createRenderTargetFrom3DApiState() {
diff --git a/gpu/src/GrGpuGL.cpp b/gpu/src/GrGpuGL.cpp
index 7162f35..8a5669c 100644
--- a/gpu/src/GrGpuGL.cpp
+++ b/gpu/src/GrGpuGL.cpp
@@ -602,6 +602,7 @@
 
     if (isRenderTarget) {
         rtDesc.fRTFBOID = desc.fPlatformRenderTarget;
+        rtDesc.fConfig = desc.fConfig;
 #if GR_USE_PLATFORM_CREATE_SAMPLE_COUNT
         if (desc.fSampleCnt) {
 #else
@@ -638,7 +639,7 @@
             format.fStencilBits = desc.fStencilBits;
             format.fTotalBits = desc.fStencilBits;
             sb = new GrGLStencilBuffer(this, 0, desc.fWidth,
-                                       desc.fHeight, format);
+                                       desc.fHeight, rtDesc.fSampleCnt, format);
         }
         rtDesc.fOwnIDs = false;
     }
@@ -836,6 +837,7 @@
     GrGLIRect viewport;
     viewport.setFromGLViewport();
     int stencilBits = get_fbo_stencil_bits(arbFBO);
+    GR_GL_GetIntegerv(GR_GL_SAMPLES, &rtDesc.fSampleCnt);
 
     GrGLStencilBuffer* sb = NULL;
     if (stencilBits) {
@@ -846,10 +848,10 @@
         format.fStencilBits = stencilBits;
         format.fTotalBits = stencilBits;
         sb = new GrGLStencilBuffer(this, 0, viewport.fWidth,
-                                   viewport.fHeight, format);
+                                   viewport.fHeight, rtDesc.fSampleCnt,
+                                   format);
     }
 
-    GR_GL_GetIntegerv(GR_GL_SAMPLES, &rtDesc.fSampleCnt);
     GrGLenum fmat = get_fbo_color_format();
     if (kUnknownGLFormat == fmat) {
         rtDesc.fConfig = get_implied_color_config(arbFBO);
@@ -1326,7 +1328,7 @@
         // that we won't go through this loop more than once after the
         // first (painful) stencil creation.
         int sIdx = (i + fLastSuccessfulStencilFmtIdx) % stencilFmtCnt;
-        // we do this if so that we don't call the multisample
+        // we do this "if" so that we don't call the multisample
         // version on a GL that doesn't have an MSAA extension.
         if (samples > 1) {
             GR_GL_NO_ERR(RenderbufferStorageMultisample(
@@ -1347,9 +1349,11 @@
             // sizes GL gives us. In that case we query for the size.
             GrGLStencilBuffer::Format format = fStencilFormats[sIdx];
             get_stencil_rb_sizes(sbID, &format);
-            sb = new GrGLStencilBuffer(this, sbID, width, height, format);
+            sb = new GrGLStencilBuffer(this, sbID, width, height, 
+                                       samples, format);
             if (this->attachStencilBufferToRenderTarget(sb, rt)) {
                 fLastSuccessfulStencilFmtIdx = sIdx;
+                rt->setStencilBuffer(sb);
                 sb->unref();
                 return true;
            }
@@ -1358,7 +1362,7 @@
         }
     }
     GR_GL(DeleteRenderbuffers(1, &sbID));
-    return NULL;
+    return false;
 }
 
 bool GrGpuGL::attachStencilBufferToRenderTarget(GrStencilBuffer* sb,
@@ -1414,7 +1418,6 @@
             }
             return false;
         } else {
-            rt->setStencilBuffer(sb);
             return true;
         }
     }
diff --git a/gpu/src/GrRenderTarget.cpp b/gpu/src/GrRenderTarget.cpp
index 8a73a84..7b26811 100644
--- a/gpu/src/GrRenderTarget.cpp
+++ b/gpu/src/GrRenderTarget.cpp
@@ -31,7 +31,10 @@
     } else {
         colorBits = GrBytesPerPixel(fConfig);
     }
-    return fAllocatedWidth * fAllocatedHeight * colorBits * GrMax(1,fSampleCnt);
+    return (size_t) fAllocatedWidth *
+                    fAllocatedHeight *
+                    colorBits *
+                    GrMax(1,fSampleCnt);
 }
 
 void GrRenderTarget::flagAsNeedingResolve(const GrIRect* rect) {
@@ -59,5 +62,13 @@
 }
 
 void GrRenderTarget::setStencilBuffer(GrStencilBuffer* stencilBuffer) {
-    GrSafeAssign(fStencilBuffer, stencilBuffer);
+    if (NULL != fStencilBuffer) {
+        fStencilBuffer->wasDetachedFromRenderTarget(this);
+        fStencilBuffer->unref();
+    }
+    fStencilBuffer = stencilBuffer;
+    if (NULL != fStencilBuffer) {
+        fStencilBuffer->wasAttachedToRenderTarget(this);
+        fStencilBuffer->ref();
+    }
 }
\ No newline at end of file
diff --git a/gpu/src/GrResourceCache.cpp b/gpu/src/GrResourceCache.cpp
index 2f5dfaf..97cbea8 100644
--- a/gpu/src/GrResourceCache.cpp
+++ b/gpu/src/GrResourceCache.cpp
@@ -155,7 +155,8 @@
 #endif
 };
 
-GrResourceEntry* GrResourceCache::findAndLock(const GrResourceKey& key) {
+GrResourceEntry* GrResourceCache::findAndLock(const GrResourceKey& key,
+                                              LockType type) {
     GrAutoResourceCacheValidate atcv(this);
 
     GrResourceEntry* entry = fCache.find(key);
@@ -163,7 +164,9 @@
         this->internalDetach(entry, false);
         // mark the entry as "busy" so it doesn't get purged
         // do this between detach and attach for locked count tracking
-        entry->lock();
+        if (kNested_LockType == type || !entry->isLocked()) {
+            entry->lock();
+        }
         this->attachToHead(entry, false);
     }
     return entry;
diff --git a/gpu/src/GrResourceCache.h b/gpu/src/GrResourceCache.h
index e431f1c..d3a8f03 100644
--- a/gpu/src/GrResourceCache.h
+++ b/gpu/src/GrResourceCache.h
@@ -209,10 +209,18 @@
     void setLimits(int maxResource, size_t maxResourceBytes);
 
     /**
+     * Controls whether locks should be nestable or not.
+     */
+    enum LockType {
+        kNested_LockType,
+        kSingle_LockType,
+    };
+
+    /**
      *  Search for an entry with the same Key. If found, "lock" it and return it.
      *  If not found, return null.
      */
-    GrResourceEntry* findAndLock(const GrResourceKey&);
+    GrResourceEntry* findAndLock(const GrResourceKey&, LockType style);
 
     /**
      *  Create a new entry, based on the specified key and resource, and return
diff --git a/gpu/src/GrStencilBuffer.cpp b/gpu/src/GrStencilBuffer.cpp
new file mode 100644
index 0000000..d004612
--- /dev/null
+++ b/gpu/src/GrStencilBuffer.cpp
@@ -0,0 +1,26 @@
+
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "GrStencilBuffer.h"
+
+#include "GrContext.h"
+#include "GrGpu.h"
+
+void GrStencilBuffer::wasDetachedFromRenderTarget(const GrRenderTarget* rt) {
+    GrAssert(fRTAttachmentCnt > 0);
+    if (0 == --fRTAttachmentCnt && NULL != fCacheEntry) {
+        this->getGpu()->getContext()->unlockStencilBuffer(fCacheEntry);
+        // At this point we could be deleted!
+    }
+}
+
+void GrStencilBuffer::transferToCacheAndLock() {
+    GrAssert(NULL == fCacheEntry);
+    fCacheEntry = 
+        this->getGpu()->getContext()->addAndLockStencilBuffer(this);
+}
diff --git a/gpu/src/GrStencilBuffer.h b/gpu/src/GrStencilBuffer.h
index 34fbe11..af96a20 100644
--- a/gpu/src/GrStencilBuffer.h
+++ b/gpu/src/GrStencilBuffer.h
@@ -13,11 +13,24 @@
 #include "GrClip.h"
 #include "GrResource.h"
 
+// REMOVE ME
+#include "GrRenderTarget.h"
+
+class GrRenderTarget;
+class GrResourceEntry;
+
 class GrStencilBuffer : public GrResource {
 public:
+    virtual ~GrStencilBuffer() {
+        // currently each rt that has attached this sb keeps a ref
+        // TODO: allow SB to be purged and detach itself from rts
+        GrAssert(0 == fRTAttachmentCnt);
+    }
+
     int width() const { return fWidth; }
     int height() const { return fHeight; }
     int bits() const { return fBits; }
+    int numSamples() const { return fSampleCnt; }
 
     // called to note the last clip drawn to this buffer.
     void setLastClip(const GrClip& clip, int width, int height) {
@@ -43,26 +56,43 @@
         return fLastClip;
     }
 
+    // places the sb in the cache and locks it. Caller transfers
+    // a ref to the the cache which will unref when purged.
+    void transferToCacheAndLock();
+
+    void wasAttachedToRenderTarget(const GrRenderTarget* rt) {
+        ++fRTAttachmentCnt;
+    }
+
+    void wasDetachedFromRenderTarget(const GrRenderTarget* rt);
+
 protected:
-    GrStencilBuffer(GrGpu* gpu, int width, int height, int bits)
+    GrStencilBuffer(GrGpu* gpu, int width, int height, int bits, int sampleCnt)
         : GrResource(gpu)
         , fWidth(width)
         , fHeight(height)
         , fBits(bits)
+        , fSampleCnt(sampleCnt)
         , fLastClip()
         , fLastClipWidth(-1)
-        , fLastClipHeight(-1) {
+        , fLastClipHeight(-1)
+        , fCacheEntry(NULL)
+        , fRTAttachmentCnt(0) {
     }
 
 private:
     int fWidth;
     int fHeight;
     int fBits;
+    int fSampleCnt;
 
     GrClip     fLastClip;
     int        fLastClipWidth;
     int        fLastClipHeight;
 
+    GrResourceEntry* fCacheEntry;
+    int              fRTAttachmentCnt;
+
     typedef GrResource INHERITED;
 };