Cache last-accessed entry in lastRenderTask table.

This table is really hot during drawing and we saw a regression when
it was introduced. This change provides wins locally but I don't have
the regressed hardware handy.

Additionally, we change from SkGoodHash to the MixCheap function used by
some other small tables in the library.

Bug: skia:10372
Change-Id: Ic2dfbf41649515245e82cbcc9765cb29270eb5cf
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/295878
Commit-Queue: Adlai Holler <adlai@google.com>
Reviewed-by: Robert Phillips <robertphillips@google.com>
diff --git a/gn/gpu.gni b/gn/gpu.gni
index 1a2da91..15b9092 100644
--- a/gn/gpu.gni
+++ b/gn/gpu.gni
@@ -107,6 +107,7 @@
   "$_src/gpu/GrGpuResource.h",
   "$_src/gpu/GrGpuResourceCacheAccess.h",
   "$_src/gpu/GrGpuResourcePriv.h",
+  "$_src/gpu/GrHashMapWithCache.h",
   "$_src/gpu/GrImageContext.cpp",
   "$_src/gpu/GrImageContextPriv.h",
   "$_src/gpu/GrImageInfo.h",
diff --git a/src/gpu/GrDrawingManager.h b/src/gpu/GrDrawingManager.h
index 8ccd648..c22fc11 100644
--- a/src/gpu/GrDrawingManager.h
+++ b/src/gpu/GrDrawingManager.h
@@ -8,12 +8,12 @@
 #ifndef GrDrawingManager_DEFINED
 #define GrDrawingManager_DEFINED
 
-#include <set>
 #include "include/core/SkSurface.h"
 #include "include/private/SkTArray.h"
 #include "include/private/SkTHash.h"
 #include "src/gpu/GrBufferAllocPool.h"
 #include "src/gpu/GrDeferredUpload.h"
+#include "src/gpu/GrHashMapWithCache.h"
 #include "src/gpu/GrPathRenderer.h"
 #include "src/gpu/GrPathRendererChain.h"
 #include "src/gpu/GrResourceCache.h"
@@ -252,8 +252,13 @@
     // Note: we do not expect a whole lot of these per flush
     SkTHashMap<uint32_t, GrRenderTargetProxy*> fDDLTargets;
 
-    // Keys are UniqueID of GrSurfaceProxys.
-    SkTHashMap<uint32_t, GrRenderTask*> fLastRenderTasks;
+    struct SurfaceIDKeyTraits {
+        static uint32_t GetInvalidKey() {
+            return GrSurfaceProxy::UniqueID::InvalidID().asUInt();
+        }
+    };
+
+    GrHashMapWithCache<uint32_t, GrRenderTask*, SurfaceIDKeyTraits, GrCheapHash> fLastRenderTasks;
 };
 
 #endif
diff --git a/src/gpu/GrHashMapWithCache.h b/src/gpu/GrHashMapWithCache.h
new file mode 100644
index 0000000..81e13cd
--- /dev/null
+++ b/src/gpu/GrHashMapWithCache.h
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef GrHashMapWithCache_DEFINED
+#define GrHashMapWithCache_DEFINED
+
+#include "include/private/SkChecksum.h"
+#include "include/private/SkNoncopyable.h"
+#include "include/private/SkTHash.h"
+
+// Cheaper than SkGoodHash and good enough for UniqueID tables.
+struct GrCheapHash {
+    uint32_t operator()(uint32_t val) {
+        return SkChecksum::CheapMix(val);
+    }
+};
+
+/** A hash map that caches the most recently accessed entry.
+    The API is a subset of SkHashMap, and you must provide a
+    sentinel key that will never be present, such as SK_InvalidUniqueID.
+
+    KeyTraits must have:
+      - static K GetInvalidKey()
+*/
+template <typename K, typename V, typename KeyTraits, typename HashT = SkGoodHash>
+class GrHashMapWithCache : public SkNoncopyable {
+public:
+    // How many key/value pairs are in the table?
+    int count() const { return fMap.count(); }
+
+    // Approximately how many bytes of memory do we use beyond sizeof(*this)?
+    size_t approxBytesUsed() const { return fMap.approxBytesUsed(); }
+
+    // N.B. The pointers returned by set() and find() are valid only until the next call to set().
+
+    // If there is key/value entry in the table with this key, return a pointer to the value.
+    // If not, return null.
+    const V* find(const K& key) const {
+        if (key != fLastKey) {
+            fLastKey = key;
+            fLastValue = fMap.find(key);
+        }
+        return fLastValue;
+    }
+
+    // Set key to val in the map, replacing any previous value with the same key.
+    // We copy both key and val, and return a pointer to the value copy now in the map.
+    const V* set(K key, V val) {
+        if (fLastValue && key == fLastKey) {
+            *fLastValue = std::move(val);
+        } else {
+            fLastKey = key;
+            fLastValue = fMap.set(std::move(key), std::move(val));
+        }
+        return fLastValue;
+    }
+
+    // Remove the key/value entry in the table with this key.
+    void remove(K key) {
+        // Match SkTHashMap requirement. The caller can find() if they're unsure.
+        SkASSERT(fMap.find(fLastKey));
+        fLastKey = std::move(key);
+        fLastValue = nullptr;
+        fMap.remove(fLastKey);
+    }
+
+    // Clear the map.
+    void reset() {
+        fLastKey = KeyTraits::GetInvalidKey();
+        fLastValue = nullptr;
+        fMap.reset();
+    }
+
+private:
+    SkTHashMap<K, V, HashT> fMap;
+    mutable K               fLastKey   = KeyTraits::GetInvalidKey();
+    mutable V*              fLastValue = nullptr;
+};
+
+#endif