blob: 1d3e2ac75c40d603c8a3009edfce9d0885aac908 [file] [log] [blame]
// Copyright 2016 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 <stdint.h>
#include <memory>
#include <unordered_map>
#include <unordered_set>
#include "base/atomic_sequence_num.h"
#include "base/containers/mru_cache.h"
#include "base/hash.h"
#include "base/memory/discardable_memory_allocator.h"
#include "base/memory/memory_coordinator_client.h"
#include "base/memory/ref_counted.h"
#include "base/numerics/safe_math.h"
#include "base/threading/thread_checker.h"
#include "base/trace_event/memory_dump_provider.h"
#include "cc/cc_export.h"
#include "cc/paint/draw_image.h"
#include "cc/resources/resource_format.h"
#include "cc/tiles/decoded_draw_image.h"
#include "cc/tiles/image_decode_cache.h"
#include "third_party/skia/include/core/SkRefCnt.h"
#include "ui/gfx/geometry/rect.h"
namespace cc {
// ImageDecodeCacheKey is a class that gets a cache key out of a given draw
// image. That is, this key uniquely identifies an image in the cache. Note that
// it's insufficient to use SkImage's unique id, since the same image can appear
// in the cache multiple times at different scales and filter qualities.
class CC_EXPORT ImageDecodeCacheKey {
static ImageDecodeCacheKey FromDrawImage(const DrawImage& image);
ImageDecodeCacheKey(const ImageDecodeCacheKey& other);
bool operator==(const ImageDecodeCacheKey& other) const {
// The image_id always has to be the same. However, after that all original
// decodes are the same, so if we can use the original decode, return true.
// If not, then we have to compare every field.
return image_id_ == other.image_id_ &&
can_use_original_size_decode_ ==
other.can_use_original_size_decode_ &&
target_color_space_ == other.target_color_space_ &&
(can_use_original_size_decode_ ||
(src_rect_ == other.src_rect_ &&
target_size_ == other.target_size_ &&
filter_quality_ == other.filter_quality_));
bool operator!=(const ImageDecodeCacheKey& other) const {
return !(*this == other);
uint32_t image_id() const { return image_id_; }
SkFilterQuality filter_quality() const { return filter_quality_; }
gfx::Rect src_rect() const { return src_rect_; }
gfx::Size target_size() const { return target_size_; }
const gfx::ColorSpace& target_color_space() const {
return target_color_space_;
bool can_use_original_size_decode() const {
return can_use_original_size_decode_;
bool should_use_subrect() const { return should_use_subrect_; }
size_t get_hash() const { return hash_; }
// Helper to figure out how much memory the locked image represented by this
// key would take.
size_t locked_bytes() const {
// TODO(vmpstr): Handle formats other than RGBA.
base::CheckedNumeric<size_t> result = 4;
result *= target_size_.width();
result *= target_size_.height();
return result.ValueOrDefault(std::numeric_limits<size_t>::max());
std::string ToString() const;
ImageDecodeCacheKey(uint32_t image_id,
const gfx::Rect& src_rect,
const gfx::Size& size,
const gfx::ColorSpace& target_color_space,
SkFilterQuality filter_quality,
bool can_use_original_size_decode,
bool should_use_subrect);
uint32_t image_id_;
gfx::Rect src_rect_;
gfx::Size target_size_;
gfx::ColorSpace target_color_space_;
SkFilterQuality filter_quality_;
bool can_use_original_size_decode_;
bool should_use_subrect_;
size_t hash_;
// Hash function for the above ImageDecodeCacheKey.
struct ImageDecodeCacheKeyHash {
size_t operator()(const ImageDecodeCacheKey& key) const {
return key.get_hash();
class CC_EXPORT SoftwareImageDecodeCache
: public ImageDecodeCache,
public base::trace_event::MemoryDumpProvider,
public base::MemoryCoordinatorClient {
using ImageKey = ImageDecodeCacheKey;
using ImageKeyHash = ImageDecodeCacheKeyHash;
SoftwareImageDecodeCache(ResourceFormat format,
size_t locked_memory_limit_bytes);
~SoftwareImageDecodeCache() override;
// ImageDecodeCache overrides.
bool GetTaskForImageAndRef(const DrawImage& image,
const TracingInfo& tracing_info,
scoped_refptr<TileTask>* task) override;
bool GetOutOfRasterDecodeTaskForImageAndRef(
const DrawImage& image,
scoped_refptr<TileTask>* task) override;
void UnrefImage(const DrawImage& image) override;
DecodedDrawImage GetDecodedImageForDraw(const DrawImage& image) override;
void DrawWithImageFinished(const DrawImage& image,
const DecodedDrawImage& decoded_image) override;
void ReduceCacheUsage() override;
// Software doesn't keep outstanding images pinned, so this is a no-op.
void SetShouldAggressivelyFreeResources(
bool aggressively_free_resources) override {}
void ClearCache() override;
// Decode the given image and store it in the cache. This is only called by an
// image decode task from a worker thread.
void DecodeImage(const ImageKey& key,
const DrawImage& image,
DecodeTaskType task_type);
void RemovePendingTask(const ImageKey& key, DecodeTaskType task_type);
// MemoryDumpProvider overrides.
bool OnMemoryDump(const base::trace_event::MemoryDumpArgs& args,
base::trace_event::ProcessMemoryDump* pmd) override;
size_t GetNumCacheEntriesForTesting() const { return decoded_images_.size(); }
// DecodedImage is a convenience storage for discardable memory. It can also
// construct an image out of SkImageInfo and stored discardable memory.
class DecodedImage {
DecodedImage(const SkImageInfo& info,
std::unique_ptr<base::DiscardableMemory> memory,
const SkSize& src_rect_offset,
uint64_t tracing_id);
const sk_sp<SkImage>& image() const {
return image_;
const SkSize& src_rect_offset() const { return src_rect_offset_; }
bool is_locked() const { return locked_; }
bool Lock();
void Unlock();
const base::DiscardableMemory* memory() const { return memory_.get(); }
// An ID which uniquely identifies this DecodedImage within the image decode
// cache. Used in memory tracing.
uint64_t tracing_id() const { return tracing_id_; }
// Mark this image as being used in either a draw or as a source for a
// scaled image. Either case represents this decode as being valuable and
// not wasted.
void mark_used() { usage_stats_.used = true; }
struct UsageStats {
// We can only create a decoded image in a locked state, so the initial
// lock count is 1.
int lock_count = 1;
bool used = false;
bool last_lock_failed = false;
bool first_lock_wasted = false;
bool locked_;
SkImageInfo image_info_;
std::unique_ptr<base::DiscardableMemory> memory_;
sk_sp<SkImage> image_;
SkSize src_rect_offset_;
uint64_t tracing_id_;
UsageStats usage_stats_;
// MemoryBudget is a convenience class for memory bookkeeping and ensuring
// that we don't go over the limit when pre-decoding.
class MemoryBudget {
explicit MemoryBudget(size_t limit_bytes);
size_t AvailableMemoryBytes() const;
void AddUsage(size_t usage);
void SubtractUsage(size_t usage);
void ResetUsage();
size_t total_limit_bytes() const { return limit_bytes_; }
size_t GetCurrentUsageSafe() const;
size_t limit_bytes_;
base::CheckedNumeric<size_t> current_usage_bytes_;
using ImageMRUCache = base::
HashingMRUCache<ImageKey, std::unique_ptr<DecodedImage>, ImageKeyHash>;
// Looks for the key in the cache and returns true if it was found and was
// successfully locked (or if it was already locked). Note that if this
// function returns true, then a ref count is increased for the image.
bool LockDecodedImageIfPossibleAndRef(const ImageKey& key);
// Actually decode the image. Note that this function can (and should) be
// called with no lock acquired, since it can do a lot of work. Note that it
// can also return nullptr to indicate the decode failed.
std::unique_ptr<DecodedImage> DecodeImageInternal(
const ImageKey& key,
const DrawImage& draw_image);
// Get the decoded draw image for the given key and draw_image. Note that this
// function has to be called with no lock acquired, since it will acquire its
// own locks and might call DecodeImageInternal above. Also note that this
// function will use the provided key, even if
// ImageKey::FromDrawImage(draw_image) would return a different key.
// Note that when used internally, we still require that
// DrawWithImageFinished() is called afterwards.
DecodedDrawImage GetDecodedImageForDrawInternal(const ImageKey& key,
const DrawImage& draw_image);
// GetOriginalSizeImageDecode is called by DecodeImageInternal when the
// quality does not scale the image. Like DecodeImageInternal, it should be
// called with no lock acquired and it returns nullptr if the decoding failed.
std::unique_ptr<DecodedImage> GetOriginalSizeImageDecode(
const ImageKey& key,
sk_sp<const SkImage> image);
// GetSubrectImageDecode is similar to GetOriginalSizeImageDecode in that no
// scale is performed on the image. However, we extract a subrect (copy it
// out) and only return this subrect in order to cache a smaller amount of
// memory. Note that this uses GetOriginalSizeImageDecode to get the initial
// data, which ensures that we cache an unlocked version of the original image
// in case we need to extract multiple subrects (as would be the case in an
// atlas).
std::unique_ptr<DecodedImage> GetSubrectImageDecode(
const ImageKey& key,
sk_sp<const SkImage> image);
// GetScaledImageDecode is called by DecodeImageInternal when the quality
// requires the image be scaled. Like DecodeImageInternal, it should be
// called with no lock acquired and it returns nullptr if the decoding or
// scaling failed.
std::unique_ptr<DecodedImage> GetScaledImageDecode(
const ImageKey& key,
sk_sp<const SkImage> image);
void RefImage(const ImageKey& key);
void RefAtRasterImage(const ImageKey& key);
void UnrefAtRasterImage(const ImageKey& key);
// Helper function which dumps all images in a specific ImageMRUCache.
void DumpImageMemoryForCache(const ImageMRUCache& cache,
const char* cache_name,
base::trace_event::ProcessMemoryDump* pmd) const;
// Removes unlocked decoded images until the number of decoded images is
// reduced within the given limit.
void ReduceCacheUsageUntilWithinLimit(size_t limit);
// Overriden from base::MemoryCoordinatorClient.
void OnMemoryStateChange(base::MemoryState state) override;
void OnPurgeMemory() override;
// Helper method to get the different tasks. Note that this should be used as
// if it was public (ie, all of the locks need to be properly acquired).
bool GetTaskForImageAndRefInternal(const DrawImage& image,
const TracingInfo& tracing_info,
DecodeTaskType type,
scoped_refptr<TileTask>* task);
std::unordered_map<ImageKey, scoped_refptr<TileTask>, ImageKeyHash>
std::unordered_map<ImageKey, scoped_refptr<TileTask>, ImageKeyHash>
// The members below this comment can only be accessed if the lock is held to
// ensure that they are safe to access on multiple threads.
base::Lock lock_;
// Decoded images and ref counts (predecode path).
ImageMRUCache decoded_images_;
std::unordered_map<ImageKey, int, ImageKeyHash> decoded_images_ref_counts_;
// Decoded image and ref counts (at-raster decode path).
ImageMRUCache at_raster_decoded_images_;
std::unordered_map<ImageKey, int, ImageKeyHash>
MemoryBudget locked_images_budget_;
ResourceFormat format_;
size_t max_items_in_cache_;
// Used to uniquely identify DecodedImages for memory traces.
base::AtomicSequenceNumber next_tracing_id_;
} // namespace cc