OLD | NEW |
1 // Copyright 2012 The Chromium Authors. All rights reserved. | 1 // Copyright 2012 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "cc/resources/resource_pool.h" | 5 #include "cc/resources/resource_pool.h" |
6 | 6 |
7 #include <algorithm> | 7 #include <algorithm> |
8 | 8 |
9 #include "base/format_macros.h" | 9 #include "base/format_macros.h" |
10 #include "base/strings/stringprintf.h" | 10 #include "base/strings/stringprintf.h" |
11 #include "base/thread_task_runner_handle.h" | 11 #include "base/thread_task_runner_handle.h" |
12 #include "base/trace_event/memory_dump_manager.h" | 12 #include "base/trace_event/memory_dump_manager.h" |
13 #include "cc/resources/resource_provider.h" | 13 #include "cc/resources/resource_provider.h" |
14 #include "cc/resources/resource_util.h" | 14 #include "cc/resources/resource_util.h" |
15 #include "cc/resources/scoped_resource.h" | 15 #include "cc/resources/scoped_resource.h" |
16 | 16 |
17 namespace cc { | 17 namespace cc { |
| 18 namespace { |
| 19 |
| 20 // Delay before a resource is considered expired. |
| 21 const int kResourceExpirationDelayMs = 1000; |
| 22 |
| 23 } // namespace |
18 | 24 |
19 void ResourcePool::PoolResource::OnMemoryDump( | 25 void ResourcePool::PoolResource::OnMemoryDump( |
20 base::trace_event::ProcessMemoryDump* pmd, | 26 base::trace_event::ProcessMemoryDump* pmd, |
21 const ResourceProvider* resource_provider, | 27 const ResourceProvider* resource_provider, |
22 bool is_free) const { | 28 bool is_free) const { |
23 // Resource IDs are not process-unique, so log with the ResourceProvider's | 29 // Resource IDs are not process-unique, so log with the ResourceProvider's |
24 // unique id. | 30 // unique id. |
25 std::string parent_node = | 31 std::string parent_node = |
26 base::StringPrintf("cc/resource_memory/provider_%d/resource_%d", | 32 base::StringPrintf("cc/resource_memory/provider_%d/resource_%d", |
27 resource_provider->tracing_id(), id()); | 33 resource_provider->tracing_id(), id()); |
(...skipping 11 matching lines...) Expand all Loading... |
39 dump->AddScalar(base::trace_event::MemoryAllocatorDump::kNameSize, | 45 dump->AddScalar(base::trace_event::MemoryAllocatorDump::kNameSize, |
40 base::trace_event::MemoryAllocatorDump::kUnitsBytes, | 46 base::trace_event::MemoryAllocatorDump::kUnitsBytes, |
41 total_bytes); | 47 total_bytes); |
42 | 48 |
43 if (is_free) { | 49 if (is_free) { |
44 dump->AddScalar("free_size", | 50 dump->AddScalar("free_size", |
45 base::trace_event::MemoryAllocatorDump::kUnitsBytes, | 51 base::trace_event::MemoryAllocatorDump::kUnitsBytes, |
46 total_bytes); | 52 total_bytes); |
47 } | 53 } |
48 } | 54 } |
49 ResourcePool::ResourcePool(ResourceProvider* resource_provider) | |
50 : resource_provider_(resource_provider), | |
51 target_(0), | |
52 max_memory_usage_bytes_(0), | |
53 max_unused_memory_usage_bytes_(0), | |
54 max_resource_count_(0), | |
55 memory_usage_bytes_(0), | |
56 unused_memory_usage_bytes_(0), | |
57 resource_count_(0) { | |
58 base::trace_event::MemoryDumpManager::GetInstance()->RegisterDumpProvider( | |
59 this, base::ThreadTaskRunnerHandle::Get()); | |
60 } | |
61 | 55 |
62 ResourcePool::ResourcePool(ResourceProvider* resource_provider, GLenum target) | 56 ResourcePool::ResourcePool(ResourceProvider* resource_provider, |
| 57 base::SingleThreadTaskRunner* task_runner, |
| 58 GLenum target) |
63 : resource_provider_(resource_provider), | 59 : resource_provider_(resource_provider), |
64 target_(target), | 60 target_(target), |
65 max_memory_usage_bytes_(0), | 61 max_memory_usage_bytes_(0), |
66 max_unused_memory_usage_bytes_(0), | |
67 max_resource_count_(0), | 62 max_resource_count_(0), |
68 memory_usage_bytes_(0), | 63 in_use_memory_usage_bytes_(0), |
69 unused_memory_usage_bytes_(0), | 64 total_memory_usage_bytes_(0), |
70 resource_count_(0) { | 65 total_resource_count_(0), |
71 DCHECK_NE(0u, target); | 66 task_runner_(task_runner), |
| 67 evict_expired_resources_pending_(false), |
| 68 resource_expiration_delay_( |
| 69 base::TimeDelta::FromMilliseconds(kResourceExpirationDelayMs)), |
| 70 weak_ptr_factory_(this) { |
72 base::trace_event::MemoryDumpManager::GetInstance()->RegisterDumpProvider( | 71 base::trace_event::MemoryDumpManager::GetInstance()->RegisterDumpProvider( |
73 this, base::ThreadTaskRunnerHandle::Get()); | 72 this, task_runner_.get()); |
74 } | 73 } |
75 | 74 |
76 ResourcePool::~ResourcePool() { | 75 ResourcePool::~ResourcePool() { |
77 base::trace_event::MemoryDumpManager::GetInstance()->UnregisterDumpProvider( | 76 base::trace_event::MemoryDumpManager::GetInstance()->UnregisterDumpProvider( |
78 this); | 77 this); |
79 | 78 |
80 DCHECK_EQ(0u, in_use_resources_.size()); | 79 DCHECK_EQ(0u, in_use_resources_.size()); |
81 | 80 |
82 while (!busy_resources_.empty()) { | 81 while (!busy_resources_.empty()) { |
83 DidFinishUsingResource(busy_resources_.take_front()); | 82 DidFinishUsingResource(busy_resources_.take_front()); |
84 } | 83 } |
85 | 84 |
86 SetResourceUsageLimits(0, 0, 0); | 85 SetResourceUsageLimits(0, 0); |
87 DCHECK_EQ(0u, unused_resources_.size()); | 86 DCHECK_EQ(0u, unused_resources_.size()); |
88 DCHECK_EQ(0u, memory_usage_bytes_); | 87 DCHECK_EQ(0u, in_use_memory_usage_bytes_); |
89 DCHECK_EQ(0u, unused_memory_usage_bytes_); | 88 DCHECK_EQ(0u, total_memory_usage_bytes_); |
90 DCHECK_EQ(0u, resource_count_); | 89 DCHECK_EQ(0u, total_resource_count_); |
91 } | 90 } |
92 | 91 |
93 Resource* ResourcePool::AcquireResource(const gfx::Size& size, | 92 Resource* ResourcePool::AcquireResource(const gfx::Size& size, |
94 ResourceFormat format) { | 93 ResourceFormat format) { |
95 for (ResourceDeque::iterator it = unused_resources_.begin(); | 94 for (ResourceDeque::iterator it = unused_resources_.begin(); |
96 it != unused_resources_.end(); ++it) { | 95 it != unused_resources_.end(); ++it) { |
97 ScopedResource* resource = *it; | 96 ScopedResource* resource = *it; |
98 DCHECK(resource_provider_->CanLockForWrite(resource->id())); | 97 DCHECK(resource_provider_->CanLockForWrite(resource->id())); |
99 | 98 |
100 if (resource->format() != format) | 99 if (resource->format() != format) |
101 continue; | 100 continue; |
102 if (resource->size() != size) | 101 if (resource->size() != size) |
103 continue; | 102 continue; |
104 | 103 |
105 // Transfer resource to |in_use_resources_|. | 104 // Transfer resource to |in_use_resources_|. |
106 in_use_resources_.set(resource->id(), unused_resources_.take(it)); | 105 in_use_resources_.set(resource->id(), unused_resources_.take(it)); |
107 | 106 in_use_memory_usage_bytes_ += ResourceUtil::UncheckedSizeInBytes<size_t>( |
108 unused_memory_usage_bytes_ -= | 107 resource->size(), resource->format()); |
109 ResourceUtil::UncheckedSizeInBytes<size_t>(size, format); | |
110 return resource; | 108 return resource; |
111 } | 109 } |
112 | 110 |
113 scoped_ptr<PoolResource> pool_resource = | 111 scoped_ptr<PoolResource> pool_resource = |
114 PoolResource::Create(resource_provider_); | 112 PoolResource::Create(resource_provider_); |
115 GLenum target = | 113 GLenum target = |
116 target_ ? target_ : resource_provider_->GetImageTextureTarget(format); | 114 target_ ? target_ : resource_provider_->GetImageTextureTarget(format); |
117 pool_resource->AllocateManaged(size, target, format); | 115 pool_resource->AllocateManaged(size, target, format); |
118 | 116 |
119 DCHECK(ResourceUtil::VerifySizeInBytes<size_t>(pool_resource->size(), | 117 DCHECK(ResourceUtil::VerifySizeInBytes<size_t>(pool_resource->size(), |
120 pool_resource->format())); | 118 pool_resource->format())); |
121 memory_usage_bytes_ += ResourceUtil::UncheckedSizeInBytes<size_t>( | 119 total_memory_usage_bytes_ += ResourceUtil::UncheckedSizeInBytes<size_t>( |
122 pool_resource->size(), pool_resource->format()); | 120 pool_resource->size(), pool_resource->format()); |
123 ++resource_count_; | 121 ++total_resource_count_; |
124 | 122 |
125 Resource* resource = pool_resource.get(); | 123 Resource* resource = pool_resource.get(); |
126 in_use_resources_.set(resource->id(), pool_resource.Pass()); | 124 in_use_resources_.set(resource->id(), pool_resource.Pass()); |
| 125 in_use_memory_usage_bytes_ += ResourceUtil::UncheckedSizeInBytes<size_t>( |
| 126 resource->size(), resource->format()); |
127 return resource; | 127 return resource; |
128 } | 128 } |
129 | 129 |
130 Resource* ResourcePool::TryAcquireResourceWithContentId(uint64_t content_id) { | 130 Resource* ResourcePool::TryAcquireResourceWithContentId(uint64_t content_id) { |
131 DCHECK(content_id); | 131 DCHECK(content_id); |
132 | 132 |
133 auto it = std::find_if(unused_resources_.begin(), unused_resources_.end(), | 133 auto it = std::find_if(unused_resources_.begin(), unused_resources_.end(), |
134 [content_id](const PoolResource* pool_resource) { | 134 [content_id](const PoolResource* pool_resource) { |
135 return pool_resource->content_id() == content_id; | 135 return pool_resource->content_id() == content_id; |
136 }); | 136 }); |
137 if (it == unused_resources_.end()) | 137 if (it == unused_resources_.end()) |
138 return nullptr; | 138 return nullptr; |
139 | 139 |
140 Resource* resource = *it; | 140 Resource* resource = *it; |
141 DCHECK(resource_provider_->CanLockForWrite(resource->id())); | 141 DCHECK(resource_provider_->CanLockForWrite(resource->id())); |
142 | 142 |
143 // Transfer resource to |in_use_resources_|. | 143 // Transfer resource to |in_use_resources_|. |
144 in_use_resources_.set(resource->id(), unused_resources_.take(it)); | 144 in_use_resources_.set(resource->id(), unused_resources_.take(it)); |
145 | 145 in_use_memory_usage_bytes_ += ResourceUtil::UncheckedSizeInBytes<size_t>( |
146 unused_memory_usage_bytes_ -= ResourceUtil::UncheckedSizeInBytes<size_t>( | |
147 resource->size(), resource->format()); | 146 resource->size(), resource->format()); |
148 return resource; | 147 return resource; |
149 } | 148 } |
150 | 149 |
151 void ResourcePool::ReleaseResource(Resource* resource, uint64_t content_id) { | 150 void ResourcePool::ReleaseResource(Resource* resource, uint64_t content_id) { |
152 auto it = in_use_resources_.find(resource->id()); | 151 auto it = in_use_resources_.find(resource->id()); |
153 DCHECK(it != in_use_resources_.end()); | 152 DCHECK(it != in_use_resources_.end()); |
154 | 153 |
155 PoolResource* pool_resource = it->second; | 154 PoolResource* pool_resource = it->second; |
156 pool_resource->set_content_id(content_id); | 155 pool_resource->set_content_id(content_id); |
| 156 pool_resource->set_last_usage(base::TimeTicks::Now()); |
157 | 157 |
158 // Transfer resource to |busy_resources_|. | 158 // Transfer resource to |busy_resources_|. |
159 busy_resources_.push_back(in_use_resources_.take_and_erase(it)); | 159 busy_resources_.push_back(in_use_resources_.take_and_erase(it)); |
| 160 in_use_memory_usage_bytes_ -= ResourceUtil::UncheckedSizeInBytes<size_t>( |
| 161 pool_resource->size(), pool_resource->format()); |
| 162 |
| 163 // Now that we have evictable resources, schedule an eviction call for this |
| 164 // resource if necessary. |
| 165 ScheduleEvictExpiredResourcesIn(resource_expiration_delay_); |
160 } | 166 } |
161 | 167 |
162 void ResourcePool::SetResourceUsageLimits(size_t max_memory_usage_bytes, | 168 void ResourcePool::SetResourceUsageLimits(size_t max_memory_usage_bytes, |
163 size_t max_unused_memory_usage_bytes, | |
164 size_t max_resource_count) { | 169 size_t max_resource_count) { |
165 max_memory_usage_bytes_ = max_memory_usage_bytes; | 170 max_memory_usage_bytes_ = max_memory_usage_bytes; |
166 max_unused_memory_usage_bytes_ = max_unused_memory_usage_bytes; | |
167 max_resource_count_ = max_resource_count; | 171 max_resource_count_ = max_resource_count; |
168 | 172 |
169 ReduceResourceUsage(); | 173 ReduceResourceUsage(); |
170 } | 174 } |
171 | 175 |
172 void ResourcePool::ReduceResourceUsage() { | 176 void ResourcePool::ReduceResourceUsage() { |
173 while (!unused_resources_.empty()) { | 177 while (!unused_resources_.empty()) { |
174 if (!ResourceUsageTooHigh()) | 178 if (!ResourceUsageTooHigh()) |
175 break; | 179 break; |
176 | 180 |
177 // LRU eviction pattern. Most recently used might be blocked by | 181 // LRU eviction pattern. Most recently used might be blocked by |
178 // a read lock fence but it's still better to evict the least | 182 // a read lock fence but it's still better to evict the least |
179 // recently used as it prevents a resource that is hard to reuse | 183 // recently used as it prevents a resource that is hard to reuse |
180 // because of unique size from being kept around. Resources that | 184 // because of unique size from being kept around. Resources that |
181 // can't be locked for write might also not be truly free-able. | 185 // can't be locked for write might also not be truly free-able. |
182 // We can free the resource here but it doesn't mean that the | 186 // We can free the resource here but it doesn't mean that the |
183 // memory is necessarily returned to the OS. | 187 // memory is necessarily returned to the OS. |
184 scoped_ptr<PoolResource> resource = unused_resources_.take_front(); | 188 DeleteResource(unused_resources_.take_front()); |
185 unused_memory_usage_bytes_ -= ResourceUtil::UncheckedSizeInBytes<size_t>( | |
186 resource->size(), resource->format()); | |
187 DeleteResource(resource.Pass()); | |
188 } | 189 } |
189 } | 190 } |
190 | 191 |
191 bool ResourcePool::ResourceUsageTooHigh() { | 192 bool ResourcePool::ResourceUsageTooHigh() { |
192 if (resource_count_ > max_resource_count_) | 193 if (total_resource_count_ > max_resource_count_) |
193 return true; | 194 return true; |
194 if (memory_usage_bytes_ > max_memory_usage_bytes_) | 195 if (total_memory_usage_bytes_ > max_memory_usage_bytes_) |
195 return true; | |
196 if (unused_memory_usage_bytes_ > max_unused_memory_usage_bytes_) | |
197 return true; | 196 return true; |
198 return false; | 197 return false; |
199 } | 198 } |
200 | 199 |
201 void ResourcePool::DeleteResource(scoped_ptr<PoolResource> resource) { | 200 void ResourcePool::DeleteResource(scoped_ptr<PoolResource> resource) { |
202 size_t resource_bytes = ResourceUtil::UncheckedSizeInBytes<size_t>( | 201 size_t resource_bytes = ResourceUtil::UncheckedSizeInBytes<size_t>( |
203 resource->size(), resource->format()); | 202 resource->size(), resource->format()); |
204 memory_usage_bytes_ -= resource_bytes; | 203 total_memory_usage_bytes_ -= resource_bytes; |
205 --resource_count_; | 204 --total_resource_count_; |
206 } | 205 } |
207 | 206 |
208 void ResourcePool::CheckBusyResources() { | 207 void ResourcePool::CheckBusyResources() { |
209 for (size_t i = 0; i < busy_resources_.size();) { | 208 for (size_t i = 0; i < busy_resources_.size();) { |
210 ResourceDeque::iterator it(busy_resources_.begin() + i); | 209 ResourceDeque::iterator it(busy_resources_.begin() + i); |
211 PoolResource* resource = *it; | 210 PoolResource* resource = *it; |
212 | 211 |
213 if (resource_provider_->CanLockForWrite(resource->id())) { | 212 if (resource_provider_->CanLockForWrite(resource->id())) { |
214 DidFinishUsingResource(busy_resources_.take(it)); | 213 DidFinishUsingResource(busy_resources_.take(it)); |
215 } else if (resource_provider_->IsLost(resource->id())) { | 214 } else if (resource_provider_->IsLost(resource->id())) { |
216 // Remove lost resources from pool. | 215 // Remove lost resources from pool. |
217 DeleteResource(busy_resources_.take(it)); | 216 DeleteResource(busy_resources_.take(it)); |
218 } else { | 217 } else { |
219 ++i; | 218 ++i; |
220 } | 219 } |
221 } | 220 } |
222 } | 221 } |
223 | 222 |
224 void ResourcePool::DidFinishUsingResource(scoped_ptr<PoolResource> resource) { | 223 void ResourcePool::DidFinishUsingResource(scoped_ptr<PoolResource> resource) { |
225 unused_memory_usage_bytes_ += ResourceUtil::UncheckedSizeInBytes<size_t>( | |
226 resource->size(), resource->format()); | |
227 unused_resources_.push_back(resource.Pass()); | 224 unused_resources_.push_back(resource.Pass()); |
228 } | 225 } |
229 | 226 |
| 227 void ResourcePool::ScheduleEvictExpiredResourcesIn( |
| 228 base::TimeDelta time_from_now) { |
| 229 if (evict_expired_resources_pending_) |
| 230 return; |
| 231 |
| 232 evict_expired_resources_pending_ = true; |
| 233 |
| 234 task_runner_->PostDelayedTask(FROM_HERE, |
| 235 base::Bind(&ResourcePool::EvictExpiredResources, |
| 236 weak_ptr_factory_.GetWeakPtr()), |
| 237 time_from_now); |
| 238 } |
| 239 |
| 240 void ResourcePool::EvictExpiredResources() { |
| 241 evict_expired_resources_pending_ = false; |
| 242 base::TimeTicks current_time = base::TimeTicks::Now(); |
| 243 |
| 244 EvictResourcesNotUsedSince(current_time - resource_expiration_delay_); |
| 245 |
| 246 if (unused_resources_.empty() && busy_resources_.empty()) { |
| 247 // Nothing is evictable. |
| 248 return; |
| 249 } |
| 250 |
| 251 // If we still have evictable resources, schedule a call to |
| 252 // EvictExpiredResources at the time when the LRU buffer expires. |
| 253 ScheduleEvictExpiredResourcesIn(GetUsageTimeForLRUResource() + |
| 254 resource_expiration_delay_ - current_time); |
| 255 } |
| 256 |
| 257 void ResourcePool::EvictResourcesNotUsedSince(base::TimeTicks time_limit) { |
| 258 while (!unused_resources_.empty()) { |
| 259 // |unused_resources_| is not strictly ordered with regards to last_usage, |
| 260 // as this may not exactly line up with the time a resource became non-busy. |
| 261 // However, this should be roughly ordered, and will only introduce slight |
| 262 // delays in freeing expired resources. |
| 263 if (unused_resources_.front()->last_usage() > time_limit) |
| 264 return; |
| 265 |
| 266 DeleteResource(unused_resources_.take_front()); |
| 267 } |
| 268 |
| 269 // Also free busy resources older than the delay. With a sufficiently large |
| 270 // delay, such as the 1 second used here, any "busy" resources which have |
| 271 // expired are not likely to be busy. Additionally, freeing a "busy" resource |
| 272 // has no downside other than incorrect accounting. |
| 273 while (!busy_resources_.empty()) { |
| 274 if (busy_resources_.front()->last_usage() > time_limit) |
| 275 return; |
| 276 |
| 277 DeleteResource(busy_resources_.take_front()); |
| 278 } |
| 279 } |
| 280 |
| 281 base::TimeTicks ResourcePool::GetUsageTimeForLRUResource() const { |
| 282 if (!unused_resources_.empty()) { |
| 283 return unused_resources_.front()->last_usage(); |
| 284 } |
| 285 |
| 286 // This is only called when we have at least one evictable resource. |
| 287 DCHECK(!busy_resources_.empty()); |
| 288 return busy_resources_.front()->last_usage(); |
| 289 } |
| 290 |
230 bool ResourcePool::OnMemoryDump(const base::trace_event::MemoryDumpArgs& args, | 291 bool ResourcePool::OnMemoryDump(const base::trace_event::MemoryDumpArgs& args, |
231 base::trace_event::ProcessMemoryDump* pmd) { | 292 base::trace_event::ProcessMemoryDump* pmd) { |
232 for (const auto& resource : unused_resources_) { | 293 for (const auto& resource : unused_resources_) { |
233 resource->OnMemoryDump(pmd, resource_provider_, true /* is_free */); | 294 resource->OnMemoryDump(pmd, resource_provider_, true /* is_free */); |
234 } | 295 } |
235 for (const auto& resource : busy_resources_) { | 296 for (const auto& resource : busy_resources_) { |
236 resource->OnMemoryDump(pmd, resource_provider_, false /* is_free */); | 297 resource->OnMemoryDump(pmd, resource_provider_, false /* is_free */); |
237 } | 298 } |
238 for (const auto& entry : in_use_resources_) { | 299 for (const auto& entry : in_use_resources_) { |
239 entry.second->OnMemoryDump(pmd, resource_provider_, false /* is_free */); | 300 entry.second->OnMemoryDump(pmd, resource_provider_, false /* is_free */); |
240 } | 301 } |
241 return true; | 302 return true; |
242 } | 303 } |
243 | 304 |
244 } // namespace cc | 305 } // namespace cc |
OLD | NEW |