OLD | NEW |
| (Empty) |
1 // Copyright 2015 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/tiles/software_image_decode_controller.h" | |
6 | |
7 #include <inttypes.h> | |
8 #include <stdint.h> | |
9 | |
10 #include <algorithm> | |
11 #include <functional> | |
12 | |
13 #include "base/format_macros.h" | |
14 #include "base/macros.h" | |
15 #include "base/memory/discardable_memory.h" | |
16 #include "base/memory/memory_coordinator_client_registry.h" | |
17 #include "base/memory/ptr_util.h" | |
18 #include "base/metrics/histogram_macros.h" | |
19 #include "base/strings/stringprintf.h" | |
20 #include "base/threading/thread_task_runner_handle.h" | |
21 #include "base/trace_event/memory_dump_manager.h" | |
22 #include "cc/debug/devtools_instrumentation.h" | |
23 #include "cc/raster/tile_task.h" | |
24 #include "cc/resources/resource_format_utils.h" | |
25 #include "cc/tiles/mipmap_util.h" | |
26 #include "third_party/skia/include/core/SkCanvas.h" | |
27 #include "third_party/skia/include/core/SkImage.h" | |
28 #include "third_party/skia/include/core/SkPixmap.h" | |
29 #include "ui/gfx/skia_util.h" | |
30 | |
31 using base::trace_event::MemoryAllocatorDump; | |
32 using base::trace_event::MemoryDumpLevelOfDetail; | |
33 | |
34 namespace cc { | |
35 namespace { | |
36 | |
37 // The largest single high quality image to try and process. Images above this | |
38 // size will drop down to medium quality. | |
39 const size_t kMaxHighQualityImageSizeBytes = 64 * 1024 * 1024; | |
40 | |
41 // The number of entries to keep around in the cache. This limit can be breached | |
42 // if more items are locked. That is, locked items ignore this limit. | |
43 // Depending on the memory state of the system, we limit the amount of items | |
44 // differently. | |
45 const size_t kNormalMaxItemsInCache = 1000; | |
46 const size_t kThrottledMaxItemsInCache = 100; | |
47 const size_t kSuspendedMaxItemsInCache = 0; | |
48 | |
49 // If the size of the original sized image breaches kMemoryRatioToSubrect but we | |
50 // don't need to scale the image, consider caching only the needed subrect. | |
51 // The second part that much be true is that we cache only the needed subrect if | |
52 // the total size needed for the subrect is at most kMemoryRatioToSubrect * | |
53 // (size needed for the full original image). | |
54 const size_t kMemoryThresholdToSubrect = 64 * 1024 * 1024; | |
55 const float kMemoryRatioToSubrect = 0.5f; | |
56 | |
57 class AutoRemoveKeyFromTaskMap { | |
58 public: | |
59 AutoRemoveKeyFromTaskMap( | |
60 std::unordered_map<SoftwareImageDecodeController::ImageKey, | |
61 scoped_refptr<TileTask>, | |
62 SoftwareImageDecodeController::ImageKeyHash>* task_map, | |
63 const SoftwareImageDecodeController::ImageKey& key) | |
64 : task_map_(task_map), key_(key) {} | |
65 ~AutoRemoveKeyFromTaskMap() { task_map_->erase(key_); } | |
66 | |
67 private: | |
68 std::unordered_map<SoftwareImageDecodeController::ImageKey, | |
69 scoped_refptr<TileTask>, | |
70 SoftwareImageDecodeController::ImageKeyHash>* task_map_; | |
71 const SoftwareImageDecodeController::ImageKey& key_; | |
72 }; | |
73 | |
74 class AutoDrawWithImageFinished { | |
75 public: | |
76 AutoDrawWithImageFinished(SoftwareImageDecodeController* controller, | |
77 const DrawImage& draw_image, | |
78 const DecodedDrawImage& decoded_draw_image) | |
79 : controller_(controller), | |
80 draw_image_(draw_image), | |
81 decoded_draw_image_(decoded_draw_image) {} | |
82 ~AutoDrawWithImageFinished() { | |
83 controller_->DrawWithImageFinished(draw_image_, decoded_draw_image_); | |
84 } | |
85 | |
86 private: | |
87 SoftwareImageDecodeController* controller_; | |
88 const DrawImage& draw_image_; | |
89 const DecodedDrawImage& decoded_draw_image_; | |
90 }; | |
91 | |
92 class ImageDecodeTaskImpl : public TileTask { | |
93 public: | |
94 ImageDecodeTaskImpl(SoftwareImageDecodeController* controller, | |
95 const SoftwareImageDecodeController::ImageKey& image_key, | |
96 const DrawImage& image, | |
97 const ImageDecodeController::TracingInfo& tracing_info) | |
98 : TileTask(true), | |
99 controller_(controller), | |
100 image_key_(image_key), | |
101 image_(image), | |
102 tracing_info_(tracing_info) {} | |
103 | |
104 // Overridden from Task: | |
105 void RunOnWorkerThread() override { | |
106 TRACE_EVENT2("cc", "ImageDecodeTaskImpl::RunOnWorkerThread", "mode", | |
107 "software", "source_prepare_tiles_id", | |
108 tracing_info_.prepare_tiles_id); | |
109 devtools_instrumentation::ScopedImageDecodeTask image_decode_task( | |
110 image_.image().get(), | |
111 devtools_instrumentation::ScopedImageDecodeTask::SOFTWARE); | |
112 controller_->DecodeImage(image_key_, image_); | |
113 } | |
114 | |
115 // Overridden from TileTask: | |
116 void OnTaskCompleted() override { | |
117 controller_->RemovePendingTask(image_key_); | |
118 } | |
119 | |
120 protected: | |
121 ~ImageDecodeTaskImpl() override {} | |
122 | |
123 private: | |
124 SoftwareImageDecodeController* controller_; | |
125 SoftwareImageDecodeController::ImageKey image_key_; | |
126 DrawImage image_; | |
127 const ImageDecodeController::TracingInfo tracing_info_; | |
128 | |
129 DISALLOW_COPY_AND_ASSIGN(ImageDecodeTaskImpl); | |
130 }; | |
131 | |
132 SkSize GetScaleAdjustment(const ImageDecodeControllerKey& key) { | |
133 // If the requested filter quality did not require scale, then the adjustment | |
134 // is identity. | |
135 if (key.can_use_original_decode() || key.should_use_subrect()) { | |
136 return SkSize::Make(1.f, 1.f); | |
137 } else if (key.filter_quality() == kMedium_SkFilterQuality) { | |
138 return MipMapUtil::GetScaleAdjustmentForSize(key.src_rect().size(), | |
139 key.target_size()); | |
140 } else { | |
141 float x_scale = | |
142 key.target_size().width() / static_cast<float>(key.src_rect().width()); | |
143 float y_scale = key.target_size().height() / | |
144 static_cast<float>(key.src_rect().height()); | |
145 return SkSize::Make(x_scale, y_scale); | |
146 } | |
147 } | |
148 | |
149 SkFilterQuality GetDecodedFilterQuality(const ImageDecodeControllerKey& key) { | |
150 return std::min(key.filter_quality(), kLow_SkFilterQuality); | |
151 } | |
152 | |
153 SkImageInfo CreateImageInfo(size_t width, | |
154 size_t height, | |
155 ResourceFormat format) { | |
156 return SkImageInfo::Make(width, height, | |
157 ResourceFormatToClosestSkColorType(format), | |
158 kPremul_SkAlphaType); | |
159 } | |
160 | |
161 void RecordLockExistingCachedImageHistogram(TilePriority::PriorityBin bin, | |
162 bool success) { | |
163 switch (bin) { | |
164 case TilePriority::NOW: | |
165 UMA_HISTOGRAM_BOOLEAN("Renderer4.LockExistingCachedImage.Software.NOW", | |
166 success); | |
167 case TilePriority::SOON: | |
168 UMA_HISTOGRAM_BOOLEAN("Renderer4.LockExistingCachedImage.Software.SOON", | |
169 success); | |
170 case TilePriority::EVENTUALLY: | |
171 UMA_HISTOGRAM_BOOLEAN( | |
172 "Renderer4.LockExistingCachedImage.Software.EVENTUALLY", success); | |
173 } | |
174 } | |
175 | |
176 } // namespace | |
177 | |
178 SoftwareImageDecodeController::SoftwareImageDecodeController( | |
179 ResourceFormat format, | |
180 size_t locked_memory_limit_bytes) | |
181 : decoded_images_(ImageMRUCache::NO_AUTO_EVICT), | |
182 at_raster_decoded_images_(ImageMRUCache::NO_AUTO_EVICT), | |
183 locked_images_budget_(locked_memory_limit_bytes), | |
184 format_(format), | |
185 max_items_in_cache_(kNormalMaxItemsInCache) { | |
186 // In certain cases, ThreadTaskRunnerHandle isn't set (Android Webview). | |
187 // Don't register a dump provider in these cases. | |
188 if (base::ThreadTaskRunnerHandle::IsSet()) { | |
189 base::trace_event::MemoryDumpManager::GetInstance()->RegisterDumpProvider( | |
190 this, "cc::SoftwareImageDecodeController", | |
191 base::ThreadTaskRunnerHandle::Get()); | |
192 } | |
193 // Register this component with base::MemoryCoordinatorClientRegistry. | |
194 base::MemoryCoordinatorClientRegistry::GetInstance()->Register(this); | |
195 } | |
196 | |
197 SoftwareImageDecodeController::~SoftwareImageDecodeController() { | |
198 DCHECK_EQ(0u, decoded_images_ref_counts_.size()); | |
199 DCHECK_EQ(0u, at_raster_decoded_images_ref_counts_.size()); | |
200 | |
201 // It is safe to unregister, even if we didn't register in the constructor. | |
202 base::trace_event::MemoryDumpManager::GetInstance()->UnregisterDumpProvider( | |
203 this); | |
204 // Unregister this component with memory_coordinator::ClientRegistry. | |
205 base::MemoryCoordinatorClientRegistry::GetInstance()->Unregister(this); | |
206 } | |
207 | |
208 bool SoftwareImageDecodeController::GetTaskForImageAndRef( | |
209 const DrawImage& image, | |
210 const TracingInfo& tracing_info, | |
211 scoped_refptr<TileTask>* task) { | |
212 // If the image already exists or if we're going to create a task for it, then | |
213 // we'll likely need to ref this image (the exception is if we're prerolling | |
214 // the image only). That means the image is or will be in the cache. When the | |
215 // ref goes to 0, it will be unpinned but will remain in the cache. If the | |
216 // image does not fit into the budget, then we don't ref this image, since it | |
217 // will be decoded at raster time which is when it will be temporarily put in | |
218 // the cache. | |
219 ImageKey key = ImageKey::FromDrawImage(image); | |
220 TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("cc.debug"), | |
221 "SoftwareImageDecodeController::GetTaskForImageAndRef", "key", | |
222 key.ToString()); | |
223 | |
224 // If the target size is empty, we can skip this image during draw (and thus | |
225 // we don't need to decode it or ref it). | |
226 if (key.target_size().IsEmpty()) { | |
227 *task = nullptr; | |
228 return false; | |
229 } | |
230 | |
231 base::AutoLock lock(lock_); | |
232 | |
233 // If we already have the image in cache, then we can return it. | |
234 auto decoded_it = decoded_images_.Get(key); | |
235 bool new_image_fits_in_memory = | |
236 locked_images_budget_.AvailableMemoryBytes() >= key.locked_bytes(); | |
237 if (decoded_it != decoded_images_.end()) { | |
238 bool image_was_locked = decoded_it->second->is_locked(); | |
239 if (image_was_locked || | |
240 (new_image_fits_in_memory && decoded_it->second->Lock())) { | |
241 RefImage(key); | |
242 *task = nullptr; | |
243 SanityCheckState(__LINE__, true); | |
244 | |
245 // If the image wasn't locked, then we just succeeded in locking it. | |
246 if (!image_was_locked) { | |
247 RecordLockExistingCachedImageHistogram(tracing_info.requesting_tile_bin, | |
248 true); | |
249 } | |
250 return true; | |
251 } | |
252 | |
253 // If the image fits in memory, then we at least tried to lock it and | |
254 // failed. This means that it's not valid anymore. | |
255 if (new_image_fits_in_memory) { | |
256 RecordLockExistingCachedImageHistogram(tracing_info.requesting_tile_bin, | |
257 false); | |
258 decoded_images_.Erase(decoded_it); | |
259 } | |
260 } | |
261 | |
262 // If the task exists, return it. | |
263 scoped_refptr<TileTask>& existing_task = pending_image_tasks_[key]; | |
264 if (existing_task) { | |
265 RefImage(key); | |
266 *task = existing_task; | |
267 SanityCheckState(__LINE__, true); | |
268 return true; | |
269 } | |
270 | |
271 // At this point, we have to create a new image/task, so we need to abort if | |
272 // it doesn't fit into memory and there are currently no raster tasks that | |
273 // would have already accounted for memory. The latter part is possible if | |
274 // there's a running raster task that could not be canceled, and still has a | |
275 // ref to the image that is now being reffed for the new schedule. | |
276 if (!new_image_fits_in_memory && (decoded_images_ref_counts_.find(key) == | |
277 decoded_images_ref_counts_.end())) { | |
278 *task = nullptr; | |
279 SanityCheckState(__LINE__, true); | |
280 return false; | |
281 } | |
282 | |
283 // Actually create the task. RefImage will account for memory on the first | |
284 // ref. | |
285 RefImage(key); | |
286 existing_task = make_scoped_refptr( | |
287 new ImageDecodeTaskImpl(this, key, image, tracing_info)); | |
288 *task = existing_task; | |
289 SanityCheckState(__LINE__, true); | |
290 return true; | |
291 } | |
292 | |
293 void SoftwareImageDecodeController::RefImage(const ImageKey& key) { | |
294 TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("cc.debug"), | |
295 "SoftwareImageDecodeController::RefImage", "key", | |
296 key.ToString()); | |
297 lock_.AssertAcquired(); | |
298 int ref = ++decoded_images_ref_counts_[key]; | |
299 if (ref == 1) { | |
300 DCHECK_GE(locked_images_budget_.AvailableMemoryBytes(), key.locked_bytes()); | |
301 locked_images_budget_.AddUsage(key.locked_bytes()); | |
302 } | |
303 } | |
304 | |
305 void SoftwareImageDecodeController::UnrefImage(const DrawImage& image) { | |
306 // When we unref the image, there are several situations we need to consider: | |
307 // 1. The ref did not reach 0, which means we have to keep the image locked. | |
308 // 2. The ref reached 0, we should unlock it. | |
309 // 2a. The image isn't in the locked cache because we didn't get to decode | |
310 // it yet (or failed to decode it). | |
311 // 2b. Unlock the image but keep it in list. | |
312 const ImageKey& key = ImageKey::FromDrawImage(image); | |
313 TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("cc.debug"), | |
314 "SoftwareImageDecodeController::UnrefImage", "key", | |
315 key.ToString()); | |
316 | |
317 base::AutoLock lock(lock_); | |
318 auto ref_count_it = decoded_images_ref_counts_.find(key); | |
319 DCHECK(ref_count_it != decoded_images_ref_counts_.end()); | |
320 | |
321 --ref_count_it->second; | |
322 if (ref_count_it->second == 0) { | |
323 decoded_images_ref_counts_.erase(ref_count_it); | |
324 locked_images_budget_.SubtractUsage(key.locked_bytes()); | |
325 | |
326 auto decoded_image_it = decoded_images_.Peek(key); | |
327 // If we've never decoded the image before ref reached 0, then we wouldn't | |
328 // have it in our cache. This would happen if we canceled tasks. | |
329 if (decoded_image_it == decoded_images_.end()) { | |
330 SanityCheckState(__LINE__, true); | |
331 return; | |
332 } | |
333 DCHECK(decoded_image_it->second->is_locked()); | |
334 decoded_image_it->second->Unlock(); | |
335 } | |
336 SanityCheckState(__LINE__, true); | |
337 } | |
338 | |
339 void SoftwareImageDecodeController::DecodeImage(const ImageKey& key, | |
340 const DrawImage& image) { | |
341 TRACE_EVENT1("cc", "SoftwareImageDecodeController::DecodeImage", "key", | |
342 key.ToString()); | |
343 base::AutoLock lock(lock_); | |
344 AutoRemoveKeyFromTaskMap remove_key_from_task_map(&pending_image_tasks_, key); | |
345 | |
346 // We could have finished all of the raster tasks (cancelled) while the task | |
347 // was just starting to run. Since this task already started running, it | |
348 // wasn't cancelled. So, if the ref count for the image is 0 then we can just | |
349 // abort. | |
350 if (decoded_images_ref_counts_.find(key) == | |
351 decoded_images_ref_counts_.end()) { | |
352 return; | |
353 } | |
354 | |
355 auto image_it = decoded_images_.Peek(key); | |
356 if (image_it != decoded_images_.end()) { | |
357 if (image_it->second->is_locked() || image_it->second->Lock()) | |
358 return; | |
359 decoded_images_.Erase(image_it); | |
360 } | |
361 | |
362 std::unique_ptr<DecodedImage> decoded_image; | |
363 { | |
364 base::AutoUnlock unlock(lock_); | |
365 decoded_image = DecodeImageInternal(key, image); | |
366 } | |
367 | |
368 // Abort if we failed to decode the image. | |
369 if (!decoded_image) | |
370 return; | |
371 | |
372 // At this point, it could have been the case that this image was decoded in | |
373 // place by an already running raster task from a previous schedule. If that's | |
374 // the case, then it would have already been placed into the cache (possibly | |
375 // locked). Remove it if that was the case. | |
376 image_it = decoded_images_.Peek(key); | |
377 if (image_it != decoded_images_.end()) { | |
378 if (image_it->second->is_locked() || image_it->second->Lock()) { | |
379 // Make sure to unlock the decode we did in this function. | |
380 decoded_image->Unlock(); | |
381 return; | |
382 } | |
383 decoded_images_.Erase(image_it); | |
384 } | |
385 | |
386 // We could have finished all of the raster tasks (cancelled) while this image | |
387 // decode task was running, which means that we now have a locked image but no | |
388 // ref counts. Unlock it immediately in this case. | |
389 if (decoded_images_ref_counts_.find(key) == | |
390 decoded_images_ref_counts_.end()) { | |
391 decoded_image->Unlock(); | |
392 } | |
393 | |
394 decoded_images_.Put(key, std::move(decoded_image)); | |
395 SanityCheckState(__LINE__, true); | |
396 } | |
397 | |
398 std::unique_ptr<SoftwareImageDecodeController::DecodedImage> | |
399 SoftwareImageDecodeController::DecodeImageInternal( | |
400 const ImageKey& key, | |
401 const DrawImage& draw_image) { | |
402 TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("cc.debug"), | |
403 "SoftwareImageDecodeController::DecodeImageInternal", "key", | |
404 key.ToString()); | |
405 sk_sp<const SkImage> image = draw_image.image(); | |
406 if (!image) | |
407 return nullptr; | |
408 | |
409 switch (key.filter_quality()) { | |
410 case kNone_SkFilterQuality: | |
411 case kLow_SkFilterQuality: | |
412 if (key.should_use_subrect()) | |
413 return GetSubrectImageDecode(key, std::move(image)); | |
414 return GetOriginalImageDecode(std::move(image)); | |
415 case kMedium_SkFilterQuality: | |
416 case kHigh_SkFilterQuality: | |
417 return GetScaledImageDecode(key, std::move(image)); | |
418 default: | |
419 NOTREACHED(); | |
420 return nullptr; | |
421 } | |
422 } | |
423 | |
424 DecodedDrawImage SoftwareImageDecodeController::GetDecodedImageForDraw( | |
425 const DrawImage& draw_image) { | |
426 ImageKey key = ImageKey::FromDrawImage(draw_image); | |
427 TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("cc.debug"), | |
428 "SoftwareImageDecodeController::GetDecodedImageForDraw", "key", | |
429 key.ToString()); | |
430 // If the target size is empty, we can skip this image draw. | |
431 if (key.target_size().IsEmpty()) | |
432 return DecodedDrawImage(nullptr, kNone_SkFilterQuality); | |
433 | |
434 return GetDecodedImageForDrawInternal(key, draw_image); | |
435 } | |
436 | |
437 DecodedDrawImage SoftwareImageDecodeController::GetDecodedImageForDrawInternal( | |
438 const ImageKey& key, | |
439 const DrawImage& draw_image) { | |
440 TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("cc.debug"), | |
441 "SoftwareImageDecodeController::GetDecodedImageForDrawInternal", | |
442 "key", key.ToString()); | |
443 base::AutoLock lock(lock_); | |
444 auto decoded_images_it = decoded_images_.Get(key); | |
445 // If we found the image and it's locked, then return it. If it's not locked, | |
446 // erase it from the cache since it might be put into the at-raster cache. | |
447 std::unique_ptr<DecodedImage> scoped_decoded_image; | |
448 DecodedImage* decoded_image = nullptr; | |
449 if (decoded_images_it != decoded_images_.end()) { | |
450 decoded_image = decoded_images_it->second.get(); | |
451 if (decoded_image->is_locked()) { | |
452 RefImage(key); | |
453 decoded_image->mark_used(); | |
454 SanityCheckState(__LINE__, true); | |
455 return DecodedDrawImage( | |
456 decoded_image->image(), decoded_image->src_rect_offset(), | |
457 GetScaleAdjustment(key), GetDecodedFilterQuality(key)); | |
458 } else { | |
459 scoped_decoded_image = std::move(decoded_images_it->second); | |
460 decoded_images_.Erase(decoded_images_it); | |
461 } | |
462 } | |
463 | |
464 // See if another thread already decoded this image at raster time. If so, we | |
465 // can just use that result directly. | |
466 auto at_raster_images_it = at_raster_decoded_images_.Get(key); | |
467 if (at_raster_images_it != at_raster_decoded_images_.end()) { | |
468 DCHECK(at_raster_images_it->second->is_locked()); | |
469 RefAtRasterImage(key); | |
470 SanityCheckState(__LINE__, true); | |
471 DecodedImage* at_raster_decoded_image = at_raster_images_it->second.get(); | |
472 at_raster_decoded_image->mark_used(); | |
473 auto decoded_draw_image = | |
474 DecodedDrawImage(at_raster_decoded_image->image(), | |
475 at_raster_decoded_image->src_rect_offset(), | |
476 GetScaleAdjustment(key), GetDecodedFilterQuality(key)); | |
477 decoded_draw_image.set_at_raster_decode(true); | |
478 return decoded_draw_image; | |
479 } | |
480 | |
481 // Now we know that we don't have a locked image, and we seem to be the first | |
482 // thread encountering this image (that might not be true, since other threads | |
483 // might be decoding it already). This means that we need to decode the image | |
484 // assuming we can't lock the one we found in the cache. | |
485 bool check_at_raster_cache = false; | |
486 if (!decoded_image || !decoded_image->Lock()) { | |
487 // Note that we have to release the lock, since this lock is also accessed | |
488 // on the compositor thread. This means holding on to the lock might stall | |
489 // the compositor thread for the duration of the decode! | |
490 base::AutoUnlock unlock(lock_); | |
491 scoped_decoded_image = DecodeImageInternal(key, draw_image); | |
492 decoded_image = scoped_decoded_image.get(); | |
493 | |
494 // Skip the image if we couldn't decode it. | |
495 if (!decoded_image) | |
496 return DecodedDrawImage(nullptr, kNone_SkFilterQuality); | |
497 check_at_raster_cache = true; | |
498 } | |
499 | |
500 DCHECK(decoded_image == scoped_decoded_image.get()); | |
501 | |
502 // While we unlocked the lock, it could be the case that another thread | |
503 // already decoded this already and put it in the at-raster cache. Look it up | |
504 // first. | |
505 if (check_at_raster_cache) { | |
506 at_raster_images_it = at_raster_decoded_images_.Get(key); | |
507 if (at_raster_images_it != at_raster_decoded_images_.end()) { | |
508 // We have to drop our decode, since the one in the cache is being used by | |
509 // another thread. | |
510 decoded_image->Unlock(); | |
511 decoded_image = at_raster_images_it->second.get(); | |
512 scoped_decoded_image = nullptr; | |
513 } | |
514 } | |
515 | |
516 // If we really are the first ones, or if the other thread already unlocked | |
517 // the image, then put our work into at-raster time cache. | |
518 if (scoped_decoded_image) | |
519 at_raster_decoded_images_.Put(key, std::move(scoped_decoded_image)); | |
520 | |
521 DCHECK(decoded_image); | |
522 DCHECK(decoded_image->is_locked()); | |
523 RefAtRasterImage(key); | |
524 SanityCheckState(__LINE__, true); | |
525 decoded_image->mark_used(); | |
526 auto decoded_draw_image = | |
527 DecodedDrawImage(decoded_image->image(), decoded_image->src_rect_offset(), | |
528 GetScaleAdjustment(key), GetDecodedFilterQuality(key)); | |
529 decoded_draw_image.set_at_raster_decode(true); | |
530 return decoded_draw_image; | |
531 } | |
532 | |
533 std::unique_ptr<SoftwareImageDecodeController::DecodedImage> | |
534 SoftwareImageDecodeController::GetOriginalImageDecode( | |
535 sk_sp<const SkImage> image) { | |
536 SkImageInfo decoded_info = | |
537 CreateImageInfo(image->width(), image->height(), format_); | |
538 std::unique_ptr<base::DiscardableMemory> decoded_pixels; | |
539 { | |
540 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("cc.debug"), | |
541 "SoftwareImageDecodeController::GetOriginalImageDecode - " | |
542 "allocate decoded pixels"); | |
543 decoded_pixels = | |
544 base::DiscardableMemoryAllocator::GetInstance() | |
545 ->AllocateLockedDiscardableMemory(decoded_info.minRowBytes() * | |
546 decoded_info.height()); | |
547 } | |
548 { | |
549 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("cc.debug"), | |
550 "SoftwareImageDecodeController::GetOriginalImageDecode - " | |
551 "read pixels"); | |
552 bool result = image->readPixels(decoded_info, decoded_pixels->data(), | |
553 decoded_info.minRowBytes(), 0, 0, | |
554 SkImage::kDisallow_CachingHint); | |
555 | |
556 if (!result) { | |
557 decoded_pixels->Unlock(); | |
558 return nullptr; | |
559 } | |
560 } | |
561 return base::MakeUnique<DecodedImage>(decoded_info, std::move(decoded_pixels), | |
562 SkSize::Make(0, 0), | |
563 next_tracing_id_.GetNext()); | |
564 } | |
565 | |
566 std::unique_ptr<SoftwareImageDecodeController::DecodedImage> | |
567 SoftwareImageDecodeController::GetSubrectImageDecode( | |
568 const ImageKey& key, | |
569 sk_sp<const SkImage> image) { | |
570 // Construct a key to use in GetDecodedImageForDrawInternal(). | |
571 // This allows us to reuse an image in any cache if available. | |
572 gfx::Rect full_image_rect(image->width(), image->height()); | |
573 DrawImage original_size_draw_image(std::move(image), | |
574 gfx::RectToSkIRect(full_image_rect), | |
575 kNone_SkFilterQuality, SkMatrix::I()); | |
576 ImageKey original_size_key = | |
577 ImageKey::FromDrawImage(original_size_draw_image); | |
578 // Sanity checks. | |
579 DCHECK(original_size_key.can_use_original_decode()) | |
580 << original_size_key.ToString(); | |
581 DCHECK(full_image_rect.size() == original_size_key.target_size()); | |
582 | |
583 auto decoded_draw_image = GetDecodedImageForDrawInternal( | |
584 original_size_key, original_size_draw_image); | |
585 AutoDrawWithImageFinished auto_finish_draw(this, original_size_draw_image, | |
586 decoded_draw_image); | |
587 if (!decoded_draw_image.image()) | |
588 return nullptr; | |
589 | |
590 SkImageInfo subrect_info = CreateImageInfo( | |
591 key.target_size().width(), key.target_size().height(), format_); | |
592 std::unique_ptr<base::DiscardableMemory> subrect_pixels; | |
593 { | |
594 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("cc.debug"), | |
595 "SoftwareImageDecodeController::GetSubrectImageDecode - " | |
596 "allocate subrect pixels"); | |
597 // TODO(vmpstr): This is using checked math to diagnose a problem reported | |
598 // in crbug.com/662217. If this is causing crashes, then it should be fixed | |
599 // elsewhere by skipping images that are too large. | |
600 base::CheckedNumeric<size_t> byte_size = subrect_info.minRowBytes(); | |
601 byte_size *= subrect_info.height(); | |
602 subrect_pixels = | |
603 base::DiscardableMemoryAllocator::GetInstance() | |
604 ->AllocateLockedDiscardableMemory(byte_size.ValueOrDie()); | |
605 } | |
606 { | |
607 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("cc.debug"), | |
608 "SoftwareImageDecodeController::GetOriginalImageDecode - " | |
609 "read pixels"); | |
610 bool result = decoded_draw_image.image()->readPixels( | |
611 subrect_info, subrect_pixels->data(), subrect_info.minRowBytes(), | |
612 key.src_rect().x(), key.src_rect().y(), SkImage::kDisallow_CachingHint); | |
613 // We have a decoded image, and we're reading into already allocated memory. | |
614 // This should never fail. | |
615 DCHECK(result); | |
616 } | |
617 return base::WrapUnique( | |
618 new DecodedImage(subrect_info, std::move(subrect_pixels), | |
619 SkSize::Make(-key.src_rect().x(), -key.src_rect().y()), | |
620 next_tracing_id_.GetNext())); | |
621 } | |
622 | |
623 std::unique_ptr<SoftwareImageDecodeController::DecodedImage> | |
624 SoftwareImageDecodeController::GetScaledImageDecode( | |
625 const ImageKey& key, | |
626 sk_sp<const SkImage> image) { | |
627 // Construct a key to use in GetDecodedImageForDrawInternal(). | |
628 // This allows us to reuse an image in any cache if available. | |
629 gfx::Rect full_image_rect(image->width(), image->height()); | |
630 DrawImage original_size_draw_image(std::move(image), | |
631 gfx::RectToSkIRect(full_image_rect), | |
632 kNone_SkFilterQuality, SkMatrix::I()); | |
633 ImageKey original_size_key = | |
634 ImageKey::FromDrawImage(original_size_draw_image); | |
635 // Sanity checks. | |
636 DCHECK(original_size_key.can_use_original_decode()) | |
637 << original_size_key.ToString(); | |
638 DCHECK(full_image_rect.size() == original_size_key.target_size()); | |
639 | |
640 auto decoded_draw_image = GetDecodedImageForDrawInternal( | |
641 original_size_key, original_size_draw_image); | |
642 AutoDrawWithImageFinished auto_finish_draw(this, original_size_draw_image, | |
643 decoded_draw_image); | |
644 if (!decoded_draw_image.image()) | |
645 return nullptr; | |
646 | |
647 SkPixmap decoded_pixmap; | |
648 bool result = decoded_draw_image.image()->peekPixels(&decoded_pixmap); | |
649 DCHECK(result) << key.ToString(); | |
650 if (key.src_rect() != full_image_rect) { | |
651 result = decoded_pixmap.extractSubset(&decoded_pixmap, | |
652 gfx::RectToSkIRect(key.src_rect())); | |
653 DCHECK(result) << key.ToString(); | |
654 } | |
655 | |
656 DCHECK(!key.target_size().IsEmpty()); | |
657 SkImageInfo scaled_info = CreateImageInfo( | |
658 key.target_size().width(), key.target_size().height(), format_); | |
659 std::unique_ptr<base::DiscardableMemory> scaled_pixels; | |
660 { | |
661 TRACE_EVENT0( | |
662 TRACE_DISABLED_BY_DEFAULT("cc.debug"), | |
663 "SoftwareImageDecodeController::ScaleImage - allocate scaled pixels"); | |
664 scaled_pixels = base::DiscardableMemoryAllocator::GetInstance() | |
665 ->AllocateLockedDiscardableMemory( | |
666 scaled_info.minRowBytes() * scaled_info.height()); | |
667 } | |
668 SkPixmap scaled_pixmap(scaled_info, scaled_pixels->data(), | |
669 scaled_info.minRowBytes()); | |
670 DCHECK(key.filter_quality() == kHigh_SkFilterQuality || | |
671 key.filter_quality() == kMedium_SkFilterQuality); | |
672 { | |
673 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("cc.debug"), | |
674 "SoftwareImageDecodeController::ScaleImage - scale pixels"); | |
675 bool result = | |
676 decoded_pixmap.scalePixels(scaled_pixmap, key.filter_quality()); | |
677 DCHECK(result) << key.ToString(); | |
678 } | |
679 | |
680 return base::MakeUnique<DecodedImage>( | |
681 scaled_info, std::move(scaled_pixels), | |
682 SkSize::Make(-key.src_rect().x(), -key.src_rect().y()), | |
683 next_tracing_id_.GetNext()); | |
684 } | |
685 | |
686 void SoftwareImageDecodeController::DrawWithImageFinished( | |
687 const DrawImage& image, | |
688 const DecodedDrawImage& decoded_image) { | |
689 TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("cc.debug"), | |
690 "SoftwareImageDecodeController::DrawWithImageFinished", "key", | |
691 ImageKey::FromDrawImage(image).ToString()); | |
692 ImageKey key = ImageKey::FromDrawImage(image); | |
693 if (!decoded_image.image()) | |
694 return; | |
695 | |
696 if (decoded_image.is_at_raster_decode()) | |
697 UnrefAtRasterImage(key); | |
698 else | |
699 UnrefImage(image); | |
700 SanityCheckState(__LINE__, false); | |
701 } | |
702 | |
703 void SoftwareImageDecodeController::RefAtRasterImage(const ImageKey& key) { | |
704 TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("cc.debug"), | |
705 "SoftwareImageDecodeController::RefAtRasterImage", "key", | |
706 key.ToString()); | |
707 DCHECK(at_raster_decoded_images_.Peek(key) != | |
708 at_raster_decoded_images_.end()); | |
709 ++at_raster_decoded_images_ref_counts_[key]; | |
710 } | |
711 | |
712 void SoftwareImageDecodeController::UnrefAtRasterImage(const ImageKey& key) { | |
713 TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("cc.debug"), | |
714 "SoftwareImageDecodeController::UnrefAtRasterImage", "key", | |
715 key.ToString()); | |
716 base::AutoLock lock(lock_); | |
717 | |
718 auto ref_it = at_raster_decoded_images_ref_counts_.find(key); | |
719 DCHECK(ref_it != at_raster_decoded_images_ref_counts_.end()); | |
720 --ref_it->second; | |
721 if (ref_it->second == 0) { | |
722 at_raster_decoded_images_ref_counts_.erase(ref_it); | |
723 auto at_raster_image_it = at_raster_decoded_images_.Peek(key); | |
724 DCHECK(at_raster_image_it != at_raster_decoded_images_.end()); | |
725 | |
726 // The ref for our image reached 0 and it's still locked. We need to figure | |
727 // out what the best thing to do with the image. There are several | |
728 // situations: | |
729 // 1. The image is not in the main cache and... | |
730 // 1a. ... its ref count is 0: unlock our image and put it in the main | |
731 // cache. | |
732 // 1b. ... ref count is not 0: keep the image locked and put it in the | |
733 // main cache. | |
734 // 2. The image is in the main cache... | |
735 // 2a. ... and is locked: unlock our image and discard it | |
736 // 2b. ... and is unlocked and... | |
737 // 2b1. ... its ref count is 0: unlock our image and replace the | |
738 // existing one with ours. | |
739 // 2b2. ... its ref count is not 0: this shouldn't be possible. | |
740 auto image_it = decoded_images_.Peek(key); | |
741 if (image_it == decoded_images_.end()) { | |
742 if (decoded_images_ref_counts_.find(key) == | |
743 decoded_images_ref_counts_.end()) { | |
744 at_raster_image_it->second->Unlock(); | |
745 } | |
746 decoded_images_.Put(key, std::move(at_raster_image_it->second)); | |
747 } else if (image_it->second->is_locked()) { | |
748 at_raster_image_it->second->Unlock(); | |
749 } else { | |
750 DCHECK(decoded_images_ref_counts_.find(key) == | |
751 decoded_images_ref_counts_.end()); | |
752 at_raster_image_it->second->Unlock(); | |
753 decoded_images_.Erase(image_it); | |
754 decoded_images_.Put(key, std::move(at_raster_image_it->second)); | |
755 } | |
756 at_raster_decoded_images_.Erase(at_raster_image_it); | |
757 } | |
758 } | |
759 | |
760 void SoftwareImageDecodeController::ReduceCacheUsage() { | |
761 TRACE_EVENT0("cc", "SoftwareImageDecodeController::ReduceCacheUsage"); | |
762 base::AutoLock lock(lock_); | |
763 size_t num_to_remove = (decoded_images_.size() > max_items_in_cache_) | |
764 ? (decoded_images_.size() - max_items_in_cache_) | |
765 : 0; | |
766 for (auto it = decoded_images_.rbegin(); | |
767 num_to_remove != 0 && it != decoded_images_.rend();) { | |
768 if (it->second->is_locked()) { | |
769 ++it; | |
770 continue; | |
771 } | |
772 | |
773 it = decoded_images_.Erase(it); | |
774 --num_to_remove; | |
775 } | |
776 } | |
777 | |
778 void SoftwareImageDecodeController::RemovePendingTask(const ImageKey& key) { | |
779 base::AutoLock lock(lock_); | |
780 pending_image_tasks_.erase(key); | |
781 } | |
782 | |
783 bool SoftwareImageDecodeController::OnMemoryDump( | |
784 const base::trace_event::MemoryDumpArgs& args, | |
785 base::trace_event::ProcessMemoryDump* pmd) { | |
786 base::AutoLock lock(lock_); | |
787 | |
788 if (args.level_of_detail == MemoryDumpLevelOfDetail::BACKGROUND) { | |
789 std::string dump_name = | |
790 base::StringPrintf("cc/image_memory/controller_0x%" PRIXPTR, | |
791 reinterpret_cast<uintptr_t>(this)); | |
792 MemoryAllocatorDump* dump = pmd->CreateAllocatorDump(dump_name); | |
793 dump->AddScalar("locked_size", MemoryAllocatorDump::kUnitsBytes, | |
794 locked_images_budget_.GetCurrentUsageSafe()); | |
795 } else { | |
796 // Dump each of our caches. | |
797 DumpImageMemoryForCache(decoded_images_, "cached", pmd); | |
798 DumpImageMemoryForCache(at_raster_decoded_images_, "at_raster", pmd); | |
799 } | |
800 | |
801 // Memory dump can't fail, always return true. | |
802 return true; | |
803 } | |
804 | |
805 void SoftwareImageDecodeController::DumpImageMemoryForCache( | |
806 const ImageMRUCache& cache, | |
807 const char* cache_name, | |
808 base::trace_event::ProcessMemoryDump* pmd) const { | |
809 lock_.AssertAcquired(); | |
810 | |
811 for (const auto& image_pair : cache) { | |
812 std::string dump_name = base::StringPrintf( | |
813 "cc/image_memory/controller_0x%" PRIXPTR "/%s/image_%" PRIu64 "_id_%d", | |
814 reinterpret_cast<uintptr_t>(this), cache_name, | |
815 image_pair.second->tracing_id(), image_pair.first.image_id()); | |
816 // CreateMemoryAllocatorDump will automatically add tracking values for the | |
817 // total size. If locked, we also add a "locked_size" below. | |
818 MemoryAllocatorDump* dump = | |
819 image_pair.second->memory()->CreateMemoryAllocatorDump( | |
820 dump_name.c_str(), pmd); | |
821 DCHECK(dump); | |
822 if (image_pair.second->is_locked()) { | |
823 dump->AddScalar("locked_size", MemoryAllocatorDump::kUnitsBytes, | |
824 image_pair.first.locked_bytes()); | |
825 } | |
826 } | |
827 } | |
828 | |
829 void SoftwareImageDecodeController::SanityCheckState(int line, | |
830 bool lock_acquired) { | |
831 #if DCHECK_IS_ON() | |
832 if (!lock_acquired) { | |
833 base::AutoLock lock(lock_); | |
834 SanityCheckState(line, true); | |
835 return; | |
836 } | |
837 | |
838 MemoryBudget budget(locked_images_budget_.total_limit_bytes()); | |
839 for (const auto& image_pair : decoded_images_) { | |
840 const auto& key = image_pair.first; | |
841 const auto& image = image_pair.second; | |
842 | |
843 auto ref_it = decoded_images_ref_counts_.find(key); | |
844 if (image->is_locked()) { | |
845 budget.AddUsage(key.locked_bytes()); | |
846 DCHECK(ref_it != decoded_images_ref_counts_.end()) << line; | |
847 } else { | |
848 DCHECK(ref_it == decoded_images_ref_counts_.end() || | |
849 pending_image_tasks_.find(key) != pending_image_tasks_.end()) | |
850 << line; | |
851 } | |
852 } | |
853 DCHECK_GE(budget.AvailableMemoryBytes(), | |
854 locked_images_budget_.AvailableMemoryBytes()) | |
855 << line; | |
856 #endif // DCHECK_IS_ON() | |
857 } | |
858 | |
859 // SoftwareImageDecodeControllerKey | |
860 ImageDecodeControllerKey ImageDecodeControllerKey::FromDrawImage( | |
861 const DrawImage& image) { | |
862 const SkSize& scale = image.scale(); | |
863 // If the src_rect falls outside of the image, we need to clip it since | |
864 // otherwise we might end up with uninitialized memory in the decode process. | |
865 // Note that the scale is still unchanged and the target size is now a | |
866 // function of the new src_rect. | |
867 gfx::Rect src_rect = gfx::IntersectRects( | |
868 gfx::SkIRectToRect(image.src_rect()), | |
869 gfx::Rect(image.image()->width(), image.image()->height())); | |
870 | |
871 gfx::Size target_size( | |
872 SkScalarRoundToInt(std::abs(src_rect.width() * scale.width())), | |
873 SkScalarRoundToInt(std::abs(src_rect.height() * scale.height()))); | |
874 | |
875 // Start with the quality that was requested. | |
876 SkFilterQuality quality = image.filter_quality(); | |
877 | |
878 // If we're not going to do a scale, we can use low filter quality. Note that | |
879 // checking if the sizes are the same is better than checking if scale is 1.f, | |
880 // because even non-1 scale can result in the same (rounded) width/height. | |
881 // If either dimension is a downscale, then use mipmaps (medium filter | |
882 // quality). | |
883 if (target_size.width() == src_rect.width() && | |
884 target_size.height() == src_rect.height()) { | |
885 quality = std::min(quality, kLow_SkFilterQuality); | |
886 } else if (target_size.width() < src_rect.width() || | |
887 target_size.height() < src_rect.height()) { | |
888 quality = std::min(quality, kMedium_SkFilterQuality); | |
889 } | |
890 | |
891 // Drop from high to medium if the the matrix we applied wasn't decomposable, | |
892 // or if the scaled image will be too large. | |
893 if (quality == kHigh_SkFilterQuality) { | |
894 if (!image.matrix_is_decomposable()) { | |
895 quality = kMedium_SkFilterQuality; | |
896 } else { | |
897 base::CheckedNumeric<size_t> size = 4u; | |
898 size *= target_size.width(); | |
899 size *= target_size.height(); | |
900 if (size.ValueOrDefault(std::numeric_limits<size_t>::max()) > | |
901 kMaxHighQualityImageSizeBytes) { | |
902 quality = kMedium_SkFilterQuality; | |
903 } | |
904 } | |
905 } | |
906 | |
907 // Drop from medium to low if the matrix we applied wasn't decomposable or if | |
908 // we're enlarging the image in both dimensions. | |
909 if (quality == kMedium_SkFilterQuality) { | |
910 if (!image.matrix_is_decomposable() || | |
911 (scale.width() >= 1.f && scale.height() >= 1.f)) { | |
912 quality = kLow_SkFilterQuality; | |
913 } | |
914 } | |
915 | |
916 bool can_use_original_decode = | |
917 quality == kLow_SkFilterQuality || quality == kNone_SkFilterQuality; | |
918 bool should_use_subrect = false; | |
919 if (can_use_original_decode) { | |
920 base::CheckedNumeric<size_t> checked_original_size = 4u; | |
921 checked_original_size *= image.image()->width(); | |
922 checked_original_size *= image.image()->height(); | |
923 size_t original_size = checked_original_size.ValueOrDefault( | |
924 std::numeric_limits<size_t>::max()); | |
925 | |
926 base::CheckedNumeric<size_t> checked_src_rect_size = 4u; | |
927 checked_src_rect_size *= src_rect.width(); | |
928 checked_src_rect_size *= src_rect.height(); | |
929 size_t src_rect_size = checked_src_rect_size.ValueOrDefault( | |
930 std::numeric_limits<size_t>::max()); | |
931 if (original_size > kMemoryThresholdToSubrect && | |
932 src_rect_size <= original_size * kMemoryRatioToSubrect) { | |
933 should_use_subrect = true; | |
934 can_use_original_decode = false; | |
935 } | |
936 } | |
937 | |
938 // If we're going to use the original decode, then the target size should be | |
939 // the full image size, since that will allow for proper memory accounting. | |
940 // Note we skip the decode if the target size is empty altogether, so don't | |
941 // update the target size in that case. | |
942 if (!target_size.IsEmpty()) { | |
943 if (should_use_subrect) | |
944 target_size = src_rect.size(); | |
945 else if (can_use_original_decode) | |
946 target_size = gfx::Size(image.image()->width(), image.image()->height()); | |
947 } | |
948 | |
949 if (quality == kMedium_SkFilterQuality && !target_size.IsEmpty()) { | |
950 SkSize mip_target_size = | |
951 MipMapUtil::GetScaleAdjustmentForSize(src_rect.size(), target_size); | |
952 target_size.set_width(src_rect.width() * mip_target_size.width()); | |
953 target_size.set_height(src_rect.height() * mip_target_size.height()); | |
954 } | |
955 | |
956 return ImageDecodeControllerKey(image.image()->uniqueID(), src_rect, | |
957 target_size, quality, can_use_original_decode, | |
958 should_use_subrect); | |
959 } | |
960 | |
961 ImageDecodeControllerKey::ImageDecodeControllerKey( | |
962 uint32_t image_id, | |
963 const gfx::Rect& src_rect, | |
964 const gfx::Size& target_size, | |
965 SkFilterQuality filter_quality, | |
966 bool can_use_original_decode, | |
967 bool should_use_subrect) | |
968 : image_id_(image_id), | |
969 src_rect_(src_rect), | |
970 target_size_(target_size), | |
971 filter_quality_(filter_quality), | |
972 can_use_original_decode_(can_use_original_decode), | |
973 should_use_subrect_(should_use_subrect) { | |
974 if (can_use_original_decode_) { | |
975 hash_ = std::hash<uint32_t>()(image_id_); | |
976 } else { | |
977 // TODO(vmpstr): This is a mess. Maybe it's faster to just search the vector | |
978 // always (forwards or backwards to account for LRU). | |
979 uint64_t src_rect_hash = base::HashInts( | |
980 static_cast<uint64_t>(base::HashInts(src_rect_.x(), src_rect_.y())), | |
981 static_cast<uint64_t>( | |
982 base::HashInts(src_rect_.width(), src_rect_.height()))); | |
983 | |
984 uint64_t target_size_hash = | |
985 base::HashInts(target_size_.width(), target_size_.height()); | |
986 | |
987 hash_ = base::HashInts(base::HashInts(src_rect_hash, target_size_hash), | |
988 base::HashInts(image_id_, filter_quality_)); | |
989 } | |
990 } | |
991 | |
992 ImageDecodeControllerKey::ImageDecodeControllerKey( | |
993 const ImageDecodeControllerKey& other) = default; | |
994 | |
995 std::string ImageDecodeControllerKey::ToString() const { | |
996 std::ostringstream str; | |
997 str << "id[" << image_id_ << "] src_rect[" << src_rect_.x() << "," | |
998 << src_rect_.y() << " " << src_rect_.width() << "x" << src_rect_.height() | |
999 << "] target_size[" << target_size_.width() << "x" | |
1000 << target_size_.height() << "] filter_quality[" << filter_quality_ | |
1001 << "] can_use_original_decode [" << can_use_original_decode_ | |
1002 << "] should_use_subrect [" << should_use_subrect_ << "] hash [" << hash_ | |
1003 << "]"; | |
1004 return str.str(); | |
1005 } | |
1006 | |
1007 // DecodedImage | |
1008 SoftwareImageDecodeController::DecodedImage::DecodedImage( | |
1009 const SkImageInfo& info, | |
1010 std::unique_ptr<base::DiscardableMemory> memory, | |
1011 const SkSize& src_rect_offset, | |
1012 uint64_t tracing_id) | |
1013 : locked_(true), | |
1014 image_info_(info), | |
1015 memory_(std::move(memory)), | |
1016 src_rect_offset_(src_rect_offset), | |
1017 tracing_id_(tracing_id) { | |
1018 SkPixmap pixmap(image_info_, memory_->data(), image_info_.minRowBytes()); | |
1019 image_ = SkImage::MakeFromRaster( | |
1020 pixmap, [](const void* pixels, void* context) {}, nullptr); | |
1021 } | |
1022 | |
1023 SoftwareImageDecodeController::DecodedImage::~DecodedImage() { | |
1024 DCHECK(!locked_); | |
1025 // lock_count | used | last lock failed | result state | |
1026 // ===========+=======+==================+================== | |
1027 // 1 | false | false | WASTED | |
1028 // 1 | false | true | WASTED | |
1029 // 1 | true | false | USED | |
1030 // 1 | true | true | USED_RELOCK_FAILED | |
1031 // >1 | false | false | WASTED_RELOCKED | |
1032 // >1 | false | true | WASTED_RELOCKED | |
1033 // >1 | true | false | USED_RELOCKED | |
1034 // >1 | true | true | USED_RELOCKED | |
1035 // Note that it's important not to reorder the following enums, since the | |
1036 // numerical values are used in the histogram code. | |
1037 enum State : int { | |
1038 DECODED_IMAGE_STATE_WASTED, | |
1039 DECODED_IMAGE_STATE_USED, | |
1040 DECODED_IMAGE_STATE_USED_RELOCK_FAILED, | |
1041 DECODED_IMAGE_STATE_WASTED_RELOCKED, | |
1042 DECODED_IMAGE_STATE_USED_RELOCKED, | |
1043 DECODED_IMAGE_STATE_COUNT | |
1044 } state = DECODED_IMAGE_STATE_WASTED; | |
1045 | |
1046 if (usage_stats_.lock_count == 1) { | |
1047 if (!usage_stats_.used) | |
1048 state = DECODED_IMAGE_STATE_WASTED; | |
1049 else if (usage_stats_.last_lock_failed) | |
1050 state = DECODED_IMAGE_STATE_USED_RELOCK_FAILED; | |
1051 else | |
1052 state = DECODED_IMAGE_STATE_USED; | |
1053 } else { | |
1054 if (usage_stats_.used) | |
1055 state = DECODED_IMAGE_STATE_USED_RELOCKED; | |
1056 else | |
1057 state = DECODED_IMAGE_STATE_WASTED_RELOCKED; | |
1058 } | |
1059 | |
1060 UMA_HISTOGRAM_ENUMERATION("Renderer4.SoftwareImageDecodeState", state, | |
1061 DECODED_IMAGE_STATE_COUNT); | |
1062 UMA_HISTOGRAM_BOOLEAN("Renderer4.SoftwareImageDecodeState.FirstLockWasted", | |
1063 usage_stats_.first_lock_wasted); | |
1064 } | |
1065 | |
1066 bool SoftwareImageDecodeController::DecodedImage::Lock() { | |
1067 DCHECK(!locked_); | |
1068 bool success = memory_->Lock(); | |
1069 if (!success) { | |
1070 usage_stats_.last_lock_failed = true; | |
1071 return false; | |
1072 } | |
1073 locked_ = true; | |
1074 ++usage_stats_.lock_count; | |
1075 return true; | |
1076 } | |
1077 | |
1078 void SoftwareImageDecodeController::DecodedImage::Unlock() { | |
1079 DCHECK(locked_); | |
1080 memory_->Unlock(); | |
1081 locked_ = false; | |
1082 if (usage_stats_.lock_count == 1) | |
1083 usage_stats_.first_lock_wasted = !usage_stats_.used; | |
1084 } | |
1085 | |
1086 // MemoryBudget | |
1087 SoftwareImageDecodeController::MemoryBudget::MemoryBudget(size_t limit_bytes) | |
1088 : limit_bytes_(limit_bytes), current_usage_bytes_(0u) {} | |
1089 | |
1090 size_t SoftwareImageDecodeController::MemoryBudget::AvailableMemoryBytes() | |
1091 const { | |
1092 size_t usage = GetCurrentUsageSafe(); | |
1093 return usage >= limit_bytes_ ? 0u : (limit_bytes_ - usage); | |
1094 } | |
1095 | |
1096 void SoftwareImageDecodeController::MemoryBudget::AddUsage(size_t usage) { | |
1097 current_usage_bytes_ += usage; | |
1098 } | |
1099 | |
1100 void SoftwareImageDecodeController::MemoryBudget::SubtractUsage(size_t usage) { | |
1101 DCHECK_GE(current_usage_bytes_.ValueOrDefault(0u), usage); | |
1102 current_usage_bytes_ -= usage; | |
1103 } | |
1104 | |
1105 void SoftwareImageDecodeController::MemoryBudget::ResetUsage() { | |
1106 current_usage_bytes_ = 0; | |
1107 } | |
1108 | |
1109 size_t SoftwareImageDecodeController::MemoryBudget::GetCurrentUsageSafe() | |
1110 const { | |
1111 return current_usage_bytes_.ValueOrDie(); | |
1112 } | |
1113 | |
1114 void SoftwareImageDecodeController::OnMemoryStateChange( | |
1115 base::MemoryState state) { | |
1116 { | |
1117 base::AutoLock hold(lock_); | |
1118 switch (state) { | |
1119 case base::MemoryState::NORMAL: | |
1120 max_items_in_cache_ = kNormalMaxItemsInCache; | |
1121 break; | |
1122 case base::MemoryState::THROTTLED: | |
1123 max_items_in_cache_ = kThrottledMaxItemsInCache; | |
1124 break; | |
1125 case base::MemoryState::SUSPENDED: | |
1126 max_items_in_cache_ = kSuspendedMaxItemsInCache; | |
1127 break; | |
1128 case base::MemoryState::UNKNOWN: | |
1129 NOTREACHED(); | |
1130 return; | |
1131 } | |
1132 } | |
1133 ReduceCacheUsage(); | |
1134 } | |
1135 | |
1136 } // namespace cc | |
OLD | NEW |