cc: ResourcePool release lost resources

Currently ResourcePool keeps lost resources forever, taking
up memory accounting, and possibly real memory. It should
check for lost resources and free them from the pool and
ResourceProvider.

Add some basic unit tests for ResourcePool along with
unit tests for this change.

BUG=512265
CQ_INCLUDE_TRYBOTS=tryserver.blink:linux_blink_rel

Review URL: https://codereview.chromium.org/1244173006

Cr-Commit-Position: refs/heads/master@{#339988}
diff --git a/cc/BUILD.gn b/cc/BUILD.gn
index 1a241061..67daed5 100644
--- a/cc/BUILD.gn
+++ b/cc/BUILD.gn
@@ -790,6 +790,7 @@
     "raster/texture_compressor_etc1_unittest.cc",
     "raster/tile_task_worker_pool_unittest.cc",
     "resources/platform_color_unittest.cc",
+    "resources/resource_pool_unittest.cc",
     "resources/resource_provider_unittest.cc",
     "resources/scoped_resource_unittest.cc",
     "resources/video_resource_updater_unittest.cc",
diff --git a/cc/cc_tests.gyp b/cc/cc_tests.gyp
index b44d9d5..0a24de7 100644
--- a/cc/cc_tests.gyp
+++ b/cc/cc_tests.gyp
@@ -94,6 +94,7 @@
       'raster/texture_compressor_etc1_unittest.cc',
       'raster/tile_task_worker_pool_unittest.cc',
       'resources/platform_color_unittest.cc',
+      'resources/resource_pool_unittest.cc',
       'resources/resource_provider_unittest.cc',
       'resources/scoped_resource_unittest.cc',
       'resources/video_resource_updater_unittest.cc',
diff --git a/cc/resources/resource_pool.cc b/cc/resources/resource_pool.cc
index f5c7167..7b65d34 100644
--- a/cc/resources/resource_pool.cc
+++ b/cc/resources/resource_pool.cc
@@ -112,12 +112,9 @@
     // memory is necessarily returned to the OS.
     ScopedResource* resource = unused_resources_.front().resource;
     unused_resources_.pop_front();
-    size_t resource_bytes = Resource::UncheckedMemorySizeBytes(
+    unused_memory_usage_bytes_ -= Resource::UncheckedMemorySizeBytes(
         resource->size(), resource->format());
-    memory_usage_bytes_ -= resource_bytes;
-    unused_memory_usage_bytes_ -= resource_bytes;
-    --resource_count_;
-    delete resource;
+    DeleteResource(resource);
   }
 }
 
@@ -131,6 +128,14 @@
   return false;
 }
 
+void ResourcePool::DeleteResource(ScopedResource* resource) {
+  size_t resource_bytes =
+      Resource::UncheckedMemorySizeBytes(resource->size(), resource->format());
+  memory_usage_bytes_ -= resource_bytes;
+  --resource_count_;
+  delete resource;
+}
+
 void ResourcePool::CheckBusyResources(bool wait_if_needed) {
   ResourceList::iterator it = busy_resources_.begin();
 
@@ -143,6 +148,10 @@
     if (resource_provider_->CanLockForWrite(resource->id())) {
       DidFinishUsingResource(resource, it->content_id);
       it = busy_resources_.erase(it);
+    } else if (resource_provider_->IsLost(resource->id())) {
+      // Remove lost resources from pool.
+      DeleteResource(resource);
+      it = busy_resources_.erase(it);
     } else {
       ++it;
     }
diff --git a/cc/resources/resource_pool.h b/cc/resources/resource_pool.h
index 53f4135..88816aa 100644
--- a/cc/resources/resource_pool.h
+++ b/cc/resources/resource_pool.h
@@ -58,6 +58,7 @@
 
  private:
   void DidFinishUsingResource(ScopedResource* resource, uint64_t content_id);
+  void DeleteResource(ScopedResource* resource);
 
   ResourceProvider* resource_provider_;
   const GLenum target_;
diff --git a/cc/resources/resource_pool_unittest.cc b/cc/resources/resource_pool_unittest.cc
new file mode 100644
index 0000000..96e7591
--- /dev/null
+++ b/cc/resources/resource_pool_unittest.cc
@@ -0,0 +1,141 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "cc/resources/resource_pool.h"
+
+#include "cc/resources/scoped_resource.h"
+#include "cc/test/fake_output_surface.h"
+#include "cc/test/fake_output_surface_client.h"
+#include "cc/test/fake_resource_provider.h"
+#include "cc/test/test_shared_bitmap_manager.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace cc {
+namespace {
+
+class ResourcePoolTest : public testing::Test {
+ public:
+  void SetUp() override {
+    output_surface_ = FakeOutputSurface::Create3d();
+    ASSERT_TRUE(output_surface_->BindToClient(&output_surface_client_));
+    shared_bitmap_manager_.reset(new TestSharedBitmapManager());
+    resource_provider_ = FakeResourceProvider::Create(
+        output_surface_.get(), shared_bitmap_manager_.get());
+    resource_pool_ =
+        ResourcePool::Create(resource_provider_.get(), GL_TEXTURE_2D);
+  }
+
+ protected:
+  FakeOutputSurfaceClient output_surface_client_;
+  scoped_ptr<FakeOutputSurface> output_surface_;
+  scoped_ptr<SharedBitmapManager> shared_bitmap_manager_;
+  scoped_ptr<ResourceProvider> resource_provider_;
+  scoped_ptr<ResourcePool> resource_pool_;
+};
+
+TEST_F(ResourcePoolTest, AcquireRelease) {
+  gfx::Size size(100, 100);
+  ResourceFormat format = RGBA_8888;
+  scoped_ptr<ScopedResource> resource =
+      resource_pool_->AcquireResource(size, format);
+  EXPECT_EQ(size, resource->size());
+  EXPECT_EQ(format, resource->format());
+  EXPECT_TRUE(resource_provider_->CanLockForWrite(resource->id()));
+
+  resource_pool_->ReleaseResource(resource.Pass(), 0u);
+}
+
+TEST_F(ResourcePoolTest, AccountingSingleResource) {
+  // Limits high enough to not be hit by this test.
+  size_t bytes_limit = 10 * 1024 * 1024;
+  size_t count_limit = 100;
+  resource_pool_->SetResourceUsageLimits(bytes_limit, bytes_limit, count_limit);
+
+  gfx::Size size(100, 100);
+  ResourceFormat format = RGBA_8888;
+  size_t resource_bytes = Resource::UncheckedMemorySizeBytes(size, format);
+  scoped_ptr<ScopedResource> resource =
+      resource_pool_->AcquireResource(size, format);
+
+  EXPECT_EQ(resource_bytes, resource_pool_->total_memory_usage_bytes());
+  EXPECT_EQ(resource_bytes, resource_pool_->acquired_memory_usage_bytes());
+  EXPECT_EQ(1u, resource_pool_->total_resource_count());
+  EXPECT_EQ(1u, resource_pool_->acquired_resource_count());
+  EXPECT_EQ(0u, resource_pool_->busy_resource_count());
+
+  resource_pool_->ReleaseResource(resource.Pass(), 0u);
+  EXPECT_EQ(resource_bytes, resource_pool_->total_memory_usage_bytes());
+  EXPECT_EQ(1u, resource_pool_->total_resource_count());
+  EXPECT_EQ(1u, resource_pool_->busy_resource_count());
+
+  bool wait_if_needed = false;
+  resource_pool_->CheckBusyResources(wait_if_needed);
+  EXPECT_EQ(resource_bytes, resource_pool_->total_memory_usage_bytes());
+  EXPECT_EQ(0u, resource_pool_->acquired_memory_usage_bytes());
+  EXPECT_EQ(1u, resource_pool_->total_resource_count());
+  EXPECT_EQ(0u, resource_pool_->acquired_resource_count());
+  EXPECT_EQ(0u, resource_pool_->busy_resource_count());
+
+  resource_pool_->SetResourceUsageLimits(0u, 0u, 0u);
+  resource_pool_->ReduceResourceUsage();
+  EXPECT_EQ(0u, resource_pool_->total_memory_usage_bytes());
+  EXPECT_EQ(0u, resource_pool_->acquired_memory_usage_bytes());
+  EXPECT_EQ(0u, resource_pool_->total_resource_count());
+  EXPECT_EQ(0u, resource_pool_->acquired_resource_count());
+  EXPECT_EQ(0u, resource_pool_->busy_resource_count());
+}
+
+TEST_F(ResourcePoolTest, SimpleResourceReuse) {
+  // Limits high enough to not be hit by this test.
+  size_t bytes_limit = 10 * 1024 * 1024;
+  size_t count_limit = 100;
+  resource_pool_->SetResourceUsageLimits(bytes_limit, bytes_limit, count_limit);
+
+  gfx::Size size(100, 100);
+  ResourceFormat format = RGBA_8888;
+  bool wait_if_needed = false;
+
+  scoped_ptr<ScopedResource> resource =
+      resource_pool_->AcquireResource(size, format);
+  resource_pool_->ReleaseResource(resource.Pass(), 0u);
+  resource_pool_->CheckBusyResources(wait_if_needed);
+  EXPECT_EQ(1u, resource_provider_->num_resources());
+
+  // Same size/format should re-use resource.
+  resource = resource_pool_->AcquireResource(size, format);
+  EXPECT_EQ(1u, resource_provider_->num_resources());
+  resource_pool_->ReleaseResource(resource.Pass(), 0u);
+  resource_pool_->CheckBusyResources(wait_if_needed);
+  EXPECT_EQ(1u, resource_provider_->num_resources());
+
+  // Different size/format should alloate new resource.
+  resource = resource_pool_->AcquireResource(gfx::Size(50, 50), LUMINANCE_8);
+  EXPECT_EQ(2u, resource_provider_->num_resources());
+  resource_pool_->ReleaseResource(resource.Pass(), 0u);
+  resource_pool_->CheckBusyResources(wait_if_needed);
+  EXPECT_EQ(2u, resource_provider_->num_resources());
+}
+
+TEST_F(ResourcePoolTest, LostResource) {
+  // Limits high enough to not be hit by this test.
+  size_t bytes_limit = 10 * 1024 * 1024;
+  size_t count_limit = 100;
+  resource_pool_->SetResourceUsageLimits(bytes_limit, bytes_limit, count_limit);
+
+  gfx::Size size(100, 100);
+  ResourceFormat format = RGBA_8888;
+  bool wait_if_needed = false;
+
+  scoped_ptr<ScopedResource> resource =
+      resource_pool_->AcquireResource(size, format);
+  EXPECT_EQ(1u, resource_provider_->num_resources());
+
+  resource_provider_->LoseResourceForTesting(resource->id());
+  resource_pool_->ReleaseResource(resource.Pass(), 0u);
+  resource_pool_->CheckBusyResources(wait_if_needed);
+  EXPECT_EQ(0u, resource_provider_->num_resources());
+}
+
+}  // namespace
+}  // namespace cc
diff --git a/cc/resources/resource_provider.cc b/cc/resources/resource_provider.cc
index 4c7c9da..9f68066 100644
--- a/cc/resources/resource_provider.cc
+++ b/cc/resources/resource_provider.cc
@@ -441,6 +441,12 @@
   return resource->lost;
 }
 
+void ResourceProvider::LoseResourceForTesting(ResourceId id) {
+  Resource* resource = GetResource(id);
+  DCHECK(resource);
+  resource->lost = true;
+}
+
 ResourceId ResourceProvider::CreateResource(const gfx::Size& size,
                                             GLint wrap_mode,
                                             TextureHint hint,
diff --git a/cc/resources/resource_provider.h b/cc/resources/resource_provider.h
index d5b57127..fbed140 100644
--- a/cc/resources/resource_provider.h
+++ b/cc/resources/resource_provider.h
@@ -110,6 +110,7 @@
 
   bool IsLost(ResourceId id);
 
+  void LoseResourceForTesting(ResourceId id);
   void EnableReadLockFencesForTesting(ResourceId id);
 
   // Producer interface.