cc: Adding DidFinishImplFrame to LTHI.
[chromium-blink-merge.git] / cc / resources / prioritized_resource_manager.cc
blob236377b851184d66308d90e38ecec5b550ae153a
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
3 // found in the LICENSE file.
5 #include "cc/resources/prioritized_resource_manager.h"
7 #include <algorithm>
9 #include "base/trace_event/trace_event.h"
10 #include "cc/resources/prioritized_resource.h"
11 #include "cc/resources/priority_calculator.h"
12 #include "cc/trees/proxy.h"
14 namespace cc {
16 PrioritizedResourceManager::PrioritizedResourceManager(const Proxy* proxy)
17 : max_memory_limit_bytes_(DefaultMemoryAllocationLimit()),
18 external_priority_cutoff_(PriorityCalculator::AllowEverythingCutoff()),
19 memory_use_bytes_(0),
20 memory_above_cutoff_bytes_(0),
21 max_memory_needed_bytes_(0),
22 memory_available_bytes_(0),
23 proxy_(proxy),
24 backings_tail_not_sorted_(false),
25 memory_visible_bytes_(0),
26 memory_visible_and_nearby_bytes_(0),
27 memory_visible_last_pushed_bytes_(0),
28 memory_visible_and_nearby_last_pushed_bytes_(0) {}
30 PrioritizedResourceManager::~PrioritizedResourceManager() {
31 while (textures_.size() > 0)
32 UnregisterTexture(*textures_.begin());
34 UnlinkAndClearEvictedBackings();
35 DCHECK(evicted_backings_.empty());
37 // Each remaining backing is a leaked opengl texture. There should be none.
38 DCHECK(backings_.empty());
41 size_t PrioritizedResourceManager::MemoryVisibleBytes() const {
42 DCHECK(proxy_->IsImplThread());
43 return memory_visible_last_pushed_bytes_;
46 size_t PrioritizedResourceManager::MemoryVisibleAndNearbyBytes() const {
47 DCHECK(proxy_->IsImplThread());
48 return memory_visible_and_nearby_last_pushed_bytes_;
51 void PrioritizedResourceManager::PrioritizeTextures() {
52 TRACE_EVENT0("cc", "PrioritizedResourceManager::PrioritizeTextures");
53 DCHECK(proxy_->IsMainThread());
55 // Sorting textures in this function could be replaced by a slightly
56 // modified O(n) quick-select to partition textures rather than
57 // sort them (if performance of the sort becomes an issue).
59 TextureVector& sorted_textures = temp_texture_vector_;
60 sorted_textures.clear();
62 // Copy all textures into a vector, sort them, and collect memory requirements
63 // statistics.
64 memory_visible_bytes_ = 0;
65 memory_visible_and_nearby_bytes_ = 0;
66 for (TextureSet::iterator it = textures_.begin(); it != textures_.end();
67 ++it) {
68 PrioritizedResource* texture = (*it);
69 sorted_textures.push_back(texture);
70 if (PriorityCalculator::priority_is_higher(
71 texture->request_priority(),
72 PriorityCalculator::AllowVisibleOnlyCutoff()))
73 memory_visible_bytes_ += texture->bytes();
74 if (PriorityCalculator::priority_is_higher(
75 texture->request_priority(),
76 PriorityCalculator::AllowVisibleAndNearbyCutoff()))
77 memory_visible_and_nearby_bytes_ += texture->bytes();
79 std::sort(sorted_textures.begin(), sorted_textures.end(), CompareTextures);
81 // Compute a priority cutoff based on memory pressure
82 memory_available_bytes_ = max_memory_limit_bytes_;
83 priority_cutoff_ = external_priority_cutoff_;
84 size_t memory_bytes = 0;
85 for (TextureVector::iterator it = sorted_textures.begin();
86 it != sorted_textures.end();
87 ++it) {
88 if ((*it)->is_self_managed()) {
89 // Account for self-managed memory immediately by reducing the memory
90 // available (since it never gets acquired).
91 size_t new_memory_bytes = memory_bytes + (*it)->bytes();
92 if (new_memory_bytes > memory_available_bytes_) {
93 priority_cutoff_ = (*it)->request_priority();
94 memory_available_bytes_ = memory_bytes;
95 break;
97 memory_available_bytes_ -= (*it)->bytes();
98 } else {
99 size_t new_memory_bytes = memory_bytes + (*it)->bytes();
100 if (new_memory_bytes > memory_available_bytes_) {
101 priority_cutoff_ = (*it)->request_priority();
102 break;
104 memory_bytes = new_memory_bytes;
108 // Disallow any textures with priority below the external cutoff to have
109 // backings.
110 for (TextureVector::iterator it = sorted_textures.begin();
111 it != sorted_textures.end();
112 ++it) {
113 PrioritizedResource* texture = (*it);
114 if (!PriorityCalculator::priority_is_higher(texture->request_priority(),
115 external_priority_cutoff_) &&
116 texture->have_backing_texture())
117 texture->Unlink();
120 // Only allow textures if they are higher than the cutoff. All textures
121 // of the same priority are accepted or rejected together, rather than
122 // being partially allowed randomly.
123 max_memory_needed_bytes_ = 0;
124 memory_above_cutoff_bytes_ = 0;
125 for (TextureVector::iterator it = sorted_textures.begin();
126 it != sorted_textures.end();
127 ++it) {
128 PrioritizedResource* resource = *it;
129 bool is_above_priority_cutoff = PriorityCalculator::priority_is_higher(
130 resource->request_priority(), priority_cutoff_);
131 resource->set_above_priority_cutoff(is_above_priority_cutoff);
132 if (!resource->is_self_managed()) {
133 max_memory_needed_bytes_ += resource->bytes();
134 if (is_above_priority_cutoff)
135 memory_above_cutoff_bytes_ += resource->bytes();
138 sorted_textures.clear();
140 DCHECK_LE(memory_above_cutoff_bytes_, memory_available_bytes_);
141 DCHECK_LE(MemoryAboveCutoffBytes(), MaxMemoryLimitBytes());
144 void PrioritizedResourceManager::PushTexturePrioritiesToBackings() {
145 TRACE_EVENT0("cc",
146 "PrioritizedResourceManager::PushTexturePrioritiesToBackings");
147 DCHECK(proxy_->IsImplThread() && proxy_->IsMainThreadBlocked());
149 AssertInvariants();
150 for (BackingList::iterator it = backings_.begin(); it != backings_.end();
151 ++it)
152 (*it)->UpdatePriority();
153 SortBackings();
154 AssertInvariants();
156 // Push memory requirements to the impl thread structure.
157 memory_visible_last_pushed_bytes_ = memory_visible_bytes_;
158 memory_visible_and_nearby_last_pushed_bytes_ =
159 memory_visible_and_nearby_bytes_;
162 void PrioritizedResourceManager::UpdateBackingsState(
163 ResourceProvider* resource_provider) {
164 TRACE_EVENT0("cc",
165 "PrioritizedResourceManager::UpdateBackingsInDrawingImplTree");
166 DCHECK(proxy_->IsImplThread() && proxy_->IsMainThreadBlocked());
168 AssertInvariants();
169 for (BackingList::iterator it = backings_.begin(); it != backings_.end();
170 ++it) {
171 PrioritizedResource::Backing* backing = (*it);
172 backing->UpdateState(resource_provider);
174 SortBackings();
175 AssertInvariants();
178 void PrioritizedResourceManager::SortBackings() {
179 TRACE_EVENT0("cc", "PrioritizedResourceManager::SortBackings");
180 DCHECK(proxy_->IsImplThread());
182 // Put backings in eviction/recycling order.
183 backings_.sort(CompareBackings);
184 backings_tail_not_sorted_ = false;
187 void PrioritizedResourceManager::ClearPriorities() {
188 DCHECK(proxy_->IsMainThread());
189 for (TextureSet::iterator it = textures_.begin(); it != textures_.end();
190 ++it) {
191 // TODO(reveman): We should remove this and just set all priorities to
192 // PriorityCalculator::lowestPriority() once we have priorities for all
193 // textures (we can't currently calculate distances for off-screen
194 // textures).
195 (*it)->set_request_priority(
196 PriorityCalculator::LingeringPriority((*it)->request_priority()));
200 bool PrioritizedResourceManager::RequestLate(PrioritizedResource* texture) {
201 DCHECK(proxy_->IsMainThread());
203 // This is already above cutoff, so don't double count it's memory below.
204 if (texture->is_above_priority_cutoff())
205 return true;
207 // Allow textures that have priority equal to the cutoff, but not strictly
208 // lower.
209 if (PriorityCalculator::priority_is_lower(texture->request_priority(),
210 priority_cutoff_))
211 return false;
213 // Disallow textures that do not have a priority strictly higher than the
214 // external cutoff.
215 if (!PriorityCalculator::priority_is_higher(texture->request_priority(),
216 external_priority_cutoff_))
217 return false;
219 size_t new_memory_bytes = memory_above_cutoff_bytes_ + texture->bytes();
220 if (new_memory_bytes > memory_available_bytes_)
221 return false;
223 memory_above_cutoff_bytes_ = new_memory_bytes;
224 texture->set_above_priority_cutoff(true);
225 return true;
228 void PrioritizedResourceManager::AcquireBackingTextureIfNeeded(
229 PrioritizedResource* texture,
230 ResourceProvider* resource_provider) {
231 DCHECK(proxy_->IsImplThread() && proxy_->IsMainThreadBlocked());
232 DCHECK(!texture->is_self_managed());
233 DCHECK(texture->is_above_priority_cutoff());
234 if (texture->backing() || !texture->is_above_priority_cutoff())
235 return;
237 // Find a backing below, by either recycling or allocating.
238 PrioritizedResource::Backing* backing = NULL;
240 // First try to recycle
241 for (BackingList::iterator it = backings_.begin(); it != backings_.end();
242 ++it) {
243 if (!(*it)->CanBeRecycledIfNotInExternalUse())
244 break;
245 if (resource_provider->InUseByConsumer((*it)->id()))
246 continue;
247 if ((*it)->size() == texture->size() &&
248 (*it)->format() == texture->format()) {
249 backing = (*it);
250 backings_.erase(it);
251 break;
255 // Otherwise reduce memory and just allocate a new backing texures.
256 if (!backing) {
257 EvictBackingsToReduceMemory(memory_available_bytes_ - texture->bytes(),
258 PriorityCalculator::AllowEverythingCutoff(),
259 EVICT_ONLY_RECYCLABLE,
260 DO_NOT_UNLINK_BACKINGS,
261 resource_provider);
262 backing =
263 CreateBacking(texture->size(), texture->format(), resource_provider);
266 // Move the used backing to the end of the eviction list, and note that
267 // the tail is not sorted.
268 if (backing->owner())
269 backing->owner()->Unlink();
270 texture->Link(backing);
271 backings_.push_back(backing);
272 backings_tail_not_sorted_ = true;
274 // Update the backing's priority from its new owner.
275 backing->UpdatePriority();
278 bool PrioritizedResourceManager::EvictBackingsToReduceMemory(
279 size_t limit_bytes,
280 int priority_cutoff,
281 EvictionPolicy eviction_policy,
282 UnlinkPolicy unlink_policy,
283 ResourceProvider* resource_provider) {
284 DCHECK(proxy_->IsImplThread());
285 if (unlink_policy == UNLINK_BACKINGS)
286 DCHECK(proxy_->IsMainThreadBlocked());
287 if (MemoryUseBytes() <= limit_bytes &&
288 PriorityCalculator::AllowEverythingCutoff() == priority_cutoff)
289 return false;
291 // Destroy backings until we are below the limit,
292 // or until all backings remaining are above the cutoff.
293 bool evicted_anything = false;
294 while (backings_.size() > 0) {
295 PrioritizedResource::Backing* backing = backings_.front();
296 if (MemoryUseBytes() <= limit_bytes &&
297 PriorityCalculator::priority_is_higher(
298 backing->request_priority_at_last_priority_update(),
299 priority_cutoff))
300 break;
301 if (eviction_policy == EVICT_ONLY_RECYCLABLE &&
302 !backing->CanBeRecycledIfNotInExternalUse())
303 break;
304 if (unlink_policy == UNLINK_BACKINGS && backing->owner())
305 backing->owner()->Unlink();
306 EvictFirstBackingResource(resource_provider);
307 evicted_anything = true;
309 return evicted_anything;
312 void PrioritizedResourceManager::ReduceWastedMemory(
313 ResourceProvider* resource_provider) {
314 // We currently collect backings from deleted textures for later recycling.
315 // However, if we do that forever we will always use the max limit even if
316 // we really need very little memory. This should probably be solved by
317 // reducing the limit externally, but until then this just does some "clean
318 // up" of unused backing textures (any more than 10%).
319 size_t wasted_memory = 0;
320 for (BackingList::iterator it = backings_.begin(); it != backings_.end();
321 ++it) {
322 if ((*it)->owner())
323 break;
324 if ((*it)->in_parent_compositor())
325 continue;
326 wasted_memory += (*it)->bytes();
328 size_t wasted_memory_to_allow = memory_available_bytes_ / 10;
329 // If the external priority cutoff indicates that unused memory should be
330 // freed, then do not allow any memory for texture recycling.
331 if (external_priority_cutoff_ != PriorityCalculator::AllowEverythingCutoff())
332 wasted_memory_to_allow = 0;
333 if (wasted_memory > wasted_memory_to_allow)
334 EvictBackingsToReduceMemory(MemoryUseBytes() -
335 (wasted_memory - wasted_memory_to_allow),
336 PriorityCalculator::AllowEverythingCutoff(),
337 EVICT_ONLY_RECYCLABLE,
338 DO_NOT_UNLINK_BACKINGS,
339 resource_provider);
342 void PrioritizedResourceManager::ReduceMemory(
343 ResourceProvider* resource_provider) {
344 DCHECK(proxy_->IsImplThread() && proxy_->IsMainThreadBlocked());
345 EvictBackingsToReduceMemory(memory_available_bytes_,
346 PriorityCalculator::AllowEverythingCutoff(),
347 EVICT_ANYTHING,
348 UNLINK_BACKINGS,
349 resource_provider);
350 DCHECK_LE(MemoryUseBytes(), memory_available_bytes_);
352 ReduceWastedMemory(resource_provider);
355 void PrioritizedResourceManager::ClearAllMemory(
356 ResourceProvider* resource_provider) {
357 DCHECK(proxy_->IsImplThread() && proxy_->IsMainThreadBlocked());
358 if (!resource_provider) {
359 DCHECK(backings_.empty());
360 return;
362 EvictBackingsToReduceMemory(0,
363 PriorityCalculator::AllowEverythingCutoff(),
364 EVICT_ANYTHING,
365 DO_NOT_UNLINK_BACKINGS,
366 resource_provider);
369 bool PrioritizedResourceManager::ReduceMemoryOnImplThread(
370 size_t limit_bytes,
371 int priority_cutoff,
372 ResourceProvider* resource_provider) {
373 DCHECK(proxy_->IsImplThread());
374 DCHECK(resource_provider);
376 // If we are in the process of uploading a new frame then the backings at the
377 // very end of the list are not sorted by priority. Sort them before doing the
378 // eviction.
379 if (backings_tail_not_sorted_)
380 SortBackings();
381 return EvictBackingsToReduceMemory(limit_bytes,
382 priority_cutoff,
383 EVICT_ANYTHING,
384 DO_NOT_UNLINK_BACKINGS,
385 resource_provider);
388 void PrioritizedResourceManager::UnlinkAndClearEvictedBackings() {
389 DCHECK(proxy_->IsMainThread());
390 base::AutoLock scoped_lock(evicted_backings_lock_);
391 for (BackingList::const_iterator it = evicted_backings_.begin();
392 it != evicted_backings_.end();
393 ++it) {
394 PrioritizedResource::Backing* backing = (*it);
395 if (backing->owner())
396 backing->owner()->Unlink();
397 delete backing;
399 evicted_backings_.clear();
402 bool PrioritizedResourceManager::LinkedEvictedBackingsExist() const {
403 DCHECK(proxy_->IsImplThread() && proxy_->IsMainThreadBlocked());
404 base::AutoLock scoped_lock(evicted_backings_lock_);
405 for (BackingList::const_iterator it = evicted_backings_.begin();
406 it != evicted_backings_.end();
407 ++it) {
408 if ((*it)->owner())
409 return true;
411 return false;
414 void PrioritizedResourceManager::RegisterTexture(PrioritizedResource* texture) {
415 DCHECK(proxy_->IsMainThread());
416 DCHECK(texture);
417 DCHECK(!texture->resource_manager());
418 DCHECK(!texture->backing());
419 DCHECK(!ContainsKey(textures_, texture));
421 texture->set_manager_internal(this);
422 textures_.insert(texture);
425 void PrioritizedResourceManager::UnregisterTexture(
426 PrioritizedResource* texture) {
427 DCHECK(proxy_->IsMainThread() ||
428 (proxy_->IsImplThread() && proxy_->IsMainThreadBlocked()));
429 DCHECK(texture);
430 DCHECK(ContainsKey(textures_, texture));
432 ReturnBackingTexture(texture);
433 texture->set_manager_internal(NULL);
434 textures_.erase(texture);
435 texture->set_above_priority_cutoff(false);
438 void PrioritizedResourceManager::ReturnBackingTexture(
439 PrioritizedResource* texture) {
440 DCHECK(proxy_->IsMainThread() ||
441 (proxy_->IsImplThread() && proxy_->IsMainThreadBlocked()));
442 if (texture->backing())
443 texture->Unlink();
446 PrioritizedResource::Backing* PrioritizedResourceManager::CreateBacking(
447 const gfx::Size& size,
448 ResourceFormat format,
449 ResourceProvider* resource_provider) {
450 DCHECK(proxy_->IsImplThread() && proxy_->IsMainThreadBlocked());
451 DCHECK(resource_provider);
452 ResourceProvider::ResourceId resource_id =
453 resource_provider->CreateManagedResource(
454 size, GL_TEXTURE_2D, GL_CLAMP_TO_EDGE,
455 ResourceProvider::TEXTURE_HINT_IMMUTABLE, format);
456 PrioritizedResource::Backing* backing = new PrioritizedResource::Backing(
457 resource_id, resource_provider, size, format);
458 memory_use_bytes_ += backing->bytes();
459 return backing;
462 void PrioritizedResourceManager::EvictFirstBackingResource(
463 ResourceProvider* resource_provider) {
464 DCHECK(proxy_->IsImplThread());
465 DCHECK(resource_provider);
466 DCHECK(!backings_.empty());
467 PrioritizedResource::Backing* backing = backings_.front();
469 // Note that we create a backing and its resource at the same time, but we
470 // delete the backing structure and its resource in two steps. This is because
471 // we can delete the resource while the main thread is running, but we cannot
472 // unlink backings while the main thread is running.
473 backing->DeleteResource(resource_provider);
474 memory_use_bytes_ -= backing->bytes();
475 backings_.pop_front();
476 base::AutoLock scoped_lock(evicted_backings_lock_);
477 evicted_backings_.push_back(backing);
480 void PrioritizedResourceManager::AssertInvariants() {
481 #if DCHECK_IS_ON()
482 DCHECK(proxy_->IsImplThread() && proxy_->IsMainThreadBlocked());
484 // If we hit any of these asserts, there is a bug in this class. To see
485 // where the bug is, call this function at the beginning and end of
486 // every public function.
488 // Backings/textures must be doubly-linked and only to other backings/textures
489 // in this manager.
490 for (BackingList::iterator it = backings_.begin(); it != backings_.end();
491 ++it) {
492 if ((*it)->owner()) {
493 DCHECK(ContainsKey(textures_, (*it)->owner()));
494 DCHECK((*it)->owner()->backing() == (*it));
497 for (TextureSet::iterator it = textures_.begin(); it != textures_.end();
498 ++it) {
499 PrioritizedResource* texture = (*it);
500 PrioritizedResource::Backing* backing = texture->backing();
501 base::AutoLock scoped_lock(evicted_backings_lock_);
502 if (backing) {
503 if (backing->ResourceHasBeenDeleted()) {
504 DCHECK(std::find(backings_.begin(), backings_.end(), backing) ==
505 backings_.end());
506 DCHECK(std::find(evicted_backings_.begin(),
507 evicted_backings_.end(),
508 backing) != evicted_backings_.end());
509 } else {
510 DCHECK(std::find(backings_.begin(), backings_.end(), backing) !=
511 backings_.end());
512 DCHECK(std::find(evicted_backings_.begin(),
513 evicted_backings_.end(),
514 backing) == evicted_backings_.end());
516 DCHECK(backing->owner() == texture);
520 // At all times, backings that can be evicted must always come before
521 // backings that can't be evicted in the backing texture list (otherwise
522 // ReduceMemory will not find all textures available for eviction/recycling).
523 bool reached_unrecyclable = false;
524 PrioritizedResource::Backing* previous_backing = NULL;
525 for (BackingList::iterator it = backings_.begin(); it != backings_.end();
526 ++it) {
527 PrioritizedResource::Backing* backing = *it;
528 if (previous_backing &&
529 (!backings_tail_not_sorted_ ||
530 !backing->was_above_priority_cutoff_at_last_priority_update()))
531 DCHECK(CompareBackings(previous_backing, backing));
532 if (!backing->CanBeRecycledIfNotInExternalUse())
533 reached_unrecyclable = true;
534 if (reached_unrecyclable)
535 DCHECK(!backing->CanBeRecycledIfNotInExternalUse());
536 else
537 DCHECK(backing->CanBeRecycledIfNotInExternalUse());
538 previous_backing = backing;
540 #endif // DCHECK_IS_ON()
543 const Proxy* PrioritizedResourceManager::ProxyForDebug() const {
544 return proxy_;
547 } // namespace cc