| 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 | 
|  |