| // Copyright 2023 The Chromium Authors | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| #include "gpu/command_buffer/service/graphite_image_provider.h" | |
| #include "base/metrics/histogram_macros.h" | |
| #include "third_party/skia/include/gpu/graphite/Image.h" | |
| namespace gpu { | |
| GraphiteImageProvider::ImageHolder::ImageHolder(sk_sp<SkImage> image) | |
| : image(std::move(image)), last_use_time(base::TimeTicks::Now()) {} | |
| GraphiteImageProvider::ImageHolder::ImageHolder(ImageHolder&&) = default; | |
| GraphiteImageProvider::ImageHolder& | |
| GraphiteImageProvider::ImageHolder::operator=(ImageHolder&&) = default; | |
| GraphiteImageProvider::ImageHolder::~ImageHolder() = default; | |
| GraphiteImageProvider::GraphiteImageProvider(size_t max_cache_bytes) | |
| : preferred_max_cache_bytes_(max_cache_bytes) { | |
| CHECK(preferred_max_cache_bytes_); | |
| } | |
| GraphiteImageProvider::~GraphiteImageProvider() = default; | |
| sk_sp<SkImage> GraphiteImageProvider::findOrCreate( | |
| skgpu::graphite::Recorder* recorder, | |
| const SkImage* image, | |
| SkImage::RequiredProperties required_props) { | |
| // If no mipmaps are required, check to see if we have a mipmapped version | |
| // anyway - since it can be used in that case. | |
| // TODO: we could get fancy and, if ever a mipmapped key eclipsed a | |
| // non-mipmapped key, we could remove the hidden non-mipmapped key/image | |
| // from the cache. | |
| if (!required_props.fMipmapped) { | |
| auto mipmapped_props = required_props; | |
| mipmapped_props.fMipmapped = true; | |
| ImageKey mipmapped_key(image, mipmapped_props); | |
| auto result = cache_.Get(mipmapped_key); | |
| if (result != cache_.end()) { | |
| result->second.last_use_time = base::TimeTicks::Now(); | |
| return result->second.image; | |
| } | |
| } | |
| ImageKey key(image, required_props); | |
| // Check whether this image has an entry in the cache and return it if so. | |
| auto it = cache_.Get(key); | |
| bool hit_in_cache = it != cache_.end(); | |
| UMA_HISTOGRAM_BOOLEAN("Gpu.Graphite.GraphiteImageProviderAccessHitInCache", | |
| hit_in_cache); | |
| if (hit_in_cache) { | |
| it->second.last_use_time = base::TimeTicks::Now(); | |
| return it->second.image; | |
| } | |
| // Create a Graphite-backed image for `image`. | |
| sk_sp<SkImage> new_image = | |
| SkImages::TextureFromImage(recorder, image, required_props); | |
| if (!new_image) { | |
| return nullptr; | |
| } | |
| // Add the just-created Graphite-backed image to the cache. | |
| size_t new_image_bytes = new_image->textureSize(); | |
| PurgeCacheIfNecessaryToAllowForNewImage(new_image_bytes); | |
| cache_.Put(key, ImageHolder(new_image)); | |
| // Update the current size of the cache, which should not have overflowed its | |
| // max size unless the new image is itself larger than the maximum allowed | |
| // size. | |
| current_cache_bytes_ += new_image_bytes; | |
| CHECK(current_cache_bytes_ <= preferred_max_cache_bytes_ || | |
| current_cache_bytes_ == new_image_bytes); | |
| return new_image; | |
| } | |
| void GraphiteImageProvider::PurgeCacheIfNecessaryToAllowForNewImage( | |
| size_t new_bytes) { | |
| // Check for the corner case of the new image being larger than the maximum | |
| // allowed size, in which case we just empty the cache. | |
| if (new_bytes > preferred_max_cache_bytes_) { | |
| ClearImageCache(); | |
| return; | |
| } | |
| // Remove entries from the cache in LRU order until it is small enough to | |
| // admit `new_bytes` while remaining at or below the maximum allowed size. | |
| while (current_cache_bytes_ + new_bytes > preferred_max_cache_bytes_) { | |
| CHECK(!cache_.empty()); | |
| current_cache_bytes_ -= cache_.rbegin()->second.image->textureSize(); | |
| cache_.Erase(cache_.rbegin()); | |
| } | |
| } | |
| void GraphiteImageProvider::PurgeImagesNotUsedSince( | |
| base::TimeDelta last_use_delta) { | |
| base::TimeTicks now = base::TimeTicks::Now(); | |
| while (!cache_.empty() && | |
| cache_.rbegin()->second.last_use_time + last_use_delta < now) { | |
| current_cache_bytes_ -= cache_.rbegin()->second.image->textureSize(); | |
| cache_.Erase(cache_.rbegin()); | |
| } | |
| } | |
| void GraphiteImageProvider::ClearImageCache() { | |
| cache_.Clear(); | |
| current_cache_bytes_ = 0; | |
| } | |
| } // namespace gpu |