| Index: cc/CCPrioritizedTextureManager.cpp
 | 
| diff --git a/cc/CCPrioritizedTextureManager.cpp b/cc/CCPrioritizedTextureManager.cpp
 | 
| index b6bb38dbb6a33c7ee5b93e2bd20d7a10797b79e4..d571e5f08532ddc8881a418940bd6144e62f90b1 100644
 | 
| --- a/cc/CCPrioritizedTextureManager.cpp
 | 
| +++ b/cc/CCPrioritizedTextureManager.cpp
 | 
| @@ -22,6 +22,7 @@ CCPrioritizedTextureManager::CCPrioritizedTextureManager(size_t maxMemoryLimitBy
 | 
|      , m_memoryAboveCutoffBytes(0)
 | 
|      , m_memoryAvailableBytes(0)
 | 
|      , m_pool(pool)
 | 
| +    , m_needsUpdateBackingsPrioritites(false)
 | 
|  {
 | 
|  }
 | 
|  
 | 
| @@ -30,10 +31,10 @@ CCPrioritizedTextureManager::~CCPrioritizedTextureManager()
 | 
|      while (m_textures.size() > 0)
 | 
|          unregisterTexture(*m_textures.begin());
 | 
|  
 | 
| -    // Each remaining backing is a leaked opengl texture. We don't have the resourceProvider
 | 
| -    // to delete the textures at this time so clearMemory() needs to be called before this.
 | 
| -    while (m_backings.size() > 0)
 | 
| -        destroyBacking(*m_backings.begin(), 0);
 | 
| +    deleteEvictedBackings();
 | 
| +
 | 
| +    // Each remaining backing is a leaked opengl texture. There should be none.
 | 
| +    ASSERT(m_backings.isEmpty());
 | 
|  }
 | 
|  
 | 
|  void CCPrioritizedTextureManager::prioritizeTextures()
 | 
| @@ -41,18 +42,12 @@ void CCPrioritizedTextureManager::prioritizeTextures()
 | 
|      TRACE_EVENT0("cc", "CCPrioritizedTextureManager::prioritizeTextures");
 | 
|      ASSERT(CCProxy::isMainThread());
 | 
|  
 | 
| -#if !ASSERT_DISABLED
 | 
| -    assertInvariants();
 | 
| -#endif
 | 
| -
 | 
|      // 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& sortedTextures = m_tempTextureVector;
 | 
| -    BackingVector& sortedBackings = m_tempBackingVector;
 | 
|      sortedTextures.clear();
 | 
| -    sortedBackings.clear();
 | 
|  
 | 
|      // Copy all textures into a vector and sort them.
 | 
|      for (TextureSet::iterator it = m_textures.begin(); it != m_textures.end(); ++it)
 | 
| @@ -96,24 +91,44 @@ void CCPrioritizedTextureManager::prioritizeTextures()
 | 
|          if (isAbovePriorityCutoff && !(*it)->isSelfManaged())
 | 
|              m_memoryAboveCutoffBytes += (*it)->bytes();
 | 
|      }
 | 
| +    sortedTextures.clear();
 | 
| +
 | 
| +    m_needsUpdateBackingsPrioritites = true;
 | 
| +
 | 
|      ASSERT(m_memoryAboveCutoffBytes <= m_memoryAvailableBytes);
 | 
| +    ASSERT(memoryAboveCutoffBytes() <= maxMemoryLimitBytes());
 | 
| +}
 | 
| +
 | 
| +void CCPrioritizedTextureManager::updateBackingsPriorities()
 | 
| +{
 | 
| +    TRACE_EVENT0("cc", "CCPrioritizedTextureManager::updateBackingsPriorities");
 | 
| +    ASSERT(CCProxy::isImplThread() && CCProxy::isMainThreadBlocked());
 | 
| +
 | 
| +    if (!m_needsUpdateBackingsPrioritites)
 | 
| +        return;
 | 
|  
 | 
| -    // Put backings in eviction/recycling order.
 | 
| -    for (BackingSet::iterator it = m_backings.begin(); it != m_backings.end(); ++it)
 | 
| +#if !ASSERT_DISABLED
 | 
| +    assertInvariants();
 | 
| +#endif
 | 
| +
 | 
| +    // Update backings' priorities and put backings in eviction/recycling order.
 | 
| +    BackingVector& sortedBackings = m_tempBackingVector;
 | 
| +    sortedBackings.clear();
 | 
| +    for (BackingSet::iterator it = m_backings.begin(); it != m_backings.end(); ++it) {
 | 
| +        (*it)->updatePriority();
 | 
|          sortedBackings.append(*it);
 | 
| +    }
 | 
|      std::sort(sortedBackings.begin(), sortedBackings.end(), compareBackings);
 | 
|  
 | 
|      for (BackingVector::iterator it = sortedBackings.begin(); it != sortedBackings.end(); ++it) {
 | 
|          m_backings.remove(*it);
 | 
|          m_backings.add(*it);
 | 
|      }
 | 
| -
 | 
| -    sortedTextures.clear();
 | 
|      sortedBackings.clear();
 | 
| +    m_needsUpdateBackingsPrioritites = false;
 | 
|  
 | 
|  #if !ASSERT_DISABLED
 | 
|      assertInvariants();
 | 
| -    ASSERT(memoryAboveCutoffBytes() <= maxMemoryLimitBytes());
 | 
|  #endif
 | 
|  }
 | 
|  
 | 
| @@ -146,10 +161,7 @@ bool CCPrioritizedTextureManager::requestLate(CCPrioritizedTexture* texture)
 | 
|  
 | 
|      m_memoryAboveCutoffBytes = newMemoryBytes;
 | 
|      texture->setAbovePriorityCutoff(true);
 | 
| -    if (texture->backing()) {
 | 
| -        m_backings.remove(texture->backing());
 | 
| -        m_backings.add(texture->backing());
 | 
| -    }
 | 
| +    m_needsUpdateBackingsPrioritites = true;
 | 
|      return true;
 | 
|  }
 | 
|  
 | 
| @@ -161,12 +173,15 @@ void CCPrioritizedTextureManager::acquireBackingTextureIfNeeded(CCPrioritizedTex
 | 
|      if (texture->backing() || !texture->isAbovePriorityCutoff())
 | 
|          return;
 | 
|  
 | 
| +    // Make sure that the backings list is up to date and sorted before traversing it.
 | 
| +    updateBackingsPriorities();
 | 
| +
 | 
|      // Find a backing below, by either recycling or allocating.
 | 
|      CCPrioritizedTexture::Backing* backing = 0;
 | 
|  
 | 
|      // First try to recycle
 | 
|      for (BackingSet::iterator it = m_backings.begin(); it != m_backings.end(); ++it) {
 | 
| -        if ((*it)->owner() && (*it)->owner()->isAbovePriorityCutoff())
 | 
| +        if ((*it)->hadOwnerAtLastPriorityUpdate() && (*it)->wasAbovePriorityCutoffAtLastPriorityUpdate())
 | 
|              break;
 | 
|          if ((*it)->size() == texture->size() && (*it)->format() == texture->format()) {
 | 
|              backing = (*it);
 | 
| @@ -176,7 +191,7 @@ void CCPrioritizedTextureManager::acquireBackingTextureIfNeeded(CCPrioritizedTex
 | 
|  
 | 
|      // Otherwise reduce memory and just allocate a new backing texures.
 | 
|      if (!backing) {
 | 
| -        reduceMemory(m_memoryAvailableBytes - texture->bytes(), resourceProvider);
 | 
| +        evictBackingsToReduceMemory(m_memoryAvailableBytes - texture->bytes(), RespectManagerPriorityCutoff, resourceProvider);
 | 
|          backing = createBacking(texture->size(), texture->format(), resourceProvider);
 | 
|      }
 | 
|  
 | 
| @@ -186,27 +201,36 @@ void CCPrioritizedTextureManager::acquireBackingTextureIfNeeded(CCPrioritizedTex
 | 
|      texture->link(backing);
 | 
|      m_backings.remove(backing);
 | 
|      m_backings.add(backing);
 | 
| +
 | 
| +    // Update the backing's priority from its new owner.
 | 
| +    backing->updatePriority();
 | 
|  }
 | 
|  
 | 
| -void CCPrioritizedTextureManager::reduceMemory(size_t limitBytes, CCResourceProvider* resourceProvider)
 | 
| +void CCPrioritizedTextureManager::evictBackingsToReduceMemory(size_t limitBytes, EvictionPriorityPolicy evictionPolicy, CCResourceProvider* resourceProvider)
 | 
|  {
 | 
| -    ASSERT(CCProxy::isImplThread() && CCProxy::isMainThreadBlocked());
 | 
| +    ASSERT(CCProxy::isImplThread());
 | 
|      if (memoryUseBytes() <= limitBytes)
 | 
|          return;
 | 
| +
 | 
|      // Destroy backings until we are below the limit,
 | 
|      // or until all backings remaining are above the cutoff.
 | 
|      while (memoryUseBytes() > limitBytes && m_backings.size() > 0) {
 | 
| -        BackingSet::iterator it = m_backings.begin();
 | 
| -        if ((*it)->owner() && (*it)->owner()->isAbovePriorityCutoff())
 | 
| -            break;
 | 
| -        destroyBacking((*it), resourceProvider);
 | 
| +        CCPrioritizedTexture::Backing* backing = *m_backings.begin();
 | 
| +        if (evictionPolicy == RespectManagerPriorityCutoff)
 | 
| +            if (backing->hadOwnerAtLastPriorityUpdate() && backing->wasAbovePriorityCutoffAtLastPriorityUpdate())
 | 
| +                break;
 | 
| +        evictBackingResource(backing, resourceProvider);
 | 
|      }
 | 
|  }
 | 
|  
 | 
|  void CCPrioritizedTextureManager::reduceMemory(CCResourceProvider* resourceProvider)
 | 
|  {
 | 
|      ASSERT(CCProxy::isImplThread() && CCProxy::isMainThreadBlocked());
 | 
| -    reduceMemory(m_memoryAvailableBytes, resourceProvider);
 | 
| +
 | 
| +    // Make sure that the backings list is up to date and sorted before traversing it.
 | 
| +    updateBackingsPriorities();
 | 
| +
 | 
| +    evictBackingsToReduceMemory(m_memoryAvailableBytes, RespectManagerPriorityCutoff, resourceProvider);
 | 
|      ASSERT(memoryUseBytes() <= maxMemoryLimitBytes());
 | 
|  
 | 
|      // We currently collect backings from deleted textures for later recycling.
 | 
| @@ -221,42 +245,64 @@ void CCPrioritizedTextureManager::reduceMemory(CCResourceProvider* resourceProvi
 | 
|          wastedMemory += (*it)->bytes();
 | 
|      }
 | 
|      size_t tenPercentOfMemory = m_memoryAvailableBytes / 10;
 | 
| -    if (wastedMemory <= tenPercentOfMemory)
 | 
| -        return;
 | 
| -    reduceMemory(memoryUseBytes() - (wastedMemory - tenPercentOfMemory), resourceProvider);
 | 
| +    if (wastedMemory > tenPercentOfMemory)
 | 
| +        evictBackingsToReduceMemory(memoryUseBytes() - (wastedMemory - tenPercentOfMemory), RespectManagerPriorityCutoff, resourceProvider);
 | 
| +
 | 
| +    deleteEvictedBackings();
 | 
|  }
 | 
|  
 | 
|  void CCPrioritizedTextureManager::clearAllMemory(CCResourceProvider* resourceProvider)
 | 
|  {
 | 
|      ASSERT(CCProxy::isImplThread() && CCProxy::isMainThreadBlocked());
 | 
|      ASSERT(resourceProvider);
 | 
| -    // Unlink and destroy all backing textures.
 | 
| -    while (m_backings.size() > 0) {
 | 
| -        BackingSet::iterator it = m_backings.begin();
 | 
| -        if ((*it)->owner())
 | 
| -            (*it)->owner()->unlink();
 | 
| -        destroyBacking((*it), resourceProvider);
 | 
| -    }
 | 
| +    evictBackingsToReduceMemory(0, DoNotRespectManagerPriorityCutoff, resourceProvider);
 | 
| +    deleteEvictedBackings();
 | 
|  }
 | 
|  
 | 
| -void CCPrioritizedTextureManager::unlinkAllBackings()
 | 
| +void CCPrioritizedTextureManager::reduceMemoryOnImplThread(size_t limitBytes, CCResourceProvider* resourceProvider)
 | 
|  {
 | 
| -    ASSERT(CCProxy::isMainThread());
 | 
| -    for (BackingSet::iterator it = m_backings.begin(); it != m_backings.end(); ++it)
 | 
| -        if ((*it)->owner())
 | 
| -            (*it)->owner()->unlink();
 | 
| +    ASSERT(CCProxy::isImplThread());
 | 
| +    ASSERT(resourceProvider);
 | 
| +
 | 
| +    evictBackingsToReduceMemory(limitBytes, DoNotRespectManagerPriorityCutoff, resourceProvider);
 | 
| +
 | 
| +    // Deleting just some (not all) resources is not supported yet because we do not clear
 | 
| +    // only the deleted resources from the texture upload queues (rather, we clear all uploads).
 | 
| +    // Make sure that if we evict all resources.
 | 
| +    ASSERT(m_backings.isEmpty());
 | 
|  }
 | 
|  
 | 
| -void CCPrioritizedTextureManager::deleteAllUnlinkedBackings()
 | 
| +void CCPrioritizedTextureManager::getEvictedBackings(BackingVector& evictedBackings)
 | 
|  {
 | 
| -    ASSERT(CCProxy::isImplThread() && CCProxy::isMainThreadBlocked());
 | 
| -    BackingVector backingsToDelete;
 | 
| -    for (BackingSet::iterator it = m_backings.begin(); it != m_backings.end(); ++it)
 | 
| -        if (!(*it)->owner())
 | 
| -            backingsToDelete.append((*it));
 | 
| +    ASSERT(CCProxy::isImplThread());
 | 
| +    evictedBackings.clear();
 | 
| +    evictedBackings.append(m_evictedBackings);
 | 
| +}
 | 
|  
 | 
| -    for (BackingVector::iterator it = backingsToDelete.begin(); it != backingsToDelete.end(); ++it)
 | 
| -        destroyBacking((*it), 0);
 | 
| +void CCPrioritizedTextureManager::unlinkEvictedBackings(const BackingVector& evictedBackings)
 | 
| +{
 | 
| +    ASSERT(CCProxy::isMainThread());
 | 
| +    for (BackingVector::const_iterator it = evictedBackings.begin(); it != evictedBackings.end(); ++it) {
 | 
| +        CCPrioritizedTexture::Backing* backing = (*it);
 | 
| +        if (backing->owner())
 | 
| +            backing->owner()->unlink();
 | 
| +    }
 | 
| +}
 | 
| +
 | 
| +bool CCPrioritizedTextureManager::deleteEvictedBackings()
 | 
| +{
 | 
| +    ASSERT(CCProxy::isMainThread() || (CCProxy::isImplThread() && CCProxy::isMainThreadBlocked()));
 | 
| +    bool linkedEvictedBackingsExisted = false;
 | 
| +    for (BackingVector::const_iterator it = m_evictedBackings.begin(); it != m_evictedBackings.end(); ++it) {
 | 
| +        CCPrioritizedTexture::Backing* backing = (*it);
 | 
| +        if (backing->owner()) {
 | 
| +            linkedEvictedBackingsExisted = true;
 | 
| +            backing->owner()->unlink();
 | 
| +        }
 | 
| +        delete backing;
 | 
| +    }
 | 
| +    m_evictedBackings.clear();
 | 
| +    return linkedEvictedBackingsExisted;
 | 
|  }
 | 
|  
 | 
|  void CCPrioritizedTextureManager::registerTexture(CCPrioritizedTexture* texture)
 | 
| @@ -288,10 +334,8 @@ void CCPrioritizedTextureManager::returnBackingTexture(CCPrioritizedTexture* tex
 | 
|  {
 | 
|      ASSERT(CCProxy::isMainThread() || (CCProxy::isImplThread() && CCProxy::isMainThreadBlocked()));
 | 
|      if (texture->backing()) {
 | 
| -        // Move the backing texture to the front for eviction/recycling and unlink it.
 | 
| -        m_backings.remove(texture->backing());
 | 
| -        m_backings.insertBefore(m_backings.begin(), texture->backing());
 | 
|          texture->unlink();
 | 
| +        m_needsUpdateBackingsPrioritites = true;
 | 
|      }
 | 
|  }
 | 
|  
 | 
| @@ -307,28 +351,24 @@ CCPrioritizedTexture::Backing* CCPrioritizedTextureManager::createBacking(IntSiz
 | 
|      return backing;
 | 
|  }
 | 
|  
 | 
| -void CCPrioritizedTextureManager::destroyBacking(CCPrioritizedTexture::Backing* backing, CCResourceProvider* resourceProvider)
 | 
| +void CCPrioritizedTextureManager::evictBackingResource(CCPrioritizedTexture::Backing* backing, CCResourceProvider* resourceProvider)
 | 
|  {
 | 
| +    ASSERT(CCProxy::isImplThread());
 | 
|      ASSERT(backing);
 | 
| -    ASSERT(!backing->owner() || !backing->owner()->isAbovePriorityCutoff());
 | 
| -    ASSERT(!backing->owner() || !backing->owner()->isSelfManaged());
 | 
| +    ASSERT(resourceProvider);
 | 
|      ASSERT(m_backings.find(backing) != m_backings.end());
 | 
|  
 | 
| -    if (resourceProvider)
 | 
| -        resourceProvider->deleteResource(backing->id());
 | 
| -    if (backing->owner())
 | 
| -        backing->owner()->unlink();
 | 
| +    resourceProvider->deleteResource(backing->id());
 | 
| +    backing->setId(0);
 | 
|      m_memoryUseBytes -= backing->bytes();
 | 
|      m_backings.remove(backing);
 | 
| -
 | 
| -    delete backing;
 | 
| +    m_evictedBackings.append(backing);
 | 
|  }
 | 
|  
 | 
| -
 | 
|  #if !ASSERT_DISABLED
 | 
|  void CCPrioritizedTextureManager::assertInvariants()
 | 
|  {
 | 
| -    ASSERT(CCProxy::isMainThread());
 | 
| +    ASSERT(CCProxy::isImplThread() && CCProxy::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
 | 
| @@ -351,12 +391,19 @@ void CCPrioritizedTextureManager::assertInvariants()
 | 
|      // 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 reachedProtected = false;
 | 
| +    bool reachedOwned = false;
 | 
| +    bool reachedAboveCutoff = false;
 | 
|      for (BackingSet::iterator it = m_backings.begin(); it != m_backings.end(); ++it) {
 | 
| -        if ((*it)->owner() && (*it)->owner()->isAbovePriorityCutoff())
 | 
| -            reachedProtected = true;
 | 
| -        if (reachedProtected)
 | 
| -            ASSERT((*it)->owner() && (*it)->owner()->isAbovePriorityCutoff());
 | 
| +        if ((*it)->hadOwnerAtLastPriorityUpdate())
 | 
| +            reachedOwned = true;
 | 
| +        if ((*it)->wasAbovePriorityCutoffAtLastPriorityUpdate())
 | 
| +            reachedAboveCutoff = true;
 | 
| +        if (reachedOwned)
 | 
| +            ASSERT((*it)->hadOwnerAtLastPriorityUpdate());
 | 
| +        if (reachedAboveCutoff) {
 | 
| +            ASSERT((*it)->hadOwnerAtLastPriorityUpdate() && (*it)->wasAbovePriorityCutoffAtLastPriorityUpdate());
 | 
| +            ASSERT(reachedOwned);
 | 
| +        }
 | 
|      }
 | 
|  }
 | 
|  #endif
 | 
| 
 |