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

Unified Diff: cc/tiles/gpu_image_decode_controller.h

Issue 2042133002: Add display-resolution caching to GPU IDC (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@drt
Patch Set: Add display-resolution caching to GPU IDC Created 4 years, 6 months 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
Index: cc/tiles/gpu_image_decode_controller.h
diff --git a/cc/tiles/gpu_image_decode_controller.h b/cc/tiles/gpu_image_decode_controller.h
index 5ced06527577704a4c5a71566ef4329b17890ecb..ba89cd4ac5caad14bfea31825cd7146e19a140a7 100644
--- a/cc/tiles/gpu_image_decode_controller.h
+++ b/cc/tiles/gpu_image_decode_controller.h
@@ -24,6 +24,8 @@ namespace cc {
class ContextProvider;
+// OVERVIEW:
+//
// 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
// decoded/uploaded images for later re-use.
@@ -49,6 +51,157 @@ class ContextProvider;
// 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
// 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).
+//
+// Consider the following two cases, which show the cache-usage patterns.
+//
+// Case 1: Larger (1x) scale used before smaller (0.5x) scale.
+//
+// Step 1: Create/Find Tasks and ImageData for 1x Scale (GetTaskForImageAndRef)
+// 1a: No existing cache data exists, 1x ImageData created
+// 1b: 1x ImageData cached in |persistent_cache_| with image's unique ID as
+// key.
+// 1c: 1x ImageData cached in |in_use_cache_| with 1x Scale key.
+// 1d: 1x Decode task created, cached in 1x ImageData.
+// 1e: 1x Upload task created, cached in 1x ImageData.
+//
+// ╔═══════════════════╗ ╔══════════════════════╗
+// ║ Persistent Cache ║ ║ In-Use Cache ║
+// ╟───────┬───────────╢ ╟───────────┬──────────╢
+// ║ image │ 1x ║ ║ 1x Scale │ 1x ║
+// ║ ID Key│ ImageData ║ ║ Key │ ImageData║
+// ╚═══════╧═══════════╝ ╚═══════════╧══════════╝
+//
+// Step 2: Create/Find Tasks and ImageData for 0.5x Scale
+// 2a: Find 1x ImageData in |persistent_cache_|. 0.5x scale can use this.
+// 2b: 1x ImageData cached in |in_use_cache_| with 0.5x scale key.
+//
+// ╔═══════════════════╗ ╔══════════════════════╗
+// ║ Persistent Cache ║ ║ In-Use Cache ║
+// ╟───────┬───────────╢ ╟───────────┬──────────╢
+// ║ image │ 1x ║ ║ 1x Scale │ 1x ║
+// ║ ID Key│ ImageData ║ ║ Key │ ImageData║
+// ╚═══════╧═══════════╝ ╟───────────┼──────────╢
+// ║ 0.5x Scale│ 1x ║
+// ║ Key │ ImageData║
+// ╚═══════════╧══════════╝
+//
+// Step 4: Draw 1x Scale
+// 1x scale looked up in |in_use_cache_| via 1x key.
+//
+// Step 5: Draw 0.5x Scale
+// 1x scale looked up in |in_use_cache_| via 0.5x key.
+//
+// Step 6: Finished, refs released
+// Tasks are deleted from ImageData.
+// Entries are deleted from |in_use_cache_|.
+// 1x scale preserved in |persistent_cache_|.
+//
+// ╔═══════════════════╗ ╔════════════════════╗
+// ║ Persistent Cache ║ ║ In-Use Cache ║
+// ╟───────┬───────────╢ ╚════════════════════╝
+// ║ image │ 1x ║
+// ║ ID Key│ ImageData ║
+// ╚═══════╧═══════════╝
+//
+// Case 2: Smaller (0.5x) scale used before larger (1x) scale.
+//
+// Step 1: Create/Find Tasks and ImageData for 0.5x Scale
+// (GetTaskForImageAndRef)
+// 1a: No existing cache data exists, 0.5x ImageData created
+// 1b: 0.5x ImageData cached in |persistent_cache_| with image's unique ID
+// as key.
+// 1c: 0.5x ImageData cached in |in_use_cache_| with 0.5x Scale key.
+// 1d: 0.5x Decode task created, cached in 0.5x ImageData.
+// 1e: 0.5x Upload task created, cached in 0.5x ImageData.
+//
+// ╔═══════════════════╗ ╔══════════════════════╗
+// ║ Persistent Cache ║ ║ In-Use Cache ║
+// ╟───────┬───────────╢ ╟───────────┬──────────╢
+// ║ image │ 0.5 ║ ║ 0.5x Scale│ 0.5x ║
+// ║ ID Key│ ImageData ║ ║ Key │ ImageData║
+// ╚═══════╧═══════════╝ ╚═══════════╧══════════╝
+//
+// Step 2: Create/Find Tasks and ImageData for 1x Scale
+// 2a: Find 0.5x ImageData in |persistent_cache_|. 1x scale can't use this.
+// 2b: The 0.5x scale in the persistent cache is about to be replaced by the
+// 1x scale. Remove it from the persistent_cache_. Note that the 0.5x
+// scale is still kept alive in the |in_use_cache_|.
+// 2c: 1x ImageData created
+// 2d: 1x ImageData cached in |persistent_cache_| with image's unique ID as
+// key.
+// 2e: 1x ImageData cached in |in_use_cache_| with 1x Scale key.
+// 2f: 1x Decode task created, cached in 1x ImageData.
+// 2g: 1x Upload task created, cached in 1x ImageData.
+//
+// ╔═══════════════════╗ ╔══════════════════════╗
+// ║ Persistent Cache ║ ║ In-Use Cache ║
+// ╟───────┬───────────╢ ╟───────────┬──────────╢
+// ║ image │ 1x ║ ║ 1x Scale │ 1x ║
+// ║ ID Key│ ImageData ║ ║ Key │ ImageData║
+// ╚═══════╧═══════════╝ ╟───────────┼──────────╢
+// ║ 0.5x Scale│ 0.5x ║
+// ║ Key │ ImageData║
+// ╚═══════════╧══════════╝
+//
+// Step 4: Draw 0.5x Scale
+// 0.5x scale looked up in |in_use_cache_| via 0.5x key.
+//
+// Step 5: Draw 1x Scale
+// 1x scale looked up in |in_use_cache_| via 1x key.
+//
+// Step 6: Finished, refs released
+// Tasks are deleted from ImageDatas.
+// Entries are deleted from |in_use_cache_|.
+// 1x scale preserved in |persistent_cache_|.
+//
+// ╔═══════════════════╗ ╔════════════════════╗
+// ║ Persistent Cache ║ ║ In-Use Cache ║
+// ╟───────┬───────────╢ ╚════════════════════╝
+// ║ image │ 1x ║
+// ║ ID Key│ ImageData ║
+// ╚═══════╧═══════════╝
+//
+// 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
: public ImageDecodeController,
public base::trace_event::MemoryDumpProvider {
@@ -93,6 +246,7 @@ class CC_EXPORT GpuImageDecodeController
cached_bytes_limit_ = limit;
}
size_t GetBytesUsedForTesting() const { return bytes_used_; }
+ size_t GetDrawImageSizeForTesting(const DrawImage& image);
void SetImageDecodingFailedForTesting(const DrawImage& image);
bool DiscardableIsLockedForTesting(const DrawImage& image);
@@ -110,13 +264,13 @@ class CC_EXPORT GpuImageDecodeController
void SetLockedData(std::unique_ptr<base::DiscardableMemory> data);
void ResetData();
base::DiscardableMemory* data() const { return data_.get(); }
-
void mark_used() { usage_stats_.used = true; }
- // May be null if image not yet decoded.
uint32_t ref_count = 0;
// Set to true if the image was corrupt and could not be decoded.
bool decode_failure = false;
+ // If non-null, this is the pending decode task for this image.
+ scoped_refptr<TileTask> task;
private:
struct UsageStats {
@@ -149,6 +303,8 @@ class CC_EXPORT GpuImageDecodeController
// True if the image is counting against our memory limits.
bool budgeted = false;
uint32_t ref_count = 0;
+ // If non-null, this is the pending upload task for this image.
+ scoped_refptr<TileTask> task;
private:
struct UsageStats {
@@ -164,20 +320,44 @@ class CC_EXPORT GpuImageDecodeController
UsageStats usage_stats_;
};
- struct ImageData {
- ImageData(DecodedDataMode mode, size_t size);
- ~ImageData();
+ struct ImageData : public base::RefCounted<ImageData> {
+ ImageData(DecodedDataMode mode,
+ size_t size,
+ int upload_scale_mip_level,
+ SkFilterQuality upload_scale_filter_quality);
const DecodedDataMode mode;
const size_t size;
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;
+ bool is_orphaned = false;
vmpstr 2016/06/22 21:33:35 Add a comment here about what orphaned is
ericrk 2016/06/23 18:16:55 Done.
+
DecodedImageData decode;
UploadedImageData upload;
+
+ private:
+ friend class base::RefCounted<ImageData>;
+ ~ImageData();
+ };
+
+ // A ref-count and ImageData, used to associate the ImageData with a specific
+ // 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;
};
- using ImageDataMRUCache =
- base::MRUCache<uint32_t, std::unique_ptr<ImageData>>;
+ // 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
// functions also require the |context_| lock. These are indicated by
@@ -193,7 +373,10 @@ class CC_EXPORT GpuImageDecodeController
void UnrefImageDecode(const DrawImage& draw_image);
void RefImage(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
// unreferenced cache entries if necessary to make room.
@@ -204,9 +387,19 @@ class CC_EXPORT GpuImageDecodeController
void DecodeImageIfNecessary(const DrawImage& draw_image,
ImageData* image_data);
- std::unique_ptr<GpuImageDecodeController::ImageData> CreateImageData(
+ scoped_refptr<GpuImageDecodeController::ImageData> CreateImageData(
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 IsCompatibleWithDrawImage(const ImageData* image_data,
vmpstr 2016/06/22 21:33:35 I think "IsCompatible" is good enough, but maybe t
ericrk 2016/06/23 18:16:55 sure.
+ const DrawImage& draw_image) const;
// The following two functions also require the |context_| lock to be held.
void UploadImageIfNecessary(const DrawImage& draw_image,
@@ -220,12 +413,14 @@ class CC_EXPORT GpuImageDecodeController
// All members below this point must only be accessed while holding |lock_|.
base::Lock lock_;
- std::unordered_map<uint32_t, scoped_refptr<TileTask>>
- pending_image_upload_tasks_;
- std::unordered_map<uint32_t, scoped_refptr<TileTask>>
- pending_image_decode_tasks_;
+ // |persistent_cache_| represents the long-lived cache, keeping a certain
+ // budget of ImageDatas alive even when their ref count reaches zero.
+ using ImageDataMRUCache = base::MRUCache<uint32_t, scoped_refptr<ImageData>>;
+ ImageDataMRUCache 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.
+ std::unordered_map<InUseCacheKey, InUseCacheEntry> in_use_cache_;
size_t cached_items_limit_;
size_t cached_bytes_limit_;

Powered by Google App Engine
This is Rietveld 408576698