Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(693)

Side by Side Diff: cc/prioritized_resource_manager.cc

Issue 12471007: Part 8 of cc/ directory shuffles: resources (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 7 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « cc/prioritized_resource_manager.h ('k') | cc/prioritized_resource_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
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.
4
5 #include "cc/prioritized_resource_manager.h"
6
7 #include <algorithm>
8
9 #include "base/debug/trace_event.h"
10 #include "base/stl_util.h"
11 #include "cc/prioritized_resource.h"
12 #include "cc/priority_calculator.h"
13 #include "cc/trees/proxy.h"
14
15 namespace cc {
16
17 PrioritizedResourceManager::PrioritizedResourceManager(const Proxy* proxy)
18 : m_proxy(proxy)
19 , m_maxMemoryLimitBytes(defaultMemoryAllocationLimit())
20 , m_externalPriorityCutoff(PriorityCalculator::AllowEverythingCutoff())
21 , m_memoryUseBytes(0)
22 , m_memoryAboveCutoffBytes(0)
23 , m_memoryAvailableBytes(0)
24 , m_backingsTailNotSorted(false)
25 , m_memoryVisibleBytes(0)
26 , m_memoryVisibleAndNearbyBytes(0)
27 , m_memoryVisibleLastPushedBytes(0)
28 , m_memoryVisibleAndNearbyLastPushedBytes(0)
29 {
30 }
31
32 PrioritizedResourceManager::~PrioritizedResourceManager()
33 {
34 while (m_textures.size() > 0)
35 unregisterTexture(*m_textures.begin());
36
37 unlinkAndClearEvictedBackings();
38 DCHECK(m_evictedBackings.empty());
39
40 // Each remaining backing is a leaked opengl texture. There should be none.
41 DCHECK(m_backings.empty());
42 }
43
44 size_t PrioritizedResourceManager::memoryVisibleBytes() const
45 {
46 DCHECK(m_proxy->IsImplThread());
47 return m_memoryVisibleLastPushedBytes;
48 }
49
50 size_t PrioritizedResourceManager::memoryVisibleAndNearbyBytes() const
51 {
52 DCHECK(m_proxy->IsImplThread());
53 return m_memoryVisibleAndNearbyLastPushedBytes;
54 }
55
56 void PrioritizedResourceManager::prioritizeTextures()
57 {
58 TRACE_EVENT0("cc", "PrioritizedResourceManager::prioritizeTextures");
59 DCHECK(m_proxy->IsMainThread());
60
61 // Sorting textures in this function could be replaced by a slightly
62 // modified O(n) quick-select to partition textures rather than
63 // sort them (if performance of the sort becomes an issue).
64
65 TextureVector& sortedTextures = m_tempTextureVector;
66 sortedTextures.clear();
67
68 // Copy all textures into a vector, sort them, and collect memory requiremen ts statistics.
69 m_memoryVisibleBytes = 0;
70 m_memoryVisibleAndNearbyBytes = 0;
71 for (TextureSet::iterator it = m_textures.begin(); it != m_textures.end(); + +it) {
72 PrioritizedResource* texture = (*it);
73 sortedTextures.push_back(texture);
74 if (PriorityCalculator::priority_is_higher(texture->requestPriority(), P riorityCalculator::AllowVisibleOnlyCutoff()))
75 m_memoryVisibleBytes += texture->bytes();
76 if (PriorityCalculator::priority_is_higher(texture->requestPriority(), P riorityCalculator::AllowVisibleAndNearbyCutoff()))
77 m_memoryVisibleAndNearbyBytes += texture->bytes();
78 }
79 std::sort(sortedTextures.begin(), sortedTextures.end(), compareTextures);
80
81 // Compute a priority cutoff based on memory pressure
82 m_memoryAvailableBytes = m_maxMemoryLimitBytes;
83 m_priorityCutoff = m_externalPriorityCutoff;
84 size_t memoryBytes = 0;
85 for (TextureVector::iterator it = sortedTextures.begin(); it != sortedTextur es.end(); ++it) {
86 if ((*it)->isSelfManaged()) {
87 // Account for self-managed memory immediately by reducing the memor y
88 // available (since it never gets acquired).
89 size_t newMemoryBytes = memoryBytes + (*it)->bytes();
90 if (newMemoryBytes > m_memoryAvailableBytes) {
91 m_priorityCutoff = (*it)->requestPriority();
92 m_memoryAvailableBytes = memoryBytes;
93 break;
94 }
95 m_memoryAvailableBytes -= (*it)->bytes();
96 } else {
97 size_t newMemoryBytes = memoryBytes + (*it)->bytes();
98 if (newMemoryBytes > m_memoryAvailableBytes) {
99 m_priorityCutoff = (*it)->requestPriority();
100 break;
101 }
102 memoryBytes = newMemoryBytes;
103 }
104 }
105
106 // Disallow any textures with priority below the external cutoff to have bac kings.
107 for (TextureVector::iterator it = sortedTextures.begin(); it != sortedTextur es.end(); ++it) {
108 PrioritizedResource* texture = (*it);
109 if (!PriorityCalculator::priority_is_higher(texture->requestPriority(), m_externalPriorityCutoff) &&
110 texture->haveBackingTexture())
111 texture->unlink();
112 }
113
114 // Only allow textures if they are higher than the cutoff. All textures
115 // of the same priority are accepted or rejected together, rather than
116 // being partially allowed randomly.
117 m_memoryAboveCutoffBytes = 0;
118 for (TextureVector::iterator it = sortedTextures.begin(); it != sortedTextur es.end(); ++it) {
119 bool isAbovePriorityCutoff = PriorityCalculator::priority_is_higher((*it )->requestPriority(), m_priorityCutoff);
120 (*it)->setAbovePriorityCutoff(isAbovePriorityCutoff);
121 if (isAbovePriorityCutoff && !(*it)->isSelfManaged())
122 m_memoryAboveCutoffBytes += (*it)->bytes();
123 }
124 sortedTextures.clear();
125
126 DCHECK(m_memoryAboveCutoffBytes <= m_memoryAvailableBytes);
127 DCHECK(memoryAboveCutoffBytes() <= maxMemoryLimitBytes());
128 }
129
130 void PrioritizedResourceManager::pushTexturePrioritiesToBackings()
131 {
132 TRACE_EVENT0("cc", "PrioritizedResourceManager::pushTexturePrioritiesToBacki ngs");
133 DCHECK(m_proxy->IsImplThread() && m_proxy->IsMainThreadBlocked());
134
135 assertInvariants();
136 for (BackingList::iterator it = m_backings.begin(); it != m_backings.end(); ++it)
137 (*it)->updatePriority();
138 sortBackings();
139 assertInvariants();
140
141 // Push memory requirements to the impl thread structure.
142 m_memoryVisibleLastPushedBytes = m_memoryVisibleBytes;
143 m_memoryVisibleAndNearbyLastPushedBytes = m_memoryVisibleAndNearbyBytes;
144 }
145
146 void PrioritizedResourceManager::updateBackingsInDrawingImplTree()
147 {
148 TRACE_EVENT0("cc", "PrioritizedResourceManager::updateBackingsInDrawingImplT ree");
149 DCHECK(m_proxy->IsImplThread() && m_proxy->IsMainThreadBlocked());
150
151 assertInvariants();
152 for (BackingList::iterator it = m_backings.begin(); it != m_backings.end(); ++it) {
153 PrioritizedResource::Backing* backing = (*it);
154 backing->updateInDrawingImplTree();
155 }
156 sortBackings();
157 assertInvariants();
158 }
159
160 void PrioritizedResourceManager::sortBackings()
161 {
162 TRACE_EVENT0("cc", "PrioritizedResourceManager::sortBackings");
163 DCHECK(m_proxy->IsImplThread());
164
165 // Put backings in eviction/recycling order.
166 m_backings.sort(compareBackings);
167 m_backingsTailNotSorted = false;
168 }
169
170 void PrioritizedResourceManager::clearPriorities()
171 {
172 DCHECK(m_proxy->IsMainThread());
173 for (TextureSet::iterator it = m_textures.begin(); it != m_textures.end(); + +it) {
174 // FIXME: We should remove this and just set all priorities to
175 // PriorityCalculator::lowestPriority() once we have priorities
176 // for all textures (we can't currently calculate distances for
177 // off-screen textures).
178 (*it)->setRequestPriority(PriorityCalculator::LingeringPriority((*it)->r equestPriority()));
179 }
180 }
181
182 bool PrioritizedResourceManager::requestLate(PrioritizedResource* texture)
183 {
184 DCHECK(m_proxy->IsMainThread());
185
186 // This is already above cutoff, so don't double count it's memory below.
187 if (texture->isAbovePriorityCutoff())
188 return true;
189
190 // Allow textures that have priority equal to the cutoff, but not strictly l ower.
191 if (PriorityCalculator::priority_is_lower(texture->requestPriority(), m_prio rityCutoff))
192 return false;
193
194 // Disallow textures that do not have a priority strictly higher than the ex ternal cutoff.
195 if (!PriorityCalculator::priority_is_higher(texture->requestPriority(), m_ex ternalPriorityCutoff))
196 return false;
197
198 size_t newMemoryBytes = m_memoryAboveCutoffBytes + texture->bytes();
199 if (newMemoryBytes > m_memoryAvailableBytes)
200 return false;
201
202 m_memoryAboveCutoffBytes = newMemoryBytes;
203 texture->setAbovePriorityCutoff(true);
204 return true;
205 }
206
207 void PrioritizedResourceManager::acquireBackingTextureIfNeeded(PrioritizedResour ce* texture, ResourceProvider* resourceProvider)
208 {
209 DCHECK(m_proxy->IsImplThread() && m_proxy->IsMainThreadBlocked());
210 DCHECK(!texture->isSelfManaged());
211 DCHECK(texture->isAbovePriorityCutoff());
212 if (texture->backing() || !texture->isAbovePriorityCutoff())
213 return;
214
215 // Find a backing below, by either recycling or allocating.
216 PrioritizedResource::Backing* backing = 0;
217
218 // First try to recycle
219 for (BackingList::iterator it = m_backings.begin(); it != m_backings.end(); ++it) {
220 if (!(*it)->canBeRecycled())
221 break;
222 if (resourceProvider->InUseByConsumer((*it)->id()))
223 continue;
224 if ((*it)->size() == texture->size() && (*it)->format() == texture->form at()) {
225 backing = (*it);
226 m_backings.erase(it);
227 break;
228 }
229 }
230
231 // Otherwise reduce memory and just allocate a new backing texures.
232 if (!backing) {
233 evictBackingsToReduceMemory(m_memoryAvailableBytes - texture->bytes(),
234 PriorityCalculator::AllowEverythingCutoff(),
235 EvictOnlyRecyclable,
236 DoNotUnlinkBackings,
237 resourceProvider);
238 backing = createBacking(texture->size(), texture->format(), resourceProv ider);
239 }
240
241 // Move the used backing to the end of the eviction list, and note that
242 // the tail is not sorted.
243 if (backing->owner())
244 backing->owner()->unlink();
245 texture->link(backing);
246 m_backings.push_back(backing);
247 m_backingsTailNotSorted = true;
248
249 // Update the backing's priority from its new owner.
250 backing->updatePriority();
251 }
252
253 bool PrioritizedResourceManager::evictBackingsToReduceMemory(size_t limitBytes,
254 int priorityCutoff,
255 EvictionPolicy evic tionPolicy,
256 UnlinkPolicy unlink Policy,
257 ResourceProvider* r esourceProvider)
258 {
259 DCHECK(m_proxy->IsImplThread());
260 if (unlinkPolicy == UnlinkBackings)
261 DCHECK(m_proxy->IsMainThreadBlocked());
262 if (memoryUseBytes() <= limitBytes && PriorityCalculator::AllowEverythingCut off() == priorityCutoff)
263 return false;
264
265 // Destroy backings until we are below the limit,
266 // or until all backings remaining are above the cutoff.
267 while (m_backings.size() > 0) {
268 PrioritizedResource::Backing* backing = m_backings.front();
269 if (memoryUseBytes() <= limitBytes &&
270 PriorityCalculator::priority_is_higher(backing->requestPriorityAtLas tPriorityUpdate(), priorityCutoff))
271 break;
272 if (evictionPolicy == EvictOnlyRecyclable && !backing->canBeRecycled())
273 break;
274 if (unlinkPolicy == UnlinkBackings && backing->owner())
275 backing->owner()->unlink();
276 evictFirstBackingResource(resourceProvider);
277 }
278 return true;
279 }
280
281 void PrioritizedResourceManager::reduceWastedMemory(ResourceProvider* resourcePr ovider)
282 {
283 // We currently collect backings from deleted textures for later recycling.
284 // However, if we do that forever we will always use the max limit even if
285 // we really need very little memory. This should probably be solved by redu cing the
286 // limit externally, but until then this just does some "clean up" of unused
287 // backing textures (any more than 10%).
288 size_t wastedMemory = 0;
289 for (BackingList::iterator it = m_backings.begin(); it != m_backings.end(); ++it) {
290 if ((*it)->owner())
291 break;
292 wastedMemory += (*it)->bytes();
293 }
294 size_t tenPercentOfMemory = m_memoryAvailableBytes / 10;
295 if (wastedMemory > tenPercentOfMemory)
296 evictBackingsToReduceMemory(memoryUseBytes() - (wastedMemory - tenPercen tOfMemory),
297 PriorityCalculator::AllowEverythingCutoff(),
298 EvictOnlyRecyclable,
299 DoNotUnlinkBackings,
300 resourceProvider);
301 }
302
303 void PrioritizedResourceManager::reduceMemory(ResourceProvider* resourceProvider )
304 {
305 DCHECK(m_proxy->IsImplThread() && m_proxy->IsMainThreadBlocked());
306 evictBackingsToReduceMemory(m_memoryAvailableBytes,
307 PriorityCalculator::AllowEverythingCutoff(),
308 EvictAnything,
309 UnlinkBackings,
310 resourceProvider);
311 DCHECK(memoryUseBytes() <= m_memoryAvailableBytes);
312
313 reduceWastedMemory(resourceProvider);
314 }
315
316 void PrioritizedResourceManager::clearAllMemory(ResourceProvider* resourceProvid er)
317 {
318 DCHECK(m_proxy->IsImplThread() && m_proxy->IsMainThreadBlocked());
319 if (!resourceProvider) {
320 DCHECK(m_backings.empty());
321 return;
322 }
323 evictBackingsToReduceMemory(0,
324 PriorityCalculator::AllowEverythingCutoff(),
325 EvictAnything,
326 DoNotUnlinkBackings,
327 resourceProvider);
328 }
329
330 bool PrioritizedResourceManager::reduceMemoryOnImplThread(size_t limitBytes, int priorityCutoff, ResourceProvider* resourceProvider)
331 {
332 DCHECK(m_proxy->IsImplThread());
333 DCHECK(resourceProvider);
334 // If we are in the process of uploading a new frame then the backings at th e very end of
335 // the list are not sorted by priority. Sort them before doing the eviction.
336 if (m_backingsTailNotSorted)
337 sortBackings();
338 return evictBackingsToReduceMemory(limitBytes,
339 priorityCutoff,
340 EvictAnything,
341 DoNotUnlinkBackings,
342 resourceProvider);
343 }
344
345 void PrioritizedResourceManager::reduceWastedMemoryOnImplThread(ResourceProvider * resourceProvider)
346 {
347 DCHECK(m_proxy->IsImplThread());
348 DCHECK(resourceProvider);
349 // If we are in the process of uploading a new frame then the backings at th e very end of
350 // the list are not sorted by priority. Sort them before doing the eviction.
351 if (m_backingsTailNotSorted)
352 sortBackings();
353 reduceWastedMemory(resourceProvider);
354 }
355
356 void PrioritizedResourceManager::unlinkAndClearEvictedBackings()
357 {
358 DCHECK(m_proxy->IsMainThread());
359 base::AutoLock scoped_lock(m_evictedBackingsLock);
360 for (BackingList::const_iterator it = m_evictedBackings.begin(); it != m_evi ctedBackings.end(); ++it) {
361 PrioritizedResource::Backing* backing = (*it);
362 if (backing->owner())
363 backing->owner()->unlink();
364 delete backing;
365 }
366 m_evictedBackings.clear();
367 }
368
369 bool PrioritizedResourceManager::linkedEvictedBackingsExist() const
370 {
371 DCHECK(m_proxy->IsImplThread() && m_proxy->IsMainThreadBlocked());
372 base::AutoLock scoped_lock(m_evictedBackingsLock);
373 for (BackingList::const_iterator it = m_evictedBackings.begin(); it != m_evi ctedBackings.end(); ++it) {
374 if ((*it)->owner())
375 return true;
376 }
377 return false;
378 }
379
380 void PrioritizedResourceManager::registerTexture(PrioritizedResource* texture)
381 {
382 DCHECK(m_proxy->IsMainThread());
383 DCHECK(texture);
384 DCHECK(!texture->resourceManager());
385 DCHECK(!texture->backing());
386 DCHECK(!ContainsKey(m_textures, texture));
387
388 texture->setManagerInternal(this);
389 m_textures.insert(texture);
390
391 }
392
393 void PrioritizedResourceManager::unregisterTexture(PrioritizedResource* texture)
394 {
395 DCHECK(m_proxy->IsMainThread() || (m_proxy->IsImplThread() && m_proxy->IsMai nThreadBlocked()));
396 DCHECK(texture);
397 DCHECK(ContainsKey(m_textures, texture));
398
399 returnBackingTexture(texture);
400 texture->setManagerInternal(0);
401 m_textures.erase(texture);
402 texture->setAbovePriorityCutoff(false);
403 }
404
405 void PrioritizedResourceManager::returnBackingTexture(PrioritizedResource* textu re)
406 {
407 DCHECK(m_proxy->IsMainThread() || (m_proxy->IsImplThread() && m_proxy->IsMai nThreadBlocked()));
408 if (texture->backing())
409 texture->unlink();
410 }
411
412 PrioritizedResource::Backing* PrioritizedResourceManager::createBacking(gfx::Siz e size, GLenum format, ResourceProvider* resourceProvider)
413 {
414 DCHECK(m_proxy->IsImplThread() && m_proxy->IsMainThreadBlocked());
415 DCHECK(resourceProvider);
416 ResourceProvider::ResourceId resourceId = resourceProvider->CreateManagedRes ource(size, format, ResourceProvider::TextureUsageAny);
417 PrioritizedResource::Backing* backing = new PrioritizedResource::Backing(res ourceId, resourceProvider, size, format);
418 m_memoryUseBytes += backing->bytes();
419 return backing;
420 }
421
422 void PrioritizedResourceManager::evictFirstBackingResource(ResourceProvider* res ourceProvider)
423 {
424 DCHECK(m_proxy->IsImplThread());
425 DCHECK(resourceProvider);
426 DCHECK(!m_backings.empty());
427 PrioritizedResource::Backing* backing = m_backings.front();
428
429 // Note that we create a backing and its resource at the same time, but we
430 // delete the backing structure and its resource in two steps. This is becau se
431 // we can delete the resource while the main thread is running, but we canno t
432 // unlink backings while the main thread is running.
433 backing->deleteResource(resourceProvider);
434 m_memoryUseBytes -= backing->bytes();
435 m_backings.pop_front();
436 base::AutoLock scoped_lock(m_evictedBackingsLock);
437 m_evictedBackings.push_back(backing);
438 }
439
440 void PrioritizedResourceManager::assertInvariants()
441 {
442 #ifndef NDEBUG
443 DCHECK(m_proxy->IsImplThread() && m_proxy->IsMainThreadBlocked());
444
445 // If we hit any of these asserts, there is a bug in this class. To see
446 // where the bug is, call this function at the beginning and end of
447 // every public function.
448
449 // Backings/textures must be doubly-linked and only to other backings/textur es in this manager.
450 for (BackingList::iterator it = m_backings.begin(); it != m_backings.end(); ++it) {
451 if ((*it)->owner()) {
452 DCHECK(ContainsKey(m_textures, (*it)->owner()));
453 DCHECK((*it)->owner()->backing() == (*it));
454 }
455 }
456 for (TextureSet::iterator it = m_textures.begin(); it != m_textures.end(); + +it) {
457 PrioritizedResource* texture = (*it);
458 PrioritizedResource::Backing* backing = texture->backing();
459 base::AutoLock scoped_lock(m_evictedBackingsLock);
460 if (backing) {
461 if (backing->resourceHasBeenDeleted()) {
462 DCHECK(std::find(m_backings.begin(), m_backings.end(), backing) == m_backings.end());
463 DCHECK(std::find(m_evictedBackings.begin(), m_evictedBackings.en d(), backing) != m_evictedBackings.end());
464 } else {
465 DCHECK(std::find(m_backings.begin(), m_backings.end(), backing) != m_backings.end());
466 DCHECK(std::find(m_evictedBackings.begin(), m_evictedBackings.en d(), backing) == m_evictedBackings.end());
467 }
468 DCHECK(backing->owner() == texture);
469 }
470 }
471
472 // At all times, backings that can be evicted must always come before
473 // backings that can't be evicted in the backing texture list (otherwise
474 // reduceMemory will not find all textures available for eviction/recycling) .
475 bool reachedUnrecyclable = false;
476 PrioritizedResource::Backing* previous_backing = NULL;
477 for (BackingList::iterator it = m_backings.begin(); it != m_backings.end(); ++it) {
478 PrioritizedResource::Backing* backing = *it;
479 if (previous_backing && (!m_backingsTailNotSorted || !backing->wasAboveP riorityCutoffAtLastPriorityUpdate()))
480 DCHECK(compareBackings(previous_backing, backing));
481 if (!backing->canBeRecycled())
482 reachedUnrecyclable = true;
483 if (reachedUnrecyclable)
484 DCHECK(!backing->canBeRecycled());
485 else
486 DCHECK(backing->canBeRecycled());
487 previous_backing = backing;
488 }
489 #endif
490 }
491
492 const Proxy* PrioritizedResourceManager::proxyForDebug() const
493 {
494 return m_proxy;
495 }
496
497 } // namespace cc
OLDNEW
« no previous file with comments | « cc/prioritized_resource_manager.h ('k') | cc/prioritized_resource_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698