Alter approximate scratch texture binning after 1024

As the powers-of-2 get larger the coarse binning can burn a lot of VRAM.

Granted it isn't the best metric but, with this CL, the number of textures created and scratch textures reused remains unchanged when running the GMs.

Change-Id: I84abbbae0ed01aabb387671b5ee0e4fcdb82b671
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/226226
Commit-Queue: Robert Phillips <robertphillips@google.com>
Reviewed-by: Brian Salomon <bsalomon@google.com>
diff --git a/src/core/SkMathPriv.h b/src/core/SkMathPriv.h
index fb2389f..d0ce3b2 100644
--- a/src/core/SkMathPriv.h
+++ b/src/core/SkMathPriv.h
@@ -215,7 +215,7 @@
 ///////////////////////////////////////////////////////////////////////////////
 
 /**
- *  Return the next power of 2 >= n.
+ *  Return the smallest power-of-2 >= n.
  */
 static inline uint32_t GrNextPow2(uint32_t n) {
     return n ? (1 << (32 - SkCLZ(n - 1))) : 1;
diff --git a/src/gpu/GrGpu.cpp b/src/gpu/GrGpu.cpp
index 2e23380..79cfbf4 100644
--- a/src/gpu/GrGpu.cpp
+++ b/src/gpu/GrGpu.cpp
@@ -569,6 +569,7 @@
     out->appendf("Transfers from Surface: %d\n", fTransfersFromSurface);
     out->appendf("Stencil Buffer Creates: %d\n", fStencilAttachmentCreates);
     out->appendf("Number of draws: %d\n", fNumDraws);
+    out->appendf("Number of Scratch Textures reused %d\n", fNumScratchTexturesReused);
 }
 
 void GrGpu::Stats::dumpKeyValuePairs(SkTArray<SkString>* keys, SkTArray<double>* values) {
diff --git a/src/gpu/GrGpu.h b/src/gpu/GrGpu.h
index cf5bbe2..fa64036 100644
--- a/src/gpu/GrGpu.h
+++ b/src/gpu/GrGpu.h
@@ -340,27 +340,41 @@
 
         int renderTargetBinds() const { return fRenderTargetBinds; }
         void incRenderTargetBinds() { fRenderTargetBinds++; }
+
         int shaderCompilations() const { return fShaderCompilations; }
         void incShaderCompilations() { fShaderCompilations++; }
+
         int textureCreates() const { return fTextureCreates; }
         void incTextureCreates() { fTextureCreates++; }
+
         int textureUploads() const { return fTextureUploads; }
         void incTextureUploads() { fTextureUploads++; }
+
         int transfersToTexture() const { return fTransfersToTexture; }
-        int transfersFromSurface() const { return fTransfersFromSurface; }
         void incTransfersToTexture() { fTransfersToTexture++; }
+
+        int transfersFromSurface() const { return fTransfersFromSurface; }
         void incTransfersFromSurface() { fTransfersFromSurface++; }
+
+        int stencilAttachmentCreates() const { return fStencilAttachmentCreates; }
         void incStencilAttachmentCreates() { fStencilAttachmentCreates++; }
+
+        int numDraws() const { return fNumDraws; }
         void incNumDraws() { fNumDraws++; }
+
+        int numFailedDraws() const { return fNumFailedDraws; }
         void incNumFailedDraws() { ++fNumFailedDraws; }
+
+        int numFinishFlushes() const { return fNumFinishFlushes; }
         void incNumFinishFlushes() { ++fNumFinishFlushes; }
+
+        int numScratchTexturesReused() const { return fNumScratchTexturesReused; }
+        void incNumScratchTexturesReused() { ++fNumScratchTexturesReused; }
+
 #if GR_TEST_UTILS
         void dump(SkString*);
         void dumpKeyValuePairs(SkTArray<SkString>* keys, SkTArray<double>* values);
 #endif
-        int numDraws() const { return fNumDraws; }
-        int numFailedDraws() const { return fNumFailedDraws; }
-        int numFinishFlushes() const { return fNumFinishFlushes; }
     private:
         int fRenderTargetBinds = 0;
         int fShaderCompilations = 0;
@@ -372,6 +386,7 @@
         int fNumDraws = 0;
         int fNumFailedDraws = 0;
         int fNumFinishFlushes = 0;
+        int fNumScratchTexturesReused = 0;
 #else
 
 #if GR_TEST_UTILS
diff --git a/src/gpu/GrResourceProvider.cpp b/src/gpu/GrResourceProvider.cpp
index 0e171a8..db9ead2 100644
--- a/src/gpu/GrResourceProvider.cpp
+++ b/src/gpu/GrResourceProvider.cpp
@@ -209,6 +209,32 @@
     return fGpu->createTexture(desc, budgeted);
 }
 
+// Map 'value' to a larger multiple of 2. Values <= 'kMagicTol' will pop up to
+// the next power of 2. Those above 'kMagicTol' will only go up half the floor power of 2.
+uint32_t GrResourceProvider::MakeApprox(uint32_t value) {
+    static const int kMagicTol = 1024;
+
+    value = SkTMax(kMinScratchTextureSize, value);
+
+    if (SkIsPow2(value)) {
+        return value;
+    }
+
+    uint32_t ceilPow2 = GrNextPow2(value);
+    if (value <= kMagicTol) {
+        return ceilPow2;
+    }
+
+    uint32_t floorPow2 = ceilPow2 >> 1;
+    uint32_t mid = floorPow2 + (floorPow2 >> 1);
+
+    if (value <= mid) {
+        return mid;
+    }
+
+    return ceilPow2;
+}
+
 sk_sp<GrTexture> GrResourceProvider::createApproxTexture(const GrSurfaceDesc& desc,
                                                          Flags flags) {
     ASSERT_SINGLE_OWNER
@@ -233,12 +259,12 @@
 
     SkTCopyOnFirstWrite<GrSurfaceDesc> copyDesc(desc);
 
-    // bin by pow2 with a reasonable min
+    // bin by some multiple or power of 2 with a reasonable min
     if (!SkToBool(desc.fFlags & kPerformInitialClear_GrSurfaceFlag) &&
         (fGpu->caps()->reuseScratchTextures() || (desc.fFlags & kRenderTarget_GrSurfaceFlag))) {
         GrSurfaceDesc* wdesc = copyDesc.writable();
-        wdesc->fWidth  = SkTMax(kMinScratchTextureSize, GrNextPow2(desc.fWidth));
-        wdesc->fHeight = SkTMax(kMinScratchTextureSize, GrNextPow2(desc.fHeight));
+        wdesc->fWidth = MakeApprox(wdesc->fWidth);
+        wdesc->fHeight = MakeApprox(wdesc->fHeight);
     }
 
     if (auto tex = this->refScratchTexture(*copyDesc, flags)) {
@@ -273,6 +299,7 @@
                                                                     GrSurface::WorstCaseSize(desc),
                                                                     scratchFlags);
         if (resource) {
+            fGpu->stats()->incNumScratchTexturesReused();
             GrSurface* surface = static_cast<GrSurface*>(resource);
             return sk_sp<GrTexture>(surface->asTexture());
         }
diff --git a/src/gpu/GrResourceProvider.h b/src/gpu/GrResourceProvider.h
index 46b4710..1ac19ec 100644
--- a/src/gpu/GrResourceProvider.h
+++ b/src/gpu/GrResourceProvider.h
@@ -253,6 +253,8 @@
     const GrCaps* caps() const { return fCaps.get(); }
     bool overBudget() const { return fCache->overBudget(); }
 
+    static uint32_t MakeApprox(uint32_t value);
+
     inline GrResourceProviderPriv priv();
     inline const GrResourceProviderPriv priv() const;
 
diff --git a/src/gpu/GrSurface.cpp b/src/gpu/GrSurface.cpp
index d29d50d..87d9e9c 100644
--- a/src/gpu/GrSurface.cpp
+++ b/src/gpu/GrSurface.cpp
@@ -16,15 +16,11 @@
 #include "src/core/SkMathPriv.h"
 #include "src/gpu/SkGr.h"
 
-size_t GrSurface::WorstCaseSize(const GrSurfaceDesc& desc, bool useNextPow2) {
+size_t GrSurface::WorstCaseSize(const GrSurfaceDesc& desc, bool binSize) {
     size_t size;
 
-    int width = useNextPow2
-                ? SkTMax(GrResourceProvider::kMinScratchTextureSize, GrNextPow2(desc.fWidth))
-                : desc.fWidth;
-    int height = useNextPow2
-                ? SkTMax(GrResourceProvider::kMinScratchTextureSize, GrNextPow2(desc.fHeight))
-                : desc.fHeight;
+    int width  = binSize ? GrResourceProvider::MakeApprox(desc.fWidth)  : desc.fWidth;
+    int height = binSize ? GrResourceProvider::MakeApprox(desc.fHeight) : desc.fHeight;
 
     bool isRenderTarget = SkToBool(desc.fFlags & kRenderTarget_GrSurfaceFlag);
     if (isRenderTarget) {
@@ -63,15 +59,11 @@
                               int height,
                               int colorSamplesPerPixel,
                               GrMipMapped mipMapped,
-                              bool useNextPow2) {
+                              bool binSize) {
     size_t colorSize;
 
-    width = useNextPow2
-            ? SkTMax(GrResourceProvider::kMinScratchTextureSize, GrNextPow2(width))
-            : width;
-    height = useNextPow2
-            ? SkTMax(GrResourceProvider::kMinScratchTextureSize, GrNextPow2(height))
-            : height;
+    width  = binSize ? GrResourceProvider::MakeApprox(width)  : width;
+    height = binSize ? GrResourceProvider::MakeApprox(height) : height;
 
     SkASSERT(kUnknown_GrPixelConfig != config);
     if (GrPixelConfigIsCompressed(config)) {
diff --git a/src/gpu/GrSurfaceProxy.cpp b/src/gpu/GrSurfaceProxy.cpp
index 903a74e..9bcfa7c 100644
--- a/src/gpu/GrSurfaceProxy.cpp
+++ b/src/gpu/GrSurfaceProxy.cpp
@@ -316,7 +316,7 @@
     if (SkBackingFit::kExact == fFit) {
         return fWidth;
     }
-    return SkTMax(GrResourceProvider::kMinScratchTextureSize, GrNextPow2(fWidth));
+    return GrResourceProvider::MakeApprox(fWidth);
 }
 
 int GrSurfaceProxy::worstCaseHeight() const {
@@ -328,7 +328,7 @@
     if (SkBackingFit::kExact == fFit) {
         return fHeight;
     }
-    return SkTMax(GrResourceProvider::kMinScratchTextureSize, GrNextPow2(fHeight));
+    return GrResourceProvider::MakeApprox(fHeight);
 }
 
 #ifdef SK_DEBUG
diff --git a/src/gpu/GrTexture.cpp b/src/gpu/GrTexture.cpp
index a3761aa..34a2bd4 100644
--- a/src/gpu/GrTexture.cpp
+++ b/src/gpu/GrTexture.cpp
@@ -32,7 +32,7 @@
 
 size_t GrTexture::onGpuMemorySize() const {
     return GrSurface::ComputeSize(this->config(), this->width(), this->height(), 1,
-                                  this->texturePriv().mipMapped(), false);
+                                  this->texturePriv().mipMapped());
 }
 
 /////////////////////////////////////////////////////////////////////////////
diff --git a/src/gpu/mtl/GrMtlTextureRenderTarget.h b/src/gpu/mtl/GrMtlTextureRenderTarget.h
index a358909..68f716f 100644
--- a/src/gpu/mtl/GrMtlTextureRenderTarget.h
+++ b/src/gpu/mtl/GrMtlTextureRenderTarget.h
@@ -73,7 +73,7 @@
             ++numColorSamples;
         }
         return GrSurface::ComputeSize(this->config(), this->width(), this->height(),
-                                      numColorSamples, GrMipMapped::kNo, false);
+                                      numColorSamples, GrMipMapped::kNo);
     }
 };