ccpr: Initialize the atlas size more intelligently

Rather than always starting the atlas at 1024 x 1024, begin with the
first pow2 dimensions whose area is theoretically large enough to
contain the pending paths.

Bug: skia:
Change-Id: I263e77ff6a697e865f6b3b62b9df7002225f9544
Reviewed-on: https://skia-review.googlesource.com/133660
Commit-Queue: Chris Dalton <csmartdalton@google.com>
Reviewed-by: Brian Salomon <bsalomon@google.com>
Reviewed-by: Robert Phillips <robertphillips@google.com>
diff --git a/src/gpu/ccpr/GrCCAtlas.cpp b/src/gpu/ccpr/GrCCAtlas.cpp
index df84ed7..551b2a0 100644
--- a/src/gpu/ccpr/GrCCAtlas.cpp
+++ b/src/gpu/ccpr/GrCCAtlas.cpp
@@ -21,9 +21,6 @@
 #include "ccpr/GrCCPathParser.h"
 #include "ops/GrDrawOp.h"
 
-static constexpr int kAtlasMinSize = 1024;
-static constexpr int kPadding = 1;
-
 class GrCCAtlas::Node {
 public:
     Node(std::unique_ptr<Node> previous, int l, int t, int r, int b)
@@ -97,15 +94,27 @@
     typedef GrDrawOp INHERITED;
 };
 
-GrCCAtlas::GrCCAtlas(const GrCaps& caps, int minSize)
-        : fMaxAtlasSize(SkTMax(minSize, caps.maxPreferredRenderTargetSize())) {
-    // Caller should have cropped any paths to the destination render target instead of asking for
-    // an atlas larger than maxRenderTargetSize.
-    SkASSERT(fMaxAtlasSize <= caps.maxRenderTargetSize());
-    int initialSize = GrNextPow2(minSize + kPadding);
-    initialSize = SkTMax(kAtlasMinSize, initialSize);
-    initialSize = SkTMin(initialSize, fMaxAtlasSize);
-    fHeight = fWidth = initialSize;
+GrCCAtlas::GrCCAtlas(const Specs& specs)
+        : fMaxTextureSize(SkTMax(SkTMax(specs.fMinHeight, specs.fMinWidth),
+                                 specs.fMaxPreferredTextureSize)) {
+    SkASSERT(specs.fMaxPreferredTextureSize > 0);
+
+    // Begin with the first pow2 dimensions whose area is theoretically large enough to contain the
+    // pending paths, favoring height over width if necessary.
+    int log2area = SkNextLog2(SkTMax(specs.fApproxNumPixels, 1));
+    fHeight = 1 << ((log2area + 1) / 2);
+    fWidth = 1 << (log2area / 2);
+
+    fWidth = SkTClamp(fWidth, specs.fMinTextureSize, specs.fMaxPreferredTextureSize);
+    fHeight = SkTClamp(fHeight, specs.fMinTextureSize, specs.fMaxPreferredTextureSize);
+
+    if (fWidth < specs.fMinWidth || fHeight < specs.fMinHeight) {
+        // They want to stuff a particularly large path into the atlas. Just punt and go with their
+        // min width and height. The atlas will grow as needed.
+        fWidth = SkTMin(specs.fMinWidth + kPadding, fMaxTextureSize);
+        fHeight = SkTMin(specs.fMinHeight + kPadding, fMaxTextureSize);
+    }
+
     fTopNode = skstd::make_unique<Node>(nullptr, 0, 0, fWidth, fHeight);
 }
 
@@ -128,26 +137,26 @@
 
 bool GrCCAtlas::internalPlaceRect(int w, int h, SkIPoint16* loc) {
     for (Node* node = fTopNode.get(); node; node = node->previous()) {
-        if (node->addRect(w, h, loc, fMaxAtlasSize)) {
+        if (node->addRect(w, h, loc, fMaxTextureSize)) {
             return true;
         }
     }
 
     // The rect didn't fit. Grow the atlas and try again.
     do {
-        if (fWidth == fMaxAtlasSize && fHeight == fMaxAtlasSize) {
+        if (fWidth == fMaxTextureSize && fHeight == fMaxTextureSize) {
             return false;
         }
         if (fHeight <= fWidth) {
             int top = fHeight;
-            fHeight = SkTMin(fHeight * 2, fMaxAtlasSize);
+            fHeight = SkTMin(fHeight * 2, fMaxTextureSize);
             fTopNode = skstd::make_unique<Node>(std::move(fTopNode), 0, top, fWidth, fHeight);
         } else {
             int left = fWidth;
-            fWidth = SkTMin(fWidth * 2, fMaxAtlasSize);
+            fWidth = SkTMin(fWidth * 2, fMaxTextureSize);
             fTopNode = skstd::make_unique<Node>(std::move(fTopNode), left, 0, fWidth, fHeight);
         }
-    } while (!fTopNode->addRect(w, h, loc, fMaxAtlasSize));
+    } while (!fTopNode->addRect(w, h, loc, fMaxTextureSize));
 
     return true;
 }
@@ -156,6 +165,9 @@
                                                  sk_sp<const GrCCPathParser> parser) {
     SkASSERT(fCoverageCountBatchID);
     SkASSERT(!fTextureProxy);
+    // Caller should have cropped any paths to the destination render target instead of asking for
+    // an atlas larger than maxRenderTargetSize.
+    SkASSERT(fMaxTextureSize <= onFlushRP->caps()->maxRenderTargetSize());
 
     GrSurfaceDesc desc;
     desc.fFlags = kRenderTarget_GrSurfaceFlag;