Index: cc/tiles/software_image_decode_controller.cc |
diff --git a/cc/tiles/software_image_decode_controller.cc b/cc/tiles/software_image_decode_controller.cc |
deleted file mode 100644 |
index 1277ed57e7dc3cb6072db4063be20c0601f6a0f5..0000000000000000000000000000000000000000 |
--- a/cc/tiles/software_image_decode_controller.cc |
+++ /dev/null |
@@ -1,1136 +0,0 @@ |
-// Copyright 2015 The Chromium Authors. All rights reserved. |
-// Use of this source code is governed by a BSD-style license that can be |
-// found in the LICENSE file. |
- |
-#include "cc/tiles/software_image_decode_controller.h" |
- |
-#include <inttypes.h> |
-#include <stdint.h> |
- |
-#include <algorithm> |
-#include <functional> |
- |
-#include "base/format_macros.h" |
-#include "base/macros.h" |
-#include "base/memory/discardable_memory.h" |
-#include "base/memory/memory_coordinator_client_registry.h" |
-#include "base/memory/ptr_util.h" |
-#include "base/metrics/histogram_macros.h" |
-#include "base/strings/stringprintf.h" |
-#include "base/threading/thread_task_runner_handle.h" |
-#include "base/trace_event/memory_dump_manager.h" |
-#include "cc/debug/devtools_instrumentation.h" |
-#include "cc/raster/tile_task.h" |
-#include "cc/resources/resource_format_utils.h" |
-#include "cc/tiles/mipmap_util.h" |
-#include "third_party/skia/include/core/SkCanvas.h" |
-#include "third_party/skia/include/core/SkImage.h" |
-#include "third_party/skia/include/core/SkPixmap.h" |
-#include "ui/gfx/skia_util.h" |
- |
-using base::trace_event::MemoryAllocatorDump; |
-using base::trace_event::MemoryDumpLevelOfDetail; |
- |
-namespace cc { |
-namespace { |
- |
-// The largest single high quality image to try and process. Images above this |
-// size will drop down to medium quality. |
-const size_t kMaxHighQualityImageSizeBytes = 64 * 1024 * 1024; |
- |
-// The number of entries to keep around in the cache. This limit can be breached |
-// if more items are locked. That is, locked items ignore this limit. |
-// Depending on the memory state of the system, we limit the amount of items |
-// differently. |
-const size_t kNormalMaxItemsInCache = 1000; |
-const size_t kThrottledMaxItemsInCache = 100; |
-const size_t kSuspendedMaxItemsInCache = 0; |
- |
-// If the size of the original sized image breaches kMemoryRatioToSubrect but we |
-// don't need to scale the image, consider caching only the needed subrect. |
-// The second part that much be true is that we cache only the needed subrect if |
-// the total size needed for the subrect is at most kMemoryRatioToSubrect * |
-// (size needed for the full original image). |
-const size_t kMemoryThresholdToSubrect = 64 * 1024 * 1024; |
-const float kMemoryRatioToSubrect = 0.5f; |
- |
-class AutoRemoveKeyFromTaskMap { |
- public: |
- AutoRemoveKeyFromTaskMap( |
- std::unordered_map<SoftwareImageDecodeController::ImageKey, |
- scoped_refptr<TileTask>, |
- SoftwareImageDecodeController::ImageKeyHash>* task_map, |
- const SoftwareImageDecodeController::ImageKey& key) |
- : task_map_(task_map), key_(key) {} |
- ~AutoRemoveKeyFromTaskMap() { task_map_->erase(key_); } |
- |
- private: |
- std::unordered_map<SoftwareImageDecodeController::ImageKey, |
- scoped_refptr<TileTask>, |
- SoftwareImageDecodeController::ImageKeyHash>* task_map_; |
- const SoftwareImageDecodeController::ImageKey& key_; |
-}; |
- |
-class AutoDrawWithImageFinished { |
- public: |
- AutoDrawWithImageFinished(SoftwareImageDecodeController* controller, |
- const DrawImage& draw_image, |
- const DecodedDrawImage& decoded_draw_image) |
- : controller_(controller), |
- draw_image_(draw_image), |
- decoded_draw_image_(decoded_draw_image) {} |
- ~AutoDrawWithImageFinished() { |
- controller_->DrawWithImageFinished(draw_image_, decoded_draw_image_); |
- } |
- |
- private: |
- SoftwareImageDecodeController* controller_; |
- const DrawImage& draw_image_; |
- const DecodedDrawImage& decoded_draw_image_; |
-}; |
- |
-class ImageDecodeTaskImpl : public TileTask { |
- public: |
- ImageDecodeTaskImpl(SoftwareImageDecodeController* controller, |
- const SoftwareImageDecodeController::ImageKey& image_key, |
- const DrawImage& image, |
- const ImageDecodeController::TracingInfo& tracing_info) |
- : TileTask(true), |
- controller_(controller), |
- image_key_(image_key), |
- image_(image), |
- tracing_info_(tracing_info) {} |
- |
- // Overridden from Task: |
- void RunOnWorkerThread() override { |
- TRACE_EVENT2("cc", "ImageDecodeTaskImpl::RunOnWorkerThread", "mode", |
- "software", "source_prepare_tiles_id", |
- tracing_info_.prepare_tiles_id); |
- devtools_instrumentation::ScopedImageDecodeTask image_decode_task( |
- image_.image().get(), |
- devtools_instrumentation::ScopedImageDecodeTask::SOFTWARE); |
- controller_->DecodeImage(image_key_, image_); |
- } |
- |
- // Overridden from TileTask: |
- void OnTaskCompleted() override { |
- controller_->RemovePendingTask(image_key_); |
- } |
- |
- protected: |
- ~ImageDecodeTaskImpl() override {} |
- |
- private: |
- SoftwareImageDecodeController* controller_; |
- SoftwareImageDecodeController::ImageKey image_key_; |
- DrawImage image_; |
- const ImageDecodeController::TracingInfo tracing_info_; |
- |
- DISALLOW_COPY_AND_ASSIGN(ImageDecodeTaskImpl); |
-}; |
- |
-SkSize GetScaleAdjustment(const ImageDecodeControllerKey& key) { |
- // If the requested filter quality did not require scale, then the adjustment |
- // is identity. |
- if (key.can_use_original_decode() || key.should_use_subrect()) { |
- return SkSize::Make(1.f, 1.f); |
- } else if (key.filter_quality() == kMedium_SkFilterQuality) { |
- return MipMapUtil::GetScaleAdjustmentForSize(key.src_rect().size(), |
- key.target_size()); |
- } else { |
- float x_scale = |
- key.target_size().width() / static_cast<float>(key.src_rect().width()); |
- float y_scale = key.target_size().height() / |
- static_cast<float>(key.src_rect().height()); |
- return SkSize::Make(x_scale, y_scale); |
- } |
-} |
- |
-SkFilterQuality GetDecodedFilterQuality(const ImageDecodeControllerKey& key) { |
- return std::min(key.filter_quality(), kLow_SkFilterQuality); |
-} |
- |
-SkImageInfo CreateImageInfo(size_t width, |
- size_t height, |
- ResourceFormat format) { |
- return SkImageInfo::Make(width, height, |
- ResourceFormatToClosestSkColorType(format), |
- kPremul_SkAlphaType); |
-} |
- |
-void RecordLockExistingCachedImageHistogram(TilePriority::PriorityBin bin, |
- bool success) { |
- switch (bin) { |
- case TilePriority::NOW: |
- UMA_HISTOGRAM_BOOLEAN("Renderer4.LockExistingCachedImage.Software.NOW", |
- success); |
- case TilePriority::SOON: |
- UMA_HISTOGRAM_BOOLEAN("Renderer4.LockExistingCachedImage.Software.SOON", |
- success); |
- case TilePriority::EVENTUALLY: |
- UMA_HISTOGRAM_BOOLEAN( |
- "Renderer4.LockExistingCachedImage.Software.EVENTUALLY", success); |
- } |
-} |
- |
-} // namespace |
- |
-SoftwareImageDecodeController::SoftwareImageDecodeController( |
- ResourceFormat format, |
- size_t locked_memory_limit_bytes) |
- : decoded_images_(ImageMRUCache::NO_AUTO_EVICT), |
- at_raster_decoded_images_(ImageMRUCache::NO_AUTO_EVICT), |
- locked_images_budget_(locked_memory_limit_bytes), |
- format_(format), |
- max_items_in_cache_(kNormalMaxItemsInCache) { |
- // In certain cases, ThreadTaskRunnerHandle isn't set (Android Webview). |
- // Don't register a dump provider in these cases. |
- if (base::ThreadTaskRunnerHandle::IsSet()) { |
- base::trace_event::MemoryDumpManager::GetInstance()->RegisterDumpProvider( |
- this, "cc::SoftwareImageDecodeController", |
- base::ThreadTaskRunnerHandle::Get()); |
- } |
- // Register this component with base::MemoryCoordinatorClientRegistry. |
- base::MemoryCoordinatorClientRegistry::GetInstance()->Register(this); |
-} |
- |
-SoftwareImageDecodeController::~SoftwareImageDecodeController() { |
- DCHECK_EQ(0u, decoded_images_ref_counts_.size()); |
- DCHECK_EQ(0u, at_raster_decoded_images_ref_counts_.size()); |
- |
- // It is safe to unregister, even if we didn't register in the constructor. |
- base::trace_event::MemoryDumpManager::GetInstance()->UnregisterDumpProvider( |
- this); |
- // Unregister this component with memory_coordinator::ClientRegistry. |
- base::MemoryCoordinatorClientRegistry::GetInstance()->Unregister(this); |
-} |
- |
-bool SoftwareImageDecodeController::GetTaskForImageAndRef( |
- const DrawImage& image, |
- const TracingInfo& tracing_info, |
- scoped_refptr<TileTask>* task) { |
- // If the image already exists or if we're going to create a task for it, then |
- // we'll likely need to ref this image (the exception is if we're prerolling |
- // the image only). That means the image is or will be in the cache. When the |
- // ref goes to 0, it will be unpinned but will remain in the cache. If the |
- // image does not fit into the budget, then we don't ref this image, since it |
- // will be decoded at raster time which is when it will be temporarily put in |
- // the cache. |
- ImageKey key = ImageKey::FromDrawImage(image); |
- TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("cc.debug"), |
- "SoftwareImageDecodeController::GetTaskForImageAndRef", "key", |
- key.ToString()); |
- |
- // If the target size is empty, we can skip this image during draw (and thus |
- // we don't need to decode it or ref it). |
- if (key.target_size().IsEmpty()) { |
- *task = nullptr; |
- return false; |
- } |
- |
- base::AutoLock lock(lock_); |
- |
- // If we already have the image in cache, then we can return it. |
- auto decoded_it = decoded_images_.Get(key); |
- bool new_image_fits_in_memory = |
- locked_images_budget_.AvailableMemoryBytes() >= key.locked_bytes(); |
- if (decoded_it != decoded_images_.end()) { |
- bool image_was_locked = decoded_it->second->is_locked(); |
- if (image_was_locked || |
- (new_image_fits_in_memory && decoded_it->second->Lock())) { |
- RefImage(key); |
- *task = nullptr; |
- SanityCheckState(__LINE__, true); |
- |
- // If the image wasn't locked, then we just succeeded in locking it. |
- if (!image_was_locked) { |
- RecordLockExistingCachedImageHistogram(tracing_info.requesting_tile_bin, |
- true); |
- } |
- return true; |
- } |
- |
- // If the image fits in memory, then we at least tried to lock it and |
- // failed. This means that it's not valid anymore. |
- if (new_image_fits_in_memory) { |
- RecordLockExistingCachedImageHistogram(tracing_info.requesting_tile_bin, |
- false); |
- decoded_images_.Erase(decoded_it); |
- } |
- } |
- |
- // If the task exists, return it. |
- scoped_refptr<TileTask>& existing_task = pending_image_tasks_[key]; |
- if (existing_task) { |
- RefImage(key); |
- *task = existing_task; |
- SanityCheckState(__LINE__, true); |
- return true; |
- } |
- |
- // At this point, we have to create a new image/task, so we need to abort if |
- // it doesn't fit into memory and there are currently no raster tasks that |
- // would have already accounted for memory. The latter part is possible if |
- // there's a running raster task that could not be canceled, and still has a |
- // ref to the image that is now being reffed for the new schedule. |
- if (!new_image_fits_in_memory && (decoded_images_ref_counts_.find(key) == |
- decoded_images_ref_counts_.end())) { |
- *task = nullptr; |
- SanityCheckState(__LINE__, true); |
- return false; |
- } |
- |
- // Actually create the task. RefImage will account for memory on the first |
- // ref. |
- RefImage(key); |
- existing_task = make_scoped_refptr( |
- new ImageDecodeTaskImpl(this, key, image, tracing_info)); |
- *task = existing_task; |
- SanityCheckState(__LINE__, true); |
- return true; |
-} |
- |
-void SoftwareImageDecodeController::RefImage(const ImageKey& key) { |
- TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("cc.debug"), |
- "SoftwareImageDecodeController::RefImage", "key", |
- key.ToString()); |
- lock_.AssertAcquired(); |
- int ref = ++decoded_images_ref_counts_[key]; |
- if (ref == 1) { |
- DCHECK_GE(locked_images_budget_.AvailableMemoryBytes(), key.locked_bytes()); |
- locked_images_budget_.AddUsage(key.locked_bytes()); |
- } |
-} |
- |
-void SoftwareImageDecodeController::UnrefImage(const DrawImage& image) { |
- // When we unref the image, there are several situations we need to consider: |
- // 1. The ref did not reach 0, which means we have to keep the image locked. |
- // 2. The ref reached 0, we should unlock it. |
- // 2a. The image isn't in the locked cache because we didn't get to decode |
- // it yet (or failed to decode it). |
- // 2b. Unlock the image but keep it in list. |
- const ImageKey& key = ImageKey::FromDrawImage(image); |
- TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("cc.debug"), |
- "SoftwareImageDecodeController::UnrefImage", "key", |
- key.ToString()); |
- |
- base::AutoLock lock(lock_); |
- auto ref_count_it = decoded_images_ref_counts_.find(key); |
- DCHECK(ref_count_it != decoded_images_ref_counts_.end()); |
- |
- --ref_count_it->second; |
- if (ref_count_it->second == 0) { |
- decoded_images_ref_counts_.erase(ref_count_it); |
- locked_images_budget_.SubtractUsage(key.locked_bytes()); |
- |
- auto decoded_image_it = decoded_images_.Peek(key); |
- // If we've never decoded the image before ref reached 0, then we wouldn't |
- // have it in our cache. This would happen if we canceled tasks. |
- if (decoded_image_it == decoded_images_.end()) { |
- SanityCheckState(__LINE__, true); |
- return; |
- } |
- DCHECK(decoded_image_it->second->is_locked()); |
- decoded_image_it->second->Unlock(); |
- } |
- SanityCheckState(__LINE__, true); |
-} |
- |
-void SoftwareImageDecodeController::DecodeImage(const ImageKey& key, |
- const DrawImage& image) { |
- TRACE_EVENT1("cc", "SoftwareImageDecodeController::DecodeImage", "key", |
- key.ToString()); |
- base::AutoLock lock(lock_); |
- AutoRemoveKeyFromTaskMap remove_key_from_task_map(&pending_image_tasks_, key); |
- |
- // We could have finished all of the raster tasks (cancelled) while the task |
- // was just starting to run. Since this task already started running, it |
- // wasn't cancelled. So, if the ref count for the image is 0 then we can just |
- // abort. |
- if (decoded_images_ref_counts_.find(key) == |
- decoded_images_ref_counts_.end()) { |
- return; |
- } |
- |
- auto image_it = decoded_images_.Peek(key); |
- if (image_it != decoded_images_.end()) { |
- if (image_it->second->is_locked() || image_it->second->Lock()) |
- return; |
- decoded_images_.Erase(image_it); |
- } |
- |
- std::unique_ptr<DecodedImage> decoded_image; |
- { |
- base::AutoUnlock unlock(lock_); |
- decoded_image = DecodeImageInternal(key, image); |
- } |
- |
- // Abort if we failed to decode the image. |
- if (!decoded_image) |
- return; |
- |
- // At this point, it could have been the case that this image was decoded in |
- // place by an already running raster task from a previous schedule. If that's |
- // the case, then it would have already been placed into the cache (possibly |
- // locked). Remove it if that was the case. |
- image_it = decoded_images_.Peek(key); |
- if (image_it != decoded_images_.end()) { |
- if (image_it->second->is_locked() || image_it->second->Lock()) { |
- // Make sure to unlock the decode we did in this function. |
- decoded_image->Unlock(); |
- return; |
- } |
- decoded_images_.Erase(image_it); |
- } |
- |
- // We could have finished all of the raster tasks (cancelled) while this image |
- // decode task was running, which means that we now have a locked image but no |
- // ref counts. Unlock it immediately in this case. |
- if (decoded_images_ref_counts_.find(key) == |
- decoded_images_ref_counts_.end()) { |
- decoded_image->Unlock(); |
- } |
- |
- decoded_images_.Put(key, std::move(decoded_image)); |
- SanityCheckState(__LINE__, true); |
-} |
- |
-std::unique_ptr<SoftwareImageDecodeController::DecodedImage> |
-SoftwareImageDecodeController::DecodeImageInternal( |
- const ImageKey& key, |
- const DrawImage& draw_image) { |
- TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("cc.debug"), |
- "SoftwareImageDecodeController::DecodeImageInternal", "key", |
- key.ToString()); |
- sk_sp<const SkImage> image = draw_image.image(); |
- if (!image) |
- return nullptr; |
- |
- switch (key.filter_quality()) { |
- case kNone_SkFilterQuality: |
- case kLow_SkFilterQuality: |
- if (key.should_use_subrect()) |
- return GetSubrectImageDecode(key, std::move(image)); |
- return GetOriginalImageDecode(std::move(image)); |
- case kMedium_SkFilterQuality: |
- case kHigh_SkFilterQuality: |
- return GetScaledImageDecode(key, std::move(image)); |
- default: |
- NOTREACHED(); |
- return nullptr; |
- } |
-} |
- |
-DecodedDrawImage SoftwareImageDecodeController::GetDecodedImageForDraw( |
- const DrawImage& draw_image) { |
- ImageKey key = ImageKey::FromDrawImage(draw_image); |
- TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("cc.debug"), |
- "SoftwareImageDecodeController::GetDecodedImageForDraw", "key", |
- key.ToString()); |
- // If the target size is empty, we can skip this image draw. |
- if (key.target_size().IsEmpty()) |
- return DecodedDrawImage(nullptr, kNone_SkFilterQuality); |
- |
- return GetDecodedImageForDrawInternal(key, draw_image); |
-} |
- |
-DecodedDrawImage SoftwareImageDecodeController::GetDecodedImageForDrawInternal( |
- const ImageKey& key, |
- const DrawImage& draw_image) { |
- TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("cc.debug"), |
- "SoftwareImageDecodeController::GetDecodedImageForDrawInternal", |
- "key", key.ToString()); |
- base::AutoLock lock(lock_); |
- auto decoded_images_it = decoded_images_.Get(key); |
- // If we found the image and it's locked, then return it. If it's not locked, |
- // erase it from the cache since it might be put into the at-raster cache. |
- std::unique_ptr<DecodedImage> scoped_decoded_image; |
- DecodedImage* decoded_image = nullptr; |
- if (decoded_images_it != decoded_images_.end()) { |
- decoded_image = decoded_images_it->second.get(); |
- if (decoded_image->is_locked()) { |
- RefImage(key); |
- decoded_image->mark_used(); |
- SanityCheckState(__LINE__, true); |
- return DecodedDrawImage( |
- decoded_image->image(), decoded_image->src_rect_offset(), |
- GetScaleAdjustment(key), GetDecodedFilterQuality(key)); |
- } else { |
- scoped_decoded_image = std::move(decoded_images_it->second); |
- decoded_images_.Erase(decoded_images_it); |
- } |
- } |
- |
- // See if another thread already decoded this image at raster time. If so, we |
- // can just use that result directly. |
- auto at_raster_images_it = at_raster_decoded_images_.Get(key); |
- if (at_raster_images_it != at_raster_decoded_images_.end()) { |
- DCHECK(at_raster_images_it->second->is_locked()); |
- RefAtRasterImage(key); |
- SanityCheckState(__LINE__, true); |
- DecodedImage* at_raster_decoded_image = at_raster_images_it->second.get(); |
- at_raster_decoded_image->mark_used(); |
- auto decoded_draw_image = |
- DecodedDrawImage(at_raster_decoded_image->image(), |
- at_raster_decoded_image->src_rect_offset(), |
- GetScaleAdjustment(key), GetDecodedFilterQuality(key)); |
- decoded_draw_image.set_at_raster_decode(true); |
- return decoded_draw_image; |
- } |
- |
- // Now we know that we don't have a locked image, and we seem to be the first |
- // thread encountering this image (that might not be true, since other threads |
- // might be decoding it already). This means that we need to decode the image |
- // assuming we can't lock the one we found in the cache. |
- bool check_at_raster_cache = false; |
- if (!decoded_image || !decoded_image->Lock()) { |
- // Note that we have to release the lock, since this lock is also accessed |
- // on the compositor thread. This means holding on to the lock might stall |
- // the compositor thread for the duration of the decode! |
- base::AutoUnlock unlock(lock_); |
- scoped_decoded_image = DecodeImageInternal(key, draw_image); |
- decoded_image = scoped_decoded_image.get(); |
- |
- // Skip the image if we couldn't decode it. |
- if (!decoded_image) |
- return DecodedDrawImage(nullptr, kNone_SkFilterQuality); |
- check_at_raster_cache = true; |
- } |
- |
- DCHECK(decoded_image == scoped_decoded_image.get()); |
- |
- // While we unlocked the lock, it could be the case that another thread |
- // already decoded this already and put it in the at-raster cache. Look it up |
- // first. |
- if (check_at_raster_cache) { |
- at_raster_images_it = at_raster_decoded_images_.Get(key); |
- if (at_raster_images_it != at_raster_decoded_images_.end()) { |
- // We have to drop our decode, since the one in the cache is being used by |
- // another thread. |
- decoded_image->Unlock(); |
- decoded_image = at_raster_images_it->second.get(); |
- scoped_decoded_image = nullptr; |
- } |
- } |
- |
- // If we really are the first ones, or if the other thread already unlocked |
- // the image, then put our work into at-raster time cache. |
- if (scoped_decoded_image) |
- at_raster_decoded_images_.Put(key, std::move(scoped_decoded_image)); |
- |
- DCHECK(decoded_image); |
- DCHECK(decoded_image->is_locked()); |
- RefAtRasterImage(key); |
- SanityCheckState(__LINE__, true); |
- decoded_image->mark_used(); |
- auto decoded_draw_image = |
- DecodedDrawImage(decoded_image->image(), decoded_image->src_rect_offset(), |
- GetScaleAdjustment(key), GetDecodedFilterQuality(key)); |
- decoded_draw_image.set_at_raster_decode(true); |
- return decoded_draw_image; |
-} |
- |
-std::unique_ptr<SoftwareImageDecodeController::DecodedImage> |
-SoftwareImageDecodeController::GetOriginalImageDecode( |
- sk_sp<const SkImage> image) { |
- SkImageInfo decoded_info = |
- CreateImageInfo(image->width(), image->height(), format_); |
- std::unique_ptr<base::DiscardableMemory> decoded_pixels; |
- { |
- TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("cc.debug"), |
- "SoftwareImageDecodeController::GetOriginalImageDecode - " |
- "allocate decoded pixels"); |
- decoded_pixels = |
- base::DiscardableMemoryAllocator::GetInstance() |
- ->AllocateLockedDiscardableMemory(decoded_info.minRowBytes() * |
- decoded_info.height()); |
- } |
- { |
- TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("cc.debug"), |
- "SoftwareImageDecodeController::GetOriginalImageDecode - " |
- "read pixels"); |
- bool result = image->readPixels(decoded_info, decoded_pixels->data(), |
- decoded_info.minRowBytes(), 0, 0, |
- SkImage::kDisallow_CachingHint); |
- |
- if (!result) { |
- decoded_pixels->Unlock(); |
- return nullptr; |
- } |
- } |
- return base::MakeUnique<DecodedImage>(decoded_info, std::move(decoded_pixels), |
- SkSize::Make(0, 0), |
- next_tracing_id_.GetNext()); |
-} |
- |
-std::unique_ptr<SoftwareImageDecodeController::DecodedImage> |
-SoftwareImageDecodeController::GetSubrectImageDecode( |
- const ImageKey& key, |
- sk_sp<const SkImage> image) { |
- // Construct a key to use in GetDecodedImageForDrawInternal(). |
- // This allows us to reuse an image in any cache if available. |
- gfx::Rect full_image_rect(image->width(), image->height()); |
- DrawImage original_size_draw_image(std::move(image), |
- gfx::RectToSkIRect(full_image_rect), |
- kNone_SkFilterQuality, SkMatrix::I()); |
- ImageKey original_size_key = |
- ImageKey::FromDrawImage(original_size_draw_image); |
- // Sanity checks. |
- DCHECK(original_size_key.can_use_original_decode()) |
- << original_size_key.ToString(); |
- DCHECK(full_image_rect.size() == original_size_key.target_size()); |
- |
- auto decoded_draw_image = GetDecodedImageForDrawInternal( |
- original_size_key, original_size_draw_image); |
- AutoDrawWithImageFinished auto_finish_draw(this, original_size_draw_image, |
- decoded_draw_image); |
- if (!decoded_draw_image.image()) |
- return nullptr; |
- |
- SkImageInfo subrect_info = CreateImageInfo( |
- key.target_size().width(), key.target_size().height(), format_); |
- std::unique_ptr<base::DiscardableMemory> subrect_pixels; |
- { |
- TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("cc.debug"), |
- "SoftwareImageDecodeController::GetSubrectImageDecode - " |
- "allocate subrect pixels"); |
- // TODO(vmpstr): This is using checked math to diagnose a problem reported |
- // in crbug.com/662217. If this is causing crashes, then it should be fixed |
- // elsewhere by skipping images that are too large. |
- base::CheckedNumeric<size_t> byte_size = subrect_info.minRowBytes(); |
- byte_size *= subrect_info.height(); |
- subrect_pixels = |
- base::DiscardableMemoryAllocator::GetInstance() |
- ->AllocateLockedDiscardableMemory(byte_size.ValueOrDie()); |
- } |
- { |
- TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("cc.debug"), |
- "SoftwareImageDecodeController::GetOriginalImageDecode - " |
- "read pixels"); |
- bool result = decoded_draw_image.image()->readPixels( |
- subrect_info, subrect_pixels->data(), subrect_info.minRowBytes(), |
- key.src_rect().x(), key.src_rect().y(), SkImage::kDisallow_CachingHint); |
- // We have a decoded image, and we're reading into already allocated memory. |
- // This should never fail. |
- DCHECK(result); |
- } |
- return base::WrapUnique( |
- new DecodedImage(subrect_info, std::move(subrect_pixels), |
- SkSize::Make(-key.src_rect().x(), -key.src_rect().y()), |
- next_tracing_id_.GetNext())); |
-} |
- |
-std::unique_ptr<SoftwareImageDecodeController::DecodedImage> |
-SoftwareImageDecodeController::GetScaledImageDecode( |
- const ImageKey& key, |
- sk_sp<const SkImage> image) { |
- // Construct a key to use in GetDecodedImageForDrawInternal(). |
- // This allows us to reuse an image in any cache if available. |
- gfx::Rect full_image_rect(image->width(), image->height()); |
- DrawImage original_size_draw_image(std::move(image), |
- gfx::RectToSkIRect(full_image_rect), |
- kNone_SkFilterQuality, SkMatrix::I()); |
- ImageKey original_size_key = |
- ImageKey::FromDrawImage(original_size_draw_image); |
- // Sanity checks. |
- DCHECK(original_size_key.can_use_original_decode()) |
- << original_size_key.ToString(); |
- DCHECK(full_image_rect.size() == original_size_key.target_size()); |
- |
- auto decoded_draw_image = GetDecodedImageForDrawInternal( |
- original_size_key, original_size_draw_image); |
- AutoDrawWithImageFinished auto_finish_draw(this, original_size_draw_image, |
- decoded_draw_image); |
- if (!decoded_draw_image.image()) |
- return nullptr; |
- |
- SkPixmap decoded_pixmap; |
- bool result = decoded_draw_image.image()->peekPixels(&decoded_pixmap); |
- DCHECK(result) << key.ToString(); |
- if (key.src_rect() != full_image_rect) { |
- result = decoded_pixmap.extractSubset(&decoded_pixmap, |
- gfx::RectToSkIRect(key.src_rect())); |
- DCHECK(result) << key.ToString(); |
- } |
- |
- DCHECK(!key.target_size().IsEmpty()); |
- SkImageInfo scaled_info = CreateImageInfo( |
- key.target_size().width(), key.target_size().height(), format_); |
- std::unique_ptr<base::DiscardableMemory> scaled_pixels; |
- { |
- TRACE_EVENT0( |
- TRACE_DISABLED_BY_DEFAULT("cc.debug"), |
- "SoftwareImageDecodeController::ScaleImage - allocate scaled pixels"); |
- scaled_pixels = base::DiscardableMemoryAllocator::GetInstance() |
- ->AllocateLockedDiscardableMemory( |
- scaled_info.minRowBytes() * scaled_info.height()); |
- } |
- SkPixmap scaled_pixmap(scaled_info, scaled_pixels->data(), |
- scaled_info.minRowBytes()); |
- DCHECK(key.filter_quality() == kHigh_SkFilterQuality || |
- key.filter_quality() == kMedium_SkFilterQuality); |
- { |
- TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("cc.debug"), |
- "SoftwareImageDecodeController::ScaleImage - scale pixels"); |
- bool result = |
- decoded_pixmap.scalePixels(scaled_pixmap, key.filter_quality()); |
- DCHECK(result) << key.ToString(); |
- } |
- |
- return base::MakeUnique<DecodedImage>( |
- scaled_info, std::move(scaled_pixels), |
- SkSize::Make(-key.src_rect().x(), -key.src_rect().y()), |
- next_tracing_id_.GetNext()); |
-} |
- |
-void SoftwareImageDecodeController::DrawWithImageFinished( |
- const DrawImage& image, |
- const DecodedDrawImage& decoded_image) { |
- TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("cc.debug"), |
- "SoftwareImageDecodeController::DrawWithImageFinished", "key", |
- ImageKey::FromDrawImage(image).ToString()); |
- ImageKey key = ImageKey::FromDrawImage(image); |
- if (!decoded_image.image()) |
- return; |
- |
- if (decoded_image.is_at_raster_decode()) |
- UnrefAtRasterImage(key); |
- else |
- UnrefImage(image); |
- SanityCheckState(__LINE__, false); |
-} |
- |
-void SoftwareImageDecodeController::RefAtRasterImage(const ImageKey& key) { |
- TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("cc.debug"), |
- "SoftwareImageDecodeController::RefAtRasterImage", "key", |
- key.ToString()); |
- DCHECK(at_raster_decoded_images_.Peek(key) != |
- at_raster_decoded_images_.end()); |
- ++at_raster_decoded_images_ref_counts_[key]; |
-} |
- |
-void SoftwareImageDecodeController::UnrefAtRasterImage(const ImageKey& key) { |
- TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("cc.debug"), |
- "SoftwareImageDecodeController::UnrefAtRasterImage", "key", |
- key.ToString()); |
- base::AutoLock lock(lock_); |
- |
- auto ref_it = at_raster_decoded_images_ref_counts_.find(key); |
- DCHECK(ref_it != at_raster_decoded_images_ref_counts_.end()); |
- --ref_it->second; |
- if (ref_it->second == 0) { |
- at_raster_decoded_images_ref_counts_.erase(ref_it); |
- auto at_raster_image_it = at_raster_decoded_images_.Peek(key); |
- DCHECK(at_raster_image_it != at_raster_decoded_images_.end()); |
- |
- // The ref for our image reached 0 and it's still locked. We need to figure |
- // out what the best thing to do with the image. There are several |
- // situations: |
- // 1. The image is not in the main cache and... |
- // 1a. ... its ref count is 0: unlock our image and put it in the main |
- // cache. |
- // 1b. ... ref count is not 0: keep the image locked and put it in the |
- // main cache. |
- // 2. The image is in the main cache... |
- // 2a. ... and is locked: unlock our image and discard it |
- // 2b. ... and is unlocked and... |
- // 2b1. ... its ref count is 0: unlock our image and replace the |
- // existing one with ours. |
- // 2b2. ... its ref count is not 0: this shouldn't be possible. |
- auto image_it = decoded_images_.Peek(key); |
- if (image_it == decoded_images_.end()) { |
- if (decoded_images_ref_counts_.find(key) == |
- decoded_images_ref_counts_.end()) { |
- at_raster_image_it->second->Unlock(); |
- } |
- decoded_images_.Put(key, std::move(at_raster_image_it->second)); |
- } else if (image_it->second->is_locked()) { |
- at_raster_image_it->second->Unlock(); |
- } else { |
- DCHECK(decoded_images_ref_counts_.find(key) == |
- decoded_images_ref_counts_.end()); |
- at_raster_image_it->second->Unlock(); |
- decoded_images_.Erase(image_it); |
- decoded_images_.Put(key, std::move(at_raster_image_it->second)); |
- } |
- at_raster_decoded_images_.Erase(at_raster_image_it); |
- } |
-} |
- |
-void SoftwareImageDecodeController::ReduceCacheUsage() { |
- TRACE_EVENT0("cc", "SoftwareImageDecodeController::ReduceCacheUsage"); |
- base::AutoLock lock(lock_); |
- size_t num_to_remove = (decoded_images_.size() > max_items_in_cache_) |
- ? (decoded_images_.size() - max_items_in_cache_) |
- : 0; |
- for (auto it = decoded_images_.rbegin(); |
- num_to_remove != 0 && it != decoded_images_.rend();) { |
- if (it->second->is_locked()) { |
- ++it; |
- continue; |
- } |
- |
- it = decoded_images_.Erase(it); |
- --num_to_remove; |
- } |
-} |
- |
-void SoftwareImageDecodeController::RemovePendingTask(const ImageKey& key) { |
- base::AutoLock lock(lock_); |
- pending_image_tasks_.erase(key); |
-} |
- |
-bool SoftwareImageDecodeController::OnMemoryDump( |
- const base::trace_event::MemoryDumpArgs& args, |
- base::trace_event::ProcessMemoryDump* pmd) { |
- base::AutoLock lock(lock_); |
- |
- if (args.level_of_detail == MemoryDumpLevelOfDetail::BACKGROUND) { |
- std::string dump_name = |
- base::StringPrintf("cc/image_memory/controller_0x%" PRIXPTR, |
- reinterpret_cast<uintptr_t>(this)); |
- MemoryAllocatorDump* dump = pmd->CreateAllocatorDump(dump_name); |
- dump->AddScalar("locked_size", MemoryAllocatorDump::kUnitsBytes, |
- locked_images_budget_.GetCurrentUsageSafe()); |
- } else { |
- // Dump each of our caches. |
- DumpImageMemoryForCache(decoded_images_, "cached", pmd); |
- DumpImageMemoryForCache(at_raster_decoded_images_, "at_raster", pmd); |
- } |
- |
- // Memory dump can't fail, always return true. |
- return true; |
-} |
- |
-void SoftwareImageDecodeController::DumpImageMemoryForCache( |
- const ImageMRUCache& cache, |
- const char* cache_name, |
- base::trace_event::ProcessMemoryDump* pmd) const { |
- lock_.AssertAcquired(); |
- |
- for (const auto& image_pair : cache) { |
- std::string dump_name = base::StringPrintf( |
- "cc/image_memory/controller_0x%" PRIXPTR "/%s/image_%" PRIu64 "_id_%d", |
- reinterpret_cast<uintptr_t>(this), cache_name, |
- image_pair.second->tracing_id(), image_pair.first.image_id()); |
- // CreateMemoryAllocatorDump will automatically add tracking values for the |
- // total size. If locked, we also add a "locked_size" below. |
- MemoryAllocatorDump* dump = |
- image_pair.second->memory()->CreateMemoryAllocatorDump( |
- dump_name.c_str(), pmd); |
- DCHECK(dump); |
- if (image_pair.second->is_locked()) { |
- dump->AddScalar("locked_size", MemoryAllocatorDump::kUnitsBytes, |
- image_pair.first.locked_bytes()); |
- } |
- } |
-} |
- |
-void SoftwareImageDecodeController::SanityCheckState(int line, |
- bool lock_acquired) { |
-#if DCHECK_IS_ON() |
- if (!lock_acquired) { |
- base::AutoLock lock(lock_); |
- SanityCheckState(line, true); |
- return; |
- } |
- |
- MemoryBudget budget(locked_images_budget_.total_limit_bytes()); |
- for (const auto& image_pair : decoded_images_) { |
- const auto& key = image_pair.first; |
- const auto& image = image_pair.second; |
- |
- auto ref_it = decoded_images_ref_counts_.find(key); |
- if (image->is_locked()) { |
- budget.AddUsage(key.locked_bytes()); |
- DCHECK(ref_it != decoded_images_ref_counts_.end()) << line; |
- } else { |
- DCHECK(ref_it == decoded_images_ref_counts_.end() || |
- pending_image_tasks_.find(key) != pending_image_tasks_.end()) |
- << line; |
- } |
- } |
- DCHECK_GE(budget.AvailableMemoryBytes(), |
- locked_images_budget_.AvailableMemoryBytes()) |
- << line; |
-#endif // DCHECK_IS_ON() |
-} |
- |
-// SoftwareImageDecodeControllerKey |
-ImageDecodeControllerKey ImageDecodeControllerKey::FromDrawImage( |
- const DrawImage& image) { |
- const SkSize& scale = image.scale(); |
- // If the src_rect falls outside of the image, we need to clip it since |
- // otherwise we might end up with uninitialized memory in the decode process. |
- // Note that the scale is still unchanged and the target size is now a |
- // function of the new src_rect. |
- gfx::Rect src_rect = gfx::IntersectRects( |
- gfx::SkIRectToRect(image.src_rect()), |
- gfx::Rect(image.image()->width(), image.image()->height())); |
- |
- gfx::Size target_size( |
- SkScalarRoundToInt(std::abs(src_rect.width() * scale.width())), |
- SkScalarRoundToInt(std::abs(src_rect.height() * scale.height()))); |
- |
- // Start with the quality that was requested. |
- SkFilterQuality quality = image.filter_quality(); |
- |
- // If we're not going to do a scale, we can use low filter quality. Note that |
- // checking if the sizes are the same is better than checking if scale is 1.f, |
- // because even non-1 scale can result in the same (rounded) width/height. |
- // If either dimension is a downscale, then use mipmaps (medium filter |
- // quality). |
- if (target_size.width() == src_rect.width() && |
- target_size.height() == src_rect.height()) { |
- quality = std::min(quality, kLow_SkFilterQuality); |
- } else if (target_size.width() < src_rect.width() || |
- target_size.height() < src_rect.height()) { |
- quality = std::min(quality, kMedium_SkFilterQuality); |
- } |
- |
- // Drop from high to medium if the the matrix we applied wasn't decomposable, |
- // or if the scaled image will be too large. |
- if (quality == kHigh_SkFilterQuality) { |
- if (!image.matrix_is_decomposable()) { |
- quality = kMedium_SkFilterQuality; |
- } else { |
- base::CheckedNumeric<size_t> size = 4u; |
- size *= target_size.width(); |
- size *= target_size.height(); |
- if (size.ValueOrDefault(std::numeric_limits<size_t>::max()) > |
- kMaxHighQualityImageSizeBytes) { |
- quality = kMedium_SkFilterQuality; |
- } |
- } |
- } |
- |
- // Drop from medium to low if the matrix we applied wasn't decomposable or if |
- // we're enlarging the image in both dimensions. |
- if (quality == kMedium_SkFilterQuality) { |
- if (!image.matrix_is_decomposable() || |
- (scale.width() >= 1.f && scale.height() >= 1.f)) { |
- quality = kLow_SkFilterQuality; |
- } |
- } |
- |
- bool can_use_original_decode = |
- quality == kLow_SkFilterQuality || quality == kNone_SkFilterQuality; |
- bool should_use_subrect = false; |
- if (can_use_original_decode) { |
- base::CheckedNumeric<size_t> checked_original_size = 4u; |
- checked_original_size *= image.image()->width(); |
- checked_original_size *= image.image()->height(); |
- size_t original_size = checked_original_size.ValueOrDefault( |
- std::numeric_limits<size_t>::max()); |
- |
- base::CheckedNumeric<size_t> checked_src_rect_size = 4u; |
- checked_src_rect_size *= src_rect.width(); |
- checked_src_rect_size *= src_rect.height(); |
- size_t src_rect_size = checked_src_rect_size.ValueOrDefault( |
- std::numeric_limits<size_t>::max()); |
- if (original_size > kMemoryThresholdToSubrect && |
- src_rect_size <= original_size * kMemoryRatioToSubrect) { |
- should_use_subrect = true; |
- can_use_original_decode = false; |
- } |
- } |
- |
- // If we're going to use the original decode, then the target size should be |
- // the full image size, since that will allow for proper memory accounting. |
- // Note we skip the decode if the target size is empty altogether, so don't |
- // update the target size in that case. |
- if (!target_size.IsEmpty()) { |
- if (should_use_subrect) |
- target_size = src_rect.size(); |
- else if (can_use_original_decode) |
- target_size = gfx::Size(image.image()->width(), image.image()->height()); |
- } |
- |
- if (quality == kMedium_SkFilterQuality && !target_size.IsEmpty()) { |
- SkSize mip_target_size = |
- MipMapUtil::GetScaleAdjustmentForSize(src_rect.size(), target_size); |
- target_size.set_width(src_rect.width() * mip_target_size.width()); |
- target_size.set_height(src_rect.height() * mip_target_size.height()); |
- } |
- |
- return ImageDecodeControllerKey(image.image()->uniqueID(), src_rect, |
- target_size, quality, can_use_original_decode, |
- should_use_subrect); |
-} |
- |
-ImageDecodeControllerKey::ImageDecodeControllerKey( |
- uint32_t image_id, |
- const gfx::Rect& src_rect, |
- const gfx::Size& target_size, |
- SkFilterQuality filter_quality, |
- bool can_use_original_decode, |
- bool should_use_subrect) |
- : image_id_(image_id), |
- src_rect_(src_rect), |
- target_size_(target_size), |
- filter_quality_(filter_quality), |
- can_use_original_decode_(can_use_original_decode), |
- should_use_subrect_(should_use_subrect) { |
- if (can_use_original_decode_) { |
- hash_ = std::hash<uint32_t>()(image_id_); |
- } else { |
- // TODO(vmpstr): This is a mess. Maybe it's faster to just search the vector |
- // always (forwards or backwards to account for LRU). |
- uint64_t src_rect_hash = base::HashInts( |
- static_cast<uint64_t>(base::HashInts(src_rect_.x(), src_rect_.y())), |
- static_cast<uint64_t>( |
- base::HashInts(src_rect_.width(), src_rect_.height()))); |
- |
- uint64_t target_size_hash = |
- base::HashInts(target_size_.width(), target_size_.height()); |
- |
- hash_ = base::HashInts(base::HashInts(src_rect_hash, target_size_hash), |
- base::HashInts(image_id_, filter_quality_)); |
- } |
-} |
- |
-ImageDecodeControllerKey::ImageDecodeControllerKey( |
- const ImageDecodeControllerKey& other) = default; |
- |
-std::string ImageDecodeControllerKey::ToString() const { |
- std::ostringstream str; |
- str << "id[" << image_id_ << "] src_rect[" << src_rect_.x() << "," |
- << src_rect_.y() << " " << src_rect_.width() << "x" << src_rect_.height() |
- << "] target_size[" << target_size_.width() << "x" |
- << target_size_.height() << "] filter_quality[" << filter_quality_ |
- << "] can_use_original_decode [" << can_use_original_decode_ |
- << "] should_use_subrect [" << should_use_subrect_ << "] hash [" << hash_ |
- << "]"; |
- return str.str(); |
-} |
- |
-// DecodedImage |
-SoftwareImageDecodeController::DecodedImage::DecodedImage( |
- const SkImageInfo& info, |
- std::unique_ptr<base::DiscardableMemory> memory, |
- const SkSize& src_rect_offset, |
- uint64_t tracing_id) |
- : locked_(true), |
- image_info_(info), |
- memory_(std::move(memory)), |
- src_rect_offset_(src_rect_offset), |
- tracing_id_(tracing_id) { |
- SkPixmap pixmap(image_info_, memory_->data(), image_info_.minRowBytes()); |
- image_ = SkImage::MakeFromRaster( |
- pixmap, [](const void* pixels, void* context) {}, nullptr); |
-} |
- |
-SoftwareImageDecodeController::DecodedImage::~DecodedImage() { |
- DCHECK(!locked_); |
- // lock_count | used | last lock failed | result state |
- // ===========+=======+==================+================== |
- // 1 | false | false | WASTED |
- // 1 | false | true | WASTED |
- // 1 | true | false | USED |
- // 1 | true | true | USED_RELOCK_FAILED |
- // >1 | false | false | WASTED_RELOCKED |
- // >1 | false | true | WASTED_RELOCKED |
- // >1 | true | false | USED_RELOCKED |
- // >1 | true | true | USED_RELOCKED |
- // Note that it's important not to reorder the following enums, since the |
- // numerical values are used in the histogram code. |
- enum State : int { |
- DECODED_IMAGE_STATE_WASTED, |
- DECODED_IMAGE_STATE_USED, |
- DECODED_IMAGE_STATE_USED_RELOCK_FAILED, |
- DECODED_IMAGE_STATE_WASTED_RELOCKED, |
- DECODED_IMAGE_STATE_USED_RELOCKED, |
- DECODED_IMAGE_STATE_COUNT |
- } state = DECODED_IMAGE_STATE_WASTED; |
- |
- if (usage_stats_.lock_count == 1) { |
- if (!usage_stats_.used) |
- state = DECODED_IMAGE_STATE_WASTED; |
- else if (usage_stats_.last_lock_failed) |
- state = DECODED_IMAGE_STATE_USED_RELOCK_FAILED; |
- else |
- state = DECODED_IMAGE_STATE_USED; |
- } else { |
- if (usage_stats_.used) |
- state = DECODED_IMAGE_STATE_USED_RELOCKED; |
- else |
- state = DECODED_IMAGE_STATE_WASTED_RELOCKED; |
- } |
- |
- UMA_HISTOGRAM_ENUMERATION("Renderer4.SoftwareImageDecodeState", state, |
- DECODED_IMAGE_STATE_COUNT); |
- UMA_HISTOGRAM_BOOLEAN("Renderer4.SoftwareImageDecodeState.FirstLockWasted", |
- usage_stats_.first_lock_wasted); |
-} |
- |
-bool SoftwareImageDecodeController::DecodedImage::Lock() { |
- DCHECK(!locked_); |
- bool success = memory_->Lock(); |
- if (!success) { |
- usage_stats_.last_lock_failed = true; |
- return false; |
- } |
- locked_ = true; |
- ++usage_stats_.lock_count; |
- return true; |
-} |
- |
-void SoftwareImageDecodeController::DecodedImage::Unlock() { |
- DCHECK(locked_); |
- memory_->Unlock(); |
- locked_ = false; |
- if (usage_stats_.lock_count == 1) |
- usage_stats_.first_lock_wasted = !usage_stats_.used; |
-} |
- |
-// MemoryBudget |
-SoftwareImageDecodeController::MemoryBudget::MemoryBudget(size_t limit_bytes) |
- : limit_bytes_(limit_bytes), current_usage_bytes_(0u) {} |
- |
-size_t SoftwareImageDecodeController::MemoryBudget::AvailableMemoryBytes() |
- const { |
- size_t usage = GetCurrentUsageSafe(); |
- return usage >= limit_bytes_ ? 0u : (limit_bytes_ - usage); |
-} |
- |
-void SoftwareImageDecodeController::MemoryBudget::AddUsage(size_t usage) { |
- current_usage_bytes_ += usage; |
-} |
- |
-void SoftwareImageDecodeController::MemoryBudget::SubtractUsage(size_t usage) { |
- DCHECK_GE(current_usage_bytes_.ValueOrDefault(0u), usage); |
- current_usage_bytes_ -= usage; |
-} |
- |
-void SoftwareImageDecodeController::MemoryBudget::ResetUsage() { |
- current_usage_bytes_ = 0; |
-} |
- |
-size_t SoftwareImageDecodeController::MemoryBudget::GetCurrentUsageSafe() |
- const { |
- return current_usage_bytes_.ValueOrDie(); |
-} |
- |
-void SoftwareImageDecodeController::OnMemoryStateChange( |
- base::MemoryState state) { |
- { |
- base::AutoLock hold(lock_); |
- switch (state) { |
- case base::MemoryState::NORMAL: |
- max_items_in_cache_ = kNormalMaxItemsInCache; |
- break; |
- case base::MemoryState::THROTTLED: |
- max_items_in_cache_ = kThrottledMaxItemsInCache; |
- break; |
- case base::MemoryState::SUSPENDED: |
- max_items_in_cache_ = kSuspendedMaxItemsInCache; |
- break; |
- case base::MemoryState::UNKNOWN: |
- NOTREACHED(); |
- return; |
- } |
- } |
- ReduceCacheUsage(); |
-} |
- |
-} // namespace cc |