Simplify domain insetting for bulk texture ops

Previously, the normalization, origin flip, and insetting occurred
simultanesouly. The problem was that whether or not the insetting
was needed had a cyclic dependency with the overall filter level
(not known until the end of the loop). So it had an ugly hack that
kept track of the early appended quads and would update their domains
if needed.

This will no longer work when perspective clipping is implemented because
we won't have a 1-1 mapping from quads+viewcount pairs back to the
texture set entries. However, by splitting the insetting out from the
rest of the normalization step, we can defer it until we're in
onPrePrepare w/o losing any optimization capabilities and scales with
the perspective clipping change in the future. It should also make it
easier to upgrade an op from kNearest to kBilerp in onCombineIfPossible
because the prior op will not have applied the bilerp-insetting yet.

Bug: skia:9779
Change-Id: I71b269730de200b8452e2a6b39b777928967f621
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/269151
Commit-Queue: Michael Ludwig <michaelludwig@google.com>
Reviewed-by: Brian Osman <brianosman@google.com>
diff --git a/src/gpu/ops/GrTextureOp.cpp b/src/gpu/ops/GrTextureOp.cpp
index 550b648..994a7824 100644
--- a/src/gpu/ops/GrTextureOp.cpp
+++ b/src/gpu/ops/GrTextureOp.cpp
@@ -120,19 +120,18 @@
     }
 }
 
-static void correct_domain_for_bilerp(const NormalizationParams& params,
-                                      SkRect* domainRect) {
+static SkRect inset_domain_for_bilerp(const NormalizationParams& params, const SkRect& domainRect) {
     // Normalized pixel size is also equal to iw and ih, so the insets for bilerp are just
     // in those units and can be applied safely after normalization. However, if the domain is
     // smaller than a texel, it should clamp to the center of that axis.
-    float dw = domainRect->width() < params.fIW ? domainRect->width() : params.fIW;
-    float dh = domainRect->height() < params.fIH ? domainRect->height() : params.fIH;
-    domainRect->inset(0.5f * dw, 0.5f * dh);
+    float dw = domainRect.width() < params.fIW ? domainRect.width() : params.fIW;
+    float dh = domainRect.height() < params.fIH ? domainRect.height() : params.fIH;
+    return domainRect.makeInset(0.5f * dw, 0.5f * dh);
 }
 
-// Normalize the domain and inset for bilerp as necessary. If 'domainRect' is null, it is assumed
-// no domain constraint is desired, so a sufficiently large rect is returned even if the quad
-// ends up batched with an op that uses domains overall.
+// Normalize the domain. If 'domainRect' is null, it is assumed no domain constraint is desired,
+// so a sufficiently large rect is returned even if the quad ends up batched with an op that uses
+// domains overall.
 static SkRect normalize_domain(GrSamplerState::Filter filter,
                                const NormalizationParams& params,
                                const SkRect* domainRect) {
@@ -155,10 +154,6 @@
 
     SkRect out;
     ltrb.store(&out);
-
-    if (filter != GrSamplerState::Filter::kNearest) {
-        correct_domain_for_bilerp(params, &out);
-    }
     return out;
 }
 
@@ -520,11 +515,6 @@
         Domain netDomain = Domain::kNo;
         GrSamplerState::Filter netFilter = GrSamplerState::Filter::kNearest;
 
-        // Net domain and filter quality are being determined simultaneously while iterating through
-        // the entry set. When filter changes to bilerp, all prior normalized domains in the
-        // GrQuadBuffer must be updated to reflect the 1/2px inset required. All quads appended
-        // afterwards will properly take that into account.
-        int correctDomainUpToIndex = 0;
         const GrSurfaceProxy* curProxy = nullptr;
         // 'q' is the index in 'set' and fQuadBuffer; 'p' is the index in fViewCountPairs and only
         // increases when set[q]'s proxy changes.
@@ -566,16 +556,12 @@
                 quad.fLocal = GrQuad(set[q].fSrcRect);
             }
 
-            // Before normalizing the source coordinates, determine if bilerp is actually needed
             if (netFilter != filter && filter_has_effect(quad.fLocal, quad.fDevice)) {
                 // The only way netFilter != filter is if bilerp is requested and we haven't yet
                 // found a quad that requires bilerp (so net is still nearest).
                 SkASSERT(netFilter == GrSamplerState::Filter::kNearest &&
                          filter == GrSamplerState::Filter::kBilerp);
                 netFilter = GrSamplerState::Filter::kBilerp;
-                // All quads index < q with domains were calculated as if there was no filtering,
-                // which is no longer true.
-                correctDomainUpToIndex = q;
             }
 
             // Normalize the src quads and apply origin
@@ -610,10 +596,13 @@
                     domainForQuad = &set[q].fSrcRect;
                 }
             }
+            // This domain may represent a no-op, otherwise it will have the origin and dimensions
+            // of the texture applied to it. Insetting for bilinear filtering is deferred until
+            // on[Pre]Prepare so that the overall filter can be lazily determined.
+            SkRect domain = normalize_domain(filter, proxyParams, domainForQuad);
 
             // Always append a quad, it just may refer back to a prior ViewCountPair
             // (this frequently happens when Chrome draws 9-patches).
-            SkRect domain = normalize_domain(filter, proxyParams, domainForQuad);
             float alpha = SkTPin(set[q].fAlpha, 0.f, 1.f);
             fQuads.append(quad.fDevice, {{alpha, alpha, alpha, alpha}, domain, quad.fEdgeFlags},
                           &quad.fLocal);
@@ -624,25 +613,6 @@
         SkASSERT((p + 1) == fMetadata.fProxyCount);
         SkASSERT(fQuads.count() == fMetadata.fTotalQuadCount);
 
-        // All the quads have been recorded, but some domains need to be fixed
-        if (netDomain == Domain::kYes && correctDomainUpToIndex > 0) {
-            int p = 0; // for fViewCountPairs
-            int q = 0; // for set/fQuads
-            int netVCt = 0;
-            auto iter = fQuads.metadata();
-            while(q < correctDomainUpToIndex && iter.next()) {
-                NormalizationParams proxyParams = proxy_normalization_params(
-                        fViewCountPairs[p].fProxy.get(), set[q].fProxyView.origin());
-                correct_domain_for_bilerp(proxyParams, &(iter->fDomainRect));
-                q++;
-                if (q - netVCt >= fViewCountPairs[p].fQuadCnt) {
-                    // Advance to the next view count pair
-                    netVCt += fViewCountPairs[p].fQuadCnt;
-                    p++;
-                }
-            }
-        }
-
         fMetadata.fAAType = static_cast<uint16_t>(netAAType);
         fMetadata.fFilter = static_cast<uint16_t>(netFilter);
         fMetadata.fDomain = static_cast<uint16_t>(netDomain);
@@ -693,11 +663,21 @@
                 SkASSERT(meshIndex < desc->fNumProxies);
 
                 if (pVertexData) {
+                    // Can just use top-left for origin here since we only need the dimensions to
+                    // determine the texel size for insetting.
+                    NormalizationParams params = proxy_normalization_params(
+                            op.fViewCountPairs[p].fProxy.get(), kTopLeft_GrSurfaceOrigin);
+
+                    bool inset = texOp->fMetadata.filter() != GrSamplerState::Filter::kNearest;
+
                     for (int i = 0; i < quadCnt && iter.next(); ++i) {
                         SkASSERT(iter.isLocalValid());
                         const ColorDomainAndAA& info = iter.metadata();
-                        tessellator.append(iter.deviceQuad(), iter.localQuad(),
-                                           info.fColor, info.fDomainRect, info.aaFlags());
+
+                        tessellator.append(iter.deviceQuad(), iter.localQuad(), info.fColor,
+                                           inset ? inset_domain_for_bilerp(params, info.fDomainRect)
+                                                 : info.fDomainRect,
+                                           info.aaFlags());
                     }
                     desc->setMeshProxy(meshIndex, op.fViewCountPairs[p].fProxy.get());