Handle merging ops tasks with color clears better

This fixes the reduceOpsTaskSplitting bot.

Bug: skia:10877
Change-Id: Ief0343f5ce62121edd8f87aa57c68b74b736bd80
Docs-Preview: https://skia.org/?cl=363941
Cq-Include-Trybots: luci.skia.skia.primary:Test-Debian10-Clang-NUC7i5BNK-GPU-IntelIris640-x86_64-Debug-All-ASAN_ReduceOpsTaskSplitting_Vulkan
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/363941
Reviewed-by: Robert Phillips <robertphillips@google.com>
Commit-Queue: Adlai Holler <adlai@google.com>
diff --git a/src/gpu/GrOpsTask.cpp b/src/gpu/GrOpsTask.cpp
index d9f5749..8eb03cc 100644
--- a/src/gpu/GrOpsTask.cpp
+++ b/src/gpu/GrOpsTask.cpp
@@ -673,10 +673,18 @@
     }
 }
 
+void GrOpsTask::reset() {
+    fSampledProxies.reset();
+    fClipAllocators.reset();
+    fClippedContentBounds = SkIRect::MakeEmpty();
+    fTotalBounds = SkRect::MakeEmpty();
+    fOpChains.reset();
+    fRenderPassXferBarriers = GrXferBarrierFlags::kNone;
+}
+
 int GrOpsTask::mergeFrom(SkSpan<const sk_sp<GrRenderTask>> tasks) {
-    GrOpsTask* last = this;
-    int addlProxyCount = 0;
-    int addlOpChainCount = 0;
+    // Find the index of the last color-clearing task. -1 indicates this or "there are none."
+    int indexOfLastColorClear = -1;
     int mergedCount = 0;
     for (const sk_sp<GrRenderTask>& task : tasks) {
         auto opsTask = task->asOpsTask();
@@ -685,24 +693,51 @@
         }
         SkASSERT(fTargetSwizzle == opsTask->fTargetSwizzle);
         SkASSERT(fTargetOrigin == opsTask->fTargetOrigin);
+        if (GrLoadOp::kClear == opsTask->fColorLoadOp) {
+            indexOfLastColorClear = &task - tasks.begin();
+        }
         mergedCount += 1;
+    }
+    if (0 == mergedCount) {
+        return 0;
+    }
+
+    SkSpan<const sk_sp<GrOpsTask>> opsTasks(reinterpret_cast<const sk_sp<GrOpsTask>*>(tasks.data()),
+                                            SkToSizeT(mergedCount));
+    if (indexOfLastColorClear >= 0) {
+        // If any dropped task needs to preserve stencil, for now just bail on the merge.
+        // Could keep the merge and insert a clear op, but might be tricky due to closed task.
+        if (fMustPreserveStencil) {
+            return 0;
+        }
+        for (const auto& opsTask : opsTasks.first(indexOfLastColorClear)) {
+            if (opsTask->fMustPreserveStencil) {
+                return 0;
+            }
+        }
+        // Clear `this` and forget about the tasks pre-color-clear.
+        this->reset();
+        opsTasks = opsTasks.last(opsTasks.count() - indexOfLastColorClear);
+        // Copy the color-clear into `this`.
+        fColorLoadOp = GrLoadOp::kClear;
+        fLoadClearColor = opsTasks.front()->fLoadClearColor;
+    }
+    int addlProxyCount = 0;
+    int addlOpChainCount = 0;
+    for (const auto& opsTask : opsTasks) {
         addlProxyCount += opsTask->fSampledProxies.count();
         addlOpChainCount += opsTask->fOpChains.count();
         fClippedContentBounds.join(opsTask->fClippedContentBounds);
         fTotalBounds.join(opsTask->fTotalBounds);
         fRenderPassXferBarriers |= opsTask->fRenderPassXferBarriers;
         SkDEBUGCODE(fNumClips += opsTask->fNumClips);
-        last = opsTask;
     }
-    if (last == this) {
-        return 0;
-    }
+
     fLastClipStackGenID = SK_InvalidUniqueID;
     fSampledProxies.reserve_back(addlProxyCount);
     fOpChains.reserve_back(addlOpChainCount);
-    fClipAllocators.reserve_back(mergedCount);
-    for (const sk_sp<GrRenderTask>& task : tasks.first(mergedCount)) {
-        auto opsTask = reinterpret_cast<GrOpsTask*>(task.get());
+    fClipAllocators.reserve_back(opsTasks.count());
+    for (const auto& opsTask : opsTasks) {
         fSampledProxies.move_back_n(opsTask->fSampledProxies.count(),
                                     opsTask->fSampledProxies.data());
         fOpChains.move_back_n(opsTask->fOpChains.count(),
@@ -713,7 +748,7 @@
         opsTask->fSampledProxies.reset();
         opsTask->fOpChains.reset();
     }
-    fMustPreserveStencil = last->fMustPreserveStencil;
+    fMustPreserveStencil = opsTasks.back()->fMustPreserveStencil;
     return mergedCount;
 }