Commit af0093b3 authored by ericrk's avatar ericrk Committed by Commit bot

Add display-resolution caching to GPU IDC

In order to save memory and upload time, images which are
going to be drawn at less than their native resolution may
be uploaded at a smaller scale. This change adds additional
caching and upload logic to allow this.
See https://goo.gl/0zCd9Z for a complete description.

BUG=623688
CQ_INCLUDE_TRYBOTS=tryserver.blink:linux_blink_rel;tryserver.blink:mac_blink_rel;tryserver.blink:win_blink_rel

Committed: https://crrev.com/bc8acf2632042e5e93982088d5b7dcb7f1c0d168
Review-Url: https://codereview.chromium.org/2042133002
Cr-Original-Commit-Position: refs/heads/master@{#402377}
Cr-Commit-Position: refs/heads/master@{#402694}
parent aca9e00e
...@@ -34,8 +34,12 @@ class CC_EXPORT DrawImage { ...@@ -34,8 +34,12 @@ class CC_EXPORT DrawImage {
const SkMatrix& matrix() const { return matrix_; } const SkMatrix& matrix() const { return matrix_; }
DrawImage ApplyScale(float scale) const { DrawImage ApplyScale(float scale) const {
return ApplyScale(SkSize::Make(scale, scale));
}
DrawImage ApplyScale(const SkSize& scale) const {
SkMatrix scaled_matrix = matrix_; SkMatrix scaled_matrix = matrix_;
scaled_matrix.postScale(scale, scale); scaled_matrix.postScale(scale.width(), scale.height());
return DrawImage(image_, src_rect_, filter_quality_, scaled_matrix); return DrawImage(image_, src_rect_, filter_quality_, scaled_matrix);
} }
......
...@@ -67,6 +67,12 @@ namespace cc { ...@@ -67,6 +67,12 @@ namespace cc {
EXPECT_FLOAT_EQ((expected).height(), (actual).height()); \ EXPECT_FLOAT_EQ((expected).height(), (actual).height()); \
} while (false) } while (false)
#define EXPECT_SIZE_EQ(expected, actual) \
do { \
EXPECT_EQ((expected).width(), (actual).width()); \
EXPECT_EQ((expected).height(), (actual).height()); \
} while (false)
// This is a function rather than a macro because when this is included as a // This is a function rather than a macro because when this is included as a
// macro in bulk, it causes a significant slow-down in compilation time. This // macro in bulk, it causes a significant slow-down in compilation time. This
// problem exists with both gcc and clang, and bugs have been filed at // problem exists with both gcc and clang, and bugs have been filed at
......
This diff is collapsed.
...@@ -24,6 +24,8 @@ namespace cc { ...@@ -24,6 +24,8 @@ namespace cc {
class ContextProvider; class ContextProvider;
// OVERVIEW:
//
// GpuImageDecodeController handles the decode and upload of images that will // GpuImageDecodeController handles the decode and upload of images that will
// be used by Skia's GPU raster path. It also maintains a cache of these // be used by Skia's GPU raster path. It also maintains a cache of these
// decoded/uploaded images for later re-use. // decoded/uploaded images for later re-use.
...@@ -49,6 +51,50 @@ class ContextProvider; ...@@ -49,6 +51,50 @@ class ContextProvider;
// used in raster. Cache entries for at-raster tasks are marked as such, which // used in raster. Cache entries for at-raster tasks are marked as such, which
// prevents future tasks from taking a dependency on them and extending their // prevents future tasks from taking a dependency on them and extending their
// lifetime longer than is necessary. // lifetime longer than is necessary.
//
// RASTER-SCALE CACHING:
//
// In order to save memory, images which are going to be scaled may be uploaded
// at lower than original resolution. In these cases, we may later need to
// re-upload the image at a higher resolution. To handle multiple images of
// different scales being in use at the same time, we have a two-part caching
// system.
//
// The first cache, |persistent_cache_|, stores one ImageData per image id.
// These ImageDatas are not necessarily associated with a given DrawImage, and
// are saved (persisted) even when their ref-count reaches zero (assuming they
// fit in the current memory budget). This allows for future re-use of image
// resources.
//
// The second cache, |in_use_cache_|, stores one image data per DrawImage -
// this may be the same ImageData that is in the persistent_cache_. These
// cache entries are more transient and are deleted as soon as all refs to the
// given DrawImage are released (the image is no longer in-use).
//
// For examples of raster-scale caching, see https://goo.gl/0zCd9Z
//
// REF COUNTING:
//
// In dealing with the two caches in GpuImageDecodeController, there are three
// ref-counting concepts in use:
// 1) ImageData upload/decode ref-counts.
// These ref-counts represent the overall number of references to the
// upload or decode portion of an ImageData. These ref-counts control
// both whether the upload/decode data can be freed, as well as whether an
// ImageData can be removed from the |persistent_cache_|. ImageDatas are
// only removed from the |persistent_cache_| if their upload/decode
// ref-counts are zero or if they are orphaned and replaced by a new entry.
// 2) InUseCacheEntry ref-counts.
// These ref-counts represent the number of references to an
// InUseCacheEntry from a specific DrawImage. When the InUseCacheEntry's
// ref-count reaches 0 it will be deleted.
// 3) scoped_refptr ref-counts.
// Because both the persistent_cache_ and the in_use_cache_ point at the
// same ImageDatas (and may need to keep these ImageDatas alive independent
// of each other), they hold ImageDatas by scoped_refptr. The scoped_refptr
// keeps an ImageData alive while it is present in either the
// |persistent_cache_| or |in_use_cache_|.
//
class CC_EXPORT GpuImageDecodeController class CC_EXPORT GpuImageDecodeController
: public ImageDecodeController, : public ImageDecodeController,
public base::trace_event::MemoryDumpProvider { public base::trace_event::MemoryDumpProvider {
...@@ -93,6 +139,7 @@ class CC_EXPORT GpuImageDecodeController ...@@ -93,6 +139,7 @@ class CC_EXPORT GpuImageDecodeController
cached_bytes_limit_ = limit; cached_bytes_limit_ = limit;
} }
size_t GetBytesUsedForTesting() const { return bytes_used_; } size_t GetBytesUsedForTesting() const { return bytes_used_; }
size_t GetDrawImageSizeForTesting(const DrawImage& image);
void SetImageDecodingFailedForTesting(const DrawImage& image); void SetImageDecodingFailedForTesting(const DrawImage& image);
bool DiscardableIsLockedForTesting(const DrawImage& image); bool DiscardableIsLockedForTesting(const DrawImage& image);
...@@ -110,13 +157,13 @@ class CC_EXPORT GpuImageDecodeController ...@@ -110,13 +157,13 @@ class CC_EXPORT GpuImageDecodeController
void SetLockedData(std::unique_ptr<base::DiscardableMemory> data); void SetLockedData(std::unique_ptr<base::DiscardableMemory> data);
void ResetData(); void ResetData();
base::DiscardableMemory* data() const { return data_.get(); } base::DiscardableMemory* data() const { return data_.get(); }
void mark_used() { usage_stats_.used = true; } void mark_used() { usage_stats_.used = true; }
// May be null if image not yet decoded.
uint32_t ref_count = 0; uint32_t ref_count = 0;
// Set to true if the image was corrupt and could not be decoded. // Set to true if the image was corrupt and could not be decoded.
bool decode_failure = false; bool decode_failure = false;
// If non-null, this is the pending decode task for this image.
scoped_refptr<TileTask> task;
private: private:
struct UsageStats { struct UsageStats {
...@@ -149,6 +196,8 @@ class CC_EXPORT GpuImageDecodeController ...@@ -149,6 +196,8 @@ class CC_EXPORT GpuImageDecodeController
// True if the image is counting against our memory limits. // True if the image is counting against our memory limits.
bool budgeted = false; bool budgeted = false;
uint32_t ref_count = 0; uint32_t ref_count = 0;
// If non-null, this is the pending upload task for this image.
scoped_refptr<TileTask> task;
private: private:
struct UsageStats { struct UsageStats {
...@@ -164,20 +213,46 @@ class CC_EXPORT GpuImageDecodeController ...@@ -164,20 +213,46 @@ class CC_EXPORT GpuImageDecodeController
UsageStats usage_stats_; UsageStats usage_stats_;
}; };
struct ImageData { struct ImageData : public base::RefCounted<ImageData> {
ImageData(DecodedDataMode mode, size_t size); ImageData(DecodedDataMode mode,
~ImageData(); size_t size,
int upload_scale_mip_level,
SkFilterQuality upload_scale_filter_quality);
const DecodedDataMode mode; const DecodedDataMode mode;
const size_t size; const size_t size;
bool is_at_raster = false; bool is_at_raster = false;
// Variables used to identify/track multiple scale levels of a single image.
int upload_scale_mip_level = 0;
SkFilterQuality upload_scale_filter_quality = kNone_SkFilterQuality;
// If true, this image is no longer in our |persistent_cache_| and will be
// deleted as soon as its ref count reaches zero.
bool is_orphaned = false;
DecodedImageData decode; DecodedImageData decode;
UploadedImageData upload; UploadedImageData upload;
private:
friend class base::RefCounted<ImageData>;
~ImageData();
}; };
using ImageDataMRUCache = // A ref-count and ImageData, used to associate the ImageData with a specific
base::MRUCache<uint32_t, std::unique_ptr<ImageData>>; // DrawImage in the |in_use_cache_|.
struct InUseCacheEntry {
explicit InUseCacheEntry(scoped_refptr<ImageData> image_data);
InUseCacheEntry(const InUseCacheEntry& other);
InUseCacheEntry(InUseCacheEntry&& other);
~InUseCacheEntry();
uint32_t ref_count = 0;
scoped_refptr<ImageData> image_data;
};
// Uniquely identifies (without collisions) a specific DrawImage for use in
// the |in_use_cache_|.
using InUseCacheKey = uint64_t;
// All private functions should only be called while holding |lock_|. Some // All private functions should only be called while holding |lock_|. Some
// functions also require the |context_| lock. These are indicated by // functions also require the |context_| lock. These are indicated by
...@@ -193,7 +268,10 @@ class CC_EXPORT GpuImageDecodeController ...@@ -193,7 +268,10 @@ class CC_EXPORT GpuImageDecodeController
void UnrefImageDecode(const DrawImage& draw_image); void UnrefImageDecode(const DrawImage& draw_image);
void RefImage(const DrawImage& draw_image); void RefImage(const DrawImage& draw_image);
void UnrefImageInternal(const DrawImage& draw_image); void UnrefImageInternal(const DrawImage& draw_image);
void RefCountChanged(ImageData* image_data);
// Called any time the ownership of an object changed. This includes changes
// to ref-count or to orphaned status.
void OwnershipChanged(ImageData* image_data);
// Ensures that the cache can hold an element of |required_size|, freeing // Ensures that the cache can hold an element of |required_size|, freeing
// unreferenced cache entries if necessary to make room. // unreferenced cache entries if necessary to make room.
...@@ -204,9 +282,19 @@ class CC_EXPORT GpuImageDecodeController ...@@ -204,9 +282,19 @@ class CC_EXPORT GpuImageDecodeController
void DecodeImageIfNecessary(const DrawImage& draw_image, void DecodeImageIfNecessary(const DrawImage& draw_image,
ImageData* image_data); ImageData* image_data);
std::unique_ptr<GpuImageDecodeController::ImageData> CreateImageData( scoped_refptr<GpuImageDecodeController::ImageData> CreateImageData(
const DrawImage& image); const DrawImage& image);
SkImageInfo CreateImageInfoForDrawImage(const DrawImage& draw_image) const; SkImageInfo CreateImageInfoForDrawImage(const DrawImage& draw_image,
int upload_scale_mip_level) const;
// Finds the ImageData that should be used for the given DrawImage. Looks
// first in the |in_use_cache_|, and then in the |persistent_cache_|.
ImageData* GetImageDataForDrawImage(const DrawImage& image);
// Returns true if the given ImageData can be used to draw the specified
// DrawImage.
bool IsCompatible(const ImageData* image_data,
const DrawImage& draw_image) const;
// The following two functions also require the |context_| lock to be held. // The following two functions also require the |context_| lock to be held.
void UploadImageIfNecessary(const DrawImage& draw_image, void UploadImageIfNecessary(const DrawImage& draw_image,
...@@ -220,12 +308,15 @@ class CC_EXPORT GpuImageDecodeController ...@@ -220,12 +308,15 @@ class CC_EXPORT GpuImageDecodeController
// All members below this point must only be accessed while holding |lock_|. // All members below this point must only be accessed while holding |lock_|.
base::Lock lock_; base::Lock lock_;
std::unordered_map<uint32_t, scoped_refptr<TileTask>> // |persistent_cache_| represents the long-lived cache, keeping a certain
pending_image_upload_tasks_; // budget of ImageDatas alive even when their ref count reaches zero.
std::unordered_map<uint32_t, scoped_refptr<TileTask>> using PersistentCache = base::MRUCache<uint32_t, scoped_refptr<ImageData>>;
pending_image_decode_tasks_; PersistentCache persistent_cache_;
ImageDataMRUCache image_data_; // |in_use_cache_| represents the in-use (short-lived) cache. Entries are
// cleaned up as soon as their ref count reaches zero.
using InUseCache = std::unordered_map<InUseCacheKey, InUseCacheEntry>;
InUseCache in_use_cache_;
size_t cached_items_limit_; size_t cached_items_limit_;
size_t cached_bytes_limit_; size_t cached_bytes_limit_;
......
...@@ -55,14 +55,22 @@ SkSize MipMapUtil::GetScaleAdjustmentForLevel(const gfx::Size& src_size, ...@@ -55,14 +55,22 @@ SkSize MipMapUtil::GetScaleAdjustmentForLevel(const gfx::Size& src_size,
if (src_size.width() == 0 || src_size.height() == 0 || mip_level == -1) if (src_size.width() == 0 || src_size.height() == 0 || mip_level == -1)
return SkSize::Make(-1, -1); return SkSize::Make(-1, -1);
gfx::Size target_size(ScaleAxisToMipLevel(src_size.width(), mip_level), gfx::Size target_size = GetSizeForLevel(src_size, mip_level);
ScaleAxisToMipLevel(src_size.height(), mip_level));
return SkSize::Make( return SkSize::Make(
static_cast<float>(target_size.width()) / src_size.width(), static_cast<float>(target_size.width()) / src_size.width(),
static_cast<float>(target_size.height()) / src_size.height()); static_cast<float>(target_size.height()) / src_size.height());
} }
gfx::Size MipMapUtil::GetSizeForLevel(const gfx::Size& src_size,
int mip_level) {
if (src_size.width() == 0 || src_size.height() == 0 || mip_level == -1)
return gfx::Size(-1, -1);
return gfx::Size(ScaleAxisToMipLevel(src_size.width(), mip_level),
ScaleAxisToMipLevel(src_size.height(), mip_level));
}
SkSize MipMapUtil::GetScaleAdjustmentForSize(const gfx::Size& src_size, SkSize MipMapUtil::GetScaleAdjustmentForSize(const gfx::Size& src_size,
const gfx::Size& target_size) { const gfx::Size& target_size) {
int target_mip_level = GetLevelForSize(src_size, target_size); int target_mip_level = GetLevelForSize(src_size, target_size);
......
...@@ -27,6 +27,11 @@ class CC_EXPORT MipMapUtil { ...@@ -27,6 +27,11 @@ class CC_EXPORT MipMapUtil {
static SkSize GetScaleAdjustmentForLevel(const gfx::Size& src_size, static SkSize GetScaleAdjustmentForLevel(const gfx::Size& src_size,
int mip_level); int mip_level);
// Determine the size of the given |mip_level|. Returns (-1, -1) if
// |src_size| is invalid (width/height <= 0) or if mip-level is invalid (==
// -1).
static gfx::Size GetSizeForLevel(const gfx::Size& src_size, int mip_level);
// Determines the scale factor for the smallest mip level that is larger than // Determines the scale factor for the smallest mip level that is larger than
// |target_size|. Returns (-1, -1) if |src_size| or |target_size| is invalid // |target_size|. Returns (-1, -1) if |src_size| or |target_size| is invalid
// (width/height <= 0). // (width/height <= 0).
......
...@@ -20,6 +20,8 @@ TEST(MipMapUtilTest, Basic) { ...@@ -20,6 +20,8 @@ TEST(MipMapUtilTest, Basic) {
EXPECT_EQ(target_level, MipMapUtil::GetLevelForSize(src_size, target_size)); EXPECT_EQ(target_level, MipMapUtil::GetLevelForSize(src_size, target_size));
EXPECT_FLOAT_SIZE_EQ(expected_scale, MipMapUtil::GetScaleAdjustmentForLevel( EXPECT_FLOAT_SIZE_EQ(expected_scale, MipMapUtil::GetScaleAdjustmentForLevel(
src_size, target_level)); src_size, target_level));
EXPECT_SIZE_EQ(target_size,
MipMapUtil::GetSizeForLevel(src_size, target_level));
EXPECT_FLOAT_SIZE_EQ(expected_scale, MipMapUtil::GetScaleAdjustmentForSize( EXPECT_FLOAT_SIZE_EQ(expected_scale, MipMapUtil::GetScaleAdjustmentForSize(
src_size, target_size)); src_size, target_size));
} }
...@@ -34,6 +36,8 @@ TEST(MipMapUtilTest, NoScale) { ...@@ -34,6 +36,8 @@ TEST(MipMapUtilTest, NoScale) {
EXPECT_EQ(target_level, MipMapUtil::GetLevelForSize(src_size, target_size)); EXPECT_EQ(target_level, MipMapUtil::GetLevelForSize(src_size, target_size));
EXPECT_FLOAT_SIZE_EQ(expected_scale, MipMapUtil::GetScaleAdjustmentForLevel( EXPECT_FLOAT_SIZE_EQ(expected_scale, MipMapUtil::GetScaleAdjustmentForLevel(
src_size, target_level)); src_size, target_level));
EXPECT_SIZE_EQ(target_size,
MipMapUtil::GetSizeForLevel(src_size, target_level));
EXPECT_FLOAT_SIZE_EQ(expected_scale, MipMapUtil::GetScaleAdjustmentForSize( EXPECT_FLOAT_SIZE_EQ(expected_scale, MipMapUtil::GetScaleAdjustmentForSize(
src_size, target_size)); src_size, target_size));
} }
...@@ -48,6 +52,7 @@ TEST(MipMapUtilTest, Upscale) { ...@@ -48,6 +52,7 @@ TEST(MipMapUtilTest, Upscale) {
EXPECT_EQ(result_level, MipMapUtil::GetLevelForSize(src_size, target_size)); EXPECT_EQ(result_level, MipMapUtil::GetLevelForSize(src_size, target_size));
EXPECT_FLOAT_SIZE_EQ(result_size, MipMapUtil::GetScaleAdjustmentForSize( EXPECT_FLOAT_SIZE_EQ(result_size, MipMapUtil::GetScaleAdjustmentForSize(
src_size, target_size)); src_size, target_size));
EXPECT_SIZE_EQ(src_size, MipMapUtil::GetSizeForLevel(src_size, result_level));
} }
// Ensures that the maximum mip level GetLevelForSize will ever return is 30. // Ensures that the maximum mip level GetLevelForSize will ever return is 30.
...@@ -71,6 +76,8 @@ TEST(MipMapUtilTest, NonSquare) { ...@@ -71,6 +76,8 @@ TEST(MipMapUtilTest, NonSquare) {
EXPECT_EQ(target_level, MipMapUtil::GetLevelForSize(src_size, target_size)); EXPECT_EQ(target_level, MipMapUtil::GetLevelForSize(src_size, target_size));
EXPECT_FLOAT_SIZE_EQ(expected_scale, MipMapUtil::GetScaleAdjustmentForLevel( EXPECT_FLOAT_SIZE_EQ(expected_scale, MipMapUtil::GetScaleAdjustmentForLevel(
src_size, target_level)); src_size, target_level));
EXPECT_SIZE_EQ(target_size,
MipMapUtil::GetSizeForLevel(src_size, target_level));
EXPECT_FLOAT_SIZE_EQ(expected_scale, MipMapUtil::GetScaleAdjustmentForSize( EXPECT_FLOAT_SIZE_EQ(expected_scale, MipMapUtil::GetScaleAdjustmentForSize(
src_size, target_size)); src_size, target_size));
} }
...@@ -97,6 +104,10 @@ TEST(MipMapUtilTest, Rounding) { ...@@ -97,6 +104,10 @@ TEST(MipMapUtilTest, Rounding) {
EXPECT_FLOAT_SIZE_EQ( EXPECT_FLOAT_SIZE_EQ(
expected_scale_smaller, expected_scale_smaller,
MipMapUtil::GetScaleAdjustmentForLevel(src_size, target_level_smaller)); MipMapUtil::GetScaleAdjustmentForLevel(src_size, target_level_smaller));
EXPECT_SIZE_EQ(src_size,
MipMapUtil::GetSizeForLevel(src_size, target_level_larger));
EXPECT_SIZE_EQ(target_size_smaller,
MipMapUtil::GetSizeForLevel(src_size, target_level_smaller));
EXPECT_FLOAT_SIZE_EQ( EXPECT_FLOAT_SIZE_EQ(
expected_scale_larger, expected_scale_larger,
MipMapUtil::GetScaleAdjustmentForSize(src_size, target_size_larger)); MipMapUtil::GetScaleAdjustmentForSize(src_size, target_size_larger));
...@@ -109,7 +120,8 @@ TEST(MipMapUtilTest, Rounding) { ...@@ -109,7 +120,8 @@ TEST(MipMapUtilTest, Rounding) {
TEST(MipMapUtilTest, Invalid) { TEST(MipMapUtilTest, Invalid) {
const gfx::Size valid_size(1024, 1024); const gfx::Size valid_size(1024, 1024);
const gfx::Size invalid_size(0, 1024); const gfx::Size invalid_size(0, 1024);
const SkSize invalid_result_size = SkSize::Make(-1, -1); const gfx::Size invalid_result_size(-1, -1);
const SkSize invalid_float_result_size = SkSize::Make(-1, -1);
const int invalid_result_level = -1; const int invalid_result_level = -1;
EXPECT_EQ(invalid_result_level, EXPECT_EQ(invalid_result_level,
...@@ -117,15 +129,19 @@ TEST(MipMapUtilTest, Invalid) { ...@@ -117,15 +129,19 @@ TEST(MipMapUtilTest, Invalid) {
EXPECT_EQ(invalid_result_level, EXPECT_EQ(invalid_result_level,
MipMapUtil::GetLevelForSize(invalid_size, valid_size)); MipMapUtil::GetLevelForSize(invalid_size, valid_size));
EXPECT_FLOAT_SIZE_EQ( EXPECT_FLOAT_SIZE_EQ(
invalid_result_size, invalid_float_result_size,
MipMapUtil::GetScaleAdjustmentForSize(valid_size, invalid_size)); MipMapUtil::GetScaleAdjustmentForSize(valid_size, invalid_size));
EXPECT_FLOAT_SIZE_EQ( EXPECT_FLOAT_SIZE_EQ(
invalid_result_size, invalid_float_result_size,
MipMapUtil::GetScaleAdjustmentForSize(invalid_size, valid_size)); MipMapUtil::GetScaleAdjustmentForSize(invalid_size, valid_size));
EXPECT_FLOAT_SIZE_EQ(invalid_result_size, EXPECT_SIZE_EQ(invalid_result_size,
MipMapUtil::GetSizeForLevel(valid_size, invalid_result_level));
EXPECT_SIZE_EQ(invalid_result_size,
MipMapUtil::GetSizeForLevel(invalid_size, 0));
EXPECT_FLOAT_SIZE_EQ(invalid_float_result_size,
MipMapUtil::GetScaleAdjustmentForLevel(invalid_size, 0)); MipMapUtil::GetScaleAdjustmentForLevel(invalid_size, 0));
EXPECT_FLOAT_SIZE_EQ( EXPECT_FLOAT_SIZE_EQ(
invalid_result_size, invalid_float_result_size,
MipMapUtil::GetScaleAdjustmentForLevel(valid_size, invalid_result_level)); MipMapUtil::GetScaleAdjustmentForLevel(valid_size, invalid_result_level));
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment