OLD | NEW |
| (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/resources/prioritized_resource_manager.h" | |
6 | |
7 #include <algorithm> | |
8 | |
9 #include "base/trace_event/trace_event.h" | |
10 #include "cc/resources/prioritized_resource.h" | |
11 #include "cc/resources/priority_calculator.h" | |
12 #include "cc/trees/proxy.h" | |
13 | |
14 namespace cc { | |
15 | |
16 PrioritizedResourceManager::PrioritizedResourceManager(const Proxy* proxy) | |
17 : max_memory_limit_bytes_(DefaultMemoryAllocationLimit()), | |
18 external_priority_cutoff_(PriorityCalculator::AllowEverythingCutoff()), | |
19 memory_use_bytes_(0), | |
20 memory_above_cutoff_bytes_(0), | |
21 max_memory_needed_bytes_(0), | |
22 memory_available_bytes_(0), | |
23 proxy_(proxy), | |
24 backings_tail_not_sorted_(false), | |
25 memory_visible_bytes_(0), | |
26 memory_visible_and_nearby_bytes_(0), | |
27 memory_visible_last_pushed_bytes_(0), | |
28 memory_visible_and_nearby_last_pushed_bytes_(0) {} | |
29 | |
30 PrioritizedResourceManager::~PrioritizedResourceManager() { | |
31 while (textures_.size() > 0) | |
32 UnregisterTexture(*textures_.begin()); | |
33 | |
34 UnlinkAndClearEvictedBackings(); | |
35 DCHECK(evicted_backings_.empty()); | |
36 | |
37 // Each remaining backing is a leaked opengl texture. There should be none. | |
38 DCHECK(backings_.empty()); | |
39 } | |
40 | |
41 size_t PrioritizedResourceManager::MemoryVisibleBytes() const { | |
42 DCHECK(proxy_->IsImplThread()); | |
43 return memory_visible_last_pushed_bytes_; | |
44 } | |
45 | |
46 size_t PrioritizedResourceManager::MemoryVisibleAndNearbyBytes() const { | |
47 DCHECK(proxy_->IsImplThread()); | |
48 return memory_visible_and_nearby_last_pushed_bytes_; | |
49 } | |
50 | |
51 void PrioritizedResourceManager::PrioritizeTextures() { | |
52 TRACE_EVENT0("cc", "PrioritizedResourceManager::PrioritizeTextures"); | |
53 DCHECK(proxy_->IsMainThread()); | |
54 | |
55 // Sorting textures in this function could be replaced by a slightly | |
56 // modified O(n) quick-select to partition textures rather than | |
57 // sort them (if performance of the sort becomes an issue). | |
58 | |
59 TextureVector& sorted_textures = temp_texture_vector_; | |
60 sorted_textures.clear(); | |
61 | |
62 // Copy all textures into a vector, sort them, and collect memory requirements | |
63 // statistics. | |
64 memory_visible_bytes_ = 0; | |
65 memory_visible_and_nearby_bytes_ = 0; | |
66 for (TextureSet::iterator it = textures_.begin(); it != textures_.end(); | |
67 ++it) { | |
68 PrioritizedResource* texture = (*it); | |
69 sorted_textures.push_back(texture); | |
70 if (PriorityCalculator::priority_is_higher( | |
71 texture->request_priority(), | |
72 PriorityCalculator::AllowVisibleOnlyCutoff())) | |
73 memory_visible_bytes_ += texture->bytes(); | |
74 if (PriorityCalculator::priority_is_higher( | |
75 texture->request_priority(), | |
76 PriorityCalculator::AllowVisibleAndNearbyCutoff())) | |
77 memory_visible_and_nearby_bytes_ += texture->bytes(); | |
78 } | |
79 std::sort(sorted_textures.begin(), sorted_textures.end(), CompareTextures); | |
80 | |
81 // Compute a priority cutoff based on memory pressure | |
82 memory_available_bytes_ = max_memory_limit_bytes_; | |
83 priority_cutoff_ = external_priority_cutoff_; | |
84 size_t memory_bytes = 0; | |
85 for (TextureVector::iterator it = sorted_textures.begin(); | |
86 it != sorted_textures.end(); | |
87 ++it) { | |
88 if ((*it)->is_self_managed()) { | |
89 // Account for self-managed memory immediately by reducing the memory | |
90 // available (since it never gets acquired). | |
91 size_t new_memory_bytes = memory_bytes + (*it)->bytes(); | |
92 if (new_memory_bytes > memory_available_bytes_) { | |
93 priority_cutoff_ = (*it)->request_priority(); | |
94 memory_available_bytes_ = memory_bytes; | |
95 break; | |
96 } | |
97 memory_available_bytes_ -= (*it)->bytes(); | |
98 } else { | |
99 size_t new_memory_bytes = memory_bytes + (*it)->bytes(); | |
100 if (new_memory_bytes > memory_available_bytes_) { | |
101 priority_cutoff_ = (*it)->request_priority(); | |
102 break; | |
103 } | |
104 memory_bytes = new_memory_bytes; | |
105 } | |
106 } | |
107 | |
108 // Disallow any textures with priority below the external cutoff to have | |
109 // backings. | |
110 for (TextureVector::iterator it = sorted_textures.begin(); | |
111 it != sorted_textures.end(); | |
112 ++it) { | |
113 PrioritizedResource* texture = (*it); | |
114 if (!PriorityCalculator::priority_is_higher(texture->request_priority(), | |
115 external_priority_cutoff_) && | |
116 texture->have_backing_texture()) | |
117 texture->Unlink(); | |
118 } | |
119 | |
120 // Only allow textures if they are higher than the cutoff. All textures | |
121 // of the same priority are accepted or rejected together, rather than | |
122 // being partially allowed randomly. | |
123 max_memory_needed_bytes_ = 0; | |
124 memory_above_cutoff_bytes_ = 0; | |
125 for (TextureVector::iterator it = sorted_textures.begin(); | |
126 it != sorted_textures.end(); | |
127 ++it) { | |
128 PrioritizedResource* resource = *it; | |
129 bool is_above_priority_cutoff = PriorityCalculator::priority_is_higher( | |
130 resource->request_priority(), priority_cutoff_); | |
131 resource->set_above_priority_cutoff(is_above_priority_cutoff); | |
132 if (!resource->is_self_managed()) { | |
133 max_memory_needed_bytes_ += resource->bytes(); | |
134 if (is_above_priority_cutoff) | |
135 memory_above_cutoff_bytes_ += resource->bytes(); | |
136 } | |
137 } | |
138 sorted_textures.clear(); | |
139 | |
140 DCHECK_LE(memory_above_cutoff_bytes_, memory_available_bytes_); | |
141 DCHECK_LE(MemoryAboveCutoffBytes(), MaxMemoryLimitBytes()); | |
142 } | |
143 | |
144 void PrioritizedResourceManager::PushTexturePrioritiesToBackings() { | |
145 TRACE_EVENT0("cc", | |
146 "PrioritizedResourceManager::PushTexturePrioritiesToBackings"); | |
147 DCHECK(proxy_->IsImplThread() && proxy_->IsMainThreadBlocked()); | |
148 | |
149 AssertInvariants(); | |
150 for (BackingList::iterator it = backings_.begin(); it != backings_.end(); | |
151 ++it) | |
152 (*it)->UpdatePriority(); | |
153 SortBackings(); | |
154 AssertInvariants(); | |
155 | |
156 // Push memory requirements to the impl thread structure. | |
157 memory_visible_last_pushed_bytes_ = memory_visible_bytes_; | |
158 memory_visible_and_nearby_last_pushed_bytes_ = | |
159 memory_visible_and_nearby_bytes_; | |
160 } | |
161 | |
162 void PrioritizedResourceManager::UpdateBackingsState( | |
163 ResourceProvider* resource_provider) { | |
164 TRACE_EVENT0("cc", | |
165 "PrioritizedResourceManager::UpdateBackingsInDrawingImplTree"); | |
166 DCHECK(proxy_->IsImplThread() && proxy_->IsMainThreadBlocked()); | |
167 | |
168 AssertInvariants(); | |
169 for (BackingList::iterator it = backings_.begin(); it != backings_.end(); | |
170 ++it) { | |
171 PrioritizedResource::Backing* backing = (*it); | |
172 backing->UpdateState(resource_provider); | |
173 } | |
174 SortBackings(); | |
175 AssertInvariants(); | |
176 } | |
177 | |
178 void PrioritizedResourceManager::SortBackings() { | |
179 TRACE_EVENT0("cc", "PrioritizedResourceManager::SortBackings"); | |
180 DCHECK(proxy_->IsImplThread()); | |
181 | |
182 // Put backings in eviction/recycling order. | |
183 backings_.sort(CompareBackings); | |
184 backings_tail_not_sorted_ = false; | |
185 } | |
186 | |
187 void PrioritizedResourceManager::ClearPriorities() { | |
188 DCHECK(proxy_->IsMainThread()); | |
189 for (TextureSet::iterator it = textures_.begin(); it != textures_.end(); | |
190 ++it) { | |
191 // TODO(reveman): We should remove this and just set all priorities to | |
192 // PriorityCalculator::lowestPriority() once we have priorities for all | |
193 // textures (we can't currently calculate distances for off-screen | |
194 // textures). | |
195 (*it)->set_request_priority( | |
196 PriorityCalculator::LingeringPriority((*it)->request_priority())); | |
197 } | |
198 } | |
199 | |
200 bool PrioritizedResourceManager::RequestLate(PrioritizedResource* texture) { | |
201 DCHECK(proxy_->IsMainThread()); | |
202 | |
203 // This is already above cutoff, so don't double count it's memory below. | |
204 if (texture->is_above_priority_cutoff()) | |
205 return true; | |
206 | |
207 // Allow textures that have priority equal to the cutoff, but not strictly | |
208 // lower. | |
209 if (PriorityCalculator::priority_is_lower(texture->request_priority(), | |
210 priority_cutoff_)) | |
211 return false; | |
212 | |
213 // Disallow textures that do not have a priority strictly higher than the | |
214 // external cutoff. | |
215 if (!PriorityCalculator::priority_is_higher(texture->request_priority(), | |
216 external_priority_cutoff_)) | |
217 return false; | |
218 | |
219 size_t new_memory_bytes = memory_above_cutoff_bytes_ + texture->bytes(); | |
220 if (new_memory_bytes > memory_available_bytes_) | |
221 return false; | |
222 | |
223 memory_above_cutoff_bytes_ = new_memory_bytes; | |
224 texture->set_above_priority_cutoff(true); | |
225 return true; | |
226 } | |
227 | |
228 void PrioritizedResourceManager::AcquireBackingTextureIfNeeded( | |
229 PrioritizedResource* texture, | |
230 ResourceProvider* resource_provider) { | |
231 DCHECK(proxy_->IsImplThread() && proxy_->IsMainThreadBlocked()); | |
232 DCHECK(!texture->is_self_managed()); | |
233 DCHECK(texture->is_above_priority_cutoff()); | |
234 if (texture->backing() || !texture->is_above_priority_cutoff()) | |
235 return; | |
236 | |
237 // Find a backing below, by either recycling or allocating. | |
238 PrioritizedResource::Backing* backing = NULL; | |
239 | |
240 // First try to recycle | |
241 for (BackingList::iterator it = backings_.begin(); it != backings_.end(); | |
242 ++it) { | |
243 if (!(*it)->CanBeRecycledIfNotInExternalUse()) | |
244 break; | |
245 if (resource_provider->InUseByConsumer((*it)->id())) | |
246 continue; | |
247 if ((*it)->size() == texture->size() && | |
248 (*it)->format() == texture->format()) { | |
249 backing = (*it); | |
250 backings_.erase(it); | |
251 break; | |
252 } | |
253 } | |
254 | |
255 // Otherwise reduce memory and just allocate a new backing texures. | |
256 if (!backing) { | |
257 EvictBackingsToReduceMemory(memory_available_bytes_ - texture->bytes(), | |
258 PriorityCalculator::AllowEverythingCutoff(), | |
259 EVICT_ONLY_RECYCLABLE, | |
260 DO_NOT_UNLINK_BACKINGS, | |
261 resource_provider); | |
262 backing = | |
263 CreateBacking(texture->size(), texture->format(), resource_provider); | |
264 } | |
265 | |
266 // Move the used backing to the end of the eviction list, and note that | |
267 // the tail is not sorted. | |
268 if (backing->owner()) | |
269 backing->owner()->Unlink(); | |
270 texture->Link(backing); | |
271 backings_.push_back(backing); | |
272 backings_tail_not_sorted_ = true; | |
273 | |
274 // Update the backing's priority from its new owner. | |
275 backing->UpdatePriority(); | |
276 } | |
277 | |
278 bool PrioritizedResourceManager::EvictBackingsToReduceMemory( | |
279 size_t limit_bytes, | |
280 int priority_cutoff, | |
281 EvictionPolicy eviction_policy, | |
282 UnlinkPolicy unlink_policy, | |
283 ResourceProvider* resource_provider) { | |
284 DCHECK(proxy_->IsImplThread()); | |
285 if (unlink_policy == UNLINK_BACKINGS) | |
286 DCHECK(proxy_->IsMainThreadBlocked()); | |
287 if (MemoryUseBytes() <= limit_bytes && | |
288 PriorityCalculator::AllowEverythingCutoff() == priority_cutoff) | |
289 return false; | |
290 | |
291 // Destroy backings until we are below the limit, | |
292 // or until all backings remaining are above the cutoff. | |
293 bool evicted_anything = false; | |
294 while (backings_.size() > 0) { | |
295 PrioritizedResource::Backing* backing = backings_.front(); | |
296 if (MemoryUseBytes() <= limit_bytes && | |
297 PriorityCalculator::priority_is_higher( | |
298 backing->request_priority_at_last_priority_update(), | |
299 priority_cutoff)) | |
300 break; | |
301 if (eviction_policy == EVICT_ONLY_RECYCLABLE && | |
302 !backing->CanBeRecycledIfNotInExternalUse()) | |
303 break; | |
304 if (unlink_policy == UNLINK_BACKINGS && backing->owner()) | |
305 backing->owner()->Unlink(); | |
306 EvictFirstBackingResource(resource_provider); | |
307 evicted_anything = true; | |
308 } | |
309 return evicted_anything; | |
310 } | |
311 | |
312 void PrioritizedResourceManager::ReduceWastedMemory( | |
313 ResourceProvider* resource_provider) { | |
314 // We currently collect backings from deleted textures for later recycling. | |
315 // However, if we do that forever we will always use the max limit even if | |
316 // we really need very little memory. This should probably be solved by | |
317 // reducing the limit externally, but until then this just does some "clean | |
318 // up" of unused backing textures (any more than 10%). | |
319 size_t wasted_memory = 0; | |
320 for (BackingList::iterator it = backings_.begin(); it != backings_.end(); | |
321 ++it) { | |
322 if ((*it)->owner()) | |
323 break; | |
324 if ((*it)->in_parent_compositor()) | |
325 continue; | |
326 wasted_memory += (*it)->bytes(); | |
327 } | |
328 size_t wasted_memory_to_allow = memory_available_bytes_ / 10; | |
329 // If the external priority cutoff indicates that unused memory should be | |
330 // freed, then do not allow any memory for texture recycling. | |
331 if (external_priority_cutoff_ != PriorityCalculator::AllowEverythingCutoff()) | |
332 wasted_memory_to_allow = 0; | |
333 if (wasted_memory > wasted_memory_to_allow) | |
334 EvictBackingsToReduceMemory(MemoryUseBytes() - | |
335 (wasted_memory - wasted_memory_to_allow), | |
336 PriorityCalculator::AllowEverythingCutoff(), | |
337 EVICT_ONLY_RECYCLABLE, | |
338 DO_NOT_UNLINK_BACKINGS, | |
339 resource_provider); | |
340 } | |
341 | |
342 void PrioritizedResourceManager::ReduceMemory( | |
343 ResourceProvider* resource_provider) { | |
344 DCHECK(proxy_->IsImplThread() && proxy_->IsMainThreadBlocked()); | |
345 EvictBackingsToReduceMemory(memory_available_bytes_, | |
346 PriorityCalculator::AllowEverythingCutoff(), | |
347 EVICT_ANYTHING, | |
348 UNLINK_BACKINGS, | |
349 resource_provider); | |
350 DCHECK_LE(MemoryUseBytes(), memory_available_bytes_); | |
351 | |
352 ReduceWastedMemory(resource_provider); | |
353 } | |
354 | |
355 void PrioritizedResourceManager::ClearAllMemory( | |
356 ResourceProvider* resource_provider) { | |
357 DCHECK(proxy_->IsImplThread() && proxy_->IsMainThreadBlocked()); | |
358 if (!resource_provider) { | |
359 DCHECK(backings_.empty()); | |
360 return; | |
361 } | |
362 EvictBackingsToReduceMemory(0, | |
363 PriorityCalculator::AllowEverythingCutoff(), | |
364 EVICT_ANYTHING, | |
365 DO_NOT_UNLINK_BACKINGS, | |
366 resource_provider); | |
367 } | |
368 | |
369 bool PrioritizedResourceManager::ReduceMemoryOnImplThread( | |
370 size_t limit_bytes, | |
371 int priority_cutoff, | |
372 ResourceProvider* resource_provider) { | |
373 DCHECK(proxy_->IsImplThread()); | |
374 DCHECK(resource_provider); | |
375 | |
376 // If we are in the process of uploading a new frame then the backings at the | |
377 // very end of the list are not sorted by priority. Sort them before doing the | |
378 // eviction. | |
379 if (backings_tail_not_sorted_) | |
380 SortBackings(); | |
381 return EvictBackingsToReduceMemory(limit_bytes, | |
382 priority_cutoff, | |
383 EVICT_ANYTHING, | |
384 DO_NOT_UNLINK_BACKINGS, | |
385 resource_provider); | |
386 } | |
387 | |
388 void PrioritizedResourceManager::UnlinkAndClearEvictedBackings() { | |
389 DCHECK(proxy_->IsMainThread()); | |
390 base::AutoLock scoped_lock(evicted_backings_lock_); | |
391 for (BackingList::const_iterator it = evicted_backings_.begin(); | |
392 it != evicted_backings_.end(); | |
393 ++it) { | |
394 PrioritizedResource::Backing* backing = (*it); | |
395 if (backing->owner()) | |
396 backing->owner()->Unlink(); | |
397 delete backing; | |
398 } | |
399 evicted_backings_.clear(); | |
400 } | |
401 | |
402 bool PrioritizedResourceManager::LinkedEvictedBackingsExist() const { | |
403 DCHECK(proxy_->IsImplThread() && proxy_->IsMainThreadBlocked()); | |
404 base::AutoLock scoped_lock(evicted_backings_lock_); | |
405 for (BackingList::const_iterator it = evicted_backings_.begin(); | |
406 it != evicted_backings_.end(); | |
407 ++it) { | |
408 if ((*it)->owner()) | |
409 return true; | |
410 } | |
411 return false; | |
412 } | |
413 | |
414 void PrioritizedResourceManager::RegisterTexture(PrioritizedResource* texture) { | |
415 DCHECK(proxy_->IsMainThread()); | |
416 DCHECK(texture); | |
417 DCHECK(!texture->resource_manager()); | |
418 DCHECK(!texture->backing()); | |
419 DCHECK(!ContainsKey(textures_, texture)); | |
420 | |
421 texture->set_manager_internal(this); | |
422 textures_.insert(texture); | |
423 } | |
424 | |
425 void PrioritizedResourceManager::UnregisterTexture( | |
426 PrioritizedResource* texture) { | |
427 DCHECK(proxy_->IsMainThread() || | |
428 (proxy_->IsImplThread() && proxy_->IsMainThreadBlocked())); | |
429 DCHECK(texture); | |
430 DCHECK(ContainsKey(textures_, texture)); | |
431 | |
432 ReturnBackingTexture(texture); | |
433 texture->set_manager_internal(NULL); | |
434 textures_.erase(texture); | |
435 texture->set_above_priority_cutoff(false); | |
436 } | |
437 | |
438 void PrioritizedResourceManager::ReturnBackingTexture( | |
439 PrioritizedResource* texture) { | |
440 DCHECK(proxy_->IsMainThread() || | |
441 (proxy_->IsImplThread() && proxy_->IsMainThreadBlocked())); | |
442 if (texture->backing()) | |
443 texture->Unlink(); | |
444 } | |
445 | |
446 PrioritizedResource::Backing* PrioritizedResourceManager::CreateBacking( | |
447 const gfx::Size& size, | |
448 ResourceFormat format, | |
449 ResourceProvider* resource_provider) { | |
450 DCHECK(proxy_->IsImplThread() && proxy_->IsMainThreadBlocked()); | |
451 DCHECK(resource_provider); | |
452 ResourceProvider::ResourceId resource_id = | |
453 resource_provider->CreateManagedResource( | |
454 size, GL_TEXTURE_2D, GL_CLAMP_TO_EDGE, | |
455 ResourceProvider::TEXTURE_HINT_IMMUTABLE, format); | |
456 PrioritizedResource::Backing* backing = new PrioritizedResource::Backing( | |
457 resource_id, resource_provider, size, format); | |
458 memory_use_bytes_ += backing->bytes(); | |
459 return backing; | |
460 } | |
461 | |
462 void PrioritizedResourceManager::EvictFirstBackingResource( | |
463 ResourceProvider* resource_provider) { | |
464 DCHECK(proxy_->IsImplThread()); | |
465 DCHECK(resource_provider); | |
466 DCHECK(!backings_.empty()); | |
467 PrioritizedResource::Backing* backing = backings_.front(); | |
468 | |
469 // Note that we create a backing and its resource at the same time, but we | |
470 // delete the backing structure and its resource in two steps. This is because | |
471 // we can delete the resource while the main thread is running, but we cannot | |
472 // unlink backings while the main thread is running. | |
473 backing->DeleteResource(resource_provider); | |
474 memory_use_bytes_ -= backing->bytes(); | |
475 backings_.pop_front(); | |
476 base::AutoLock scoped_lock(evicted_backings_lock_); | |
477 evicted_backings_.push_back(backing); | |
478 } | |
479 | |
480 void PrioritizedResourceManager::AssertInvariants() { | |
481 #if DCHECK_IS_ON() | |
482 DCHECK(proxy_->IsImplThread() && proxy_->IsMainThreadBlocked()); | |
483 | |
484 // If we hit any of these asserts, there is a bug in this class. To see | |
485 // where the bug is, call this function at the beginning and end of | |
486 // every public function. | |
487 | |
488 // Backings/textures must be doubly-linked and only to other backings/textures | |
489 // in this manager. | |
490 for (BackingList::iterator it = backings_.begin(); it != backings_.end(); | |
491 ++it) { | |
492 if ((*it)->owner()) { | |
493 DCHECK(ContainsKey(textures_, (*it)->owner())); | |
494 DCHECK((*it)->owner()->backing() == (*it)); | |
495 } | |
496 } | |
497 for (TextureSet::iterator it = textures_.begin(); it != textures_.end(); | |
498 ++it) { | |
499 PrioritizedResource* texture = (*it); | |
500 PrioritizedResource::Backing* backing = texture->backing(); | |
501 base::AutoLock scoped_lock(evicted_backings_lock_); | |
502 if (backing) { | |
503 if (backing->ResourceHasBeenDeleted()) { | |
504 DCHECK(std::find(backings_.begin(), backings_.end(), backing) == | |
505 backings_.end()); | |
506 DCHECK(std::find(evicted_backings_.begin(), | |
507 evicted_backings_.end(), | |
508 backing) != evicted_backings_.end()); | |
509 } else { | |
510 DCHECK(std::find(backings_.begin(), backings_.end(), backing) != | |
511 backings_.end()); | |
512 DCHECK(std::find(evicted_backings_.begin(), | |
513 evicted_backings_.end(), | |
514 backing) == evicted_backings_.end()); | |
515 } | |
516 DCHECK(backing->owner() == texture); | |
517 } | |
518 } | |
519 | |
520 // At all times, backings that can be evicted must always come before | |
521 // backings that can't be evicted in the backing texture list (otherwise | |
522 // ReduceMemory will not find all textures available for eviction/recycling). | |
523 bool reached_unrecyclable = false; | |
524 PrioritizedResource::Backing* previous_backing = NULL; | |
525 for (BackingList::iterator it = backings_.begin(); it != backings_.end(); | |
526 ++it) { | |
527 PrioritizedResource::Backing* backing = *it; | |
528 if (previous_backing && | |
529 (!backings_tail_not_sorted_ || | |
530 !backing->was_above_priority_cutoff_at_last_priority_update())) | |
531 DCHECK(CompareBackings(previous_backing, backing)); | |
532 if (!backing->CanBeRecycledIfNotInExternalUse()) | |
533 reached_unrecyclable = true; | |
534 if (reached_unrecyclable) | |
535 DCHECK(!backing->CanBeRecycledIfNotInExternalUse()); | |
536 else | |
537 DCHECK(backing->CanBeRecycledIfNotInExternalUse()); | |
538 previous_backing = backing; | |
539 } | |
540 #endif // DCHECK_IS_ON() | |
541 } | |
542 | |
543 const Proxy* PrioritizedResourceManager::ProxyForDebug() const { | |
544 return proxy_; | |
545 } | |
546 | |
547 } // namespace cc | |
OLD | NEW |