cc: Interface to evict images from cc image decode cache.

This adds interface to enable eviction of images from
image decode cache in cc. This CL sets up the interface
and eviction logic in gpu image decode cache. A follow
up would handle the sw cache eviction.

BUG=730784
CQ_INCLUDE_TRYBOTS=master.tryserver.blink:linux_trusty_blink_rel

Review-Url: https://codereview.chromium.org/2945813002
Cr-Commit-Position: refs/heads/master@{#480961}
diff --git a/cc/raster/image_hijack_canvas_unittest.cc b/cc/raster/image_hijack_canvas_unittest.cc
index 8a9f4af..5b6568d 100644
--- a/cc/raster/image_hijack_canvas_unittest.cc
+++ b/cc/raster/image_hijack_canvas_unittest.cc
@@ -33,6 +33,7 @@
   MOCK_METHOD2(GetOutOfRasterDecodeTaskForImageAndRef,
                bool(const DrawImage& image, scoped_refptr<TileTask>* task));
   MOCK_CONST_METHOD0(GetMaximumMemoryLimitBytes, size_t());
+  MOCK_METHOD1(NotifyImageUnused, void(uint32_t skimage_id));
 };
 
 TEST(ImageHijackCanvasTest, NonLazyImagesSkipped) {
diff --git a/cc/tiles/gpu_image_decode_cache.cc b/cc/tiles/gpu_image_decode_cache.cc
index 4713e8b..a6b9228 100644
--- a/cc/tiles/gpu_image_decode_cache.cc
+++ b/cc/tiles/gpu_image_decode_cache.cc
@@ -659,6 +659,23 @@
   return normal_max_cache_bytes_;
 }
 
+void GpuImageDecodeCache::NotifyImageUnused(uint32_t skimage_id) {
+  auto it = persistent_cache_.Peek(skimage_id);
+  if (it != persistent_cache_.end()) {
+    if (it->second->decode.ref_count != 0 ||
+        it->second->upload.ref_count != 0) {
+      it->second->is_orphaned = true;
+    } else if (it->second->upload.image()) {
+      DCHECK(!it->second->decode.is_locked());
+      bytes_used_ -= it->second->size;
+      images_pending_deletion_.push_back(it->second->upload.image());
+      it->second->upload.SetImage(nullptr);
+      it->second->upload.budgeted = false;
+    }
+    persistent_cache_.Erase(it);
+  }
+}
+
 bool GpuImageDecodeCache::OnMemoryDump(
     const base::trace_event::MemoryDumpArgs& args,
     base::trace_event::ProcessMemoryDump* pmd) {
diff --git a/cc/tiles/gpu_image_decode_cache.h b/cc/tiles/gpu_image_decode_cache.h
index 15aa55b..c2fc02e 100644
--- a/cc/tiles/gpu_image_decode_cache.h
+++ b/cc/tiles/gpu_image_decode_cache.h
@@ -126,6 +126,7 @@
       bool aggressively_free_resources) override;
   void ClearCache() override;
   size_t GetMaximumMemoryLimitBytes() const override;
+  void NotifyImageUnused(uint32_t skimage_id) override;
 
   // MemoryDumpProvider overrides.
   bool OnMemoryDump(const base::trace_event::MemoryDumpArgs& args,
diff --git a/cc/tiles/gpu_image_decode_cache_unittest.cc b/cc/tiles/gpu_image_decode_cache_unittest.cc
index a5ae6730..0257717 100644
--- a/cc/tiles/gpu_image_decode_cache_unittest.cc
+++ b/cc/tiles/gpu_image_decode_cache_unittest.cc
@@ -1774,6 +1774,43 @@
   cache.UnrefImage(third_draw_image);
 }
 
+TEST_P(GpuImageDecodeCacheTest, RemoveUnusedImage) {
+  auto context_provider = TestContextProvider::Create();
+  context_provider->BindToCurrentThread();
+  TestGpuImageDecodeCache cache(context_provider.get(), GetParam());
+  bool is_decomposable = true;
+  SkFilterQuality quality = kHigh_SkFilterQuality;
+  std::vector<uint32_t> unique_ids(10);
+
+  for (int i = 0; i < 10; ++i) {
+    sk_sp<SkImage> image = CreateImage(100, 100);
+    unique_ids[i] = image->uniqueID();
+    DrawImage draw_image(
+        CreatePaintImage(image),
+        SkIRect::MakeWH(image->width(), image->height()), quality,
+        CreateMatrix(SkSize::Make(1.0f, 1.0f), is_decomposable),
+        DefaultColorSpace());
+    scoped_refptr<TileTask> task;
+    bool need_unref = cache.GetTaskForImageAndRef(
+        draw_image, ImageDecodeCache::TracingInfo(), &task);
+    EXPECT_TRUE(need_unref);
+    EXPECT_TRUE(task);
+    TestTileTaskRunner::ProcessTask(task->dependencies()[0].get());
+    TestTileTaskRunner::ProcessTask(task.get());
+    cache.UnrefImage(draw_image);
+  }
+
+  // We should now have data image in our cache.
+  EXPECT_GT(cache.GetBytesUsedForTesting(), 0u);
+  EXPECT_EQ(cache.GetNumCacheEntriesForTesting(), 10u);
+
+  // Remove unused ids.
+  for (uint32_t i = 0; i < 10; ++i) {
+    cache.NotifyImageUnused(unique_ids[i]);
+    EXPECT_EQ(cache.GetNumCacheEntriesForTesting(), (10 - i - 1));
+  }
+}
+
 INSTANTIATE_TEST_CASE_P(GpuImageDecodeCacheTests,
                         GpuImageDecodeCacheTest,
                         ::testing::Values(ResourceFormat::RGBA_8888,
diff --git a/cc/tiles/image_controller_unittest.cc b/cc/tiles/image_controller_unittest.cc
index 034a59d..ecc2cbb 100644
--- a/cc/tiles/image_controller_unittest.cc
+++ b/cc/tiles/image_controller_unittest.cc
@@ -129,6 +129,7 @@
   void SetShouldAggressivelyFreeResources(
       bool aggressively_free_resources) override {}
   void ClearCache() override {}
+  void NotifyImageUnused(uint32_t skimage_id) override {}
   size_t GetMaximumMemoryLimitBytes() const override {
     return 256 * 1024 * 1024;
   }
diff --git a/cc/tiles/image_decode_cache.h b/cc/tiles/image_decode_cache.h
index d0bf7be7..25ed140 100644
--- a/cc/tiles/image_decode_cache.h
+++ b/cc/tiles/image_decode_cache.h
@@ -124,6 +124,12 @@
   // memory. It is used as an esimate of whether an image can fit into the
   // locked budget before creating a task.
   virtual size_t GetMaximumMemoryLimitBytes() const = 0;
+
+  // Indicate to the cache that the image is no longer going
+  // to be used. This means it can be deleted altogether. If the
+  // image is locked, then the cache can do its best to clean it
+  // up later.
+  virtual void NotifyImageUnused(uint32_t skimage_id) = 0;
 };
 
 }  // namespace cc
diff --git a/cc/tiles/software_image_decode_cache.cc b/cc/tiles/software_image_decode_cache.cc
index 9a70b4f..83b7026 100644
--- a/cc/tiles/software_image_decode_cache.cc
+++ b/cc/tiles/software_image_decode_cache.cc
@@ -856,6 +856,10 @@
   return locked_images_budget_.total_limit_bytes();
 }
 
+void SoftwareImageDecodeCache::NotifyImageUnused(uint32_t skimage_id) {
+  // TODO(sohanjg) :Implement it, crbug.com/734982.
+}
+
 void SoftwareImageDecodeCache::RemovePendingTask(const ImageKey& key,
                                                  DecodeTaskType task_type) {
   base::AutoLock lock(lock_);
diff --git a/cc/tiles/software_image_decode_cache.h b/cc/tiles/software_image_decode_cache.h
index 02094ac..a81c5f6 100644
--- a/cc/tiles/software_image_decode_cache.h
+++ b/cc/tiles/software_image_decode_cache.h
@@ -142,6 +142,7 @@
       bool aggressively_free_resources) override {}
   void ClearCache() override;
   size_t GetMaximumMemoryLimitBytes() const override;
+  void NotifyImageUnused(uint32_t skimage_id) override;
 
   // Decode the given image and store it in the cache. This is only called by an
   // image decode task from a worker thread.