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

Unified Diff: cc/tiles/software_image_decode_controller.cc

Issue 2541183002: cc: Rename ImageDecodeController to ImageDecodeCache. (Closed)
Patch Set: rename: update Created 4 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « cc/tiles/software_image_decode_controller.h ('k') | cc/tiles/software_image_decode_controller_unittest.cc » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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
« no previous file with comments | « cc/tiles/software_image_decode_controller.h ('k') | cc/tiles/software_image_decode_controller_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698