| 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
|
|
|