text: // 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 <stddef.h>
  
  #include "base/memory/raw_ptr.h"
  #include "base/task/single_thread_task_runner.h"
  #include "base/test/test_mock_time_task_runner.h"
  #include "base/threading/thread_task_runner_handle.h"
  #include "components/viz/client/client_resource_provider.h"
  #include "components/viz/common/resources/resource_sizes.h"
  #include "components/viz/common/resources/returned_resource.h"
  #include "components/viz/test/test_context_provider.h"
  #include "components/viz/test/test_context_support.h"
  #include "components/viz/test/test_shared_bitmap_manager.h"
  #include "testing/gmock/include/gmock/gmock.h"
  #include "testing/gtest/include/gtest/gtest.h"
  
  namespace cc {
  
  class ResourcePoolTest : public testing::Test {
   public:
    void SetUp() override {
      auto context_support = std::make_unique<MockContextSupport>();
      context_support_ = context_support.get();
      context_provider_ =
          viz::TestContextProvider::Create(std::move(context_support));
      context_provider_->BindToCurrentThread();
      resource_provider_ = std::make_unique<viz::ClientResourceProvider>();
      test_task_runner_ = base::MakeRefCounted<base::TestMockTimeTaskRunner>();
      resource_pool_ = std::make_unique<ResourcePool>(
          resource_provider_.get(), context_provider_.get(), test_task_runner_,
          ResourcePool::kDefaultExpirationDelay, false);
      resource_pool_->SetClockForTesting(test_task_runner_->GetMockTickClock());
    }
  
    void TearDown() override {
      resource_provider_->ShutdownAndReleaseAllResources();
    }
  
   protected:
    class MockContextSupport : public viz::TestContextSupport {
     public:
      MockContextSupport() = default;
      MOCK_METHOD0(FlushPendingWork, void());
    };
  
    class StubGpuBacking : public ResourcePool::GpuBacking {
     public:
      void OnMemoryDump(
          base::trace_event::ProcessMemoryDump* pmd,
          const base::trace_event::MemoryAllocatorDumpGuid& buffer_dump_guid,
          uint64_t tracing_process_id,
          int importance) const override {}
    };
  
    void SetBackingOnResource(const ResourcePool::InUsePoolResource& resource) {
      auto backing = std::make_unique<StubGpuBacking>();
      backing->mailbox = gpu::Mailbox::Generate();
      backing->mailbox_sync_token.Set(
          gpu::GPU_IO, gpu::CommandBufferId::FromUnsafeValue(1), 1);
      resource.set_gpu_backing(std::move(backing));
    }
  
    void CheckAndReturnResource(ResourcePool::InUsePoolResource resource) {
      EXPECT_TRUE(!!resource);
      resource_pool_->ReleaseResource(std::move(resource));
    }
  
    viz::TestSharedBitmapManager shared_bitmap_manager_;
    raw_ptr<MockContextSupport> context_support_;
    scoped_refptr<viz::TestContextProvider> context_provider_;
    std::unique_ptr<viz::ClientResourceProvider> resource_provider_;
    scoped_refptr<base::TestMockTimeTaskRunner> test_task_runner_;
    std::unique_ptr<ResourcePool> resource_pool_;
  };
  
  TEST_F(ResourcePoolTest, AcquireRelease) {
    gfx::Size size(100, 100);
    viz::ResourceFormat format = viz::RGBA_8888;
    gfx::ColorSpace color_space = gfx::ColorSpace::CreateSRGB();
    ResourcePool::InUsePoolResource resource =
        resource_pool_->AcquireResource(size, format, color_space);
    EXPECT_EQ(size, resource.size());
    EXPECT_EQ(format, resource.format());
    EXPECT_EQ(color_space, resource.color_space());
  
    resource_pool_->ReleaseResource(std::move(resource));
  }
  
  TEST_F(ResourcePoolTest, EventuallyEvictAndFlush) {
    gfx::Size size(100, 100);
    viz::ResourceFormat format = viz::RGBA_8888;
    gfx::ColorSpace color_space = gfx::ColorSpace::CreateSRGB();
    ResourcePool::InUsePoolResource resource =
        resource_pool_->AcquireResource(size, format, color_space);
    resource_pool_->ReleaseResource(std::move(resource));
    EXPECT_EQ(1u, resource_pool_->GetTotalResourceCountForTesting());
  
    // Expect flush after eviction and flush delay.
    EXPECT_CALL(*context_support_, FlushPendingWork()).Times(testing::AtLeast(1));
    test_task_runner_->FastForwardBy(ResourcePool::kDefaultExpirationDelay +
                                     ResourcePool::kDefaultMaxFlushDelay);
    EXPECT_EQ(0u, resource_pool_->GetTotalResourceCountForTesting());
  }
  
  TEST_F(ResourcePoolTest, FlushEvenIfMoreUnusedToEvict) {
    gfx::Size size(100, 100);
    viz::ResourceFormat format = viz::RGBA_8888;
    gfx::ColorSpace color_space = gfx::ColorSpace::CreateSRGB();
    ResourcePool::InUsePoolResource resource1 =
        resource_pool_->AcquireResource(size, format, color_space);
    ResourcePool::InUsePoolResource resource2 =
        resource_pool_->AcquireResource(size, format, color_space);
  
    // Time 0: No resources evicted yet.
    EXPECT_EQ(2u, resource_pool_->GetTotalResourceCountForTesting());
  
    // Space the resource last_usage out so that they don't expire at the same
    // time. resource1 last used at time 0 (expires kDefaultExpirationDelay) and
    // resource2 last used at last_usage_gap (expires kDefaultExpireationDelay +
    // last_usage_gap).
    const base::TimeDelta last_usage_gap =
        ResourcePool::kDefaultMaxFlushDelay * 2;
    resource_pool_->ReleaseResource(std::move(resource1));
    test_task_runner_->FastForwardBy(last_usage_gap);
    resource_pool_->ReleaseResource(std::move(resource2));
  
    // Time |last_usage_gap|: No resources evicted yet.
    EXPECT_EQ(2u, resource_pool_->GetTotalResourceCountForTesting());
  
    // Time |kDefaultExpirationDelay|: resource1 evicted, but not resource2 yet.
    test_task_runner_->FastForwardBy(ResourcePool::kDefaultExpirationDelay -
                                     last_usage_gap);
    EXPECT_EQ(1u, resource_pool_->GetTotalResourceCountForTesting());
  
    // Expect at least one flush kDefaultMaxFlushDelay after an eviction.
    EXPECT_CALL(*context_support_, FlushPendingWork()).Times(testing::AtLeast(1));
    test_task_runner_->FastForwardBy(ResourcePool::kDefaultMaxFlushDelay);
  
    // Time |kDefaultExpirationDelay + kDefaultMaxFlushDelay|:
    // Check that flush was called and resource2 still not evicted.
    testing::Mock::VerifyAndClearExpectations(context_support_);
    EXPECT_EQ(1u, resource_pool_->GetTotalResourceCountForTesting());
  
    // Wait a long time and resource2 should get evicted and flushed.
    EXPECT_CALL(*context_support_, FlushPendingWork()).Times(testing::AtLeast(1));
    test_task_runner_->FastForwardBy(ResourcePool::kDefaultExpirationDelay * 100);
    EXPECT_EQ(0u, resource_pool_->GetTotalResourceCountForTesting());
  }
  
  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, count_limit);
  
    gfx::Size size(100, 100);
    viz::ResourceFormat format = viz::RGBA_8888;
    gfx::ColorSpace color_space = gfx::ColorSpace::CreateSRGB();
    size_t resource_bytes =
        viz::ResourceSizes::UncheckedSizeInBytes<size_t>(size, format);
    ResourcePool::InUsePoolResource resource =
        resource_pool_->AcquireResource(size, format, color_space);
  
    EXPECT_EQ(resource_bytes, resource_pool_->GetTotalMemoryUsageForTesting());
    EXPECT_EQ(resource_bytes, resource_pool_->memory_usage_bytes());
    EXPECT_EQ(1u, resource_pool_->GetTotalResourceCountForTesting());
    EXPECT_EQ(1u, resource_pool_->resource_count());
    EXPECT_EQ(0u, resource_pool_->GetBusyResourceCountForTesting());
  
    resource_pool_->ReleaseResource(std::move(resource));
    EXPECT_EQ(resource_bytes, resource_pool_->GetTotalMemoryUsageForTesting());
    EXPECT_EQ(0u, resource_pool_->memory_usage_bytes());
    EXPECT_EQ(1u, resource_pool_->GetTotalResourceCountForTesting());
    EXPECT_EQ(0u, resource_pool_->resource_count());
    EXPECT_EQ(0u, resource_pool_->GetBusyResourceCountForTesting());
  
    resource_pool_->SetResourceUsageLimits(0u, 0u);
    resource_pool_->ReduceResourceUsage();
    EXPECT_EQ(0u, resource_pool_->GetTotalMemoryUsageForTesting());
    EXPECT_EQ(0u, resource_pool_->memory_usage_bytes());
    EXPECT_EQ(0u, resource_pool_->GetTotalResourceCountForTesting());
    EXPECT_EQ(0u, resource_pool_->resource_count());
    EXPECT_EQ(0u, resource_pool_->GetBusyResourceCountForTesting());
  }
  
  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, count_limit);
  
    gfx::Size size(100, 100);
    viz::ResourceFormat format = viz::RGBA_8888;
    gfx::ColorSpace color_space1;
    gfx::ColorSpace color_space2 = gfx::ColorSpace::CreateSRGB();
  
    CheckAndReturnResource(
        resource_pool_->AcquireResource(size, format, color_space1));
    EXPECT_EQ(1u, resource_pool_->GetTotalResourceCountForTesting());
  
    // Same size/format should re-use resource.
    ResourcePool::InUsePoolResource resource =
        resource_pool_->AcquireResource(size, format, color_space1);
    EXPECT_EQ(1u, resource_pool_->GetTotalResourceCountForTesting());
    CheckAndReturnResource(std::move(resource));
    EXPECT_EQ(1u, resource_pool_->GetTotalResourceCountForTesting());
    EXPECT_EQ(0u, resource_pool_->GetBusyResourceCountForTesting());
  
    // Different size/format should allocate new resource.
    resource = resource_pool_->AcquireResource(gfx::Size(50, 50),
                                               viz::LUMINANCE_8, color_space1);
    EXPECT_EQ(2u, resource_pool_->GetTotalResourceCountForTesting());
    CheckAndReturnResource(std::move(resource));
    EXPECT_EQ(2u, resource_pool_->GetTotalResourceCountForTesting());
    EXPECT_EQ(0u, resource_pool_->GetBusyResourceCountForTesting());
  
    // Different color space should allocate new resource.
    resource = resource_pool_->AcquireResource(size, format, color_space2);
    EXPECT_EQ(3u, resource_pool_->GetTotalResourceCountForTesting());
    CheckAndReturnResource(std::move(resource));
    EXPECT_EQ(3u, resource_pool_->GetTotalResourceCountForTesting());
    EXPECT_EQ(0u, resource_pool_->GetBusyResourceCountForTesting());
  }
  
  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, count_limit);
  
    gfx::Size size(100, 100);
    viz::ResourceFormat format = viz::RGBA_8888;
    gfx::ColorSpace color_space = gfx::ColorSpace::CreateSRGB();
  
    ResourcePool::InUsePoolResource resource =
        resource_pool_->AcquireResource(size, format, color_space);
  
    SetBackingOnResource(resource);
    EXPECT_TRUE(resource_pool_->PrepareForExport(resource));
  
    std::vector<viz::ResourceId> export_ids = {resource.resource_id_for_export()};
    std::vector<viz::TransferableResource> transferable_resources;
    resource_provider_->PrepareSendToParent(
        export_ids, &transferable_resources,
        static_cast<viz::RasterContextProvider*>(context_provider_.get()));
    auto returned_resources =
        viz::TransferableResource::ReturnResources(transferable_resources);
    ASSERT_EQ(1u, returned_resources.size());
    returned_resources[0].lost = true;
    resource_provider_->ReceiveReturnsFromParent(std::move(returned_resources));
  
    EXPECT_EQ(1u, resource_pool_->GetTotalResourceCountForTesting());
    resource_pool_->ReleaseResource(std::move(resource));
    EXPECT_EQ(0u, resource_pool_->GetTotalResourceCountForTesting());
  }
  
  TEST_F(ResourcePoolTest, BusyResourcesNotFreed) {
    // 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, count_limit);
  
    gfx::Size size(100, 100);
    viz::ResourceFormat format = viz::RGBA_8888;
    gfx::ColorSpace color_space;
  
    ResourcePool::InUsePoolResource resource =
        resource_pool_->AcquireResource(size, format, color_space);
    EXPECT_EQ(40000u, resource_pool_->GetTotalMemoryUsageForTesting());
    EXPECT_EQ(1u, resource_pool_->resource_count());
  
    SetBackingOnResource(resource);
    EXPECT_TRUE(resource_pool_->PrepareForExport(resource));
  
    std::vector<viz::TransferableResource> transfers;
    resource_provider_->PrepareSendToParent(
        {resource.resource_id_for_export()}, &transfers,
        static_cast<viz::RasterContextProvider*>(context_provider_.get()));
  
    resource_pool_->ReleaseResource(std::move(resource));
    EXPECT_EQ(40000u, resource_pool_->GetTotalMemoryUsageForTesting());
    EXPECT_EQ(0u, resource_pool_->memory_usage_bytes());
    EXPECT_EQ(1u, resource_pool_->GetBusyResourceCountForTesting());
  
    // Wait for our resource pool to evict resources. Wait 10x the expiration
    // delay.
    test_task_runner_->FastForwardBy(ResourcePool::kDefaultExpirationDelay * 10);
  
    // Busy resources are still held, since they may be in flight to the display
    // compositor and should not be freed.
    EXPECT_EQ(40000u, resource_pool_->GetTotalMemoryUsageForTesting());
    EXPECT_EQ(0u, resource_pool_->memory_usage_bytes());
    EXPECT_EQ(1u, resource_pool_->GetBusyResourceCountForTesting());
  }
  
  TEST_F(ResourcePoolTest, UnusedResourcesEventuallyFreed) {
    // 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, count_limit);
  
    gfx::Size size(100, 100);
    viz::ResourceFormat format = viz::RGBA_8888;
    gfx::ColorSpace color_space;
  
    ResourcePool::InUsePoolResource resource =
        resource_pool_->AcquireResource(size, format, color_space);
    EXPECT_EQ(40000u, resource_pool_->GetTotalMemoryUsageForTesting());
    EXPECT_EQ(1u, resource_pool_->GetTotalResourceCountForTesting());
    EXPECT_EQ(1u, resource_pool_->resource_count());
    EXPECT_EQ(0u, resource_pool_->GetBusyResourceCountForTesting());
  
    // Export the resource to the display compositor.
    SetBackingOnResource(resource);
    EXPECT_TRUE(resource_pool_->PrepareForExport(resource));
    std::vector<viz::TransferableResource> transfers;
    resource_provider_->PrepareSendToParent(
        {resource.resource_id_for_export()}, &transfers,
        static_cast<viz::RasterContextProvider*>(context_provider_.get()));
  
    resource_pool_->ReleaseResource(std::move(resource));
    EXPECT_EQ(40000u, resource_pool_->GetTotalMemoryUsageForTesting());
    EXPECT_EQ(1u, resource_pool_->GetTotalResourceCountForTesting());
    EXPECT_EQ(0u, resource_pool_->resource_count());
    EXPECT_EQ(1u, resource_pool_->GetBusyResourceCountForTesting());
  
    // Transfer the resource from the busy pool to the unused pool.
    resource_provider_->ReceiveReturnsFromParent(
        viz::TransferableResource::ReturnResources(transfers));
    EXPECT_EQ(40000u, resource_pool_->GetTotalMemoryUsageForTesting());
    EXPECT_EQ(1u, resource_pool_->GetTotalResourceCountForTesting());
    EXPECT_EQ(0u, resource_pool_->resource_count());
    EXPECT_EQ(0u, resource_pool_->GetBusyResourceCountForTesting());
  
    // Wait for our resource pool to evict resources. Wait 10x the expiration
    // delay.
    test_task_runner_->FastForwardBy(ResourcePool::kDefaultExpirationDelay * 10);
  
    EXPECT_EQ(0u, resource_pool_->GetTotalMemoryUsageForTesting());
  }
  
  TEST_F(ResourcePoolTest, UpdateContentId) {
    gfx::Size size(100, 100);
    viz::ResourceFormat format = viz::RGBA_8888;
    gfx::ColorSpace color_space;
    uint64_t content_id = 42;
    uint64_t new_content_id = 43;
    gfx::Rect new_invalidated_rect(20, 20, 10, 10);
  
    ResourcePool::InUsePoolResource resource =
        resource_pool_->AcquireResource(size, format, color_space);
    resource_pool_->OnContentReplaced(resource, content_id);
    auto original_id = resource.unique_id_for_testing();
    resource_pool_->ReleaseResource(std::move(resource));
  
    // Ensure that we can retrieve the resource based on |content_id|.
    gfx::Rect invalidated_rect;
    ResourcePool::InUsePoolResource reacquired_resource =
        resource_pool_->TryAcquireResourceForPartialRaster(
            new_content_id, new_invalidated_rect, content_id, &invalidated_rect,
            color_space);
    EXPECT_EQ(original_id, reacquired_resource.unique_id_for_testing());
    EXPECT_EQ(new_invalidated_rect, invalidated_rect);
    resource_pool_->ReleaseResource(std::move(reacquired_resource));
  }
  
  TEST_F(ResourcePoolTest, UpdateContentIdAndInvalidatedRect) {
    gfx::Size size(100, 100);
    viz::ResourceFormat format = viz::RGBA_8888;
    gfx::ColorSpace color_space;
    uint64_t content_ids[] = {42, 43, 44};
    gfx::Rect invalidated_rect(20, 20, 10, 10);
    gfx::Rect second_invalidated_rect(25, 25, 10, 10);
    gfx::Rect expected_total_invalidated_rect(20, 20, 15, 15);
  
    // Acquire a new resource with the first content id.
    ResourcePool::InUsePoolResource resource =
        resource_pool_->AcquireResource(size, format, color_space);
    resource_pool_->OnContentReplaced(resource, content_ids[0]);
    auto original_id = resource.unique_id_for_testing();
  
    // Attempt to acquire this resource. It is in use, so its ID and invalidated
    // rect should be updated, but a new resource will be returned.
    gfx::Rect new_invalidated_rect;
    ResourcePool::InUsePoolResource reacquired_resource =
        resource_pool_->TryAcquireResourceForPartialRaster(
            content_ids[1], invalidated_rect, content_ids[0],
            &new_invalidated_rect, color_space);
    EXPECT_FALSE(!!reacquired_resource);
    EXPECT_EQ(gfx::Rect(), new_invalidated_rect);
  
    // Release the original resource, returning it to the unused pool.
    resource_pool_->ReleaseResource(std::move(resource));
  
    // Ensure that we cannot retrieve a resource based on the original content id.
    reacquired_resource = resource_pool_->TryAcquireResourceForPartialRaster(
        content_ids[1], invalidated_rect, content_ids[0], &new_invalidated_rect,
        color_space);
    EXPECT_FALSE(!!reacquired_resource);
    EXPECT_EQ(gfx::Rect(), new_invalidated_rect);
  
    // Ensure that we can retrieve the resource based on the second (updated)
    // content ID and that it has the expected invalidated rect.
    gfx::Rect total_invalidated_rect;
    reacquired_resource = resource_pool_->TryAcquireResourceForPartialRaster(
        content_ids[2], second_invalidated_rect, content_ids[1],
        &total_invalidated_rect, color_space);
    EXPECT_EQ(original_id, reacquired_resource.unique_id_for_testing());
    EXPECT_EQ(expected_total_invalidated_rect, total_invalidated_rect);
    resource_pool_->ReleaseResource(std::move(reacquired_resource));
  }
  
  TEST_F(ResourcePoolTest, LargeInvalidatedRect) {
    gfx::Size size(100, 100);
    viz::ResourceFormat format = viz::RGBA_8888;
    gfx::ColorSpace color_space;
    uint64_t content_ids[] = {42, 43, 44};
    // This rect is too large to take the area of it.
    gfx::Rect large_invalidated_rect(0, 0, std::numeric_limits<int>::max() / 2,
                                     std::numeric_limits<int>::max() / 2);
  
    // Acquire a resource with the first content id.
    ResourcePool::InUsePoolResource resource =
        resource_pool_->AcquireResource(size, format, color_space);
    resource_pool_->OnContentReplaced(resource, content_ids[0]);
  
    // Set an invalidated rect on the resource.
    gfx::Rect new_invalidated_rect;
    ResourcePool::InUsePoolResource reacquired_resource =
        resource_pool_->TryAcquireResourceForPartialRaster(
            content_ids[1], large_invalidated_rect, content_ids[0],
            &new_invalidated_rect, color_space);
    EXPECT_FALSE(!!reacquired_resource);
  
    // Release the original resource, returning it to the unused pool.
    resource_pool_->ReleaseResource(std::move(resource));
  
    // Try to get the resource again, this should work even though the area was
    // too large to compute the area for.
    resource = resource_pool_->TryAcquireResourceForPartialRaster(
        content_ids[2], large_invalidated_rect, content_ids[1],
        &new_invalidated_rect, color_space);
    EXPECT_TRUE(!!resource);
    resource_pool_->ReleaseResource(std::move(resource));
  }
  
  TEST_F(ResourcePoolTest, ReuseResource) {
    viz::ResourceFormat format = viz::RGBA_8888;
    gfx::ColorSpace color_space = gfx::ColorSpace::CreateSRGB();
  
    // Create unused resource with size 100x100.
    ResourcePool::InUsePoolResource original =
        resource_pool_->AcquireResource(gfx::Size(100, 100), format, color_space);
    auto original_id = original.unique_id_for_testing();
    CheckAndReturnResource(std::move(original));
  
    // 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 if non-exact requests are supported. Some platforms never
    // support these.
    if (resource_pool_->AllowsNonExactReUseForTesting()) {
      ResourcePool::InUsePoolResource reused = resource_pool_->AcquireResource(
          gfx::Size(50, 100), format, color_space);
      EXPECT_EQ(original_id, reused.unique_id_for_testing());
      CheckAndReturnResource(std::move(reused));
      reused = resource_pool_->AcquireResource(gfx::Size(100, 50), format,
                                               color_space);
      EXPECT_EQ(original_id, reused.unique_id_for_testing());
      CheckAndReturnResource(std::move(reused));
      reused =
          resource_pool_->AcquireResource(gfx::Size(71, 71), format, color_space);
      EXPECT_EQ(original_id, reused.unique_id_for_testing());
      CheckAndReturnResource(std::move(reused));
    } else {
      EXPECT_EQ(nullptr, resource_pool_->ReuseResource(gfx::Size(50, 100), format,
                                                       color_space));
      EXPECT_EQ(nullptr, resource_pool_->ReuseResource(gfx::Size(100, 50), format,
                                                       color_space));
      EXPECT_EQ(nullptr, 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).
    ResourcePool::InUsePoolResource resource =
        resource_pool_->AcquireResource(gfx::Size(100, 100), format, color_space);
    EXPECT_EQ(nullptr, resource_pool_->ReuseResource(gfx::Size(100, 100), format,
                                                     color_space));
    CheckAndReturnResource(std::move(resource));
  }
  
  TEST_F(ResourcePoolTest, PurgedMemory) {
    // 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, count_limit);
  
    gfx::Size size(100, 100);
    viz::ResourceFormat format = viz::RGBA_8888;
    gfx::ColorSpace color_space = gfx::ColorSpace::CreateSRGB();
    ResourcePool::InUsePoolResource resource =
        resource_pool_->AcquireResource(size, format, color_space);
    SetBackingOnResource(resource);
    EXPECT_TRUE(resource_pool_->PrepareForExport(resource));
  
    EXPECT_EQ(1u, resource_pool_->GetTotalResourceCountForTesting());
    EXPECT_EQ(0u, resource_pool_->GetBusyResourceCountForTesting());
  
    // Purging and suspending should not impact an in-use resource.
    resource_pool_->OnMemoryPressure(
        base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL);
    EXPECT_EQ(1u, resource_pool_->GetTotalResourceCountForTesting());
    EXPECT_EQ(0u, resource_pool_->GetBusyResourceCountForTesting());
  
    // Export the resource to the display compositor, so it will be busy once
    // released.
    std::vector<viz::TransferableResource> transfers;
    resource_provider_->PrepareSendToParent(
        {resource.resource_id_for_export()}, &transfers,
        static_cast<viz::RasterContextProvider*>(context_provider_.get()));
  
    // Release the resource making it busy.
    resource_pool_->ReleaseResource(std::move(resource));
    EXPECT_EQ(1u, resource_pool_->GetTotalResourceCountForTesting());
    EXPECT_EQ(1u, resource_pool_->GetBusyResourceCountForTesting());
  
    // Purging and suspending should not impact a busy resource either.
    resource_pool_->OnMemoryPressure(
        base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL);
    EXPECT_EQ(1u, resource_pool_->GetTotalResourceCountForTesting());
    EXPECT_EQ(1u, resource_pool_->GetBusyResourceCountForTesting());
  
    // The resource moves from busy to available.
    resource_provider_->ReceiveReturnsFromParent(
        viz::TransferableResource::ReturnResources(transfers));
    EXPECT_EQ(1u, resource_pool_->GetTotalResourceCountForTesting());
    EXPECT_EQ(0u, resource_pool_->GetBusyResourceCountForTesting());
  
    // Purging and suspending should drop unused resources.
    resource_pool_->OnMemoryPressure(
        base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL);
    EXPECT_EQ(0u, resource_pool_->GetTotalResourceCountForTesting());
    EXPECT_EQ(0u, resource_pool_->GetBusyResourceCountForTesting());
  }
  
  TEST_F(ResourcePoolTest, InvalidateResources) {
    // 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, count_limit);
  
    gfx::Size size(100, 100);
    viz::ResourceFormat format = viz::RGBA_8888;
    gfx::ColorSpace color_space = gfx::ColorSpace::CreateSRGB();
    ResourcePool::InUsePoolResource busy_resource =
        resource_pool_->AcquireResource(size, format, color_space);
    SetBackingOnResource(busy_resource);
    EXPECT_TRUE(resource_pool_->PrepareForExport(busy_resource));
    EXPECT_EQ(1u, resource_pool_->GetTotalResourceCountForTesting());
    EXPECT_EQ(0u, resource_pool_->GetBusyResourceCountForTesting());
    EXPECT_EQ(1u, resource_pool_->resource_count());
  
    // Make a 2nd resource which will be left available in the pool.
    ResourcePool::InUsePoolResource avail_resource =
        resource_pool_->AcquireResource(size, format, color_space);
    EXPECT_EQ(2u, resource_pool_->GetTotalResourceCountForTesting());
    EXPECT_EQ(0u, resource_pool_->GetBusyResourceCountForTesting());
    EXPECT_EQ(2u, resource_pool_->resource_count());
  
    // Make a 3nd resource which will be kept in use.
    ResourcePool::InUsePoolResource in_use_resource =
        resource_pool_->AcquireResource(size, format, color_space);
    EXPECT_EQ(3u, resource_pool_->GetTotalResourceCountForTesting());
    EXPECT_EQ(0u, resource_pool_->GetBusyResourceCountForTesting());
    EXPECT_EQ(3u, resource_pool_->resource_count());
  
    // Mark this one as available.
    resource_pool_->ReleaseResource(std::move(avail_resource));
    EXPECT_EQ(3u, resource_pool_->GetTotalResourceCountForTesting());
    EXPECT_EQ(0u, resource_pool_->GetBusyResourceCountForTesting());
    EXPECT_EQ(2u, resource_pool_->resource_count());
  
    // Export the first resource to the display compositor, so it will be busy
    // once released.
    std::vector<viz::TransferableResource> transfers;
    resource_provider_->PrepareSendToParent(
        {busy_resource.resource_id_for_export()}, &transfers,
        static_cast<viz::RasterContextProvider*>(context_provider_.get()));
  
    // Release the resource making it busy.
    resource_pool_->ReleaseResource(std::move(busy_resource));
    EXPECT_EQ(3u, resource_pool_->GetTotalResourceCountForTesting());
    EXPECT_EQ(1u, resource_pool_->GetBusyResourceCountForTesting());
    EXPECT_EQ(1u, resource_pool_->resource_count());
  
    // Invalidating resources should prevent reuse of any resource.
    resource_pool_->InvalidateResources();
  
    // The available resource is just dropped immediately.
    EXPECT_EQ(2u, resource_pool_->GetTotalResourceCountForTesting());
    EXPECT_EQ(1u, resource_pool_->GetBusyResourceCountForTesting());
    EXPECT_EQ(1u, resource_pool_->resource_count());
  
    // The resource moves from busy to available, but since we invalidated,
    // it is not kept.
    resource_provider_->ReceiveReturnsFromParent(
        viz::TransferableResource::ReturnResources(transfers));
    EXPECT_EQ(1u, resource_pool_->GetTotalResourceCountForTesting());
    EXPECT_EQ(0u, resource_pool_->GetBusyResourceCountForTesting());
    EXPECT_EQ(1u, resource_pool_->resource_count());
  
    // The last resource was in use, when it is released it will not become able
    // to be reused.
    resource_pool_->ReleaseResource(std::move(in_use_resource));
    EXPECT_EQ(0u, resource_pool_->GetTotalResourceCountForTesting());
    EXPECT_EQ(0u, resource_pool_->GetBusyResourceCountForTesting());
  }
  
  TEST_F(ResourcePoolTest, ExactRequestsRespected) {
    viz::ResourceFormat format = viz::RGBA_8888;
    gfx::ColorSpace color_space = gfx::ColorSpace::CreateSRGB();
  
    resource_pool_ = std::make_unique<ResourcePool>(
        resource_provider_.get(), context_provider_.get(), test_task_runner_,
        ResourcePool::kDefaultExpirationDelay, true);
  
    // Create unused resource with size 100x100.
    CheckAndReturnResource(resource_pool_->AcquireResource(gfx::Size(100, 100),
                                                           format, color_space));
  
    // Try some cases that are smaller than 100x100, but within 2x area which
    // would typically allow reuse. Reuse should fail.
    EXPECT_EQ(nullptr, resource_pool_->ReuseResource(gfx::Size(50, 100), format,
                                                     color_space));
    EXPECT_EQ(nullptr, resource_pool_->ReuseResource(gfx::Size(100, 50), format,
                                                     color_space));
    EXPECT_EQ(nullptr, 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).
    ResourcePool::InUsePoolResource resource =
        resource_pool_->AcquireResource(gfx::Size(100, 100), format, color_space);
    EXPECT_EQ(nullptr, resource_pool_->ReuseResource(gfx::Size(100, 100), format,
                                                     color_space));
    CheckAndReturnResource(std::move(resource));
  }
  
  TEST_F(ResourcePoolTest, MetadataSentToDisplayCompositor) {
    // 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, count_limit);
  
    // These values are all non-default values so we can tell they are propagated.
    gfx::Size size(100, 101);
    viz::ResourceFormat format = viz::RGBA_4444;
    EXPECT_NE(gfx::BufferFormat::RGBA_8888, viz::BufferFormat(format));
    gfx::ColorSpace color_space = gfx::ColorSpace::CreateSRGB();
    uint32_t target = 5;
    gpu::Mailbox mailbox;
    mailbox.name[0] = 'a';
    gpu::SyncToken sync_token(gpu::CommandBufferNamespace::GPU_IO,
                              gpu::CommandBufferId::FromUnsafeValue(0x123), 7);
  
    ResourcePool::InUsePoolResource resource =
        resource_pool_->AcquireResource(size, format, color_space);
    SetBackingOnResource(resource);
  
    // More non-default values.
    resource.gpu_backing()->mailbox = mailbox;
    resource.gpu_backing()->mailbox_sync_token = sync_token;
    resource.gpu_backing()->texture_target = target;
    resource.gpu_backing()->wait_on_fence_required = true;
    resource.gpu_backing()->overlay_candidate = true;
  
    EXPECT_TRUE(resource_pool_->PrepareForExport(resource));
  
    std::vector<viz::TransferableResource> transfer;
    resource_provider_->PrepareSendToParent(
        {resource.resource_id_for_export()}, &transfer,
        static_cast<viz::RasterContextProvider*>(context_provider_.get()));
  
    // The verified_flush flag will be set by the ResourceProvider when it exports
    // the resource.
    sync_token.SetVerifyFlush();
  
    ASSERT_EQ(transfer.size(), 1u);
    EXPECT_EQ(transfer[0].id, resource.resource_id_for_export());
    EXPECT_EQ(transfer[0].mailbox_holder.mailbox, mailbox);
    EXPECT_EQ(transfer[0].mailbox_holder.sync_token, sync_token);
    EXPECT_EQ(transfer[0].mailbox_holder.texture_target, target);
    EXPECT_EQ(transfer[0].format, format);
    EXPECT_TRUE(transfer[0].read_lock_fences_enabled);
    EXPECT_TRUE(transfer[0].is_overlay_candidate);
  
    resource_pool_->ReleaseResource(std::move(resource));
  }
  
  TEST_F(ResourcePoolTest, InvalidResource) {
    // 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, count_limit);
  
    // These values are all non-default values so we can tell they are propagated.
    gfx::Size size(100, 101);
    viz::ResourceFormat format = viz::RGBA_4444;
    EXPECT_NE(gfx::BufferFormat::RGBA_8888, viz::BufferFormat(format));
    gfx::ColorSpace color_space = gfx::ColorSpace::CreateSRGB();
    uint32_t target = 5;
  
    ResourcePool::InUsePoolResource resource =
        resource_pool_->AcquireResource(size, format, color_space);
  
    // Keep a zero mailbox
    auto backing = std::make_unique<StubGpuBacking>();
    backing->texture_target = target;
    backing->wait_on_fence_required = true;
    backing->overlay_candidate = true;
    resource.set_gpu_backing(std::move(backing));
  
    EXPECT_FALSE(resource_pool_->PrepareForExport(resource));
  
    resource_pool_->ReleaseResource(std::move(resource));
  
    // Acquire another resource. The resource should not be reused.
    resource = resource_pool_->AcquireResource(size, format, color_space);
    EXPECT_FALSE(resource.gpu_backing());
    resource_pool_->ReleaseResource(std::move(resource));
  }
  
  }  // namespace cc
  