First pass at improving temporary tex/rt reuse
Review URL: http://codereview.appspot.com/4625043/




git-svn-id: http://skia.googlecode.com/svn/trunk@1616 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/gpu/include/GrContext.h b/gpu/include/GrContext.h
index 5dfe2c9..8d482e9 100644
--- a/gpu/include/GrContext.h
+++ b/gpu/include/GrContext.h
@@ -116,6 +116,16 @@
     GrTextureEntry* lockKeylessTexture(const GrTextureDesc& desc);
 
     /**
+     * Finds a texture that approximately matches the descriptor. Will be
+     * at least as large in width and height as desc specifies. If desc
+     * specifies that texture is a render target then result will be a
+     * render target. If desc specifies a render target and doesn't set the
+     * no stencil flag then result will have a stencil. Format and aa level
+     * will always match.
+     */
+    GrTextureEntry* findApproximateKeylessTexture(const GrTextureDesc& desc);
+
+    /**
      *  When done with an entry, call unlockTexture(entry) on it, which returns
      *  it to the cache, where it may be purged.
      */
diff --git a/gpu/include/GrTypes.h b/gpu/include/GrTypes.h
index 01b8e25..76e01b0 100644
--- a/gpu/include/GrTypes.h
+++ b/gpu/include/GrTypes.h
@@ -146,6 +146,11 @@
     return n ? (1 << (32 - Gr_clz(n - 1))) : 1;
 }
 
+static inline int GrNextPow2(int n) {
+    GrAssert(n >= 0); // this impl only works for non-neg.
+    return n ? (1 << (32 - Gr_clz(n - 1))) : 1;
+}
+
 ///////////////////////////////////////////////////////////////////////////////
 
 /**
diff --git a/gpu/src/GrContext.cpp b/gpu/src/GrContext.cpp
index ab5ac06..56ebb68 100644
--- a/gpu/src/GrContext.cpp
+++ b/gpu/src/GrContext.cpp
@@ -337,6 +337,75 @@
     return entry;
 }
 
+GrTextureEntry* GrContext::findApproximateKeylessTexture(
+                                                    const GrTextureDesc& inDesc) {
+    GrTextureDesc desc = inDesc;
+    // bin by pow2 with a reasonable min
+    static const int MIN_SIZE = 256;
+    desc.fWidth  = GrMax(MIN_SIZE, GrNextPow2(desc.fWidth));
+    desc.fHeight = GrMax(MIN_SIZE, GrNextPow2(desc.fHeight));
+
+    uint32_t p0 = desc.fFormat;
+    uint32_t p1 = (desc.fAALevel << 16) | desc.fFlags;
+    
+    GrTextureEntry* entry;
+    bool keepTrying = true;
+    int origWidth = desc.fWidth;
+    int origHeight = desc.fHeight;
+    bool doubledW = false;
+    bool doubledH = false;
+
+    do {
+        GrTextureKey key(p0, p1, desc.fWidth, desc.fHeight);
+        this->finalizeTextureKey(&key, GrSamplerState::ClampNoFilter(), true);
+        entry = fTextureCache->findAndLock(key);
+
+        // if we miss, relax the fit of the flags...
+        // then try doubling width... then height.
+        if (NULL != entry) {
+            break;
+        }
+        if (!(desc.fFlags & kRenderTarget_GrTextureFlagBit)) {
+            desc.fFlags = desc.fFlags | kRenderTarget_GrTextureFlagBit;
+        } else if (desc.fFlags & kNoStencil_GrTextureFlagBit) {
+            desc.fFlags = desc.fFlags & ~kNoStencil_GrTextureFlagBit;
+        } else if (!doubledW) {
+            desc.fFlags = inDesc.fFlags;
+            desc.fWidth *= 2;
+            doubledW = true;
+        } else if (!doubledH) {
+            desc.fFlags = inDesc.fFlags;
+            desc.fWidth = origWidth;
+            desc.fHeight *= 2;
+            doubledH = true;
+        } else {
+            break;
+        }
+        
+    } while (true);
+
+    if (NULL == entry) {
+        desc.fFlags = inDesc.fFlags;
+        desc.fWidth = origWidth;
+        desc.fHeight = origHeight;
+        GrTexture* texture = fGpu->createTexture(desc, NULL, 0);
+        if (NULL != texture) {
+            GrTextureKey key(p0, p1, desc.fWidth, desc.fHeight);
+            this->finalizeTextureKey(&key, GrSamplerState::ClampNoFilter(), 
+                                     true);
+            entry = fTextureCache->createAndLock(key, texture);
+        }
+    }
+
+    // If the caller gives us the same desc/sampler twice we don't want
+    // to return the same texture the second time (unless it was previously
+    // released). So we detach the entry from the cache and reattach at release.
+    if (NULL != entry) {
+        fTextureCache->detach(entry);
+    }
+    return entry;
+}
+
 void GrContext::unlockTexture(GrTextureEntry* entry) {
     if (kKeylessBit & entry->key().getPrivateBits()) {
         fTextureCache->reattachAndUnlock(entry);
@@ -466,7 +535,8 @@
         k4x4SinglePass_Downsample,
         kFSAA_Downsample
     }                              fDownsample;
-    int                            fTileSize;
+    int                            fTileSizeX;
+    int                            fTileSizeY;
     int                            fTileCountX;
     int                            fTileCountY;
     int                            fScale;
@@ -518,14 +588,11 @@
     int boundW = boundRect.width();
     int boundH = boundRect.height();
 
-    record->fTileSize  = (int)GrNextPow2(GrMax(boundW, boundH));
-    record->fTileSize = GrMax(64, record->fTileSize);
-    record->fTileSize = GrMin(fMaxOffscreenAASize, record->fTileSize);
-
-    record->fTileCountX = GrIDivRoundUp(boundW, record->fTileSize);
-    record->fTileCountY = GrIDivRoundUp(boundH, record->fTileSize);
-
     GrTextureDesc desc;
+
+    desc.fWidth  = GrMin(fMaxOffscreenAASize, boundW);
+    desc.fHeight = GrMin(fMaxOffscreenAASize, boundH);
+
     if (requireStencil) {
         desc.fFlags = kRenderTarget_GrTextureFlagBit;
     } else {
@@ -548,26 +615,41 @@
         GR_STATIC_ASSERT(4 == OFFSCREEN_SSAA_SCALE);
         desc.fAALevel = kNone_GrAALevel;
     }
-    
-    desc.fWidth = record->fScale * record->fTileSize;
-    desc.fHeight = record->fScale * record->fTileSize;
 
-    record->fEntry0 = this->lockKeylessTexture(desc);
+    desc.fWidth *= record->fScale;
+    desc.fHeight *= record->fScale;
 
+    record->fEntry0 = this->findApproximateKeylessTexture(desc);
     if (NULL == record->fEntry0) {
         return false;
     }
+    // the approximate lookup might have given us some slop space, might as well
+    // use it when computing the tiles size.
+    // these are scale values, will adjust after considering
+    // the possible second offscreen.
+    record->fTileSizeX = record->fEntry0->texture()->width();
+    record->fTileSizeY = record->fEntry0->texture()->height();
 
     if (OffscreenRecord::k4x4TwoPass_Downsample == record->fDownsample) {
         desc.fWidth /= 2;
         desc.fHeight /= 2;
-        record->fEntry1 = this->lockKeylessTexture(desc);
+        record->fEntry1 = this->findApproximateKeylessTexture(desc);
         if (NULL == record->fEntry1) {
             this->unlockTexture(record->fEntry0);
             record->fEntry0 = NULL;
             return false;
         }
+        record->fTileSizeX = GrMin(record->fTileSizeX, 
+                                   2 * record->fEntry0->texture()->width());
+        record->fTileSizeY = GrMin(record->fTileSizeY, 
+                                   2 * record->fEntry0->texture()->height());
     }
+    record->fTileSizeX /= record->fScale;
+    record->fTileSizeY /= record->fScale;
+
+    record->fTileCountX = GrIDivRoundUp(boundW, record->fTileSizeX);
+    record->fTileCountY = GrIDivRoundUp(boundH, record->fTileSizeY);
+
     target->saveCurrentDrawState(&record->fSavedState);
     return true;
 }
@@ -586,8 +668,8 @@
     target->setRenderTarget(offRT0);
 
     GrMatrix transM;
-    int left = boundRect.fLeft + tileX * record->fTileSize;
-    int top =  boundRect.fTop  + tileY * record->fTileSize;
+    int left = boundRect.fLeft + tileX * record->fTileSizeX;
+    int top =  boundRect.fTop  + tileY * record->fTileSizeY;
     transM.setTranslate(-left * GR_Scalar1, -top * GR_Scalar1);
     target->postConcatViewMatrix(transM);
     GrMatrix scaleM;
@@ -598,9 +680,9 @@
     target->disableState(GrDrawTarget::kClip_StateBit);
 
     int w = (tileX == record->fTileCountX-1) ? boundRect.fRight - left :
-                                               record->fTileSize;
+                                               record->fTileSizeX;
     int h = (tileY == record->fTileCountY-1) ? boundRect.fBottom - top :
-                                               record->fTileSize;
+                                               record->fTileSizeY;
     GrIRect clear = SkIRect::MakeWH(record->fScale * w, 
                                     record->fScale * h);
 #if 0
@@ -627,14 +709,14 @@
     GrAssert(NULL != record->fEntry0);
     
     GrIRect tileRect;
-    tileRect.fLeft = boundRect.fLeft + tileX * record->fTileSize;
-    tileRect.fTop  = boundRect.fTop  + tileY * record->fTileSize,
+    tileRect.fLeft = boundRect.fLeft + tileX * record->fTileSizeX;
+    tileRect.fTop  = boundRect.fTop  + tileY * record->fTileSizeY,
     tileRect.fRight = (tileX == record->fTileCountX-1) ? 
                         boundRect.fRight :
-                        tileRect.fLeft + record->fTileSize;
+                        tileRect.fLeft + record->fTileSizeX;
     tileRect.fBottom = (tileY == record->fTileCountY-1) ? 
                         boundRect.fBottom :
-                        tileRect.fTop + record->fTileSize;
+                        tileRect.fTop + record->fTileSizeY;
 
     GrSamplerState::Filter filter;
     if (OffscreenRecord::k4x4SinglePass_Downsample == record->fDownsample) {
diff --git a/gpu/src/GrPathUtils.cpp b/gpu/src/GrPathUtils.cpp
index 1fb043c..b93c8a9 100644
--- a/gpu/src/GrPathUtils.cpp
+++ b/gpu/src/GrPathUtils.cpp
@@ -19,7 +19,7 @@
 
 const GrScalar GrPathUtils::gTolerance = GR_Scalar1;
 
-static const uint32_t MAX_POINTS_PER_CURVE = 1 << 10;
+static const int MAX_POINTS_PER_CURVE = 1 << 10;
 
 uint32_t GrPathUtils::quadraticPointCount(const GrPoint points[],
                                              GrScalar tol) {
diff --git a/include/gpu/SkGpuDevice.h b/include/gpu/SkGpuDevice.h
index 601da09..96afa08 100644
--- a/include/gpu/SkGpuDevice.h
+++ b/include/gpu/SkGpuDevice.h
@@ -35,11 +35,13 @@
     /**
      * The SkGpuDevice will render to the GrRenderTarget, or if the paremeter is
      * null it will create its own render target and manage that target's
-     * lifetime.
+     * lifetime. Setting isSaveLayer to true is for internal use and may cause 
+     * problems when using the device's bitmap as a src if used externally.
      */
     SkGpuDevice(GrContext*,
                 const SkBitmap& bitmap,
-                GrRenderTarget* renderTargetOrNull);
+                GrRenderTarget* renderTargetOrNull,
+                bool isSaveLayer = false);
 
     /**
      * Magic value that can be passed to constructor. Causes
@@ -128,7 +130,8 @@
     TexCache* lockCachedTexture(const SkBitmap& bitmap,
                                 const GrSamplerState& sampler,
                                 GrTexture** texture,
-                                bool forDeviceRenderTarget = false);
+                                bool forDeviceRenderTarget = false,
+                                bool isSaveLayer = false);
     void unlockCachedTexture(TexCache*);
 
     class SkAutoCachedTexture {
diff --git a/src/gpu/SkGpuDevice.cpp b/src/gpu/SkGpuDevice.cpp
index d1cb5b9..114573a 100644
--- a/src/gpu/SkGpuDevice.cpp
+++ b/src/gpu/SkGpuDevice.cpp
@@ -106,7 +106,8 @@
 
 SkGpuDevice::SkGpuDevice(GrContext* context,
                          const SkBitmap& bitmap,
-                         GrRenderTarget* renderTargetOrNull)
+                         GrRenderTarget* renderTargetOrNull,
+                         bool isSaveLayer)
         : SkDevice(NULL, bitmap, (NULL == renderTargetOrNull)) {
 
     fNeedPrepareRenderTarget = false;
@@ -1087,7 +1088,8 @@
         return;
     }
 
-    SkASSERT(NULL != grPaint.getTexture(0));
+    GrTexture* devTex = grPaint.getTexture(0);
+    SkASSERT(NULL != devTex);
 
     const SkBitmap& bm = dev->accessBitmap(false);
     int w = bm.width();
@@ -1097,12 +1099,16 @@
 
     grPaint.getTextureSampler(kBitmapTextureIdx)->setClampNoFilter();
 
-    fContext->drawRectToRect(grPaint,
-                             GrRect::MakeXYWH(GrIntToScalar(x),
-                                              GrIntToScalar(y),
-                                              GrIntToScalar(w),
-                                              GrIntToScalar(h)),
-                             GrRect::MakeWH(GR_Scalar1, GR_Scalar1));
+    GrRect dstRect = GrRect::MakeXYWH(GrIntToScalar(x),
+                                      GrIntToScalar(y),
+                                      GrIntToScalar(w),
+                                      GrIntToScalar(h));
+    // The device being drawn may not fill up its texture (saveLayer uses
+    // the approximate ).
+    GrRect srcRect = GrRect::MakeWH(GR_Scalar1 * w / devTex->width(),
+                                    GR_Scalar1 * h / devTex->height());
+
+    fContext->drawRectToRect(grPaint, dstRect, srcRect);
 }
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -1342,7 +1348,8 @@
 SkGpuDevice::TexCache* SkGpuDevice::lockCachedTexture(const SkBitmap& bitmap,
                                                       const GrSamplerState& sampler,
                                                       GrTexture** texture,
-                                                      bool forDeviceRenderTarget) {
+                                                      bool forDeviceRenderTarget,
+                                                      bool isSaveLayer) {
     GrTexture* newTexture = NULL;
     GrTextureEntry* entry = NULL;
     GrContext* ctx = this->context();
@@ -1355,7 +1362,14 @@
             bitmap.height(),
             SkGr::Bitmap2PixelConfig(bitmap)
         };
-        entry = ctx->lockKeylessTexture(desc);
+        if (isSaveLayer) {
+            // we know layers will only be drawn through drawDevice.
+            // drawDevice has been made to work with content embedded in a
+            // larger texture so its okay to use the approximate version.
+            entry = ctx->findApproximateKeylessTexture(desc);
+        } else {
+            entry = ctx->lockKeylessTexture(desc);
+        }
     } else {
         uint32_t p0, p1;
         p0 = bitmap.getGenerationID();