In iossim, ignore harmless messages from launchd.
[chromium-blink-merge.git] / cc / prioritized_resource_manager.cc
blob68cebe75500c24227512f5eacf36b05a42060e12
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/prioritized_resource_manager.h"
7 #include "base/debug/trace_event.h"
8 #include "base/stl_util.h"
9 #include "cc/prioritized_resource.h"
10 #include "cc/priority_calculator.h"
11 #include "cc/proxy.h"
12 #include <algorithm>
14 using namespace std;
16 namespace cc {
18 PrioritizedResourceManager::PrioritizedResourceManager(const Proxy* proxy)
19 : m_proxy(proxy)
20 , m_maxMemoryLimitBytes(defaultMemoryAllocationLimit())
21 , m_externalPriorityCutoff(PriorityCalculator::allowEverythingCutoff())
22 , m_memoryUseBytes(0)
23 , m_memoryAboveCutoffBytes(0)
24 , m_memoryAvailableBytes(0)
25 , m_backingsTailNotSorted(false)
26 , m_memoryVisibleBytes(0)
27 , m_memoryVisibleAndNearbyBytes(0)
28 , m_memoryVisibleLastPushedBytes(0)
29 , m_memoryVisibleAndNearbyLastPushedBytes(0)
33 PrioritizedResourceManager::~PrioritizedResourceManager()
35 while (m_textures.size() > 0)
36 unregisterTexture(*m_textures.begin());
38 unlinkAndClearEvictedBackings();
39 DCHECK(m_evictedBackings.empty());
41 // Each remaining backing is a leaked opengl texture. There should be none.
42 DCHECK(m_backings.empty());
45 size_t PrioritizedResourceManager::memoryVisibleBytes() const
47 DCHECK(m_proxy->isImplThread());
48 return m_memoryVisibleLastPushedBytes;
51 size_t PrioritizedResourceManager::memoryVisibleAndNearbyBytes() const
53 DCHECK(m_proxy->isImplThread());
54 return m_memoryVisibleAndNearbyLastPushedBytes;
57 void PrioritizedResourceManager::prioritizeTextures()
59 TRACE_EVENT0("cc", "PrioritizedResourceManager::prioritizeTextures");
60 DCHECK(m_proxy->isMainThread());
62 // Sorting textures in this function could be replaced by a slightly
63 // modified O(n) quick-select to partition textures rather than
64 // sort them (if performance of the sort becomes an issue).
66 TextureVector& sortedTextures = m_tempTextureVector;
67 sortedTextures.clear();
69 // Copy all textures into a vector, sort them, and collect memory requirements statistics.
70 m_memoryVisibleBytes = 0;
71 m_memoryVisibleAndNearbyBytes = 0;
72 for (TextureSet::iterator it = m_textures.begin(); it != m_textures.end(); ++it) {
73 PrioritizedResource* texture = (*it);
74 sortedTextures.push_back(texture);
75 if (PriorityCalculator::priorityIsHigher(texture->requestPriority(), PriorityCalculator::allowVisibleOnlyCutoff()))
76 m_memoryVisibleBytes += texture->bytes();
77 if (PriorityCalculator::priorityIsHigher(texture->requestPriority(), PriorityCalculator::allowVisibleAndNearbyCutoff()))
78 m_memoryVisibleAndNearbyBytes += texture->bytes();
80 std::sort(sortedTextures.begin(), sortedTextures.end(), compareTextures);
82 // Compute a priority cutoff based on memory pressure
83 m_memoryAvailableBytes = m_maxMemoryLimitBytes;
84 m_priorityCutoff = m_externalPriorityCutoff;
85 size_t memoryBytes = 0;
86 for (TextureVector::iterator it = sortedTextures.begin(); it != sortedTextures.end(); ++it) {
87 if ((*it)->isSelfManaged()) {
88 // Account for self-managed memory immediately by reducing the memory
89 // available (since it never gets acquired).
90 size_t newMemoryBytes = memoryBytes + (*it)->bytes();
91 if (newMemoryBytes > m_memoryAvailableBytes) {
92 m_priorityCutoff = (*it)->requestPriority();
93 m_memoryAvailableBytes = memoryBytes;
94 break;
96 m_memoryAvailableBytes -= (*it)->bytes();
97 } else {
98 size_t newMemoryBytes = memoryBytes + (*it)->bytes();
99 if (newMemoryBytes > m_memoryAvailableBytes) {
100 m_priorityCutoff = (*it)->requestPriority();
101 break;
103 memoryBytes = newMemoryBytes;
107 // Disallow any textures with priority below the external cutoff to have backings.
108 for (TextureVector::iterator it = sortedTextures.begin(); it != sortedTextures.end(); ++it) {
109 PrioritizedResource* texture = (*it);
110 if (!PriorityCalculator::priorityIsHigher(texture->requestPriority(), m_externalPriorityCutoff) &&
111 texture->haveBackingTexture())
112 texture->unlink();
115 // Only allow textures if they are higher than the cutoff. All textures
116 // of the same priority are accepted or rejected together, rather than
117 // being partially allowed randomly.
118 m_memoryAboveCutoffBytes = 0;
119 for (TextureVector::iterator it = sortedTextures.begin(); it != sortedTextures.end(); ++it) {
120 bool isAbovePriorityCutoff = PriorityCalculator::priorityIsHigher((*it)->requestPriority(), m_priorityCutoff);
121 (*it)->setAbovePriorityCutoff(isAbovePriorityCutoff);
122 if (isAbovePriorityCutoff && !(*it)->isSelfManaged())
123 m_memoryAboveCutoffBytes += (*it)->bytes();
125 sortedTextures.clear();
127 DCHECK(m_memoryAboveCutoffBytes <= m_memoryAvailableBytes);
128 DCHECK(memoryAboveCutoffBytes() <= maxMemoryLimitBytes());
131 void PrioritizedResourceManager::pushTexturePrioritiesToBackings()
133 TRACE_EVENT0("cc", "PrioritizedResourceManager::pushTexturePrioritiesToBackings");
134 DCHECK(m_proxy->isImplThread() && m_proxy->isMainThreadBlocked());
136 assertInvariants();
137 for (BackingList::iterator it = m_backings.begin(); it != m_backings.end(); ++it)
138 (*it)->updatePriority();
139 sortBackings();
140 assertInvariants();
142 // Push memory requirements to the impl thread structure.
143 m_memoryVisibleLastPushedBytes = m_memoryVisibleBytes;
144 m_memoryVisibleAndNearbyLastPushedBytes = m_memoryVisibleAndNearbyBytes;
147 void PrioritizedResourceManager::updateBackingsInDrawingImplTree()
149 TRACE_EVENT0("cc", "PrioritizedResourceManager::updateBackingsInDrawingImplTree");
150 DCHECK(m_proxy->isImplThread() && m_proxy->isMainThreadBlocked());
152 assertInvariants();
153 for (BackingList::iterator it = m_backings.begin(); it != m_backings.end(); ++it) {
154 PrioritizedResource::Backing* backing = (*it);
155 backing->updateInDrawingImplTree();
157 sortBackings();
158 assertInvariants();
161 void PrioritizedResourceManager::sortBackings()
163 TRACE_EVENT0("cc", "PrioritizedResourceManager::sortBackings");
164 DCHECK(m_proxy->isImplThread());
166 // Put backings in eviction/recycling order.
167 m_backings.sort(compareBackings);
168 m_backingsTailNotSorted = false;
171 void PrioritizedResourceManager::clearPriorities()
173 DCHECK(m_proxy->isMainThread());
174 for (TextureSet::iterator it = m_textures.begin(); it != m_textures.end(); ++it) {
175 // FIXME: We should remove this and just set all priorities to
176 // PriorityCalculator::lowestPriority() once we have priorities
177 // for all textures (we can't currently calculate distances for
178 // off-screen textures).
179 (*it)->setRequestPriority(PriorityCalculator::lingeringPriority((*it)->requestPriority()));
183 bool PrioritizedResourceManager::requestLate(PrioritizedResource* texture)
185 DCHECK(m_proxy->isMainThread());
187 // This is already above cutoff, so don't double count it's memory below.
188 if (texture->isAbovePriorityCutoff())
189 return true;
191 // Allow textures that have priority equal to the cutoff, but not strictly lower.
192 if (PriorityCalculator::priorityIsLower(texture->requestPriority(), m_priorityCutoff))
193 return false;
195 // Disallow textures that do not have a priority strictly higher than the external cutoff.
196 if (!PriorityCalculator::priorityIsHigher(texture->requestPriority(), m_externalPriorityCutoff))
197 return false;
199 size_t newMemoryBytes = m_memoryAboveCutoffBytes + texture->bytes();
200 if (newMemoryBytes > m_memoryAvailableBytes)
201 return false;
203 m_memoryAboveCutoffBytes = newMemoryBytes;
204 texture->setAbovePriorityCutoff(true);
205 return true;
208 void PrioritizedResourceManager::acquireBackingTextureIfNeeded(PrioritizedResource* texture, ResourceProvider* resourceProvider)
210 DCHECK(m_proxy->isImplThread() && m_proxy->isMainThreadBlocked());
211 DCHECK(!texture->isSelfManaged());
212 DCHECK(texture->isAbovePriorityCutoff());
213 if (texture->backing() || !texture->isAbovePriorityCutoff())
214 return;
216 // Find a backing below, by either recycling or allocating.
217 PrioritizedResource::Backing* backing = 0;
219 // First try to recycle
220 for (BackingList::iterator it = m_backings.begin(); it != m_backings.end(); ++it) {
221 if (!(*it)->canBeRecycled())
222 break;
223 if (resourceProvider->inUseByConsumer((*it)->id()))
224 continue;
225 if ((*it)->size() == texture->size() && (*it)->format() == texture->format()) {
226 backing = (*it);
227 m_backings.erase(it);
228 break;
232 // Otherwise reduce memory and just allocate a new backing texures.
233 if (!backing) {
234 evictBackingsToReduceMemory(m_memoryAvailableBytes - texture->bytes(),
235 PriorityCalculator::allowEverythingCutoff(),
236 EvictOnlyRecyclable,
237 DoNotUnlinkBackings,
238 resourceProvider);
239 backing = createBacking(texture->size(), texture->format(), resourceProvider);
242 // Move the used backing to the end of the eviction list, and note that
243 // the tail is not sorted.
244 if (backing->owner())
245 backing->owner()->unlink();
246 texture->link(backing);
247 m_backings.push_back(backing);
248 m_backingsTailNotSorted = true;
250 // Update the backing's priority from its new owner.
251 backing->updatePriority();
254 bool PrioritizedResourceManager::evictBackingsToReduceMemory(size_t limitBytes,
255 int priorityCutoff,
256 EvictionPolicy evictionPolicy,
257 UnlinkPolicy unlinkPolicy,
258 ResourceProvider* resourceProvider)
260 DCHECK(m_proxy->isImplThread());
261 if (unlinkPolicy == UnlinkBackings)
262 DCHECK(m_proxy->isMainThreadBlocked());
263 if (memoryUseBytes() <= limitBytes && PriorityCalculator::allowEverythingCutoff() == priorityCutoff)
264 return false;
266 // Destroy backings until we are below the limit,
267 // or until all backings remaining are above the cutoff.
268 while (m_backings.size() > 0) {
269 PrioritizedResource::Backing* backing = m_backings.front();
270 if (memoryUseBytes() <= limitBytes &&
271 PriorityCalculator::priorityIsHigher(backing->requestPriorityAtLastPriorityUpdate(), priorityCutoff))
272 break;
273 if (evictionPolicy == EvictOnlyRecyclable && !backing->canBeRecycled())
274 break;
275 if (unlinkPolicy == UnlinkBackings && backing->owner())
276 backing->owner()->unlink();
277 evictFirstBackingResource(resourceProvider);
279 return true;
282 void PrioritizedResourceManager::reduceMemory(ResourceProvider* resourceProvider)
284 DCHECK(m_proxy->isImplThread() && m_proxy->isMainThreadBlocked());
285 evictBackingsToReduceMemory(m_memoryAvailableBytes,
286 PriorityCalculator::allowEverythingCutoff(),
287 EvictAnything,
288 UnlinkBackings,
289 resourceProvider);
290 DCHECK(memoryUseBytes() <= m_memoryAvailableBytes);
292 // We currently collect backings from deleted textures for later recycling.
293 // However, if we do that forever we will always use the max limit even if
294 // we really need very little memory. This should probably be solved by reducing the
295 // limit externally, but until then this just does some "clean up" of unused
296 // backing textures (any more than 10%).
297 size_t wastedMemory = 0;
298 for (BackingList::iterator it = m_backings.begin(); it != m_backings.end(); ++it) {
299 if ((*it)->owner())
300 break;
301 wastedMemory += (*it)->bytes();
303 size_t tenPercentOfMemory = m_memoryAvailableBytes / 10;
304 if (wastedMemory > tenPercentOfMemory)
305 evictBackingsToReduceMemory(memoryUseBytes() - (wastedMemory - tenPercentOfMemory),
306 PriorityCalculator::allowEverythingCutoff(),
307 EvictOnlyRecyclable,
308 UnlinkBackings,
309 resourceProvider);
312 void PrioritizedResourceManager::clearAllMemory(ResourceProvider* resourceProvider)
314 DCHECK(m_proxy->isImplThread() && m_proxy->isMainThreadBlocked());
315 if (!resourceProvider) {
316 DCHECK(m_backings.empty());
317 return;
319 evictBackingsToReduceMemory(0,
320 PriorityCalculator::allowEverythingCutoff(),
321 EvictAnything,
322 DoNotUnlinkBackings,
323 resourceProvider);
326 bool PrioritizedResourceManager::reduceMemoryOnImplThread(size_t limitBytes, int priorityCutoff, ResourceProvider* resourceProvider)
328 DCHECK(m_proxy->isImplThread());
329 DCHECK(resourceProvider);
330 // If we are in the process of uploading a new frame then the backings at the very end of
331 // the list are not sorted by priority. Sort them before doing the eviction.
332 if (m_backingsTailNotSorted)
333 sortBackings();
334 return evictBackingsToReduceMemory(limitBytes,
335 priorityCutoff,
336 EvictAnything,
337 DoNotUnlinkBackings,
338 resourceProvider);
341 void PrioritizedResourceManager::unlinkAndClearEvictedBackings()
343 DCHECK(m_proxy->isMainThread());
344 base::AutoLock scoped_lock(m_evictedBackingsLock);
345 for (BackingList::const_iterator it = m_evictedBackings.begin(); it != m_evictedBackings.end(); ++it) {
346 PrioritizedResource::Backing* backing = (*it);
347 if (backing->owner())
348 backing->owner()->unlink();
349 delete backing;
351 m_evictedBackings.clear();
354 bool PrioritizedResourceManager::linkedEvictedBackingsExist() const
356 DCHECK(m_proxy->isImplThread() && m_proxy->isMainThreadBlocked());
357 base::AutoLock scoped_lock(m_evictedBackingsLock);
358 for (BackingList::const_iterator it = m_evictedBackings.begin(); it != m_evictedBackings.end(); ++it) {
359 if ((*it)->owner())
360 return true;
362 return false;
365 void PrioritizedResourceManager::registerTexture(PrioritizedResource* texture)
367 DCHECK(m_proxy->isMainThread());
368 DCHECK(texture);
369 DCHECK(!texture->resourceManager());
370 DCHECK(!texture->backing());
371 DCHECK(!ContainsKey(m_textures, texture));
373 texture->setManagerInternal(this);
374 m_textures.insert(texture);
378 void PrioritizedResourceManager::unregisterTexture(PrioritizedResource* texture)
380 DCHECK(m_proxy->isMainThread() || (m_proxy->isImplThread() && m_proxy->isMainThreadBlocked()));
381 DCHECK(texture);
382 DCHECK(ContainsKey(m_textures, texture));
384 returnBackingTexture(texture);
385 texture->setManagerInternal(0);
386 m_textures.erase(texture);
387 texture->setAbovePriorityCutoff(false);
390 void PrioritizedResourceManager::returnBackingTexture(PrioritizedResource* texture)
392 DCHECK(m_proxy->isMainThread() || (m_proxy->isImplThread() && m_proxy->isMainThreadBlocked()));
393 if (texture->backing())
394 texture->unlink();
397 PrioritizedResource::Backing* PrioritizedResourceManager::createBacking(gfx::Size size, GLenum format, ResourceProvider* resourceProvider)
399 DCHECK(m_proxy->isImplThread() && m_proxy->isMainThreadBlocked());
400 DCHECK(resourceProvider);
401 ResourceProvider::ResourceId resourceId = resourceProvider->createManagedResource(size, format, ResourceProvider::TextureUsageAny);
402 PrioritizedResource::Backing* backing = new PrioritizedResource::Backing(resourceId, resourceProvider, size, format);
403 m_memoryUseBytes += backing->bytes();
404 return backing;
407 void PrioritizedResourceManager::evictFirstBackingResource(ResourceProvider* resourceProvider)
409 DCHECK(m_proxy->isImplThread());
410 DCHECK(resourceProvider);
411 DCHECK(!m_backings.empty());
412 PrioritizedResource::Backing* backing = m_backings.front();
414 // Note that we create a backing and its resource at the same time, but we
415 // delete the backing structure and its resource in two steps. This is because
416 // we can delete the resource while the main thread is running, but we cannot
417 // unlink backings while the main thread is running.
418 backing->deleteResource(resourceProvider);
419 m_memoryUseBytes -= backing->bytes();
420 m_backings.pop_front();
421 base::AutoLock scoped_lock(m_evictedBackingsLock);
422 m_evictedBackings.push_back(backing);
425 void PrioritizedResourceManager::assertInvariants()
427 #ifndef NDEBUG
428 DCHECK(m_proxy->isImplThread() && m_proxy->isMainThreadBlocked());
430 // If we hit any of these asserts, there is a bug in this class. To see
431 // where the bug is, call this function at the beginning and end of
432 // every public function.
434 // Backings/textures must be doubly-linked and only to other backings/textures in this manager.
435 for (BackingList::iterator it = m_backings.begin(); it != m_backings.end(); ++it) {
436 if ((*it)->owner()) {
437 DCHECK(ContainsKey(m_textures, (*it)->owner()));
438 DCHECK((*it)->owner()->backing() == (*it));
441 for (TextureSet::iterator it = m_textures.begin(); it != m_textures.end(); ++it) {
442 PrioritizedResource* texture = (*it);
443 PrioritizedResource::Backing* backing = texture->backing();
444 base::AutoLock scoped_lock(m_evictedBackingsLock);
445 if (backing) {
446 if (backing->resourceHasBeenDeleted()) {
447 DCHECK(std::find(m_backings.begin(), m_backings.end(), backing) == m_backings.end());
448 DCHECK(std::find(m_evictedBackings.begin(), m_evictedBackings.end(), backing) != m_evictedBackings.end());
449 } else {
450 DCHECK(std::find(m_backings.begin(), m_backings.end(), backing) != m_backings.end());
451 DCHECK(std::find(m_evictedBackings.begin(), m_evictedBackings.end(), backing) == m_evictedBackings.end());
453 DCHECK(backing->owner() == texture);
457 // At all times, backings that can be evicted must always come before
458 // backings that can't be evicted in the backing texture list (otherwise
459 // reduceMemory will not find all textures available for eviction/recycling).
460 bool reachedUnrecyclable = false;
461 PrioritizedResource::Backing* previous_backing = NULL;
462 for (BackingList::iterator it = m_backings.begin(); it != m_backings.end(); ++it) {
463 PrioritizedResource::Backing* backing = *it;
464 if (previous_backing && (!m_backingsTailNotSorted || !backing->wasAbovePriorityCutoffAtLastPriorityUpdate()))
465 DCHECK(compareBackings(previous_backing, backing));
466 if (!backing->canBeRecycled())
467 reachedUnrecyclable = true;
468 if (reachedUnrecyclable)
469 DCHECK(!backing->canBeRecycled());
470 else
471 DCHECK(backing->canBeRecycled());
472 previous_backing = backing;
474 #endif
477 const Proxy* PrioritizedResourceManager::proxyForDebug() const
479 return m_proxy;
482 } // namespace cc