Index: cc/tiles/gpu_image_decode_controller.cc |
diff --git a/cc/tiles/gpu_image_decode_controller.cc b/cc/tiles/gpu_image_decode_controller.cc |
deleted file mode 100644 |
index d78f55fdb03ffd00b8ea69507a9718843c859182..0000000000000000000000000000000000000000 |
--- a/cc/tiles/gpu_image_decode_controller.cc |
+++ /dev/null |
@@ -1,1245 +0,0 @@ |
-// 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 "cc/tiles/gpu_image_decode_controller.h" |
- |
-#include <inttypes.h> |
- |
-#include "base/debug/alias.h" |
-#include "base/memory/discardable_memory_allocator.h" |
-#include "base/memory/memory_coordinator_client_registry.h" |
-#include "base/memory/ptr_util.h" |
-#include "base/metrics/histogram_macros.h" |
-#include "base/numerics/safe_math.h" |
-#include "base/strings/stringprintf.h" |
-#include "base/threading/thread_task_runner_handle.h" |
-#include "base/trace_event/memory_dump_manager.h" |
-#include "cc/debug/devtools_instrumentation.h" |
-#include "cc/output/context_provider.h" |
-#include "cc/raster/tile_task.h" |
-#include "cc/resources/resource_format_utils.h" |
-#include "cc/tiles/mipmap_util.h" |
-#include "gpu/command_buffer/client/context_support.h" |
-#include "gpu/command_buffer/client/gles2_interface.h" |
-#include "gpu_image_decode_controller.h" |
-#include "skia/ext/texture_handle.h" |
-#include "third_party/skia/include/core/SkCanvas.h" |
-#include "third_party/skia/include/core/SkRefCnt.h" |
-#include "third_party/skia/include/core/SkSurface.h" |
-#include "third_party/skia/include/gpu/GrContext.h" |
-#include "third_party/skia/include/gpu/GrTexture.h" |
-#include "ui/gfx/skia_util.h" |
-#include "ui/gl/trace_util.h" |
- |
-namespace cc { |
-namespace { |
- |
-// The number or entries to keep in the cache, depending on the memory state of |
-// the system. This limit can be breached by in-use cache items, which cannot |
-// be deleted. |
-static const int kNormalMaxItemsInCache = 2000; |
-static const int kThrottledMaxItemsInCache = 100; |
-static const int kSuspendedMaxItemsInCache = 0; |
- |
-// The factor by which to reduce the GPU memory size of the cache when in the |
-// THROTTLED memory state. |
-static const int kThrottledCacheSizeReductionFactor = 2; |
- |
-// The maximum size in bytes of GPU memory in the cache while SUSPENDED or not |
-// visible. This limit can be breached by in-use cache items, which cannot be |
-// deleted. |
-static const int kSuspendedOrInvisibleMaxGpuImageBytes = 0; |
- |
-// Returns true if an image would not be drawn and should therefore be |
-// skipped rather than decoded. |
-bool SkipImage(const DrawImage& draw_image) { |
- if (!SkIRect::Intersects(draw_image.src_rect(), draw_image.image()->bounds())) |
- return true; |
- if (std::abs(draw_image.scale().width()) < |
- std::numeric_limits<float>::epsilon() || |
- std::abs(draw_image.scale().height()) < |
- std::numeric_limits<float>::epsilon()) { |
- return true; |
- } |
- return false; |
-} |
- |
-// Returns the filter quality to use for scaling the image to upload scale. For |
-// GPU raster, medium and high filter quality are identical for downscales. |
-// Upload scaling is always a downscale, so cap our filter quality to medium. |
-SkFilterQuality CalculateUploadScaleFilterQuality(const DrawImage& draw_image) { |
- return std::min(kMedium_SkFilterQuality, draw_image.filter_quality()); |
-} |
- |
-// Calculate the mip level to upload-scale the image to before uploading. We use |
-// mip levels rather than exact scales to increase re-use of scaled images. |
-int CalculateUploadScaleMipLevel(const DrawImage& draw_image) { |
- // Images which are being clipped will have color-bleeding if scaled. |
- // TODO(ericrk): Investigate uploading clipped images to handle this case and |
- // provide further optimization. crbug.com/620899 |
- if (draw_image.src_rect() != draw_image.image()->bounds()) |
- return 0; |
- |
- gfx::Size base_size(draw_image.image()->width(), |
- draw_image.image()->height()); |
- // Ceil our scaled size so that the mip map generated is guaranteed to be |
- // larger. Take the abs of the scale, as mipmap functions don't handle |
- // (and aren't impacted by) negative image dimensions. |
- gfx::Size scaled_size = |
- gfx::ScaleToCeiledSize(base_size, std::abs(draw_image.scale().width()), |
- std::abs(draw_image.scale().height())); |
- |
- return MipMapUtil::GetLevelForSize(base_size, scaled_size); |
-} |
- |
-// Calculates the scale factor which can be used to scale an image to a given |
-// mip level. |
-SkSize CalculateScaleFactorForMipLevel(const DrawImage& draw_image, |
- int mip_level) { |
- gfx::Size base_size(draw_image.image()->width(), |
- draw_image.image()->height()); |
- return MipMapUtil::GetScaleAdjustmentForLevel(base_size, mip_level); |
-} |
- |
-// Calculates the size of a given mip level. |
-gfx::Size CalculateSizeForMipLevel(const DrawImage& draw_image, int mip_level) { |
- gfx::Size base_size(draw_image.image()->width(), |
- draw_image.image()->height()); |
- return MipMapUtil::GetSizeForLevel(base_size, mip_level); |
-} |
- |
-// Generates a uint64_t which uniquely identifies a DrawImage for the purposes |
-// of the |in_use_cache_|. The key is generated as follows: |
-// ╔══════════════════════╤═══════════╤═══════════╗ |
-// ║ image_id │ mip_level │ quality ║ |
-// ╚════════32═bits═══════╧══16═bits══╧══16═bits══╝ |
-uint64_t GenerateInUseCacheKey(const DrawImage& draw_image) { |
- static_assert( |
- kLast_SkFilterQuality <= std::numeric_limits<uint16_t>::max(), |
- "InUseCacheKey depends on SkFilterQuality fitting in a uint16_t."); |
- |
- SkFilterQuality filter_quality = |
- CalculateUploadScaleFilterQuality(draw_image); |
- DCHECK_LE(filter_quality, kLast_SkFilterQuality); |
- |
- // An image has at most log_2(max(width, height)) mip levels, so given our |
- // usage of 32-bit sizes for images, key.mip_level is at most 31. |
- int32_t mip_level = CalculateUploadScaleMipLevel(draw_image); |
- DCHECK_LT(mip_level, 32); |
- |
- return (static_cast<uint64_t>(draw_image.image()->uniqueID()) << 32) | |
- (mip_level << 16) | filter_quality; |
-} |
- |
-} // namespace |
- |
-GpuImageDecodeController::InUseCacheEntry::InUseCacheEntry( |
- scoped_refptr<ImageData> image_data) |
- : image_data(std::move(image_data)) {} |
-GpuImageDecodeController::InUseCacheEntry::InUseCacheEntry( |
- const InUseCacheEntry&) = default; |
-GpuImageDecodeController::InUseCacheEntry::InUseCacheEntry(InUseCacheEntry&&) = |
- default; |
-GpuImageDecodeController::InUseCacheEntry::~InUseCacheEntry() = default; |
- |
-// Task which decodes an image and stores the result in discardable memory. |
-// This task does not use GPU resources and can be run on any thread. |
-class ImageDecodeTaskImpl : public TileTask { |
- public: |
- ImageDecodeTaskImpl(GpuImageDecodeController* controller, |
- const DrawImage& draw_image, |
- const ImageDecodeController::TracingInfo& tracing_info) |
- : TileTask(true), |
- controller_(controller), |
- image_(draw_image), |
- tracing_info_(tracing_info) { |
- DCHECK(!SkipImage(draw_image)); |
- } |
- |
- // Overridden from Task: |
- void RunOnWorkerThread() override { |
- TRACE_EVENT2("cc", "ImageDecodeTaskImpl::RunOnWorkerThread", "mode", "gpu", |
- "source_prepare_tiles_id", tracing_info_.prepare_tiles_id); |
- devtools_instrumentation::ScopedImageDecodeTask image_decode_task( |
- image_.image().get(), |
- devtools_instrumentation::ScopedImageDecodeTask::GPU); |
- controller_->DecodeImage(image_); |
- } |
- |
- // Overridden from TileTask: |
- void OnTaskCompleted() override { |
- controller_->OnImageDecodeTaskCompleted(image_); |
- } |
- |
- protected: |
- ~ImageDecodeTaskImpl() override {} |
- |
- private: |
- GpuImageDecodeController* controller_; |
- DrawImage image_; |
- const ImageDecodeController::TracingInfo tracing_info_; |
- |
- DISALLOW_COPY_AND_ASSIGN(ImageDecodeTaskImpl); |
-}; |
- |
-// Task which creates an image from decoded data. Typically this involves |
-// uploading data to the GPU, which requires this task be run on the non- |
-// concurrent thread. |
-class ImageUploadTaskImpl : public TileTask { |
- public: |
- ImageUploadTaskImpl(GpuImageDecodeController* controller, |
- const DrawImage& draw_image, |
- scoped_refptr<TileTask> decode_dependency, |
- const ImageDecodeController::TracingInfo& tracing_info) |
- : TileTask(false), |
- controller_(controller), |
- image_(draw_image), |
- tracing_info_(tracing_info) { |
- DCHECK(!SkipImage(draw_image)); |
- // If an image is already decoded and locked, we will not generate a |
- // decode task. |
- if (decode_dependency) |
- dependencies_.push_back(std::move(decode_dependency)); |
- } |
- |
- // Override from Task: |
- void RunOnWorkerThread() override { |
- TRACE_EVENT2("cc", "ImageUploadTaskImpl::RunOnWorkerThread", "mode", "gpu", |
- "source_prepare_tiles_id", tracing_info_.prepare_tiles_id); |
- controller_->UploadImage(image_); |
- } |
- |
- // Overridden from TileTask: |
- void OnTaskCompleted() override { |
- controller_->OnImageUploadTaskCompleted(image_); |
- } |
- |
- protected: |
- ~ImageUploadTaskImpl() override {} |
- |
- private: |
- GpuImageDecodeController* controller_; |
- DrawImage image_; |
- const ImageDecodeController::TracingInfo tracing_info_; |
- |
- DISALLOW_COPY_AND_ASSIGN(ImageUploadTaskImpl); |
-}; |
- |
-GpuImageDecodeController::DecodedImageData::DecodedImageData() = default; |
-GpuImageDecodeController::DecodedImageData::~DecodedImageData() { |
- ResetData(); |
-} |
- |
-bool GpuImageDecodeController::DecodedImageData::Lock() { |
- DCHECK(!is_locked_); |
- is_locked_ = data_->Lock(); |
- if (is_locked_) |
- ++usage_stats_.lock_count; |
- return is_locked_; |
-} |
- |
-void GpuImageDecodeController::DecodedImageData::Unlock() { |
- DCHECK(is_locked_); |
- data_->Unlock(); |
- if (usage_stats_.lock_count == 1) |
- usage_stats_.first_lock_wasted = !usage_stats_.used; |
- is_locked_ = false; |
-} |
- |
-void GpuImageDecodeController::DecodedImageData::SetLockedData( |
- std::unique_ptr<base::DiscardableMemory> data) { |
- DCHECK(!is_locked_); |
- DCHECK(data); |
- DCHECK(!data_); |
- data_ = std::move(data); |
- is_locked_ = true; |
-} |
- |
-void GpuImageDecodeController::DecodedImageData::ResetData() { |
- DCHECK(!is_locked_); |
- if (data_) |
- ReportUsageStats(); |
- data_ = nullptr; |
- usage_stats_ = UsageStats(); |
-} |
- |
-void GpuImageDecodeController::DecodedImageData::ReportUsageStats() const { |
- // lock_count │ used │ result state |
- // ═══════════╪═══════╪══════════════════ |
- // 1 │ false │ WASTED_ONCE |
- // 1 │ true │ USED_ONCE |
- // >1 │ false │ WASTED_RELOCKED |
- // >1 │ true │ USED_RELOCKED |
- // Note that it's important not to reorder the following enums, since the |
- // numerical values are used in the histogram code. |
- enum State : int { |
- DECODED_IMAGE_STATE_WASTED_ONCE, |
- DECODED_IMAGE_STATE_USED_ONCE, |
- DECODED_IMAGE_STATE_WASTED_RELOCKED, |
- DECODED_IMAGE_STATE_USED_RELOCKED, |
- DECODED_IMAGE_STATE_COUNT |
- } state = DECODED_IMAGE_STATE_WASTED_ONCE; |
- |
- if (usage_stats_.lock_count == 1) { |
- if (usage_stats_.used) |
- state = DECODED_IMAGE_STATE_USED_ONCE; |
- else |
- state = DECODED_IMAGE_STATE_WASTED_ONCE; |
- } else { |
- if (usage_stats_.used) |
- state = DECODED_IMAGE_STATE_USED_RELOCKED; |
- else |
- state = DECODED_IMAGE_STATE_WASTED_RELOCKED; |
- } |
- |
- UMA_HISTOGRAM_ENUMERATION("Renderer4.GpuImageDecodeState", state, |
- DECODED_IMAGE_STATE_COUNT); |
- UMA_HISTOGRAM_BOOLEAN("Renderer4.GpuImageDecodeState.FirstLockWasted", |
- usage_stats_.first_lock_wasted); |
-} |
- |
-GpuImageDecodeController::UploadedImageData::UploadedImageData() = default; |
-GpuImageDecodeController::UploadedImageData::~UploadedImageData() { |
- SetImage(nullptr); |
-} |
- |
-void GpuImageDecodeController::UploadedImageData::SetImage( |
- sk_sp<SkImage> image) { |
- DCHECK(!image_ || !image); |
- if (image_) { |
- ReportUsageStats(); |
- usage_stats_ = UsageStats(); |
- } |
- image_ = std::move(image); |
-} |
- |
-void GpuImageDecodeController::UploadedImageData::ReportUsageStats() const { |
- UMA_HISTOGRAM_BOOLEAN("Renderer4.GpuImageUploadState.Used", |
- usage_stats_.used); |
- UMA_HISTOGRAM_BOOLEAN("Renderer4.GpuImageUploadState.FirstRefWasted", |
- usage_stats_.first_ref_wasted); |
-} |
- |
-GpuImageDecodeController::ImageData::ImageData( |
- DecodedDataMode mode, |
- size_t size, |
- const SkImage::DeferredTextureImageUsageParams& upload_params) |
- : mode(mode), size(size), upload_params(upload_params) {} |
- |
-GpuImageDecodeController::ImageData::~ImageData() { |
- // We should never delete ImageData while it is in use or before it has been |
- // cleaned up. |
- DCHECK_EQ(0u, upload.ref_count); |
- DCHECK_EQ(0u, decode.ref_count); |
- DCHECK_EQ(false, decode.is_locked()); |
- // This should always be cleaned up before deleting the image, as it needs to |
- // be freed with the GL context lock held. |
- DCHECK(!upload.image()); |
-} |
- |
-GpuImageDecodeController::GpuImageDecodeController(ContextProvider* context, |
- ResourceFormat decode_format, |
- size_t max_gpu_image_bytes) |
- : format_(decode_format), |
- context_(context), |
- persistent_cache_(PersistentCache::NO_AUTO_EVICT), |
- normal_max_gpu_image_bytes_(max_gpu_image_bytes) { |
- // Acquire the context_lock so that we can safely retrieve the |
- // GrContextThreadSafeProxy. This proxy can then be used with no lock held. |
- { |
- ContextProvider::ScopedContextLock context_lock(context_); |
- context_threadsafe_proxy_ = sk_sp<GrContextThreadSafeProxy>( |
- context->GrContext()->threadSafeProxy()); |
- } |
- |
- // In certain cases, ThreadTaskRunnerHandle isn't set (Android Webview). |
- // Don't register a dump provider in these cases. |
- if (base::ThreadTaskRunnerHandle::IsSet()) { |
- base::trace_event::MemoryDumpManager::GetInstance()->RegisterDumpProvider( |
- this, "cc::GpuImageDecodeController", |
- base::ThreadTaskRunnerHandle::Get()); |
- } |
- // Register this component with base::MemoryCoordinatorClientRegistry. |
- base::MemoryCoordinatorClientRegistry::GetInstance()->Register(this); |
-} |
- |
-GpuImageDecodeController::~GpuImageDecodeController() { |
- // SetShouldAggressivelyFreeResources will zero our limits and free all |
- // outstanding image memory. |
- SetShouldAggressivelyFreeResources(true); |
- |
- // It is safe to unregister, even if we didn't register in the constructor. |
- base::trace_event::MemoryDumpManager::GetInstance()->UnregisterDumpProvider( |
- this); |
- // Unregister this component with memory_coordinator::ClientRegistry. |
- base::MemoryCoordinatorClientRegistry::GetInstance()->Unregister(this); |
-} |
- |
-bool GpuImageDecodeController::GetTaskForImageAndRef( |
- const DrawImage& draw_image, |
- const TracingInfo& tracing_info, |
- scoped_refptr<TileTask>* task) { |
- TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("cc.debug"), |
- "GpuImageDecodeController::GetTaskForImageAndRef"); |
- if (SkipImage(draw_image)) { |
- *task = nullptr; |
- return false; |
- } |
- |
- base::AutoLock lock(lock_); |
- const auto image_id = draw_image.image()->uniqueID(); |
- ImageData* image_data = GetImageDataForDrawImage(draw_image); |
- scoped_refptr<ImageData> new_data; |
- if (!image_data) { |
- // We need an ImageData, create one now. |
- new_data = CreateImageData(draw_image); |
- image_data = new_data.get(); |
- } else if (image_data->is_at_raster) { |
- // Image is at-raster, just return, this usage will be at-raster as well. |
- *task = nullptr; |
- return false; |
- } else if (image_data->decode.decode_failure) { |
- // We have already tried and failed to decode this image, so just return. |
- *task = nullptr; |
- return false; |
- } else if (image_data->upload.image()) { |
- // The image is already uploaded, ref and return. |
- RefImage(draw_image); |
- *task = nullptr; |
- return true; |
- } else if (image_data->upload.task) { |
- // We had an existing upload task, ref the image and return the task. |
- RefImage(draw_image); |
- *task = image_data->upload.task; |
- return true; |
- } |
- |
- // Ensure that the image we're about to decode/upload will fit in memory. |
- if (!EnsureCapacity(image_data->size)) { |
- // Image will not fit, do an at-raster decode. |
- *task = nullptr; |
- return false; |
- } |
- |
- // If we had to create new image data, add it to our map now that we know it |
- // will fit. |
- if (new_data) |
- persistent_cache_.Put(image_id, std::move(new_data)); |
- |
- // Ref image and create a upload and decode tasks. We will release this ref |
- // in UploadTaskCompleted. |
- RefImage(draw_image); |
- *task = make_scoped_refptr(new ImageUploadTaskImpl( |
- this, draw_image, GetImageDecodeTaskAndRef(draw_image, tracing_info), |
- tracing_info)); |
- image_data->upload.task = *task; |
- |
- // Ref the image again - this ref is owned by the caller, and it is their |
- // responsibility to release it by calling UnrefImage. |
- RefImage(draw_image); |
- return true; |
-} |
- |
-void GpuImageDecodeController::UnrefImage(const DrawImage& draw_image) { |
- TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("cc.debug"), |
- "GpuImageDecodeController::UnrefImage"); |
- base::AutoLock lock(lock_); |
- UnrefImageInternal(draw_image); |
-} |
- |
-DecodedDrawImage GpuImageDecodeController::GetDecodedImageForDraw( |
- const DrawImage& draw_image) { |
- TRACE_EVENT0("cc", "GpuImageDecodeController::GetDecodedImageForDraw"); |
- |
- // We are being called during raster. The context lock must already be |
- // acquired by the caller. |
- context_->GetLock()->AssertAcquired(); |
- |
- if (SkipImage(draw_image)) |
- return DecodedDrawImage(nullptr, draw_image.filter_quality()); |
- |
- base::AutoLock lock(lock_); |
- ImageData* image_data = GetImageDataForDrawImage(draw_image); |
- if (!image_data) { |
- // We didn't find the image, create a new entry. |
- auto data = CreateImageData(draw_image); |
- image_data = data.get(); |
- persistent_cache_.Put(draw_image.image()->uniqueID(), std::move(data)); |
- } |
- |
- if (!image_data->upload.budgeted) { |
- // If image data is not budgeted by this point, it is at-raster. |
- image_data->is_at_raster = true; |
- } |
- |
- // Ref the image and decode so that they stay alive while we are |
- // decoding/uploading. |
- RefImage(draw_image); |
- RefImageDecode(draw_image); |
- |
- // We may or may not need to decode and upload the image we've found, the |
- // following functions early-out to if we already decoded. |
- DecodeImageIfNecessary(draw_image, image_data); |
- UploadImageIfNecessary(draw_image, image_data); |
- // Unref the image decode, but not the image. The image ref will be released |
- // in DrawWithImageFinished. |
- UnrefImageDecode(draw_image); |
- |
- sk_sp<SkImage> image = image_data->upload.image(); |
- image_data->upload.mark_used(); |
- DCHECK(image || image_data->decode.decode_failure); |
- |
- SkSize scale_factor = CalculateScaleFactorForMipLevel( |
- draw_image, image_data->upload_params.fPreScaleMipLevel); |
- DecodedDrawImage decoded_draw_image(std::move(image), SkSize(), scale_factor, |
- draw_image.filter_quality()); |
- decoded_draw_image.set_at_raster_decode(image_data->is_at_raster); |
- return decoded_draw_image; |
-} |
- |
-void GpuImageDecodeController::DrawWithImageFinished( |
- const DrawImage& draw_image, |
- const DecodedDrawImage& decoded_draw_image) { |
- TRACE_EVENT0("cc", "GpuImageDecodeController::DrawWithImageFinished"); |
- |
- // We are being called during raster. The context lock must already be |
- // acquired by the caller. |
- context_->GetLock()->AssertAcquired(); |
- |
- if (SkipImage(draw_image)) |
- return; |
- |
- base::AutoLock lock(lock_); |
- UnrefImageInternal(draw_image); |
- |
- // We are mid-draw and holding the context lock, ensure we clean up any |
- // textures (especially at-raster), which may have just been marked for |
- // deletion by UnrefImage. |
- DeletePendingImages(); |
-} |
- |
-void GpuImageDecodeController::ReduceCacheUsage() { |
- TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("cc.debug"), |
- "GpuImageDecodeController::ReduceCacheUsage"); |
- base::AutoLock lock(lock_); |
- EnsureCapacity(0); |
-} |
- |
-void GpuImageDecodeController::SetShouldAggressivelyFreeResources( |
- bool aggressively_free_resources) { |
- TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("cc.debug"), |
- "GpuImageDecodeController::SetShouldAggressivelyFreeResources", |
- "agressive_free_resources", aggressively_free_resources); |
- if (aggressively_free_resources) { |
- ContextProvider::ScopedContextLock context_lock(context_); |
- base::AutoLock lock(lock_); |
- // We want to keep as little in our cache as possible. Set our memory limit |
- // to zero and EnsureCapacity to clean up memory. |
- cached_bytes_limit_ = kSuspendedOrInvisibleMaxGpuImageBytes; |
- EnsureCapacity(0); |
- |
- // We are holding the context lock, so finish cleaning up deleted images |
- // now. |
- DeletePendingImages(); |
- } else { |
- base::AutoLock lock(lock_); |
- cached_bytes_limit_ = normal_max_gpu_image_bytes_; |
- } |
-} |
- |
-bool GpuImageDecodeController::OnMemoryDump( |
- const base::trace_event::MemoryDumpArgs& args, |
- base::trace_event::ProcessMemoryDump* pmd) { |
- using base::trace_event::MemoryAllocatorDump; |
- using base::trace_event::MemoryAllocatorDumpGuid; |
- using base::trace_event::MemoryDumpLevelOfDetail; |
- |
- TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("cc.debug"), |
- "GpuImageDecodeController::OnMemoryDump"); |
- |
- if (args.level_of_detail == MemoryDumpLevelOfDetail::BACKGROUND) { |
- std::string dump_name = |
- base::StringPrintf("cc/image_memory/controller_0x%" PRIXPTR, |
- reinterpret_cast<uintptr_t>(this)); |
- MemoryAllocatorDump* dump = pmd->CreateAllocatorDump(dump_name); |
- dump->AddScalar(MemoryAllocatorDump::kNameSize, |
- MemoryAllocatorDump::kUnitsBytes, bytes_used_); |
- |
- // Early out, no need for more detail in a BACKGROUND dump. |
- return true; |
- } |
- |
- for (const auto& image_pair : persistent_cache_) { |
- const ImageData* image_data = image_pair.second.get(); |
- const uint32_t image_id = image_pair.first; |
- |
- // If we have discardable decoded data, dump this here. |
- if (image_data->decode.data()) { |
- std::string discardable_dump_name = base::StringPrintf( |
- "cc/image_memory/controller_0x%" PRIXPTR "/discardable/image_%d", |
- reinterpret_cast<uintptr_t>(this), image_id); |
- MemoryAllocatorDump* dump = |
- image_data->decode.data()->CreateMemoryAllocatorDump( |
- discardable_dump_name.c_str(), pmd); |
- // If our image is locked, dump the "locked_size" as an additional |
- // column. |
- // This lets us see the amount of discardable which is contributing to |
- // memory pressure. |
- if (image_data->decode.is_locked()) { |
- dump->AddScalar("locked_size", MemoryAllocatorDump::kUnitsBytes, |
- image_data->size); |
- } |
- } |
- |
- // If we have an uploaded image (that is actually on the GPU, not just a |
- // CPU |
- // wrapper), upload it here. |
- if (image_data->upload.image() && |
- image_data->mode == DecodedDataMode::GPU) { |
- std::string gpu_dump_name = base::StringPrintf( |
- "cc/image_memory/controller_0x%" PRIXPTR "/gpu/image_%d", |
- reinterpret_cast<uintptr_t>(this), image_id); |
- MemoryAllocatorDump* dump = pmd->CreateAllocatorDump(gpu_dump_name); |
- dump->AddScalar(MemoryAllocatorDump::kNameSize, |
- MemoryAllocatorDump::kUnitsBytes, image_data->size); |
- |
- // Create a global shred GUID to associate this data with its GPU |
- // process |
- // counterpart. |
- GLuint gl_id = skia::GrBackendObjectToGrGLTextureInfo( |
- image_data->upload.image()->getTextureHandle( |
- false /* flushPendingGrContextIO */)) |
- ->fID; |
- MemoryAllocatorDumpGuid guid = gl::GetGLTextureClientGUIDForTracing( |
- context_->ContextSupport()->ShareGroupTracingGUID(), gl_id); |
- |
- // kImportance is somewhat arbitrary - we chose 3 to be higher than the |
- // value used in the GPU process (1), and Skia (2), causing us to appear |
- // as the owner in memory traces. |
- const int kImportance = 3; |
- pmd->CreateSharedGlobalAllocatorDump(guid); |
- pmd->AddOwnershipEdge(dump->guid(), guid, kImportance); |
- } |
- } |
- |
- return true; |
-} |
- |
-void GpuImageDecodeController::DecodeImage(const DrawImage& draw_image) { |
- TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("cc.debug"), |
- "GpuImageDecodeController::DecodeImage"); |
- base::AutoLock lock(lock_); |
- ImageData* image_data = GetImageDataForDrawImage(draw_image); |
- DCHECK(image_data); |
- DCHECK(!image_data->is_at_raster); |
- DecodeImageIfNecessary(draw_image, image_data); |
-} |
- |
-void GpuImageDecodeController::UploadImage(const DrawImage& draw_image) { |
- TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("cc.debug"), |
- "GpuImageDecodeController::UploadImage"); |
- ContextProvider::ScopedContextLock context_lock(context_); |
- base::AutoLock lock(lock_); |
- ImageData* image_data = GetImageDataForDrawImage(draw_image); |
- DCHECK(image_data); |
- DCHECK(!image_data->is_at_raster); |
- UploadImageIfNecessary(draw_image, image_data); |
-} |
- |
-void GpuImageDecodeController::OnImageDecodeTaskCompleted( |
- const DrawImage& draw_image) { |
- TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("cc.debug"), |
- "GpuImageDecodeController::OnImageDecodeTaskCompleted"); |
- base::AutoLock lock(lock_); |
- // Decode task is complete, remove our reference to it. |
- ImageData* image_data = GetImageDataForDrawImage(draw_image); |
- DCHECK(image_data); |
- DCHECK(image_data->decode.task); |
- image_data->decode.task = nullptr; |
- |
- // While the decode task is active, we keep a ref on the decoded data. |
- // Release that ref now. |
- UnrefImageDecode(draw_image); |
-} |
- |
-void GpuImageDecodeController::OnImageUploadTaskCompleted( |
- const DrawImage& draw_image) { |
- TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("cc.debug"), |
- "GpuImageDecodeController::OnImageUploadTaskCompleted"); |
- base::AutoLock lock(lock_); |
- // Upload task is complete, remove our reference to it. |
- ImageData* image_data = GetImageDataForDrawImage(draw_image); |
- DCHECK(image_data); |
- DCHECK(image_data->upload.task); |
- image_data->upload.task = nullptr; |
- |
- // While the upload task is active, we keep a ref on both the image it will be |
- // populating, as well as the decode it needs to populate it. Release these |
- // refs now. |
- UnrefImageDecode(draw_image); |
- UnrefImageInternal(draw_image); |
-} |
- |
-// Checks if an existing image decode exists. If not, returns a task to produce |
-// the requested decode. |
-scoped_refptr<TileTask> GpuImageDecodeController::GetImageDecodeTaskAndRef( |
- const DrawImage& draw_image, |
- const TracingInfo& tracing_info) { |
- TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("cc.debug"), |
- "GpuImageDecodeController::GetImageDecodeTaskAndRef"); |
- lock_.AssertAcquired(); |
- |
- // This ref is kept alive while an upload task may need this decode. We |
- // release this ref in UploadTaskCompleted. |
- RefImageDecode(draw_image); |
- |
- ImageData* image_data = GetImageDataForDrawImage(draw_image); |
- DCHECK(image_data); |
- if (image_data->decode.is_locked()) { |
- // We should never be creating a decode task for an at raster image. |
- DCHECK(!image_data->is_at_raster); |
- // We should never be creating a decode for an already-uploaded image. |
- DCHECK(!image_data->upload.image()); |
- return nullptr; |
- } |
- |
- // We didn't have an existing locked image, create a task to lock or decode. |
- scoped_refptr<TileTask>& existing_task = image_data->decode.task; |
- if (!existing_task) { |
- // Ref image decode and create a decode task. This ref will be released in |
- // DecodeTaskCompleted. |
- RefImageDecode(draw_image); |
- existing_task = make_scoped_refptr( |
- new ImageDecodeTaskImpl(this, draw_image, tracing_info)); |
- } |
- return existing_task; |
-} |
- |
-void GpuImageDecodeController::RefImageDecode(const DrawImage& draw_image) { |
- TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("cc.debug"), |
- "GpuImageDecodeController::RefImageDecode"); |
- lock_.AssertAcquired(); |
- auto found = in_use_cache_.find(GenerateInUseCacheKey(draw_image)); |
- DCHECK(found != in_use_cache_.end()); |
- ++found->second.ref_count; |
- ++found->second.image_data->decode.ref_count; |
- OwnershipChanged(draw_image, found->second.image_data.get()); |
-} |
- |
-void GpuImageDecodeController::UnrefImageDecode(const DrawImage& draw_image) { |
- TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("cc.debug"), |
- "GpuImageDecodeController::UnrefImageDecode"); |
- lock_.AssertAcquired(); |
- auto found = in_use_cache_.find(GenerateInUseCacheKey(draw_image)); |
- DCHECK(found != in_use_cache_.end()); |
- DCHECK_GT(found->second.image_data->decode.ref_count, 0u); |
- DCHECK_GT(found->second.ref_count, 0u); |
- --found->second.ref_count; |
- --found->second.image_data->decode.ref_count; |
- OwnershipChanged(draw_image, found->second.image_data.get()); |
- if (found->second.ref_count == 0u) { |
- in_use_cache_.erase(found); |
- } |
-} |
- |
-void GpuImageDecodeController::RefImage(const DrawImage& draw_image) { |
- TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("cc.debug"), |
- "GpuImageDecodeController::RefImage"); |
- lock_.AssertAcquired(); |
- InUseCacheKey key = GenerateInUseCacheKey(draw_image); |
- auto found = in_use_cache_.find(key); |
- |
- // If no secondary cache entry was found for the given |draw_image|, then |
- // the draw_image only exists in the |persistent_cache_|. Create an in-use |
- // cache entry now. |
- if (found == in_use_cache_.end()) { |
- auto found_image = persistent_cache_.Peek(draw_image.image()->uniqueID()); |
- DCHECK(found_image != persistent_cache_.end()); |
- DCHECK(found_image->second->upload_params.fPreScaleMipLevel <= |
- CalculateUploadScaleMipLevel(draw_image)); |
- found = in_use_cache_ |
- .insert(InUseCache::value_type( |
- key, InUseCacheEntry(found_image->second))) |
- .first; |
- } |
- |
- DCHECK(found != in_use_cache_.end()); |
- ++found->second.ref_count; |
- ++found->second.image_data->upload.ref_count; |
- OwnershipChanged(draw_image, found->second.image_data.get()); |
-} |
- |
-void GpuImageDecodeController::UnrefImageInternal(const DrawImage& draw_image) { |
- lock_.AssertAcquired(); |
- auto found = in_use_cache_.find(GenerateInUseCacheKey(draw_image)); |
- DCHECK(found != in_use_cache_.end()); |
- DCHECK_GT(found->second.image_data->upload.ref_count, 0u); |
- DCHECK_GT(found->second.ref_count, 0u); |
- --found->second.ref_count; |
- --found->second.image_data->upload.ref_count; |
- OwnershipChanged(draw_image, found->second.image_data.get()); |
- if (found->second.ref_count == 0u) { |
- in_use_cache_.erase(found); |
- } |
-} |
- |
-// Called any time an image or decode ref count changes. Takes care of any |
-// necessary memory budget book-keeping and cleanup. |
-void GpuImageDecodeController::OwnershipChanged(const DrawImage& draw_image, |
- ImageData* image_data) { |
- lock_.AssertAcquired(); |
- |
- bool has_any_refs = |
- image_data->upload.ref_count > 0 || image_data->decode.ref_count > 0; |
- |
- // Don't keep around completely empty images. This can happen if an image's |
- // decode/upload tasks were both cancelled before completing. |
- if (!has_any_refs && !image_data->upload.image() && |
- !image_data->decode.data()) { |
- auto found_persistent = |
- persistent_cache_.Peek(draw_image.image()->uniqueID()); |
- if (found_persistent != persistent_cache_.end()) |
- persistent_cache_.Erase(found_persistent); |
- } |
- |
- // Don't keep around orphaned images. |
- if (image_data->is_orphaned && !has_any_refs) { |
- images_pending_deletion_.push_back(std::move(image_data->upload.image())); |
- image_data->upload.SetImage(nullptr); |
- } |
- |
- // Don't keep CPU images if they are unused, these images can be recreated by |
- // re-locking discardable (rather than requiring a full upload like GPU |
- // images). |
- if (image_data->mode == DecodedDataMode::CPU && !has_any_refs) { |
- images_pending_deletion_.push_back(image_data->upload.image()); |
- image_data->upload.SetImage(nullptr); |
- } |
- |
- if (image_data->is_at_raster && !has_any_refs) { |
- // We have an at-raster image which has reached zero refs. If it won't fit |
- // in our cache, delete the image to allow it to fit. |
- if (image_data->upload.image() && !CanFitSize(image_data->size)) { |
- images_pending_deletion_.push_back(image_data->upload.image()); |
- image_data->upload.SetImage(nullptr); |
- } |
- |
- // We now have an at-raster image which will fit in our cache. Convert it |
- // to not-at-raster. |
- image_data->is_at_raster = false; |
- if (image_data->upload.image()) { |
- bytes_used_ += image_data->size; |
- image_data->upload.budgeted = true; |
- } |
- } |
- |
- // If we have image refs on a non-at-raster image, it must be budgeted, as it |
- // is either uploaded or pending upload. |
- if (image_data->upload.ref_count > 0 && !image_data->upload.budgeted && |
- !image_data->is_at_raster) { |
- // We should only be taking non-at-raster refs on images that fit in cache. |
- DCHECK(CanFitSize(image_data->size)); |
- |
- bytes_used_ += image_data->size; |
- image_data->upload.budgeted = true; |
- } |
- |
- // If we have no image refs on an image, it should only be budgeted if it has |
- // an uploaded image. If no image exists (upload was cancelled), we should |
- // un-budget the image. |
- if (image_data->upload.ref_count == 0 && image_data->upload.budgeted && |
- !image_data->upload.image()) { |
- DCHECK_GE(bytes_used_, image_data->size); |
- bytes_used_ -= image_data->size; |
- image_data->upload.budgeted = false; |
- } |
- |
- // We should unlock the discardable memory for the image in two cases: |
- // 1) The image is no longer being used (no decode or upload refs). |
- // 2) This is a GPU backed image that has already been uploaded (no decode |
- // refs). |
- bool should_unlock_discardable = |
- !has_any_refs || (image_data->mode == DecodedDataMode::GPU && |
- !image_data->decode.ref_count); |
- |
- if (should_unlock_discardable && image_data->decode.is_locked()) { |
- DCHECK(image_data->decode.data()); |
- image_data->decode.Unlock(); |
- } |
- |
-#if DCHECK_IS_ON() |
- // Sanity check the above logic. |
- if (image_data->upload.image()) { |
- DCHECK(image_data->is_at_raster || image_data->upload.budgeted); |
- if (image_data->mode == DecodedDataMode::CPU) |
- DCHECK(image_data->decode.is_locked()); |
- } else { |
- DCHECK(!image_data->upload.budgeted || image_data->upload.ref_count > 0); |
- } |
-#endif |
-} |
- |
-// Ensures that we can fit a new image of size |required_size| in our cache. In |
-// doing so, this function will free unreferenced image data as necessary to |
-// create rooom. |
-bool GpuImageDecodeController::EnsureCapacity(size_t required_size) { |
- TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("cc.debug"), |
- "GpuImageDecodeController::EnsureCapacity"); |
- lock_.AssertAcquired(); |
- |
- if (CanFitSize(required_size) && !ExceedsPreferredCount()) |
- return true; |
- |
- // While we are over memory or preferred item capacity, we iterate through |
- // our set of cached image data in LRU order. For each image, we can do two |
- // things: 1) We can free the uploaded image, reducing the memory usage of |
- // the cache and 2) we can remove the entry entirely, reducing the count of |
- // elements in the cache. |
- for (auto it = persistent_cache_.rbegin(); it != persistent_cache_.rend();) { |
- if (it->second->decode.ref_count != 0 || |
- it->second->upload.ref_count != 0) { |
- ++it; |
- continue; |
- } |
- |
- // Current entry has no refs. Ensure it is not locked. |
- DCHECK(!it->second->decode.is_locked()); |
- |
- // If an image without refs is budgeted, it must have an associated image |
- // upload. |
- DCHECK(!it->second->upload.budgeted || it->second->upload.image()); |
- |
- // Free the uploaded image if possible. |
- if (it->second->upload.image()) { |
- DCHECK(it->second->upload.budgeted); |
- DCHECK_GE(bytes_used_, it->second->size); |
- bytes_used_ -= it->second->size; |
- images_pending_deletion_.push_back(it->second->upload.image()); |
- it->second->upload.SetImage(nullptr); |
- it->second->upload.budgeted = false; |
- } |
- |
- // Free the entire entry if necessary. |
- if (ExceedsPreferredCount()) { |
- it = persistent_cache_.Erase(it); |
- } else { |
- ++it; |
- } |
- |
- if (CanFitSize(required_size) && !ExceedsPreferredCount()) |
- return true; |
- } |
- |
- // Preferred count is only used as a guideline when triming the cache. Allow |
- // new elements to be added as long as we are below our size limit. |
- return CanFitSize(required_size); |
-} |
- |
-bool GpuImageDecodeController::CanFitSize(size_t size) const { |
- lock_.AssertAcquired(); |
- |
- size_t bytes_limit; |
- if (memory_state_ == base::MemoryState::NORMAL) { |
- bytes_limit = cached_bytes_limit_; |
- } else if (memory_state_ == base::MemoryState::THROTTLED) { |
- bytes_limit = cached_bytes_limit_ / kThrottledCacheSizeReductionFactor; |
- } else { |
- DCHECK_EQ(base::MemoryState::SUSPENDED, memory_state_); |
- bytes_limit = kSuspendedOrInvisibleMaxGpuImageBytes; |
- } |
- |
- base::CheckedNumeric<uint32_t> new_size(bytes_used_); |
- new_size += size; |
- return new_size.IsValid() && new_size.ValueOrDie() <= bytes_limit; |
-} |
- |
-bool GpuImageDecodeController::ExceedsPreferredCount() const { |
- lock_.AssertAcquired(); |
- |
- size_t items_limit; |
- if (memory_state_ == base::MemoryState::NORMAL) { |
- items_limit = kNormalMaxItemsInCache; |
- } else if (memory_state_ == base::MemoryState::THROTTLED) { |
- items_limit = kThrottledMaxItemsInCache; |
- } else { |
- DCHECK_EQ(base::MemoryState::SUSPENDED, memory_state_); |
- items_limit = kSuspendedMaxItemsInCache; |
- } |
- |
- return persistent_cache_.size() > items_limit; |
-} |
- |
-void GpuImageDecodeController::DecodeImageIfNecessary( |
- const DrawImage& draw_image, |
- ImageData* image_data) { |
- lock_.AssertAcquired(); |
- |
- DCHECK_GT(image_data->decode.ref_count, 0u); |
- |
- if (image_data->decode.decode_failure) { |
- // We have already tried and failed to decode this image. Don't try again. |
- return; |
- } |
- |
- if (image_data->upload.image()) { |
- // We already have an uploaded image, no reason to decode. |
- return; |
- } |
- |
- if (image_data->decode.data() && |
- (image_data->decode.is_locked() || image_data->decode.Lock())) { |
- // We already decoded this, or we just needed to lock, early out. |
- return; |
- } |
- |
- TRACE_EVENT0("cc", "GpuImageDecodeController::DecodeImage"); |
- |
- image_data->decode.ResetData(); |
- std::unique_ptr<base::DiscardableMemory> backing_memory; |
- { |
- base::AutoUnlock unlock(lock_); |
- |
- backing_memory = base::DiscardableMemoryAllocator::GetInstance() |
- ->AllocateLockedDiscardableMemory(image_data->size); |
- |
- switch (image_data->mode) { |
- case DecodedDataMode::CPU: { |
- SkImageInfo image_info = CreateImageInfoForDrawImage( |
- draw_image, image_data->upload_params.fPreScaleMipLevel); |
- // In order to match GPU scaling quality (which uses mip-maps at high |
- // quality), we want to use at most medium filter quality for the |
- // scale. |
- SkPixmap image_pixmap(image_info, backing_memory->data(), |
- image_info.minRowBytes()); |
- // Note that scalePixels falls back to readPixels if the sale is 1x, so |
- // no need to special case that as an optimization. |
- if (!draw_image.image()->scalePixels( |
- image_pixmap, CalculateUploadScaleFilterQuality(draw_image), |
- SkImage::kDisallow_CachingHint)) { |
- backing_memory->Unlock(); |
- backing_memory.reset(); |
- } |
- break; |
- } |
- case DecodedDataMode::GPU: { |
- // TODO(crbug.com/649167): Params should not have changed since initial |
- // sizing. Somehow this still happens. We should investigate and re-add |
- // DCHECKs here to enforce this. |
- |
- if (!draw_image.image()->getDeferredTextureImageData( |
- *context_threadsafe_proxy_.get(), &image_data->upload_params, 1, |
- backing_memory->data())) { |
- backing_memory->Unlock(); |
- backing_memory.reset(); |
- } |
- break; |
- } |
- } |
- } |
- |
- if (image_data->decode.data()) { |
- // An at-raster task decoded this before us. Ingore our decode. |
- return; |
- } |
- |
- if (!backing_memory) { |
- // If |backing_memory| was not populated, we had a non-decodable image. |
- image_data->decode.decode_failure = true; |
- return; |
- } |
- |
- image_data->decode.SetLockedData(std::move(backing_memory)); |
-} |
- |
-void GpuImageDecodeController::UploadImageIfNecessary( |
- const DrawImage& draw_image, |
- ImageData* image_data) { |
- context_->GetLock()->AssertAcquired(); |
- lock_.AssertAcquired(); |
- |
- if (image_data->decode.decode_failure) { |
- // We were unnable to decode this image. Don't try to upload. |
- return; |
- } |
- |
- if (image_data->upload.image()) { |
- // Someone has uploaded this image before us (at raster). |
- return; |
- } |
- |
- TRACE_EVENT0("cc", "GpuImageDecodeController::UploadImage"); |
- DCHECK(image_data->decode.is_locked()); |
- DCHECK_GT(image_data->decode.ref_count, 0u); |
- DCHECK_GT(image_data->upload.ref_count, 0u); |
- |
- // We are about to upload a new image and are holding the context lock. |
- // Ensure that any images which have been marked for deletion are actually |
- // cleaned up so we don't exceed our memory limit during this upload. |
- DeletePendingImages(); |
- |
- sk_sp<SkImage> uploaded_image; |
- { |
- base::AutoUnlock unlock(lock_); |
- switch (image_data->mode) { |
- case DecodedDataMode::CPU: { |
- SkImageInfo image_info = CreateImageInfoForDrawImage( |
- draw_image, image_data->upload_params.fPreScaleMipLevel); |
- SkPixmap pixmap(image_info, image_data->decode.data()->data(), |
- image_info.minRowBytes()); |
- uploaded_image = |
- SkImage::MakeFromRaster(pixmap, [](const void*, void*) {}, nullptr); |
- break; |
- } |
- case DecodedDataMode::GPU: { |
- uploaded_image = SkImage::MakeFromDeferredTextureImageData( |
- context_->GrContext(), image_data->decode.data()->data(), |
- SkBudgeted::kNo); |
- break; |
- } |
- } |
- } |
- image_data->decode.mark_used(); |
- DCHECK(uploaded_image); |
- |
- // At-raster may have decoded this while we were unlocked. If so, ignore our |
- // result. |
- if (!image_data->upload.image()) |
- image_data->upload.SetImage(std::move(uploaded_image)); |
-} |
- |
-scoped_refptr<GpuImageDecodeController::ImageData> |
-GpuImageDecodeController::CreateImageData(const DrawImage& draw_image) { |
- TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("cc.debug"), |
- "GpuImageDecodeController::CreateImageData"); |
- lock_.AssertAcquired(); |
- |
- DecodedDataMode mode; |
- int upload_scale_mip_level = CalculateUploadScaleMipLevel(draw_image); |
- auto params = SkImage::DeferredTextureImageUsageParams( |
- draw_image.matrix(), CalculateUploadScaleFilterQuality(draw_image), |
- upload_scale_mip_level); |
- size_t data_size = draw_image.image()->getDeferredTextureImageData( |
- *context_threadsafe_proxy_.get(), ¶ms, 1, nullptr); |
- |
- if (data_size == 0) { |
- // Can't upload image, too large or other failure. Try to use SW fallback. |
- SkImageInfo image_info = |
- CreateImageInfoForDrawImage(draw_image, upload_scale_mip_level); |
- data_size = image_info.getSafeSize(image_info.minRowBytes()); |
- mode = DecodedDataMode::CPU; |
- } else { |
- mode = DecodedDataMode::GPU; |
- } |
- |
- return make_scoped_refptr(new ImageData(mode, data_size, params)); |
-} |
- |
-void GpuImageDecodeController::DeletePendingImages() { |
- context_->GetLock()->AssertAcquired(); |
- lock_.AssertAcquired(); |
- images_pending_deletion_.clear(); |
-} |
- |
-SkImageInfo GpuImageDecodeController::CreateImageInfoForDrawImage( |
- const DrawImage& draw_image, |
- int upload_scale_mip_level) const { |
- gfx::Size mip_size = |
- CalculateSizeForMipLevel(draw_image, upload_scale_mip_level); |
- return SkImageInfo::Make(mip_size.width(), mip_size.height(), |
- ResourceFormatToClosestSkColorType(format_), |
- kPremul_SkAlphaType); |
-} |
- |
-// Tries to find an ImageData that can be used to draw the provided |
-// |draw_image|. First looks for an exact entry in our |in_use_cache_|. If one |
-// cannot be found, it looks for a compatible entry in our |persistent_cache_|. |
-GpuImageDecodeController::ImageData* |
-GpuImageDecodeController::GetImageDataForDrawImage( |
- const DrawImage& draw_image) { |
- TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("cc.debug"), |
- "GpuImageDecodeController::GetImageDataForDrawImage"); |
- lock_.AssertAcquired(); |
- auto found_in_use = in_use_cache_.find(GenerateInUseCacheKey(draw_image)); |
- if (found_in_use != in_use_cache_.end()) |
- return found_in_use->second.image_data.get(); |
- |
- auto found_persistent = persistent_cache_.Get(draw_image.image()->uniqueID()); |
- if (found_persistent != persistent_cache_.end()) { |
- ImageData* image_data = found_persistent->second.get(); |
- if (IsCompatible(image_data, draw_image)) { |
- return image_data; |
- } else { |
- found_persistent->second->is_orphaned = true; |
- // Call OwnershipChanged before erasing the orphaned task from the |
- // persistent cache. This ensures that if the orphaned task has 0 |
- // references, it is cleaned up safely before it is deleted. |
- OwnershipChanged(draw_image, image_data); |
- persistent_cache_.Erase(found_persistent); |
- } |
- } |
- |
- return nullptr; |
-} |
- |
-// Determines if we can draw the provided |draw_image| using the provided |
-// |image_data|. This is true if the |image_data| is not scaled, or if it |
-// is scaled at an equal or larger scale and equal or larger quality to |
-// the provided |draw_image|. |
-bool GpuImageDecodeController::IsCompatible(const ImageData* image_data, |
- const DrawImage& draw_image) const { |
- bool is_scaled = image_data->upload_params.fPreScaleMipLevel != 0; |
- bool scale_is_compatible = CalculateUploadScaleMipLevel(draw_image) >= |
- image_data->upload_params.fPreScaleMipLevel; |
- bool quality_is_compatible = CalculateUploadScaleFilterQuality(draw_image) <= |
- image_data->upload_params.fQuality; |
- return !is_scaled || (scale_is_compatible && quality_is_compatible); |
-} |
- |
-size_t GpuImageDecodeController::GetDrawImageSizeForTesting( |
- const DrawImage& image) { |
- base::AutoLock lock(lock_); |
- scoped_refptr<ImageData> data = CreateImageData(image); |
- return data->size; |
-} |
- |
-void GpuImageDecodeController::SetImageDecodingFailedForTesting( |
- const DrawImage& image) { |
- base::AutoLock lock(lock_); |
- auto found = persistent_cache_.Peek(image.image()->uniqueID()); |
- DCHECK(found != persistent_cache_.end()); |
- ImageData* image_data = found->second.get(); |
- image_data->decode.decode_failure = true; |
-} |
- |
-bool GpuImageDecodeController::DiscardableIsLockedForTesting( |
- const DrawImage& image) { |
- base::AutoLock lock(lock_); |
- auto found = persistent_cache_.Peek(image.image()->uniqueID()); |
- DCHECK(found != persistent_cache_.end()); |
- ImageData* image_data = found->second.get(); |
- return image_data->decode.is_locked(); |
-} |
- |
-void GpuImageDecodeController::OnMemoryStateChange(base::MemoryState state) { |
- switch (state) { |
- case base::MemoryState::NORMAL: |
- memory_state_ = state; |
- break; |
- case base::MemoryState::THROTTLED: |
- case base::MemoryState::SUSPENDED: { |
- memory_state_ = state; |
- |
- // We've just changed our memory state to a (potentially) more |
- // restrictive one. Re-enforce cache limits. |
- base::AutoLock lock(lock_); |
- EnsureCapacity(0); |
- break; |
- } |
- case base::MemoryState::UNKNOWN: |
- // NOT_REACHED. |
- break; |
- } |
-} |
- |
-} // namespace cc |