blob: baf9594309a5a0f9bc2e41a7f2961cc11d492623 [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/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/base/cc_export.h"
#include "cc/playback/decoded_draw_image.h"
#include "cc/playback/draw_image.h"
#include "cc/resources/resource_format.h"
#include "cc/tiles/image_decode_controller.h"
#include "third_party/skia/include/core/SkRefCnt.h"
namespace cc {
// ImageDecodeControllerKey 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 ImageDecodeControllerKey {
static ImageDecodeControllerKey FromDrawImage(const DrawImage& image);
ImageDecodeControllerKey(const ImageDecodeControllerKey& other);
bool operator==(const ImageDecodeControllerKey& 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_decode_ == other.can_use_original_decode_ &&
(can_use_original_decode_ ||
(src_rect_ == other.src_rect_ &&
target_size_ == other.target_size_ &&
filter_quality_ == other.filter_quality_));
bool operator!=(const ImageDecodeControllerKey& 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_; }
bool can_use_original_decode() const { return can_use_original_decode_; }
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;
ImageDecodeControllerKey(uint32_t image_id,
const gfx::Rect& src_rect,
const gfx::Size& size,
SkFilterQuality filter_quality,
bool can_use_original_decode);
uint32_t image_id_;
gfx::Rect src_rect_;
gfx::Size target_size_;
SkFilterQuality filter_quality_;
bool can_use_original_decode_;
size_t hash_;
// Hash function for the above ImageDecodeControllerKey.
struct ImageDecodeControllerKeyHash {
size_t operator()(const ImageDecodeControllerKey& key) const {
return key.get_hash();
class CC_EXPORT SoftwareImageDecodeController
: public ImageDecodeController,
public base::trace_event::MemoryDumpProvider {
using ImageKey = ImageDecodeControllerKey;
using ImageKeyHash = ImageDecodeControllerKeyHash;
SoftwareImageDecodeController(ResourceFormat format,
size_t locked_memory_limit_bytes);
~SoftwareImageDecodeController() override;
// ImageDecodeController overrides.
bool GetTaskForImageAndRef(const DrawImage& image,
const TracingInfo& tracing_info,
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 {}
// 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);
void RemovePendingTask(const ImageKey& key);
// MemoryDumpProvider overrides.
bool OnMemoryDump(const base::trace_event::MemoryDumpArgs& args,
base::trace_event::ProcessMemoryDump* pmd) override;
// 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
// controller. 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,
// 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);
// GetOriginalImageDecode 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> GetOriginalImageDecode(
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 SanityCheckState(int line, bool lock_acquired);
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;
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_;
// Used to uniquely identify DecodedImages for memory traces.
base::AtomicSequenceNumber next_tracing_id_;
} // namespace cc