cc::ResourcePool - Re-use larger resources for smaller requests

Allows cc::ResourcePool to re-use larger resources to fulfill a smaller
request. This avoids expensive allocations, and is especially useful in
cases where layer sizes are changing due to animation.

Also extends the timeout for resource expiration from 1s to 5s, which
will also cut down on resource allocations.

CQ_INCLUDE_TRYBOTS=master.tryserver.blink:linux_trusty_blink_rel

Review-Url: https://codereview.chromium.org/2726263003
Cr-Commit-Position: refs/heads/master@{#456202}
diff --git a/cc/resources/resource_pool.cc b/cc/resources/resource_pool.cc
index c200375..50eef3c 100644
--- a/cc/resources/resource_pool.cc
+++ b/cc/resources/resource_pool.cc
@@ -24,8 +24,35 @@
 using base::trace_event::MemoryDumpLevelOfDetail;
 
 namespace cc {
+namespace {
+bool ResourceMeetsSizeRequirements(const gfx::Size& requested_size,
+                                   const gfx::Size& actual_size) {
+  const float kReuseThreshold = 2.0f;
+
+  // Allocating new resources is expensive, and we'd like to re-use our
+  // existing ones within reason. Allow a larger resource to be used for a
+  // smaller request.
+  if (actual_size.width() < requested_size.width() ||
+      actual_size.height() < requested_size.height())
+    return false;
+
+  // GetArea will crash on overflow, however all sizes in use are tile sizes.
+  // These are capped at ResourceProvider::max_texture_size(), and will not
+  // overflow.
+  float actual_area = actual_size.GetArea();
+  float requested_area = requested_size.GetArea();
+  // Don't use a resource that is more than |kReuseThreshold| times the
+  // requested pixel area, as we want to free unnecessarily large resources.
+  if (actual_area / requested_area > kReuseThreshold)
+    return false;
+
+  return true;
+}
+
+}  // namespace
+
 base::TimeDelta ResourcePool::kDefaultExpirationDelay =
-    base::TimeDelta::FromSeconds(1);
+    base::TimeDelta::FromSeconds(5);
 
 void ResourcePool::PoolResource::OnMemoryDump(
     base::trace_event::ProcessMemoryDump* pmd,
@@ -119,7 +146,7 @@
 
     if (resource->format() != format)
       continue;
-    if (resource->size() != size)
+    if (!ResourceMeetsSizeRequirements(size, resource->size()))
       continue;
     if (resource->color_space() != color_space)
       continue;
diff --git a/cc/resources/resource_pool_unittest.cc b/cc/resources/resource_pool_unittest.cc
index aa8e853e..ec9bae41 100644
--- a/cc/resources/resource_pool_unittest.cc
+++ b/cc/resources/resource_pool_unittest.cc
@@ -33,6 +33,12 @@
   }
 
  protected:
+  void CheckAndReturnResource(Resource* resource) {
+    EXPECT_NE(nullptr, resource);
+    resource_pool_->ReleaseResource(resource);
+    resource_pool_->CheckBusyResources();
+  }
+
   scoped_refptr<TestContextProvider> context_provider_;
   std::unique_ptr<SharedBitmapManager> shared_bitmap_manager_;
   std::unique_ptr<ResourceProvider> resource_provider_;
@@ -105,32 +111,28 @@
   gfx::ColorSpace color_space1;
   gfx::ColorSpace color_space2 = gfx::ColorSpace::CreateSRGB();
 
-  Resource* resource =
-      resource_pool_->AcquireResource(size, format, color_space1);
-  resource_pool_->ReleaseResource(resource);
-  resource_pool_->CheckBusyResources();
+  CheckAndReturnResource(
+      resource_pool_->AcquireResource(size, format, color_space1));
   EXPECT_EQ(1u, resource_provider_->num_resources());
 
   // Same size/format should re-use resource.
-  resource = resource_pool_->AcquireResource(size, format, color_space1);
+  Resource* resource =
+      resource_pool_->AcquireResource(size, format, color_space1);
   EXPECT_EQ(1u, resource_provider_->num_resources());
-  resource_pool_->ReleaseResource(resource);
-  resource_pool_->CheckBusyResources();
+  CheckAndReturnResource(resource);
   EXPECT_EQ(1u, resource_provider_->num_resources());
 
   // Different size/format should allocate new resource.
   resource = resource_pool_->AcquireResource(gfx::Size(50, 50), LUMINANCE_8,
                                              color_space1);
   EXPECT_EQ(2u, resource_provider_->num_resources());
-  resource_pool_->ReleaseResource(resource);
-  resource_pool_->CheckBusyResources();
+  CheckAndReturnResource(resource);
   EXPECT_EQ(2u, resource_provider_->num_resources());
 
   // Different color space should allocate new resource.
   resource = resource_pool_->AcquireResource(size, format, color_space2);
   EXPECT_EQ(3u, resource_provider_->num_resources());
-  resource_pool_->ReleaseResource(resource);
-  resource_pool_->CheckBusyResources();
+  CheckAndReturnResource(resource);
   EXPECT_EQ(3u, resource_provider_->num_resources());
 }
 
@@ -317,25 +319,47 @@
   ResourceFormat format = RGBA_8888;
   gfx::ColorSpace color_space = gfx::ColorSpace::CreateSRGB();
 
-  // Create unused resources with sizes close to 100, 100.
-  resource_pool_->ReleaseResource(
-      resource_pool_->CreateResource(gfx::Size(99, 100), format, color_space));
-  resource_pool_->ReleaseResource(
-      resource_pool_->CreateResource(gfx::Size(99, 99), format, color_space));
-  resource_pool_->ReleaseResource(
-      resource_pool_->CreateResource(gfx::Size(100, 99), format, color_space));
-  resource_pool_->ReleaseResource(
-      resource_pool_->CreateResource(gfx::Size(101, 101), format, color_space));
-  resource_pool_->CheckBusyResources();
+  // Create unused resource with size 100x100.
+  CheckAndReturnResource(
+      resource_pool_->CreateResource(gfx::Size(100, 100), format, color_space));
 
-  gfx::Size size(100, 100);
-  Resource* resource = resource_pool_->ReuseResource(size, format, color_space);
-  EXPECT_EQ(nullptr, resource);
-  size = gfx::Size(100, 99);
-  resource = resource_pool_->ReuseResource(size, format, color_space);
-  EXPECT_NE(nullptr, resource);
-  ASSERT_EQ(nullptr, resource_pool_->ReuseResource(size, format, color_space));
-  resource_pool_->ReleaseResource(resource);
+  // Try some cases that are too large, none should succeed.
+  EXPECT_EQ(nullptr, resource_pool_->ReuseResource(gfx::Size(101, 100), format,
+                                                   color_space));
+  EXPECT_EQ(nullptr, resource_pool_->ReuseResource(gfx::Size(100, 101), format,
+                                                   color_space));
+  EXPECT_EQ(nullptr, resource_pool_->ReuseResource(gfx::Size(90, 120), format,
+                                                   color_space));
+  EXPECT_EQ(nullptr, resource_pool_->ReuseResource(gfx::Size(120, 120), format,
+                                                   color_space));
+
+  // Try some cases that are more than 2x smaller than 100x100 in area and
+  // won't be re-used.
+  EXPECT_EQ(nullptr, resource_pool_->ReuseResource(gfx::Size(49, 100), format,
+                                                   color_space));
+  EXPECT_EQ(nullptr, resource_pool_->ReuseResource(gfx::Size(100, 49), format,
+                                                   color_space));
+  EXPECT_EQ(nullptr, resource_pool_->ReuseResource(gfx::Size(50, 50), format,
+                                                   color_space));
+  EXPECT_EQ(nullptr, resource_pool_->ReuseResource(gfx::Size(70, 70), format,
+                                                   color_space));
+
+  // Try some cases that are smaller than 100x100, but within 2x area. Reuse
+  // should succeed.
+  CheckAndReturnResource(
+      resource_pool_->ReuseResource(gfx::Size(50, 100), format, color_space));
+  CheckAndReturnResource(
+      resource_pool_->ReuseResource(gfx::Size(100, 50), format, color_space));
+  CheckAndReturnResource(
+      resource_pool_->ReuseResource(gfx::Size(71, 71), format, color_space));
+
+  // 100x100 is an exact match and should succeed. A subsequent request for
+  // the same size should fail (the resource is already in use).
+  Resource* resource =
+      resource_pool_->ReuseResource(gfx::Size(100, 100), format, color_space);
+  EXPECT_EQ(nullptr, resource_pool_->ReuseResource(gfx::Size(100, 100), format,
+                                                   color_space));
+  CheckAndReturnResource(resource);
 }
 
 TEST_F(ResourcePoolTest, MemoryStateSuspended) {