| 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
|
|
|