| // 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 <limits> |
| #include <vector> |
| |
| #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 "base/time/time.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); |
| SetBackingOnResource(resource); |
| |
| 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); |
| SetBackingOnResource(resource); |
| |
| EXPECT_EQ(40000u, resource_pool_->GetTotalMemoryUsageForTesting()); |
| EXPECT_EQ(1u, resource_pool_->resource_count()); |
| |
| 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); |
| SetBackingOnResource(resource); |
| 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. |
| 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 |