Some more clipped text optimizations.

* Limit size of text batches to keep inside default vertex buffer size
* Expand geodata allocation by 1.5x rather than 2x
* Don't add text subruns that lie outside the clip rect

Bug: skia:3990
Change-Id: I2b8f8bc5599d14c43e0a98e9633bc51980a7619c
Reviewed-on: https://skia-review.googlesource.com/62861
Commit-Queue: Jim Van Verth <jvanverth@google.com>
Reviewed-by: Robert Phillips <robertphillips@google.com>
diff --git a/src/gpu/ops/GrAtlasTextOp.cpp b/src/gpu/ops/GrAtlasTextOp.cpp
index 257fb3b..21e7c6f 100644
--- a/src/gpu/ops/GrAtlasTextOp.cpp
+++ b/src/gpu/ops/GrAtlasTextOp.cpp
@@ -315,18 +315,27 @@
         }
     }
 
+    // Keep the batch vertex buffer size below 32K so we don't have to create a special one
+    // We use the largest possible vertex size for this
+    static const int kVertexSize = sizeof(SkPoint) + sizeof(SkColor) + 2 * sizeof(uint16_t);
+    static const int kMaxGlyphs = 32768 / (4 * kVertexSize);
+    if (this->fNumGlyphs + that->fNumGlyphs > kMaxGlyphs) {
+        return false;
+    }
+
     fNumGlyphs += that->numGlyphs();
 
     // Reallocate space for geo data if necessary and then import that's geo data.
     int newGeoCount = that->fGeoCount + fGeoCount;
-    // We assume (and here enforce) that the allocation size is the smallest power of two that
-    // is greater than or equal to the number of geometries (and at least
-    // kMinGeometryAllocated).
-    int newAllocSize = GrNextPow2(newGeoCount);
-    int currAllocSize = SkTMax<int>(kMinGeometryAllocated, GrNextPow2(fGeoCount));
 
-    if (newGeoCount > currAllocSize) {
+    // We reallocate at a rate of 1.5x to try to get better total memory usage
+    if (newGeoCount > fGeoDataAllocSize) {
+        int newAllocSize = fGeoDataAllocSize + fGeoDataAllocSize/2;
+        while (newAllocSize < newGeoCount) {
+            newAllocSize += newAllocSize / 2;
+        }
         fGeoData.realloc(newAllocSize);
+        fGeoDataAllocSize = newAllocSize;
     }
 
     // We steal the ref on the blobs from the other AtlasTextOp and set its count to 0 so that
diff --git a/src/gpu/ops/GrAtlasTextOp.h b/src/gpu/ops/GrAtlasTextOp.h
index 0762736..622b993 100644
--- a/src/gpu/ops/GrAtlasTextOp.h
+++ b/src/gpu/ops/GrAtlasTextOp.h
@@ -117,8 +117,12 @@
                                 GrPixelConfigIsClamped dstIsClamped) override;
 
 private:
+    // The minimum number of Geometry we will try to allocate.
+    static constexpr auto kMinGeometryAllocated = 4;
+
     GrAtlasTextOp(GrPaint&& paint)
             : INHERITED(ClassID())
+            , fGeoDataAllocSize(kMinGeometryAllocated)
             , fColor(paint.getColor())
             , fSRGBFlags(GrPipeline::SRGBFlagsFromPaint(paint))
             , fProcessors(std::move(paint)) {}
@@ -176,9 +180,6 @@
 
     sk_sp<GrGeometryProcessor> setupDfProcessor() const;
 
-    // The minimum number of Geometry we will try to allocate.
-    enum { kMinGeometryAllocated = 4 };
-
     enum MaskType {
         kGrayscaleCoverageMask_MaskType,
         kLCDCoverageMask_MaskType,
@@ -190,6 +191,7 @@
     };
 
     SkAutoSTMalloc<kMinGeometryAllocated, Geometry> fGeoData;
+    int fGeoDataAllocSize;
     GrColor fColor;
     uint32_t fSRGBFlags;
     GrProcessorSet fProcessors;
diff --git a/src/gpu/text/GrAtlasTextBlob.cpp b/src/gpu/text/GrAtlasTextBlob.cpp
index be83dc1..a6dbd67 100644
--- a/src/gpu/text/GrAtlasTextBlob.cpp
+++ b/src/gpu/text/GrAtlasTextBlob.cpp
@@ -306,31 +306,39 @@
             continue;
         }
 
+        bool skipClip = false;
+        bool submitOp = true;
+        SkIRect clipRect = SkIRect::MakeEmpty();
         SkRect rtBounds = SkRect::MakeWH(rtc->width(), rtc->height());
         SkRRect clipRRect;
         GrAA aa;
-        // we can clip geometrically if we're not using SDFs,
+        // We can clip geometrically if we're not using SDFs,
         // and we have an axis-aligned rectangular non-AA clip
-        bool skipClip = false;
-        SkIRect clipRect = SkIRect::MakeEmpty();
         if (!info.drawAsDistanceFields() && clip.isRRect(rtBounds, &clipRRect, &aa) &&
             clipRRect.isRect() && GrAA::kNo == aa) {
             skipClip = true;
-            // we only need to do clipping work if the subrun isn't contained by the clip
+            // We only need to do clipping work if the subrun isn't contained by the clip
             SkRect subRunBounds;
             this->computeSubRunBounds(&subRunBounds, run, subRun, viewMatrix, x, y);
             if (!clipRRect.getBounds().contains(subRunBounds)) {
-                clipRRect.getBounds().round(&clipRect);
+                // If the subrun is completely outside, don't add an op for it
+                if (!clipRRect.getBounds().intersects(subRunBounds)) {
+                    submitOp = false;
+                } else {
+                    clipRRect.getBounds().round(&clipRect);
+                }
             }
         }
 
-        auto op = this->makeOp(info, glyphCount, run, subRun, viewMatrix, x, y, clipRect,
-                               std::move(paint), props, distanceAdjustTable, cache, rtc);
-        if (op) {
-            if (skipClip) {
-                rtc->addDrawOp(GrNoClip(), std::move(op));
-            } else {
-                rtc->addDrawOp(clip, std::move(op));
+        if (submitOp) {
+            auto op = this->makeOp(info, glyphCount, run, subRun, viewMatrix, x, y, clipRect,
+                                   std::move(paint), props, distanceAdjustTable, cache, rtc);
+            if (op) {
+                if (skipClip) {
+                    rtc->addDrawOp(GrNoClip(), std::move(op));
+                } else {
+                    rtc->addDrawOp(clip, std::move(op));
+                }
             }
         }
     }