Revert "Abolish absClear"

This reverts commit f948db6228fd8dce2ba2a6ff2dfec53ff95d3f90.

Reason for revert: Vulkan assertion on Nexus5

Original change's description:
> Abolish absClear
> 
> Bug: skia:
> Change-Id: Ic4a0e640623677c06112d0735ffc5682049baf11
> Reviewed-on: https://skia-review.googlesource.com/c/skia/+/240771
> Commit-Queue: Chris Dalton <csmartdalton@google.com>
> Reviewed-by: Michael Ludwig <michaelludwig@google.com>

TBR=egdaniel@google.com,bsalomon@google.com,robertphillips@google.com,csmartdalton@google.com,michaelludwig@google.com

Change-Id: I071c9878d26ce0f1d91473ada48e9a702bb472de
No-Presubmit: true
No-Tree-Checks: true
No-Try: true
Bug: skia:
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/242480
Reviewed-by: Chris Dalton <csmartdalton@google.com>
Commit-Queue: Chris Dalton <csmartdalton@google.com>
diff --git a/src/core/SkGpuBlurUtils.cpp b/src/core/SkGpuBlurUtils.cpp
index 93f6814..038ab9c 100644
--- a/src/core/SkGpuBlurUtils.cpp
+++ b/src/core/SkGpuBlurUtils.cpp
@@ -274,15 +274,14 @@
     return dstRenderTargetContext;
 }
 
-// Returns a high quality scaled-down version of src. This is used to create an intermediate,
-// shrunken version of the source image in the event that the requested blur sigma exceeds
-// MAX_BLUR_SIGMA.
+// Note: despite its name this function does not kill every tenth legionnaire.
 static sk_sp<GrTextureProxy> decimate(GrRecordingContext* context,
                                       sk_sp<GrTextureProxy> src,
                                       const SkIPoint& proxyOffset,
                                       SkIPoint* srcOffset,
                                       SkIRect* contentRect,
                                       int scaleFactorX, int scaleFactorY,
+                                      bool willBeXFiltering, bool willBeYFiltering,
                                       int radiusX, int radiusY,
                                       GrTextureDomain::Mode mode,
                                       int finalW,
@@ -381,6 +380,24 @@
 
     SkASSERT(dstRenderTargetContext);
 
+    if (willBeXFiltering) {
+        if (scaleFactorX > 1) {
+            // Clear out a radius to the right of the contentRect to prevent the
+            // X convolution from reading garbage.
+            SkIRect clearRect = SkIRect::MakeXYWH(contentRect->fRight, contentRect->fTop,
+                                                  radiusX, contentRect->height());
+            dstRenderTargetContext->priv().absClear(&clearRect);
+        }
+    } else {
+        if (scaleFactorY > 1) {
+            // Clear out a radius below the contentRect to prevent the Y
+            // convolution from reading garbage.
+            SkIRect clearRect = SkIRect::MakeXYWH(contentRect->fLeft, contentRect->fBottom,
+                                                  contentRect->width(), radiusY);
+            dstRenderTargetContext->priv().absClear(&clearRect);
+        }
+    }
+
     return dstRenderTargetContext->asTextureProxyRef();
 }
 
@@ -489,20 +506,14 @@
         xFit = SkBackingFit::kApprox;         // the y-pass will be last
     }
 
-    GrTextureDomain::Mode currDomainMode = mode;
     if (scaleFactorX > 1 || scaleFactorY > 1) {
         srcProxy = decimate(context, std::move(srcProxy), localProxyOffset, &srcOffset,
-                            &localSrcBounds, scaleFactorX, scaleFactorY, radiusX, radiusY,
-                            currDomainMode, finalW, finalH, colorSpace);
+                            &localSrcBounds, scaleFactorX, scaleFactorY, sigmaX > 0.0f,
+                            sigmaY > 0.0f, radiusX, radiusY, mode, finalW, finalH, colorSpace);
+        localProxyOffset.set(0, 0);
         if (!srcProxy) {
             return nullptr;
         }
-        localProxyOffset.set(0, 0);
-        if (GrTextureDomain::kIgnore_Mode == currDomainMode) {
-            // decimate() always returns an approx texture, possibly with garbage after the image.
-            // We can't ignore the domain anymore.
-            currDomainMode = GrTextureDomain::kClamp_Mode;
-        }
     }
 
     std::unique_ptr<GrRenderTargetContext> dstRenderTargetContext;
@@ -512,11 +523,19 @@
     if (sigmaX > 0.0f) {
         dstRenderTargetContext = convolve_gaussian(
                 context, std::move(srcProxy), localProxyOffset, srcRect, srcOffset, Direction::kX,
-                radiusX, sigmaX, &localSrcBounds, currDomainMode, finalW, finalH, colorSpace, xFit);
+                radiusX, sigmaX, &localSrcBounds, mode, finalW, finalH, colorSpace, xFit);
         if (!dstRenderTargetContext) {
             return nullptr;
         }
 
+        if (sigmaY > 0.0f) {
+            // Clear out a radius below the srcRect to prevent the Y
+            // convolution from reading garbage.
+            SkIRect clearRect = SkIRect::MakeXYWH(srcRect.fLeft, srcRect.fBottom,
+                                                  srcRect.width(), radiusY);
+            dstRenderTargetContext->priv().absClear(&clearRect);
+        }
+
         srcProxy = dstRenderTargetContext->asTextureProxyRef();
         if (!srcProxy) {
             return nullptr;
@@ -525,17 +544,12 @@
         srcRect.offsetTo(0, 0);
         srcOffset.set(0, 0);
         localProxyOffset.set(0, 0);
-        if (SkBackingFit::kApprox == xFit && GrTextureDomain::kIgnore_Mode == currDomainMode) {
-            // srcProxy is now an approx texture, possibly with garbage after the image. We can't
-            // ignore the domain anymore.
-            currDomainMode = GrTextureDomain::kClamp_Mode;
-        }
     }
 
     if (sigmaY > 0.0f) {
         dstRenderTargetContext = convolve_gaussian(
                 context, std::move(srcProxy), localProxyOffset, srcRect, srcOffset, Direction::kY,
-                radiusY, sigmaY, &localSrcBounds, currDomainMode, finalW, finalH, colorSpace, yFit);
+                radiusY, sigmaY, &localSrcBounds, mode, finalW, finalH, colorSpace, yFit);
         if (!dstRenderTargetContext) {
             return nullptr;
         }
diff --git a/src/gpu/GrOpsTask.cpp b/src/gpu/GrOpsTask.cpp
index cab3e8e..86bcf9b 100644
--- a/src/gpu/GrOpsTask.cpp
+++ b/src/gpu/GrOpsTask.cpp
@@ -507,6 +507,12 @@
 }
 
 bool GrOpsTask::resetForFullscreenClear(CanDiscardPreviousOps canDiscardPreviousOps) {
+    // Mark the color load op as discard (this may be followed by a clearColorOnLoad call to make
+    // the load op kClear, or it may be followed by an explicit op). In the event of an absClear()
+    // after a regular clear(), we could end up with a clear load op and a real clear op in the task
+    // if the load op were not reset here.
+    fColorLoadOp = GrLoadOp::kDiscard;
+
     // If we previously recorded a wait op, we cannot delete the wait op. Until we track the wait
     // ops separately from normal ops, we have to avoid clearing out any ops in this case as well.
     if (fHasWaitOp) {
diff --git a/src/gpu/GrRenderTargetContext.cpp b/src/gpu/GrRenderTargetContext.cpp
index def18a3..dd3472c 100644
--- a/src/gpu/GrRenderTargetContext.cpp
+++ b/src/gpu/GrRenderTargetContext.cpp
@@ -341,6 +341,81 @@
     }
 }
 
+void GrRenderTargetContextPriv::absClear(const SkIRect* clearRect) {
+    ASSERT_SINGLE_OWNER_PRIV
+    RETURN_IF_ABANDONED_PRIV
+    SkDEBUGCODE(fRenderTargetContext->validate();)
+    GR_CREATE_TRACE_MARKER_CONTEXT("GrRenderTargetContextPriv", "absClear",
+                                   fRenderTargetContext->fContext);
+
+    AutoCheckFlush acf(fRenderTargetContext->drawingManager());
+
+    SkIRect rtRect = SkIRect::MakeWH(fRenderTargetContext->fRenderTargetProxy->worstCaseWidth(),
+                                     fRenderTargetContext->fRenderTargetProxy->worstCaseHeight());
+
+    if (clearRect) {
+        if (clearRect->contains(rtRect)) {
+            clearRect = nullptr; // full screen
+        } else {
+            if (!rtRect.intersect(*clearRect)) {
+                return;
+            }
+        }
+    }
+
+    static const SkPMColor4f kColor = SK_PMColor4fTRANSPARENT;
+
+    // TODO: in a post-MDB world this should be handled at the OpsTask level.
+    // This makes sure to always add an op to the list, instead of marking the clear as a load op.
+    // This code follows very similar logic to internalClear() below, but critical differences are
+    // highlighted in line related to absClear()'s unique behavior.
+    if (clearRect) {
+        if (fRenderTargetContext->caps()->performPartialClearsAsDraws()) {
+            GrPaint paint;
+            clear_to_grpaint(kColor, &paint);
+
+            // Use the disabled clip; the rect geometry already matches the clear rectangle and
+            // if it were added to a scissor, that would be intersected with the logical surface
+            // bounds and not the worst case dimensions required here.
+            fRenderTargetContext->addDrawOp(
+                    GrFixedClip::Disabled(),
+                    GrFillRectOp::MakeNonAARect(fRenderTargetContext->fContext, std::move(paint),
+                                                SkMatrix::I(), SkRect::Make(rtRect)));
+        } else {
+            // Must use the ClearOp factory that takes a boolean (false) instead of a surface
+            // proxy. The surface proxy variant would intersect the clip rect with its logical
+            // bounds, which is not desired in this special case.
+            fRenderTargetContext->addOp(GrClearOp::Make(
+                    fRenderTargetContext->fContext, rtRect, kColor, /* fullscreen */ false));
+        }
+    } else {
+        if (fRenderTargetContext->getOpsTask()->resetForFullscreenClear(
+                fRenderTargetContext->canDiscardPreviousOpsOnFullClear()) &&
+            !fRenderTargetContext->caps()->performColorClearsAsDraws()) {
+            fRenderTargetContext->getOpsTask()->setColorLoadOp(GrLoadOp::kClear, kColor);
+        } else {
+            fRenderTargetContext->getOpsTask()->setColorLoadOp(GrLoadOp::kDiscard);
+
+            if (fRenderTargetContext->caps()->performColorClearsAsDraws()) {
+                // This draws a quad covering the worst case dimensions instead of just the logical
+                // width and height like in internalClear().
+                GrPaint paint;
+                clear_to_grpaint(kColor, &paint);
+                fRenderTargetContext->addDrawOp(
+                        GrFixedClip::Disabled(),
+                        GrFillRectOp::MakeNonAARect(fRenderTargetContext->fContext,
+                                                    std::move(paint), SkMatrix::I(),
+                                                    SkRect::Make(rtRect)));
+            } else {
+                // Nothing special about this path in absClear compared to internalClear()
+                fRenderTargetContext->addOp(GrClearOp::Make(
+                        fRenderTargetContext->fContext, SkIRect::MakeEmpty(), kColor,
+                        /* fullscreen */ true));
+            }
+        }
+    }
+}
+
 void GrRenderTargetContext::drawPaint(const GrClip& clip,
                                       GrPaint&& paint,
                                       const SkMatrix& viewMatrix) {
diff --git a/src/gpu/GrRenderTargetContextPriv.h b/src/gpu/GrRenderTargetContextPriv.h
index 4a12ba0..b29011d 100644
--- a/src/gpu/GrRenderTargetContextPriv.h
+++ b/src/gpu/GrRenderTargetContextPriv.h
@@ -49,6 +49,20 @@
 
     void clearStencilClip(const GrFixedClip&, bool insideStencilMask);
 
+    /*
+     * Some portions of the code, which use approximate-match rendertargets (i.e., ImageFilters),
+     * rely on clears that lie outside of the content region to still have an effect.
+     * For example, when sampling a decimated blurred image back up to full size, the GaussianBlur
+     * code draws 1-pixel rects along the left and bottom edges to be able to use bilerp for
+     * upsampling. The "absClear" entry point ignores the content bounds but does use the
+     * worst case (instantiated) bounds.
+     *
+     * This call will always clear to transparent black.
+     *
+     * @param rect      if (!null) the rect to clear, otherwise it is a full screen clear
+     */
+    void absClear(const SkIRect* rect);
+
     // While this can take a general clip, since GrReducedClip relies on this function, it must take
     // care to only provide hard clips or we could get stuck in a loop. The general clip is needed
     // so that path renderers can use this function.