| // 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. |
| |
| #ifndef CC_TILES_GPU_IMAGE_DECODE_CONTROLLER_H_ |
| #define CC_TILES_GPU_IMAGE_DECODE_CONTROLLER_H_ |
| |
| #include <memory> |
| #include <unordered_map> |
| #include <vector> |
| |
| #include "base/containers/mru_cache.h" |
| #include "base/memory/discardable_memory.h" |
| #include "base/synchronization/lock.h" |
| #include "base/trace_event/memory_dump_provider.h" |
| #include "cc/base/cc_export.h" |
| #include "cc/resources/resource_format.h" |
| #include "cc/tiles/image_decode_controller.h" |
| #include "third_party/skia/include/core/SkRefCnt.h" |
| |
| class SkImageTextureData; |
| |
| namespace cc { |
| |
| class ContextProvider; |
| |
| // 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. |
| // |
| // Generally, when an image is required for raster, GpuImageDecodeController |
| // creates two tasks, one to decode the image, and one to upload the image to |
| // the GPU. These tasks are completed before the raster task which depends on |
| // the image. We need to seperate decode and upload tasks, as decode can occur |
| // simultaneously on multiple threads, while upload requires the GL context |
| // lock must happen on our non-concurrent raster thread. |
| // |
| // Decoded and Uploaded image data share a single cache entry. Depending on how |
| // far we've progressed, this cache entry may contain CPU-side decoded data, |
| // GPU-side uploaded data, or both. Because CPU-side decoded data is stored in |
| // discardable memory, and is only locked for short periods of time (until the |
| // upload completes), this memory is not counted against our sized cache |
| // limits. Uploaded GPU memory, being non-discardable, always counts against |
| // our limits. |
| // |
| // In cases where the number of images needed exceeds our cache limits, we |
| // operate in an "at-raster" mode. In this mode, there are no decode/upload |
| // tasks, and images are decoded/uploaded as needed, immediately before being |
| // 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. |
| class CC_EXPORT GpuImageDecodeController |
| : public ImageDecodeController, |
| public base::trace_event::MemoryDumpProvider { |
| public: |
| explicit GpuImageDecodeController(ContextProvider* context, |
| ResourceFormat decode_format, |
| size_t max_gpu_image_bytes); |
| ~GpuImageDecodeController() override; |
| |
| // ImageDecodeController overrides. |
| |
| // Finds the existing uploaded image for the provided DrawImage. Creates an |
| // upload task to upload the image if an exsiting image does not exist. |
| bool GetTaskForImageAndRef(const DrawImage& image, |
| const TracingInfo& tracing_info, |
| scoped_refptr<TileTask>* task) override; |
| void UnrefImage(const DrawImage& image) override; |
| DecodedDrawImage GetDecodedImageForDraw(const DrawImage& draw_image) override; |
| void DrawWithImageFinished(const DrawImage& image, |
| const DecodedDrawImage& decoded_image) override; |
| void ReduceCacheUsage() override; |
| void SetShouldAggressivelyFreeResources( |
| bool aggressively_free_resources) override; |
| |
| // MemoryDumpProvider overrides. |
| bool OnMemoryDump(const base::trace_event::MemoryDumpArgs& args, |
| base::trace_event::ProcessMemoryDump* pmd) override; |
| |
| // Called by Decode / Upload tasks. |
| void DecodeImage(const DrawImage& image); |
| void UploadImage(const DrawImage& image); |
| |
| // Called by Decode / Upload tasks when tasks are finished. |
| void OnImageDecodeTaskCompleted(const DrawImage& image); |
| void OnImageUploadTaskCompleted(const DrawImage& image); |
| |
| // For testing only. |
| void SetCachedItemLimitForTesting(size_t limit) { |
| cached_items_limit_ = limit; |
| } |
| void SetCachedBytesLimitForTesting(size_t limit) { |
| cached_bytes_limit_ = limit; |
| } |
| size_t GetBytesUsedForTesting() const { return bytes_used_; } |
| void SetImageDecodingFailedForTesting(const DrawImage& image); |
| bool DiscardableIsLockedForTesting(const DrawImage& image); |
| |
| private: |
| enum class DecodedDataMode { GPU, CPU }; |
| |
| // Stores the CPU-side decoded bits of an image and supporting fields. |
| struct DecodedImageData { |
| DecodedImageData(); |
| ~DecodedImageData(); |
| |
| bool is_locked() const { return is_locked_; } |
| bool Lock(); |
| void Unlock(); |
| 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; |
| |
| private: |
| struct UsageStats { |
| int lock_count = 1; |
| bool used = false; |
| bool first_lock_wasted = false; |
| }; |
| |
| void ReportUsageStats() const; |
| |
| std::unique_ptr<base::DiscardableMemory> data_; |
| bool is_locked_ = false; |
| UsageStats usage_stats_; |
| }; |
| |
| // Stores the GPU-side image and supporting fields. |
| struct UploadedImageData { |
| UploadedImageData(); |
| ~UploadedImageData(); |
| |
| void SetImage(sk_sp<SkImage> image); |
| const sk_sp<SkImage>& image() const { return image_; } |
| |
| void mark_used() { usage_stats_.used = true; } |
| void notify_ref_reached_zero() { |
| if (++usage_stats_.ref_reached_zero_count == 1) |
| usage_stats_.first_ref_wasted = !usage_stats_.used; |
| } |
| |
| // True if the image is counting against our memory limits. |
| bool budgeted = false; |
| uint32_t ref_count = 0; |
| |
| private: |
| struct UsageStats { |
| bool used = false; |
| bool first_ref_wasted = false; |
| int ref_reached_zero_count = 0; |
| }; |
| |
| void ReportUsageStats() const; |
| |
| // May be null if image not yet uploaded / prepared. |
| sk_sp<SkImage> image_; |
| UsageStats usage_stats_; |
| }; |
| |
| struct ImageData { |
| ImageData(DecodedDataMode mode, size_t size); |
| ~ImageData(); |
| |
| const DecodedDataMode mode; |
| const size_t size; |
| bool is_at_raster = false; |
| |
| DecodedImageData decode; |
| UploadedImageData upload; |
| }; |
| |
| using ImageDataMRUCache = |
| base::MRUCache<uint32_t, std::unique_ptr<ImageData>>; |
| |
| // All private functions should only be called while holding |lock_|. Some |
| // functions also require the |context_| lock. These are indicated by |
| // additional comments. |
| |
| // Similar to GetTaskForImageAndRef, but gets the dependent decode task |
| // rather than the upload task, if necessary. |
| scoped_refptr<TileTask> GetImageDecodeTaskAndRef( |
| const DrawImage& image, |
| const TracingInfo& tracing_info); |
| |
| void RefImageDecode(const DrawImage& draw_image); |
| void UnrefImageDecode(const DrawImage& draw_image); |
| void RefImage(const DrawImage& draw_image); |
| void UnrefImageInternal(const DrawImage& draw_image); |
| void RefCountChanged(ImageData* image_data); |
| |
| // Ensures that the cache can hold an element of |required_size|, freeing |
| // unreferenced cache entries if necessary to make room. |
| bool EnsureCapacity(size_t required_size); |
| bool CanFitSize(size_t size) const; |
| bool ExceedsPreferredCount() const; |
| |
| void DecodeImageIfNecessary(const DrawImage& draw_image, |
| ImageData* image_data); |
| |
| std::unique_ptr<GpuImageDecodeController::ImageData> CreateImageData( |
| const DrawImage& image); |
| SkImageInfo CreateImageInfoForDrawImage(const DrawImage& draw_image) const; |
| |
| // The following two functions also require the |context_| lock to be held. |
| void UploadImageIfNecessary(const DrawImage& draw_image, |
| ImageData* image_data); |
| void DeletePendingImages(); |
| |
| const ResourceFormat format_; |
| ContextProvider* context_; |
| sk_sp<GrContextThreadSafeProxy> context_threadsafe_proxy_; |
| |
| // 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_; |
| |
| ImageDataMRUCache image_data_; |
| |
| size_t cached_items_limit_; |
| size_t cached_bytes_limit_; |
| size_t bytes_used_; |
| const size_t max_gpu_image_bytes_; |
| |
| // We can't release GPU backed SkImages without holding the context lock, |
| // so we add them to this list and defer deletion until the next time the lock |
| // is held. |
| std::vector<sk_sp<SkImage>> images_pending_deletion_; |
| }; |
| |
| } // namespace cc |
| |
| #endif // CC_TILES_GPU_IMAGE_DECODE_CONTROLLER_H_ |