diff options
author | reveman@chromium.org <reveman@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-08-16 05:47:59 +0000 |
---|---|---|
committer | reveman@chromium.org <reveman@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-08-16 05:47:59 +0000 |
commit | d069439a2f947e1af88863ec3d9699c6fbafc261 (patch) | |
tree | 334682075751607ab3fa921778242bec87045cbd /cc | |
parent | b168871584f817ee586b5482bec6b4876ea66da7 (diff) | |
download | chromium_src-d069439a2f947e1af88863ec3d9699c6fbafc261.zip chromium_src-d069439a2f947e1af88863ec3d9699c6fbafc261.tar.gz chromium_src-d069439a2f947e1af88863ec3d9699c6fbafc261.tar.bz2 |
cc: Prevent the tile manager from allocating more memory than allowed.
This adds a new ReduceMemoryUsage function to the resource pool. By
calling this function in TileManager::ScheduleTasks after creating
all resources needed to to call RasterWorkerPool::ScheduleTasks
we free enough unused resources to ensure that we never go above
the limit.
This also fixes the use of the num_resources_limit by correctly
accounting for all allocated resources.
BUG=273140
TEST=cc_unittests --gtest_filter=TileManagerTests/TileManagerTest.RespectMemoryLimit*
Review URL: https://chromiumcodereview.appspot.com/23231002
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@217951 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'cc')
-rw-r--r-- | cc/resources/resource_pool.cc | 43 | ||||
-rw-r--r-- | cc/resources/resource_pool.h | 19 | ||||
-rw-r--r-- | cc/resources/tile_manager.cc | 13 | ||||
-rw-r--r-- | cc/resources/tile_manager_unittest.cc | 62 | ||||
-rw-r--r-- | cc/test/fake_tile_manager.cc | 23 |
5 files changed, 126 insertions, 34 deletions
diff --git a/cc/resources/resource_pool.cc b/cc/resources/resource_pool.cc index 5835db5..06bc1fb 100644 --- a/cc/resources/resource_pool.cc +++ b/cc/resources/resource_pool.cc @@ -31,19 +31,20 @@ ResourcePool::ResourcePool(ResourceProvider* resource_provider) : resource_provider_(resource_provider), max_memory_usage_bytes_(0), max_unused_memory_usage_bytes_(0), + max_resource_count_(0), memory_usage_bytes_(0), unused_memory_usage_bytes_(0), - num_resources_limit_(0) { + resource_count_(0) { } ResourcePool::~ResourcePool() { - SetMemoryUsageLimits(0, 0, 0); + SetResourceUsageLimits(0, 0, 0); } scoped_ptr<ResourcePool::Resource> ResourcePool::AcquireResource( gfx::Size size, GLenum format) { - for (ResourceList::iterator it = resources_.begin(); - it != resources_.end(); ++it) { + for (ResourceList::iterator it = unused_resources_.begin(); + it != unused_resources_.end(); ++it) { Resource* resource = *it; // TODO(epenner): It would be nice to DCHECK that this @@ -57,14 +58,13 @@ scoped_ptr<ResourcePool::Resource> ResourcePool::AcquireResource( if (resource->format() != format) continue; - resources_.erase(it); + unused_resources_.erase(it); unused_memory_usage_bytes_ -= resource->bytes(); return make_scoped_ptr(resource); } // Create new resource. - Resource* resource = new Resource( - resource_provider_, size, format); + Resource* resource = new Resource(resource_provider_, size, format); // Extend all read locks on all resources until the resource is // finished being used, such that we know when resources are @@ -72,44 +72,51 @@ scoped_ptr<ResourcePool::Resource> ResourcePool::AcquireResource( resource_provider_->EnableReadLockFences(resource->id(), true); memory_usage_bytes_ += resource->bytes(); + ++resource_count_; return make_scoped_ptr(resource); } void ResourcePool::ReleaseResource( scoped_ptr<ResourcePool::Resource> resource) { - if (MemoryUsageTooHigh()) { + if (ResourceUsageTooHigh()) { memory_usage_bytes_ -= resource->bytes(); + --resource_count_; return; } unused_memory_usage_bytes_ += resource->bytes(); - resources_.push_back(resource.release()); + unused_resources_.push_back(resource.release()); } -void ResourcePool::SetMemoryUsageLimits( +void ResourcePool::SetResourceUsageLimits( size_t max_memory_usage_bytes, size_t max_unused_memory_usage_bytes, - size_t num_resources_limit) { + size_t max_resource_count) { max_memory_usage_bytes_ = max_memory_usage_bytes; max_unused_memory_usage_bytes_ = max_unused_memory_usage_bytes; - num_resources_limit_ = num_resources_limit; + max_resource_count_ = max_resource_count; - while (!resources_.empty()) { - if (!MemoryUsageTooHigh()) + ReduceResourceUsage(); +} + +void ResourcePool::ReduceResourceUsage() { + while (!unused_resources_.empty()) { + if (!ResourceUsageTooHigh()) break; // MRU eviction pattern as least recently used is less likely to // be blocked by read lock fence. - Resource* resource = resources_.back(); - resources_.pop_back(); + Resource* resource = unused_resources_.back(); + unused_resources_.pop_back(); memory_usage_bytes_ -= resource->bytes(); unused_memory_usage_bytes_ -= resource->bytes(); + --resource_count_; delete resource; } } -bool ResourcePool::MemoryUsageTooHigh() { - if (resources_.size() > num_resources_limit_) +bool ResourcePool::ResourceUsageTooHigh() { + if (resource_count_ > max_resource_count_) return true; if (memory_usage_bytes_ > max_memory_usage_bytes_) return true; diff --git a/cc/resources/resource_pool.h b/cc/resources/resource_pool.h index 309bf33..771650e 100644 --- a/cc/resources/resource_pool.h +++ b/cc/resources/resource_pool.h @@ -40,9 +40,11 @@ class CC_EXPORT ResourcePool { GLenum format); void ReleaseResource(scoped_ptr<ResourcePool::Resource>); - void SetMemoryUsageLimits(size_t max_memory_usage_bytes, - size_t max_unused_memory_usage_bytes, - size_t num_resources_limit); + void SetResourceUsageLimits(size_t max_memory_usage_bytes, + size_t max_unused_memory_usage_bytes, + size_t max_resource_count); + + void ReduceResourceUsage(); size_t total_memory_usage_bytes() const { return memory_usage_bytes_; @@ -50,23 +52,26 @@ class CC_EXPORT ResourcePool { size_t acquired_memory_usage_bytes() const { return memory_usage_bytes_ - unused_memory_usage_bytes_; } - size_t NumResources() const { return resources_.size(); } + size_t acquired_resource_count() const { + return resource_count_ - unused_resources_.size(); + } protected: explicit ResourcePool(ResourceProvider* resource_provider); - bool MemoryUsageTooHigh(); + bool ResourceUsageTooHigh(); private: ResourceProvider* resource_provider_; size_t max_memory_usage_bytes_; size_t max_unused_memory_usage_bytes_; + size_t max_resource_count_; size_t memory_usage_bytes_; size_t unused_memory_usage_bytes_; - size_t num_resources_limit_; + size_t resource_count_; typedef std::list<Resource*> ResourceList; - ResourceList resources_; + ResourceList unused_resources_; DISALLOW_COPY_AND_ASSIGN(ResourcePool); }; diff --git a/cc/resources/tile_manager.cc b/cc/resources/tile_manager.cc index 86de610..cff5883 100644 --- a/cc/resources/tile_manager.cc +++ b/cc/resources/tile_manager.cc @@ -180,7 +180,7 @@ TileManager::~TileManager() { void TileManager::SetGlobalState( const GlobalStateThatImpactsTilePriority& global_state) { global_state_ = global_state; - resource_pool_->SetMemoryUsageLimits( + resource_pool_->SetResourceUsageLimits( global_state_.memory_limit_in_bytes, global_state_.unused_memory_limit_in_bytes, global_state_.num_resources_limit); @@ -499,9 +499,10 @@ void TileManager::AssignGpuMemoryToTiles( static_cast<int64>(bytes_releasable_) + static_cast<int64>(global_state_.memory_limit_in_bytes) - static_cast<int64>(resource_pool_->acquired_memory_usage_bytes()); - int resources_available = resources_releasable_ + - global_state_.num_resources_limit - - resource_pool_->NumResources(); + int resources_available = + resources_releasable_ + + global_state_.num_resources_limit - + resource_pool_->acquired_resource_count(); size_t bytes_allocatable = std::max(static_cast<int64>(0), bytes_available); @@ -705,6 +706,10 @@ void TileManager::ScheduleTasks( tasks.Append(tile_version.raster_task_, tile->required_for_activation()); } + // We must reduce the amount of unused resoruces before calling + // ScheduleTasks to prevent usage from rising above limits. + resource_pool_->ReduceResourceUsage(); + // Schedule running of |tasks|. This replaces any previously // scheduled tasks and effectively cancels all tasks not present // in |tasks|. diff --git a/cc/resources/tile_manager_unittest.cc b/cc/resources/tile_manager_unittest.cc index 5076fb0..d3b9fac 100644 --- a/cc/resources/tile_manager_unittest.cc +++ b/cc/resources/tile_manager_unittest.cc @@ -44,6 +44,7 @@ class TileManagerTest : public testing::TestWithParam<bool> { state.memory_limit_in_bytes = 100 * 1000 * 1000; state.num_resources_limit = max_tiles; } + state.unused_memory_limit_in_bytes = state.memory_limit_in_bytes; state.memory_limit_policy = memory_limit_policy; state.tree_priority = tree_priority; @@ -56,6 +57,7 @@ class TileManagerTest : public testing::TestWithParam<bool> { gfx::Size tile_size = settings_.default_tile_size; state.memory_limit_in_bytes = max_memory_tiles_ * 4 * tile_size.width() * tile_size.height(); + state.unused_memory_limit_in_bytes = state.memory_limit_in_bytes; state.memory_limit_policy = memory_limit_policy_; state.num_resources_limit = 100; state.tree_priority = tree_priority; @@ -69,15 +71,16 @@ class TileManagerTest : public testing::TestWithParam<bool> { testing::Test::TearDown(); } - TileVector CreateTiles(int count, - TilePriority active_priority, - TilePriority pending_priority) { + TileVector CreateTilesWithSize(int count, + TilePriority active_priority, + TilePriority pending_priority, + gfx::Size tile_size) { TileVector tiles; for (int i = 0; i < count; ++i) { scoped_refptr<Tile> tile = make_scoped_refptr(new Tile(tile_manager_.get(), picture_pile_.get(), - settings_.default_tile_size, + tile_size, gfx::Rect(), gfx::Rect(), 1.0, @@ -91,6 +94,15 @@ class TileManagerTest : public testing::TestWithParam<bool> { return tiles; } + TileVector CreateTiles(int count, + TilePriority active_priority, + TilePriority pending_priority) { + return CreateTilesWithSize(count, + active_priority, + pending_priority, + settings_.default_tile_size); + } + FakeTileManager* tile_manager() { return tile_manager_.get(); } @@ -465,6 +477,48 @@ TEST_P(TileManagerTest, TextReRasterAsNoLCD) { EXPECT_EQ(0, TilesWithLCDCount(pending_tree_tiles)); } +TEST_P(TileManagerTest, RespectMemoryLimit) { + Initialize(5, ALLOW_ANYTHING, SMOOTHNESS_TAKES_PRIORITY); + TileVector large_tiles = CreateTiles( + 5, TilePriorityForNowBin(), TilePriority()); + + size_t memory_required_bytes; + size_t memory_nice_to_have_bytes; + size_t memory_allocated_bytes; + size_t memory_used_bytes; + + tile_manager()->ManageTiles(); + tile_manager()->GetMemoryStats(&memory_required_bytes, + &memory_nice_to_have_bytes, + &memory_allocated_bytes, + &memory_used_bytes); + // Allocated bytes should never be more than the memory limit. + EXPECT_LE(memory_allocated_bytes, + tile_manager()->GlobalState().memory_limit_in_bytes); + + // Finish raster of large tiles. + tile_manager()->UpdateVisibleTiles(); + + // Remove all large tiles. This will leave the memory currently + // used by these tiles as unused when ManageTiles() is called. + large_tiles.clear(); + + // Create a new set of tiles using a different size. These tiles + // can use the memory currently assigned to the lerge tiles but + // they can't use the same resources as the size doesn't match. + TileVector small_tiles = CreateTilesWithSize( + 5, TilePriorityForNowBin(), TilePriority(), gfx::Size(128, 128)); + + tile_manager()->ManageTiles(); + tile_manager()->GetMemoryStats(&memory_required_bytes, + &memory_nice_to_have_bytes, + &memory_allocated_bytes, + &memory_used_bytes); + // Allocated bytes should never be more than the memory limit. + EXPECT_LE(memory_allocated_bytes, + tile_manager()->GlobalState().memory_limit_in_bytes); +} + // If true, the max tile limit should be applied as bytes; if false, // as num_resources_limit. INSTANTIATE_TEST_CASE_P(TileManagerTests, diff --git a/cc/test/fake_tile_manager.cc b/cc/test/fake_tile_manager.cc index c132254..9e9e48a 100644 --- a/cc/test/fake_tile_manager.cc +++ b/cc/test/fake_tile_manager.cc @@ -4,6 +4,8 @@ #include "cc/test/fake_tile_manager.h" +#include <deque> + #include "cc/resources/raster_worker_pool.h" namespace cc { @@ -14,9 +16,28 @@ class FakeRasterWorkerPool : public RasterWorkerPool { public: FakeRasterWorkerPool() : RasterWorkerPool(NULL, 1) {} - virtual void ScheduleTasks(RasterTask::Queue* queue) OVERRIDE {} + virtual void ScheduleTasks(RasterTask::Queue* queue) OVERRIDE { + RasterWorkerPool::SetRasterTasks(queue); + for (RasterTaskVector::const_iterator it = raster_tasks().begin(); + it != raster_tasks().end(); ++it) { + completed_tasks_.push_back(it->get()); + } + } + virtual void CheckForCompletedTasks() OVERRIDE { + while (!completed_tasks_.empty()) { + internal::RasterWorkerPoolTask* task = completed_tasks_.front().get(); + task->WillComplete(); + task->CompleteOnOriginThread(); + task->DidComplete(); + completed_tasks_.pop_front(); + } + } virtual void OnRasterTasksFinished() OVERRIDE {} virtual void OnRasterTasksRequiredForActivationFinished() OVERRIDE {} + + private: + typedef std::deque<scoped_refptr<internal::RasterWorkerPoolTask> > TaskDeque; + TaskDeque completed_tasks_; }; } // namespace |