Merge adjacent GrOpsTasks with same target together

This allows the ops tasks to make one render pass instead of multiple.
The only case where this merging is needed is as a result of
reordering (reduceOpsTaskSplitting).

Bug: skia:10877
Change-Id: Ia967ead6efc43f7d2c1da58f770d3987da690cda
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/353656
Reviewed-by: Robert Phillips <robertphillips@google.com>
Commit-Queue: Adlai Holler <adlai@google.com>
diff --git a/src/gpu/GrDrawingManager.cpp b/src/gpu/GrDrawingManager.cpp
index 5c1b9b5..1f2ab64 100644
--- a/src/gpu/GrDrawingManager.cpp
+++ b/src/gpu/GrDrawingManager.cpp
@@ -424,17 +424,20 @@
 }
 
 // Reorder the array to match the llist without reffing & unreffing sk_sp's.
-// Both args must contain the same objects.
+// The llist must contain a subset of the entries in the array.
 // This is basically a shim because clustering uses LList but the rest of drawmgr uses array.
+// Pointers in the array are not dereferenced.
 template <typename T>
 static void reorder_array_by_llist(const SkTInternalLList<T>& llist, SkTArray<sk_sp<T>>* array) {
+    for (sk_sp<T>& t : *array) {
+        [[maybe_unused]] T* old = t.release();
+    }
     int i = 0;
     for (T* t : llist) {
-        // Release the pointer that used to live here so it doesn't get unreffed.
-        [[maybe_unused]] T* old = array->at(i).release();
         array->at(i++).reset(t);
     }
-    SkASSERT(i == array->count());
+    SkASSERT(i <= array->count());
+    array->resize_back(i);
 }
 
 void GrDrawingManager::reorderTasks() {
@@ -446,6 +449,30 @@
     }
     // TODO: Handle case where proposed order would blow our memory budget.
     // Such cases are currently pathological, so we could just return here and keep current order.
+
+    // Merge adjacent ops tasks. Note: We remove (future) tasks from the list during iteration.
+    // This works out, however, because when we access the next element in llist it will be valid.
+    for (auto task : llist) {
+        auto opsTask = task->asOpsTask();
+        if (!opsTask) {
+            continue;
+        }
+
+        int removedCount = opsTask->mergeFromLList();
+        auto removedTask = opsTask->fNext;
+        for (int i = 0; i < removedCount; i++) {
+            auto next = removedTask->fNext;
+            llist.remove(removedTask);
+
+            // After this unref, there will be a dangling sk_sp to this task in fDAG somewhere.
+            // That dangling pointer will be removed in reorder_array_by_llist.
+            removedTask->disown(this);
+            removedTask->unref();
+
+            removedTask = next;
+        }
+    }
+
     reorder_array_by_llist(llist, &fDAG);
 }