| // Copyright 2012 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/prioritized_resource_manager.h" | 
 |  | 
 | #include <algorithm> | 
 |  | 
 | #include "base/debug/trace_event.h" | 
 | #include "cc/resources/prioritized_resource.h" | 
 | #include "cc/resources/priority_calculator.h" | 
 | #include "cc/trees/proxy.h" | 
 |  | 
 | namespace cc { | 
 |  | 
 | PrioritizedResourceManager::PrioritizedResourceManager(const Proxy* proxy) | 
 |     : max_memory_limit_bytes_(DefaultMemoryAllocationLimit()), | 
 |       external_priority_cutoff_(PriorityCalculator::AllowEverythingCutoff()), | 
 |       memory_use_bytes_(0), | 
 |       memory_above_cutoff_bytes_(0), | 
 |       max_memory_needed_bytes_(0), | 
 |       memory_available_bytes_(0), | 
 |       proxy_(proxy), | 
 |       backings_tail_not_sorted_(false), | 
 |       memory_visible_bytes_(0), | 
 |       memory_visible_and_nearby_bytes_(0), | 
 |       memory_visible_last_pushed_bytes_(0), | 
 |       memory_visible_and_nearby_last_pushed_bytes_(0) {} | 
 |  | 
 | PrioritizedResourceManager::~PrioritizedResourceManager() { | 
 |   while (textures_.size() > 0) | 
 |     UnregisterTexture(*textures_.begin()); | 
 |  | 
 |   UnlinkAndClearEvictedBackings(); | 
 |   DCHECK(evicted_backings_.empty()); | 
 |  | 
 |   // Each remaining backing is a leaked opengl texture. There should be none. | 
 |   DCHECK(backings_.empty()); | 
 | } | 
 |  | 
 | size_t PrioritizedResourceManager::MemoryVisibleBytes() const { | 
 |   DCHECK(proxy_->IsImplThread()); | 
 |   return memory_visible_last_pushed_bytes_; | 
 | } | 
 |  | 
 | size_t PrioritizedResourceManager::MemoryVisibleAndNearbyBytes() const { | 
 |   DCHECK(proxy_->IsImplThread()); | 
 |   return memory_visible_and_nearby_last_pushed_bytes_; | 
 | } | 
 |  | 
 | void PrioritizedResourceManager::PrioritizeTextures() { | 
 |   TRACE_EVENT0("cc", "PrioritizedResourceManager::PrioritizeTextures"); | 
 |   DCHECK(proxy_->IsMainThread()); | 
 |  | 
 |   // Sorting textures in this function could be replaced by a slightly | 
 |   // modified O(n) quick-select to partition textures rather than | 
 |   // sort them (if performance of the sort becomes an issue). | 
 |  | 
 |   TextureVector& sorted_textures = temp_texture_vector_; | 
 |   sorted_textures.clear(); | 
 |  | 
 |   // Copy all textures into a vector, sort them, and collect memory requirements | 
 |   // statistics. | 
 |   memory_visible_bytes_ = 0; | 
 |   memory_visible_and_nearby_bytes_ = 0; | 
 |   for (TextureSet::iterator it = textures_.begin(); it != textures_.end(); | 
 |        ++it) { | 
 |     PrioritizedResource* texture = (*it); | 
 |     sorted_textures.push_back(texture); | 
 |     if (PriorityCalculator::priority_is_higher( | 
 |             texture->request_priority(), | 
 |             PriorityCalculator::AllowVisibleOnlyCutoff())) | 
 |       memory_visible_bytes_ += texture->bytes(); | 
 |     if (PriorityCalculator::priority_is_higher( | 
 |             texture->request_priority(), | 
 |             PriorityCalculator::AllowVisibleAndNearbyCutoff())) | 
 |       memory_visible_and_nearby_bytes_ += texture->bytes(); | 
 |   } | 
 |   std::sort(sorted_textures.begin(), sorted_textures.end(), CompareTextures); | 
 |  | 
 |   // Compute a priority cutoff based on memory pressure | 
 |   memory_available_bytes_ = max_memory_limit_bytes_; | 
 |   priority_cutoff_ = external_priority_cutoff_; | 
 |   size_t memory_bytes = 0; | 
 |   for (TextureVector::iterator it = sorted_textures.begin(); | 
 |        it != sorted_textures.end(); | 
 |        ++it) { | 
 |     if ((*it)->is_self_managed()) { | 
 |       // Account for self-managed memory immediately by reducing the memory | 
 |       // available (since it never gets acquired). | 
 |       size_t new_memory_bytes = memory_bytes + (*it)->bytes(); | 
 |       if (new_memory_bytes > memory_available_bytes_) { | 
 |         priority_cutoff_ = (*it)->request_priority(); | 
 |         memory_available_bytes_ = memory_bytes; | 
 |         break; | 
 |       } | 
 |       memory_available_bytes_ -= (*it)->bytes(); | 
 |     } else { | 
 |       size_t new_memory_bytes = memory_bytes + (*it)->bytes(); | 
 |       if (new_memory_bytes > memory_available_bytes_) { | 
 |         priority_cutoff_ = (*it)->request_priority(); | 
 |         break; | 
 |       } | 
 |       memory_bytes = new_memory_bytes; | 
 |     } | 
 |   } | 
 |  | 
 |   // Disallow any textures with priority below the external cutoff to have | 
 |   // backings. | 
 |   for (TextureVector::iterator it = sorted_textures.begin(); | 
 |        it != sorted_textures.end(); | 
 |        ++it) { | 
 |     PrioritizedResource* texture = (*it); | 
 |     if (!PriorityCalculator::priority_is_higher(texture->request_priority(), | 
 |                                                 external_priority_cutoff_) && | 
 |         texture->have_backing_texture()) | 
 |       texture->Unlink(); | 
 |   } | 
 |  | 
 |   // Only allow textures if they are higher than the cutoff. All textures | 
 |   // of the same priority are accepted or rejected together, rather than | 
 |   // being partially allowed randomly. | 
 |   max_memory_needed_bytes_ = 0; | 
 |   memory_above_cutoff_bytes_ = 0; | 
 |   for (TextureVector::iterator it = sorted_textures.begin(); | 
 |        it != sorted_textures.end(); | 
 |        ++it) { | 
 |     PrioritizedResource* resource = *it; | 
 |     bool is_above_priority_cutoff = PriorityCalculator::priority_is_higher( | 
 |         resource->request_priority(), priority_cutoff_); | 
 |     resource->set_above_priority_cutoff(is_above_priority_cutoff); | 
 |     if (!resource->is_self_managed()) { | 
 |       max_memory_needed_bytes_ += resource->bytes(); | 
 |       if (is_above_priority_cutoff) | 
 |         memory_above_cutoff_bytes_ += resource->bytes(); | 
 |     } | 
 |   } | 
 |   sorted_textures.clear(); | 
 |  | 
 |   DCHECK_LE(memory_above_cutoff_bytes_, memory_available_bytes_); | 
 |   DCHECK_LE(MemoryAboveCutoffBytes(), MaxMemoryLimitBytes()); | 
 | } | 
 |  | 
 | void PrioritizedResourceManager::PushTexturePrioritiesToBackings() { | 
 |   TRACE_EVENT0("cc", | 
 |                "PrioritizedResourceManager::PushTexturePrioritiesToBackings"); | 
 |   DCHECK(proxy_->IsImplThread() && proxy_->IsMainThreadBlocked()); | 
 |  | 
 |   AssertInvariants(); | 
 |   for (BackingList::iterator it = backings_.begin(); it != backings_.end(); | 
 |        ++it) | 
 |     (*it)->UpdatePriority(); | 
 |   SortBackings(); | 
 |   AssertInvariants(); | 
 |  | 
 |   // Push memory requirements to the impl thread structure. | 
 |   memory_visible_last_pushed_bytes_ = memory_visible_bytes_; | 
 |   memory_visible_and_nearby_last_pushed_bytes_ = | 
 |       memory_visible_and_nearby_bytes_; | 
 | } | 
 |  | 
 | void PrioritizedResourceManager::UpdateBackingsState( | 
 |     ResourceProvider* resource_provider) { | 
 |   TRACE_EVENT0("cc", | 
 |                "PrioritizedResourceManager::UpdateBackingsInDrawingImplTree"); | 
 |   DCHECK(proxy_->IsImplThread() && proxy_->IsMainThreadBlocked()); | 
 |  | 
 |   AssertInvariants(); | 
 |   for (BackingList::iterator it = backings_.begin(); it != backings_.end(); | 
 |        ++it) { | 
 |     PrioritizedResource::Backing* backing = (*it); | 
 |     backing->UpdateState(resource_provider); | 
 |   } | 
 |   SortBackings(); | 
 |   AssertInvariants(); | 
 | } | 
 |  | 
 | void PrioritizedResourceManager::SortBackings() { | 
 |   TRACE_EVENT0("cc", "PrioritizedResourceManager::SortBackings"); | 
 |   DCHECK(proxy_->IsImplThread()); | 
 |  | 
 |   // Put backings in eviction/recycling order. | 
 |   backings_.sort(CompareBackings); | 
 |   backings_tail_not_sorted_ = false; | 
 | } | 
 |  | 
 | void PrioritizedResourceManager::ClearPriorities() { | 
 |   DCHECK(proxy_->IsMainThread()); | 
 |   for (TextureSet::iterator it = textures_.begin(); it != textures_.end(); | 
 |        ++it) { | 
 |     // TODO(reveman): We should remove this and just set all priorities to | 
 |     // PriorityCalculator::lowestPriority() once we have priorities for all | 
 |     // textures (we can't currently calculate distances for off-screen | 
 |     // textures). | 
 |     (*it)->set_request_priority( | 
 |         PriorityCalculator::LingeringPriority((*it)->request_priority())); | 
 |   } | 
 | } | 
 |  | 
 | bool PrioritizedResourceManager::RequestLate(PrioritizedResource* texture) { | 
 |   DCHECK(proxy_->IsMainThread()); | 
 |  | 
 |   // This is already above cutoff, so don't double count it's memory below. | 
 |   if (texture->is_above_priority_cutoff()) | 
 |     return true; | 
 |  | 
 |   // Allow textures that have priority equal to the cutoff, but not strictly | 
 |   // lower. | 
 |   if (PriorityCalculator::priority_is_lower(texture->request_priority(), | 
 |                                             priority_cutoff_)) | 
 |     return false; | 
 |  | 
 |   // Disallow textures that do not have a priority strictly higher than the | 
 |   // external cutoff. | 
 |   if (!PriorityCalculator::priority_is_higher(texture->request_priority(), | 
 |                                               external_priority_cutoff_)) | 
 |     return false; | 
 |  | 
 |   size_t new_memory_bytes = memory_above_cutoff_bytes_ + texture->bytes(); | 
 |   if (new_memory_bytes > memory_available_bytes_) | 
 |     return false; | 
 |  | 
 |   memory_above_cutoff_bytes_ = new_memory_bytes; | 
 |   texture->set_above_priority_cutoff(true); | 
 |   return true; | 
 | } | 
 |  | 
 | void PrioritizedResourceManager::AcquireBackingTextureIfNeeded( | 
 |     PrioritizedResource* texture, | 
 |     ResourceProvider* resource_provider) { | 
 |   DCHECK(proxy_->IsImplThread() && proxy_->IsMainThreadBlocked()); | 
 |   DCHECK(!texture->is_self_managed()); | 
 |   DCHECK(texture->is_above_priority_cutoff()); | 
 |   if (texture->backing() || !texture->is_above_priority_cutoff()) | 
 |     return; | 
 |  | 
 |   // Find a backing below, by either recycling or allocating. | 
 |   PrioritizedResource::Backing* backing = NULL; | 
 |  | 
 |   // First try to recycle | 
 |   for (BackingList::iterator it = backings_.begin(); it != backings_.end(); | 
 |        ++it) { | 
 |     if (!(*it)->CanBeRecycledIfNotInExternalUse()) | 
 |       break; | 
 |     if (resource_provider->InUseByConsumer((*it)->id())) | 
 |       continue; | 
 |     if ((*it)->size() == texture->size() && | 
 |         (*it)->format() == texture->format()) { | 
 |       backing = (*it); | 
 |       backings_.erase(it); | 
 |       break; | 
 |     } | 
 |   } | 
 |  | 
 |   // Otherwise reduce memory and just allocate a new backing texures. | 
 |   if (!backing) { | 
 |     EvictBackingsToReduceMemory(memory_available_bytes_ - texture->bytes(), | 
 |                                 PriorityCalculator::AllowEverythingCutoff(), | 
 |                                 EVICT_ONLY_RECYCLABLE, | 
 |                                 DO_NOT_UNLINK_BACKINGS, | 
 |                                 resource_provider); | 
 |     backing = | 
 |         CreateBacking(texture->size(), texture->format(), resource_provider); | 
 |   } | 
 |  | 
 |   // Move the used backing to the end of the eviction list, and note that | 
 |   // the tail is not sorted. | 
 |   if (backing->owner()) | 
 |     backing->owner()->Unlink(); | 
 |   texture->Link(backing); | 
 |   backings_.push_back(backing); | 
 |   backings_tail_not_sorted_ = true; | 
 |  | 
 |   // Update the backing's priority from its new owner. | 
 |   backing->UpdatePriority(); | 
 | } | 
 |  | 
 | bool PrioritizedResourceManager::EvictBackingsToReduceMemory( | 
 |     size_t limit_bytes, | 
 |     int priority_cutoff, | 
 |     EvictionPolicy eviction_policy, | 
 |     UnlinkPolicy unlink_policy, | 
 |     ResourceProvider* resource_provider) { | 
 |   DCHECK(proxy_->IsImplThread()); | 
 |   if (unlink_policy == UNLINK_BACKINGS) | 
 |     DCHECK(proxy_->IsMainThreadBlocked()); | 
 |   if (MemoryUseBytes() <= limit_bytes && | 
 |       PriorityCalculator::AllowEverythingCutoff() == priority_cutoff) | 
 |     return false; | 
 |  | 
 |   // Destroy backings until we are below the limit, | 
 |   // or until all backings remaining are above the cutoff. | 
 |   bool evicted_anything = false; | 
 |   while (backings_.size() > 0) { | 
 |     PrioritizedResource::Backing* backing = backings_.front(); | 
 |     if (MemoryUseBytes() <= limit_bytes && | 
 |         PriorityCalculator::priority_is_higher( | 
 |             backing->request_priority_at_last_priority_update(), | 
 |             priority_cutoff)) | 
 |       break; | 
 |     if (eviction_policy == EVICT_ONLY_RECYCLABLE && | 
 |         !backing->CanBeRecycledIfNotInExternalUse()) | 
 |       break; | 
 |     if (unlink_policy == UNLINK_BACKINGS && backing->owner()) | 
 |       backing->owner()->Unlink(); | 
 |     EvictFirstBackingResource(resource_provider); | 
 |     evicted_anything = true; | 
 |   } | 
 |   return evicted_anything; | 
 | } | 
 |  | 
 | void PrioritizedResourceManager::ReduceWastedMemory( | 
 |     ResourceProvider* resource_provider) { | 
 |   // We currently collect backings from deleted textures for later recycling. | 
 |   // However, if we do that forever we will always use the max limit even if | 
 |   // we really need very little memory. This should probably be solved by | 
 |   // reducing the limit externally, but until then this just does some "clean | 
 |   // up" of unused backing textures (any more than 10%). | 
 |   size_t wasted_memory = 0; | 
 |   for (BackingList::iterator it = backings_.begin(); it != backings_.end(); | 
 |        ++it) { | 
 |     if ((*it)->owner()) | 
 |       break; | 
 |     if ((*it)->in_parent_compositor()) | 
 |       continue; | 
 |     wasted_memory += (*it)->bytes(); | 
 |   } | 
 |   size_t wasted_memory_to_allow = memory_available_bytes_ / 10; | 
 |   // If the external priority cutoff indicates that unused memory should be | 
 |   // freed, then do not allow any memory for texture recycling. | 
 |   if (external_priority_cutoff_ != PriorityCalculator::AllowEverythingCutoff()) | 
 |     wasted_memory_to_allow = 0; | 
 |   if (wasted_memory > wasted_memory_to_allow) | 
 |     EvictBackingsToReduceMemory(MemoryUseBytes() - | 
 |                                 (wasted_memory - wasted_memory_to_allow), | 
 |                                 PriorityCalculator::AllowEverythingCutoff(), | 
 |                                 EVICT_ONLY_RECYCLABLE, | 
 |                                 DO_NOT_UNLINK_BACKINGS, | 
 |                                 resource_provider); | 
 | } | 
 |  | 
 | void PrioritizedResourceManager::ReduceMemory( | 
 |     ResourceProvider* resource_provider) { | 
 |   DCHECK(proxy_->IsImplThread() && proxy_->IsMainThreadBlocked()); | 
 |   EvictBackingsToReduceMemory(memory_available_bytes_, | 
 |                               PriorityCalculator::AllowEverythingCutoff(), | 
 |                               EVICT_ANYTHING, | 
 |                               UNLINK_BACKINGS, | 
 |                               resource_provider); | 
 |   DCHECK_LE(MemoryUseBytes(), memory_available_bytes_); | 
 |  | 
 |   ReduceWastedMemory(resource_provider); | 
 | } | 
 |  | 
 | void PrioritizedResourceManager::ClearAllMemory( | 
 |     ResourceProvider* resource_provider) { | 
 |   DCHECK(proxy_->IsImplThread() && proxy_->IsMainThreadBlocked()); | 
 |   if (!resource_provider) { | 
 |     DCHECK(backings_.empty()); | 
 |     return; | 
 |   } | 
 |   EvictBackingsToReduceMemory(0, | 
 |                               PriorityCalculator::AllowEverythingCutoff(), | 
 |                               EVICT_ANYTHING, | 
 |                               DO_NOT_UNLINK_BACKINGS, | 
 |                               resource_provider); | 
 | } | 
 |  | 
 | bool PrioritizedResourceManager::ReduceMemoryOnImplThread( | 
 |     size_t limit_bytes, | 
 |     int priority_cutoff, | 
 |     ResourceProvider* resource_provider) { | 
 |   DCHECK(proxy_->IsImplThread()); | 
 |   DCHECK(resource_provider); | 
 |  | 
 |   // If we are in the process of uploading a new frame then the backings at the | 
 |   // very end of the list are not sorted by priority. Sort them before doing the | 
 |   // eviction. | 
 |   if (backings_tail_not_sorted_) | 
 |     SortBackings(); | 
 |   return EvictBackingsToReduceMemory(limit_bytes, | 
 |                                      priority_cutoff, | 
 |                                      EVICT_ANYTHING, | 
 |                                      DO_NOT_UNLINK_BACKINGS, | 
 |                                      resource_provider); | 
 | } | 
 |  | 
 | void PrioritizedResourceManager::UnlinkAndClearEvictedBackings() { | 
 |   DCHECK(proxy_->IsMainThread()); | 
 |   base::AutoLock scoped_lock(evicted_backings_lock_); | 
 |   for (BackingList::const_iterator it = evicted_backings_.begin(); | 
 |        it != evicted_backings_.end(); | 
 |        ++it) { | 
 |     PrioritizedResource::Backing* backing = (*it); | 
 |     if (backing->owner()) | 
 |       backing->owner()->Unlink(); | 
 |     delete backing; | 
 |   } | 
 |   evicted_backings_.clear(); | 
 | } | 
 |  | 
 | bool PrioritizedResourceManager::LinkedEvictedBackingsExist() const { | 
 |   DCHECK(proxy_->IsImplThread() && proxy_->IsMainThreadBlocked()); | 
 |   base::AutoLock scoped_lock(evicted_backings_lock_); | 
 |   for (BackingList::const_iterator it = evicted_backings_.begin(); | 
 |        it != evicted_backings_.end(); | 
 |        ++it) { | 
 |     if ((*it)->owner()) | 
 |       return true; | 
 |   } | 
 |   return false; | 
 | } | 
 |  | 
 | void PrioritizedResourceManager::RegisterTexture(PrioritizedResource* texture) { | 
 |   DCHECK(proxy_->IsMainThread()); | 
 |   DCHECK(texture); | 
 |   DCHECK(!texture->resource_manager()); | 
 |   DCHECK(!texture->backing()); | 
 |   DCHECK(!ContainsKey(textures_, texture)); | 
 |  | 
 |   texture->set_manager_internal(this); | 
 |   textures_.insert(texture); | 
 | } | 
 |  | 
 | void PrioritizedResourceManager::UnregisterTexture( | 
 |     PrioritizedResource* texture) { | 
 |   DCHECK(proxy_->IsMainThread() || | 
 |          (proxy_->IsImplThread() && proxy_->IsMainThreadBlocked())); | 
 |   DCHECK(texture); | 
 |   DCHECK(ContainsKey(textures_, texture)); | 
 |  | 
 |   ReturnBackingTexture(texture); | 
 |   texture->set_manager_internal(NULL); | 
 |   textures_.erase(texture); | 
 |   texture->set_above_priority_cutoff(false); | 
 | } | 
 |  | 
 | void PrioritizedResourceManager::ReturnBackingTexture( | 
 |     PrioritizedResource* texture) { | 
 |   DCHECK(proxy_->IsMainThread() || | 
 |          (proxy_->IsImplThread() && proxy_->IsMainThreadBlocked())); | 
 |   if (texture->backing()) | 
 |     texture->Unlink(); | 
 | } | 
 |  | 
 | PrioritizedResource::Backing* PrioritizedResourceManager::CreateBacking( | 
 |     const gfx::Size& size, | 
 |     ResourceFormat format, | 
 |     ResourceProvider* resource_provider) { | 
 |   DCHECK(proxy_->IsImplThread() && proxy_->IsMainThreadBlocked()); | 
 |   DCHECK(resource_provider); | 
 |   ResourceProvider::ResourceId resource_id = | 
 |       resource_provider->CreateManagedResource( | 
 |           size, | 
 |           GL_TEXTURE_2D, | 
 |           GL_CLAMP_TO_EDGE, | 
 |           ResourceProvider::TextureHintImmutable, | 
 |           format); | 
 |   PrioritizedResource::Backing* backing = new PrioritizedResource::Backing( | 
 |       resource_id, resource_provider, size, format); | 
 |   memory_use_bytes_ += backing->bytes(); | 
 |   return backing; | 
 | } | 
 |  | 
 | void PrioritizedResourceManager::EvictFirstBackingResource( | 
 |     ResourceProvider* resource_provider) { | 
 |   DCHECK(proxy_->IsImplThread()); | 
 |   DCHECK(resource_provider); | 
 |   DCHECK(!backings_.empty()); | 
 |   PrioritizedResource::Backing* backing = backings_.front(); | 
 |  | 
 |   // Note that we create a backing and its resource at the same time, but we | 
 |   // delete the backing structure and its resource in two steps. This is because | 
 |   // we can delete the resource while the main thread is running, but we cannot | 
 |   // unlink backings while the main thread is running. | 
 |   backing->DeleteResource(resource_provider); | 
 |   memory_use_bytes_ -= backing->bytes(); | 
 |   backings_.pop_front(); | 
 |   base::AutoLock scoped_lock(evicted_backings_lock_); | 
 |   evicted_backings_.push_back(backing); | 
 | } | 
 |  | 
 | void PrioritizedResourceManager::AssertInvariants() { | 
 | #if DCHECK_IS_ON() | 
 |   DCHECK(proxy_->IsImplThread() && proxy_->IsMainThreadBlocked()); | 
 |  | 
 |   // If we hit any of these asserts, there is a bug in this class. To see | 
 |   // where the bug is, call this function at the beginning and end of | 
 |   // every public function. | 
 |  | 
 |   // Backings/textures must be doubly-linked and only to other backings/textures | 
 |   // in this manager. | 
 |   for (BackingList::iterator it = backings_.begin(); it != backings_.end(); | 
 |        ++it) { | 
 |     if ((*it)->owner()) { | 
 |       DCHECK(ContainsKey(textures_, (*it)->owner())); | 
 |       DCHECK((*it)->owner()->backing() == (*it)); | 
 |     } | 
 |   } | 
 |   for (TextureSet::iterator it = textures_.begin(); it != textures_.end(); | 
 |        ++it) { | 
 |     PrioritizedResource* texture = (*it); | 
 |     PrioritizedResource::Backing* backing = texture->backing(); | 
 |     base::AutoLock scoped_lock(evicted_backings_lock_); | 
 |     if (backing) { | 
 |       if (backing->ResourceHasBeenDeleted()) { | 
 |         DCHECK(std::find(backings_.begin(), backings_.end(), backing) == | 
 |                backings_.end()); | 
 |         DCHECK(std::find(evicted_backings_.begin(), | 
 |                          evicted_backings_.end(), | 
 |                          backing) != evicted_backings_.end()); | 
 |       } else { | 
 |         DCHECK(std::find(backings_.begin(), backings_.end(), backing) != | 
 |                backings_.end()); | 
 |         DCHECK(std::find(evicted_backings_.begin(), | 
 |                          evicted_backings_.end(), | 
 |                          backing) == evicted_backings_.end()); | 
 |       } | 
 |       DCHECK(backing->owner() == texture); | 
 |     } | 
 |   } | 
 |  | 
 |   // At all times, backings that can be evicted must always come before | 
 |   // backings that can't be evicted in the backing texture list (otherwise | 
 |   // ReduceMemory will not find all textures available for eviction/recycling). | 
 |   bool reached_unrecyclable = false; | 
 |   PrioritizedResource::Backing* previous_backing = NULL; | 
 |   for (BackingList::iterator it = backings_.begin(); it != backings_.end(); | 
 |        ++it) { | 
 |     PrioritizedResource::Backing* backing = *it; | 
 |     if (previous_backing && | 
 |         (!backings_tail_not_sorted_ || | 
 |          !backing->was_above_priority_cutoff_at_last_priority_update())) | 
 |       DCHECK(CompareBackings(previous_backing, backing)); | 
 |     if (!backing->CanBeRecycledIfNotInExternalUse()) | 
 |       reached_unrecyclable = true; | 
 |     if (reached_unrecyclable) | 
 |       DCHECK(!backing->CanBeRecycledIfNotInExternalUse()); | 
 |     else | 
 |       DCHECK(backing->CanBeRecycledIfNotInExternalUse()); | 
 |     previous_backing = backing; | 
 |   } | 
 | #endif  // DCHECK_IS_ON() | 
 | } | 
 |  | 
 | const Proxy* PrioritizedResourceManager::ProxyForDebug() const { | 
 |   return proxy_; | 
 | } | 
 |  | 
 | }  // namespace cc |