| OLD | NEW |
| (Empty) |
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "cc/tiles/gpu_image_decode_controller.h" | |
| 6 | |
| 7 #include <inttypes.h> | |
| 8 | |
| 9 #include "base/debug/alias.h" | |
| 10 #include "base/memory/discardable_memory_allocator.h" | |
| 11 #include "base/memory/memory_coordinator_client_registry.h" | |
| 12 #include "base/memory/ptr_util.h" | |
| 13 #include "base/metrics/histogram_macros.h" | |
| 14 #include "base/numerics/safe_math.h" | |
| 15 #include "base/strings/stringprintf.h" | |
| 16 #include "base/threading/thread_task_runner_handle.h" | |
| 17 #include "base/trace_event/memory_dump_manager.h" | |
| 18 #include "cc/debug/devtools_instrumentation.h" | |
| 19 #include "cc/output/context_provider.h" | |
| 20 #include "cc/raster/tile_task.h" | |
| 21 #include "cc/resources/resource_format_utils.h" | |
| 22 #include "cc/tiles/mipmap_util.h" | |
| 23 #include "gpu/command_buffer/client/context_support.h" | |
| 24 #include "gpu/command_buffer/client/gles2_interface.h" | |
| 25 #include "gpu_image_decode_controller.h" | |
| 26 #include "skia/ext/texture_handle.h" | |
| 27 #include "third_party/skia/include/core/SkCanvas.h" | |
| 28 #include "third_party/skia/include/core/SkRefCnt.h" | |
| 29 #include "third_party/skia/include/core/SkSurface.h" | |
| 30 #include "third_party/skia/include/gpu/GrContext.h" | |
| 31 #include "third_party/skia/include/gpu/GrTexture.h" | |
| 32 #include "ui/gfx/skia_util.h" | |
| 33 #include "ui/gl/trace_util.h" | |
| 34 | |
| 35 namespace cc { | |
| 36 namespace { | |
| 37 | |
| 38 // The number or entries to keep in the cache, depending on the memory state of | |
| 39 // the system. This limit can be breached by in-use cache items, which cannot | |
| 40 // be deleted. | |
| 41 static const int kNormalMaxItemsInCache = 2000; | |
| 42 static const int kThrottledMaxItemsInCache = 100; | |
| 43 static const int kSuspendedMaxItemsInCache = 0; | |
| 44 | |
| 45 // The factor by which to reduce the GPU memory size of the cache when in the | |
| 46 // THROTTLED memory state. | |
| 47 static const int kThrottledCacheSizeReductionFactor = 2; | |
| 48 | |
| 49 // The maximum size in bytes of GPU memory in the cache while SUSPENDED or not | |
| 50 // visible. This limit can be breached by in-use cache items, which cannot be | |
| 51 // deleted. | |
| 52 static const int kSuspendedOrInvisibleMaxGpuImageBytes = 0; | |
| 53 | |
| 54 // Returns true if an image would not be drawn and should therefore be | |
| 55 // skipped rather than decoded. | |
| 56 bool SkipImage(const DrawImage& draw_image) { | |
| 57 if (!SkIRect::Intersects(draw_image.src_rect(), draw_image.image()->bounds())) | |
| 58 return true; | |
| 59 if (std::abs(draw_image.scale().width()) < | |
| 60 std::numeric_limits<float>::epsilon() || | |
| 61 std::abs(draw_image.scale().height()) < | |
| 62 std::numeric_limits<float>::epsilon()) { | |
| 63 return true; | |
| 64 } | |
| 65 return false; | |
| 66 } | |
| 67 | |
| 68 // Returns the filter quality to use for scaling the image to upload scale. For | |
| 69 // GPU raster, medium and high filter quality are identical for downscales. | |
| 70 // Upload scaling is always a downscale, so cap our filter quality to medium. | |
| 71 SkFilterQuality CalculateUploadScaleFilterQuality(const DrawImage& draw_image) { | |
| 72 return std::min(kMedium_SkFilterQuality, draw_image.filter_quality()); | |
| 73 } | |
| 74 | |
| 75 // Calculate the mip level to upload-scale the image to before uploading. We use | |
| 76 // mip levels rather than exact scales to increase re-use of scaled images. | |
| 77 int CalculateUploadScaleMipLevel(const DrawImage& draw_image) { | |
| 78 // Images which are being clipped will have color-bleeding if scaled. | |
| 79 // TODO(ericrk): Investigate uploading clipped images to handle this case and | |
| 80 // provide further optimization. crbug.com/620899 | |
| 81 if (draw_image.src_rect() != draw_image.image()->bounds()) | |
| 82 return 0; | |
| 83 | |
| 84 gfx::Size base_size(draw_image.image()->width(), | |
| 85 draw_image.image()->height()); | |
| 86 // Ceil our scaled size so that the mip map generated is guaranteed to be | |
| 87 // larger. Take the abs of the scale, as mipmap functions don't handle | |
| 88 // (and aren't impacted by) negative image dimensions. | |
| 89 gfx::Size scaled_size = | |
| 90 gfx::ScaleToCeiledSize(base_size, std::abs(draw_image.scale().width()), | |
| 91 std::abs(draw_image.scale().height())); | |
| 92 | |
| 93 return MipMapUtil::GetLevelForSize(base_size, scaled_size); | |
| 94 } | |
| 95 | |
| 96 // Calculates the scale factor which can be used to scale an image to a given | |
| 97 // mip level. | |
| 98 SkSize CalculateScaleFactorForMipLevel(const DrawImage& draw_image, | |
| 99 int mip_level) { | |
| 100 gfx::Size base_size(draw_image.image()->width(), | |
| 101 draw_image.image()->height()); | |
| 102 return MipMapUtil::GetScaleAdjustmentForLevel(base_size, mip_level); | |
| 103 } | |
| 104 | |
| 105 // Calculates the size of a given mip level. | |
| 106 gfx::Size CalculateSizeForMipLevel(const DrawImage& draw_image, int mip_level) { | |
| 107 gfx::Size base_size(draw_image.image()->width(), | |
| 108 draw_image.image()->height()); | |
| 109 return MipMapUtil::GetSizeForLevel(base_size, mip_level); | |
| 110 } | |
| 111 | |
| 112 // Generates a uint64_t which uniquely identifies a DrawImage for the purposes | |
| 113 // of the |in_use_cache_|. The key is generated as follows: | |
| 114 // ╔══════════════════════╤═══════════╤═══════════╗ | |
| 115 // ║ image_id │ mip_level │ quality ║ | |
| 116 // ╚════════32═bits═══════╧══16═bits══╧══16═bits══╝ | |
| 117 uint64_t GenerateInUseCacheKey(const DrawImage& draw_image) { | |
| 118 static_assert( | |
| 119 kLast_SkFilterQuality <= std::numeric_limits<uint16_t>::max(), | |
| 120 "InUseCacheKey depends on SkFilterQuality fitting in a uint16_t."); | |
| 121 | |
| 122 SkFilterQuality filter_quality = | |
| 123 CalculateUploadScaleFilterQuality(draw_image); | |
| 124 DCHECK_LE(filter_quality, kLast_SkFilterQuality); | |
| 125 | |
| 126 // An image has at most log_2(max(width, height)) mip levels, so given our | |
| 127 // usage of 32-bit sizes for images, key.mip_level is at most 31. | |
| 128 int32_t mip_level = CalculateUploadScaleMipLevel(draw_image); | |
| 129 DCHECK_LT(mip_level, 32); | |
| 130 | |
| 131 return (static_cast<uint64_t>(draw_image.image()->uniqueID()) << 32) | | |
| 132 (mip_level << 16) | filter_quality; | |
| 133 } | |
| 134 | |
| 135 } // namespace | |
| 136 | |
| 137 GpuImageDecodeController::InUseCacheEntry::InUseCacheEntry( | |
| 138 scoped_refptr<ImageData> image_data) | |
| 139 : image_data(std::move(image_data)) {} | |
| 140 GpuImageDecodeController::InUseCacheEntry::InUseCacheEntry( | |
| 141 const InUseCacheEntry&) = default; | |
| 142 GpuImageDecodeController::InUseCacheEntry::InUseCacheEntry(InUseCacheEntry&&) = | |
| 143 default; | |
| 144 GpuImageDecodeController::InUseCacheEntry::~InUseCacheEntry() = default; | |
| 145 | |
| 146 // Task which decodes an image and stores the result in discardable memory. | |
| 147 // This task does not use GPU resources and can be run on any thread. | |
| 148 class ImageDecodeTaskImpl : public TileTask { | |
| 149 public: | |
| 150 ImageDecodeTaskImpl(GpuImageDecodeController* controller, | |
| 151 const DrawImage& draw_image, | |
| 152 const ImageDecodeController::TracingInfo& tracing_info) | |
| 153 : TileTask(true), | |
| 154 controller_(controller), | |
| 155 image_(draw_image), | |
| 156 tracing_info_(tracing_info) { | |
| 157 DCHECK(!SkipImage(draw_image)); | |
| 158 } | |
| 159 | |
| 160 // Overridden from Task: | |
| 161 void RunOnWorkerThread() override { | |
| 162 TRACE_EVENT2("cc", "ImageDecodeTaskImpl::RunOnWorkerThread", "mode", "gpu", | |
| 163 "source_prepare_tiles_id", tracing_info_.prepare_tiles_id); | |
| 164 devtools_instrumentation::ScopedImageDecodeTask image_decode_task( | |
| 165 image_.image().get(), | |
| 166 devtools_instrumentation::ScopedImageDecodeTask::GPU); | |
| 167 controller_->DecodeImage(image_); | |
| 168 } | |
| 169 | |
| 170 // Overridden from TileTask: | |
| 171 void OnTaskCompleted() override { | |
| 172 controller_->OnImageDecodeTaskCompleted(image_); | |
| 173 } | |
| 174 | |
| 175 protected: | |
| 176 ~ImageDecodeTaskImpl() override {} | |
| 177 | |
| 178 private: | |
| 179 GpuImageDecodeController* controller_; | |
| 180 DrawImage image_; | |
| 181 const ImageDecodeController::TracingInfo tracing_info_; | |
| 182 | |
| 183 DISALLOW_COPY_AND_ASSIGN(ImageDecodeTaskImpl); | |
| 184 }; | |
| 185 | |
| 186 // Task which creates an image from decoded data. Typically this involves | |
| 187 // uploading data to the GPU, which requires this task be run on the non- | |
| 188 // concurrent thread. | |
| 189 class ImageUploadTaskImpl : public TileTask { | |
| 190 public: | |
| 191 ImageUploadTaskImpl(GpuImageDecodeController* controller, | |
| 192 const DrawImage& draw_image, | |
| 193 scoped_refptr<TileTask> decode_dependency, | |
| 194 const ImageDecodeController::TracingInfo& tracing_info) | |
| 195 : TileTask(false), | |
| 196 controller_(controller), | |
| 197 image_(draw_image), | |
| 198 tracing_info_(tracing_info) { | |
| 199 DCHECK(!SkipImage(draw_image)); | |
| 200 // If an image is already decoded and locked, we will not generate a | |
| 201 // decode task. | |
| 202 if (decode_dependency) | |
| 203 dependencies_.push_back(std::move(decode_dependency)); | |
| 204 } | |
| 205 | |
| 206 // Override from Task: | |
| 207 void RunOnWorkerThread() override { | |
| 208 TRACE_EVENT2("cc", "ImageUploadTaskImpl::RunOnWorkerThread", "mode", "gpu", | |
| 209 "source_prepare_tiles_id", tracing_info_.prepare_tiles_id); | |
| 210 controller_->UploadImage(image_); | |
| 211 } | |
| 212 | |
| 213 // Overridden from TileTask: | |
| 214 void OnTaskCompleted() override { | |
| 215 controller_->OnImageUploadTaskCompleted(image_); | |
| 216 } | |
| 217 | |
| 218 protected: | |
| 219 ~ImageUploadTaskImpl() override {} | |
| 220 | |
| 221 private: | |
| 222 GpuImageDecodeController* controller_; | |
| 223 DrawImage image_; | |
| 224 const ImageDecodeController::TracingInfo tracing_info_; | |
| 225 | |
| 226 DISALLOW_COPY_AND_ASSIGN(ImageUploadTaskImpl); | |
| 227 }; | |
| 228 | |
| 229 GpuImageDecodeController::DecodedImageData::DecodedImageData() = default; | |
| 230 GpuImageDecodeController::DecodedImageData::~DecodedImageData() { | |
| 231 ResetData(); | |
| 232 } | |
| 233 | |
| 234 bool GpuImageDecodeController::DecodedImageData::Lock() { | |
| 235 DCHECK(!is_locked_); | |
| 236 is_locked_ = data_->Lock(); | |
| 237 if (is_locked_) | |
| 238 ++usage_stats_.lock_count; | |
| 239 return is_locked_; | |
| 240 } | |
| 241 | |
| 242 void GpuImageDecodeController::DecodedImageData::Unlock() { | |
| 243 DCHECK(is_locked_); | |
| 244 data_->Unlock(); | |
| 245 if (usage_stats_.lock_count == 1) | |
| 246 usage_stats_.first_lock_wasted = !usage_stats_.used; | |
| 247 is_locked_ = false; | |
| 248 } | |
| 249 | |
| 250 void GpuImageDecodeController::DecodedImageData::SetLockedData( | |
| 251 std::unique_ptr<base::DiscardableMemory> data) { | |
| 252 DCHECK(!is_locked_); | |
| 253 DCHECK(data); | |
| 254 DCHECK(!data_); | |
| 255 data_ = std::move(data); | |
| 256 is_locked_ = true; | |
| 257 } | |
| 258 | |
| 259 void GpuImageDecodeController::DecodedImageData::ResetData() { | |
| 260 DCHECK(!is_locked_); | |
| 261 if (data_) | |
| 262 ReportUsageStats(); | |
| 263 data_ = nullptr; | |
| 264 usage_stats_ = UsageStats(); | |
| 265 } | |
| 266 | |
| 267 void GpuImageDecodeController::DecodedImageData::ReportUsageStats() const { | |
| 268 // lock_count │ used │ result state | |
| 269 // ═══════════╪═══════╪══════════════════ | |
| 270 // 1 │ false │ WASTED_ONCE | |
| 271 // 1 │ true │ USED_ONCE | |
| 272 // >1 │ false │ WASTED_RELOCKED | |
| 273 // >1 │ true │ USED_RELOCKED | |
| 274 // Note that it's important not to reorder the following enums, since the | |
| 275 // numerical values are used in the histogram code. | |
| 276 enum State : int { | |
| 277 DECODED_IMAGE_STATE_WASTED_ONCE, | |
| 278 DECODED_IMAGE_STATE_USED_ONCE, | |
| 279 DECODED_IMAGE_STATE_WASTED_RELOCKED, | |
| 280 DECODED_IMAGE_STATE_USED_RELOCKED, | |
| 281 DECODED_IMAGE_STATE_COUNT | |
| 282 } state = DECODED_IMAGE_STATE_WASTED_ONCE; | |
| 283 | |
| 284 if (usage_stats_.lock_count == 1) { | |
| 285 if (usage_stats_.used) | |
| 286 state = DECODED_IMAGE_STATE_USED_ONCE; | |
| 287 else | |
| 288 state = DECODED_IMAGE_STATE_WASTED_ONCE; | |
| 289 } else { | |
| 290 if (usage_stats_.used) | |
| 291 state = DECODED_IMAGE_STATE_USED_RELOCKED; | |
| 292 else | |
| 293 state = DECODED_IMAGE_STATE_WASTED_RELOCKED; | |
| 294 } | |
| 295 | |
| 296 UMA_HISTOGRAM_ENUMERATION("Renderer4.GpuImageDecodeState", state, | |
| 297 DECODED_IMAGE_STATE_COUNT); | |
| 298 UMA_HISTOGRAM_BOOLEAN("Renderer4.GpuImageDecodeState.FirstLockWasted", | |
| 299 usage_stats_.first_lock_wasted); | |
| 300 } | |
| 301 | |
| 302 GpuImageDecodeController::UploadedImageData::UploadedImageData() = default; | |
| 303 GpuImageDecodeController::UploadedImageData::~UploadedImageData() { | |
| 304 SetImage(nullptr); | |
| 305 } | |
| 306 | |
| 307 void GpuImageDecodeController::UploadedImageData::SetImage( | |
| 308 sk_sp<SkImage> image) { | |
| 309 DCHECK(!image_ || !image); | |
| 310 if (image_) { | |
| 311 ReportUsageStats(); | |
| 312 usage_stats_ = UsageStats(); | |
| 313 } | |
| 314 image_ = std::move(image); | |
| 315 } | |
| 316 | |
| 317 void GpuImageDecodeController::UploadedImageData::ReportUsageStats() const { | |
| 318 UMA_HISTOGRAM_BOOLEAN("Renderer4.GpuImageUploadState.Used", | |
| 319 usage_stats_.used); | |
| 320 UMA_HISTOGRAM_BOOLEAN("Renderer4.GpuImageUploadState.FirstRefWasted", | |
| 321 usage_stats_.first_ref_wasted); | |
| 322 } | |
| 323 | |
| 324 GpuImageDecodeController::ImageData::ImageData( | |
| 325 DecodedDataMode mode, | |
| 326 size_t size, | |
| 327 const SkImage::DeferredTextureImageUsageParams& upload_params) | |
| 328 : mode(mode), size(size), upload_params(upload_params) {} | |
| 329 | |
| 330 GpuImageDecodeController::ImageData::~ImageData() { | |
| 331 // We should never delete ImageData while it is in use or before it has been | |
| 332 // cleaned up. | |
| 333 DCHECK_EQ(0u, upload.ref_count); | |
| 334 DCHECK_EQ(0u, decode.ref_count); | |
| 335 DCHECK_EQ(false, decode.is_locked()); | |
| 336 // This should always be cleaned up before deleting the image, as it needs to | |
| 337 // be freed with the GL context lock held. | |
| 338 DCHECK(!upload.image()); | |
| 339 } | |
| 340 | |
| 341 GpuImageDecodeController::GpuImageDecodeController(ContextProvider* context, | |
| 342 ResourceFormat decode_format, | |
| 343 size_t max_gpu_image_bytes) | |
| 344 : format_(decode_format), | |
| 345 context_(context), | |
| 346 persistent_cache_(PersistentCache::NO_AUTO_EVICT), | |
| 347 normal_max_gpu_image_bytes_(max_gpu_image_bytes) { | |
| 348 // Acquire the context_lock so that we can safely retrieve the | |
| 349 // GrContextThreadSafeProxy. This proxy can then be used with no lock held. | |
| 350 { | |
| 351 ContextProvider::ScopedContextLock context_lock(context_); | |
| 352 context_threadsafe_proxy_ = sk_sp<GrContextThreadSafeProxy>( | |
| 353 context->GrContext()->threadSafeProxy()); | |
| 354 } | |
| 355 | |
| 356 // In certain cases, ThreadTaskRunnerHandle isn't set (Android Webview). | |
| 357 // Don't register a dump provider in these cases. | |
| 358 if (base::ThreadTaskRunnerHandle::IsSet()) { | |
| 359 base::trace_event::MemoryDumpManager::GetInstance()->RegisterDumpProvider( | |
| 360 this, "cc::GpuImageDecodeController", | |
| 361 base::ThreadTaskRunnerHandle::Get()); | |
| 362 } | |
| 363 // Register this component with base::MemoryCoordinatorClientRegistry. | |
| 364 base::MemoryCoordinatorClientRegistry::GetInstance()->Register(this); | |
| 365 } | |
| 366 | |
| 367 GpuImageDecodeController::~GpuImageDecodeController() { | |
| 368 // SetShouldAggressivelyFreeResources will zero our limits and free all | |
| 369 // outstanding image memory. | |
| 370 SetShouldAggressivelyFreeResources(true); | |
| 371 | |
| 372 // It is safe to unregister, even if we didn't register in the constructor. | |
| 373 base::trace_event::MemoryDumpManager::GetInstance()->UnregisterDumpProvider( | |
| 374 this); | |
| 375 // Unregister this component with memory_coordinator::ClientRegistry. | |
| 376 base::MemoryCoordinatorClientRegistry::GetInstance()->Unregister(this); | |
| 377 } | |
| 378 | |
| 379 bool GpuImageDecodeController::GetTaskForImageAndRef( | |
| 380 const DrawImage& draw_image, | |
| 381 const TracingInfo& tracing_info, | |
| 382 scoped_refptr<TileTask>* task) { | |
| 383 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("cc.debug"), | |
| 384 "GpuImageDecodeController::GetTaskForImageAndRef"); | |
| 385 if (SkipImage(draw_image)) { | |
| 386 *task = nullptr; | |
| 387 return false; | |
| 388 } | |
| 389 | |
| 390 base::AutoLock lock(lock_); | |
| 391 const auto image_id = draw_image.image()->uniqueID(); | |
| 392 ImageData* image_data = GetImageDataForDrawImage(draw_image); | |
| 393 scoped_refptr<ImageData> new_data; | |
| 394 if (!image_data) { | |
| 395 // We need an ImageData, create one now. | |
| 396 new_data = CreateImageData(draw_image); | |
| 397 image_data = new_data.get(); | |
| 398 } else if (image_data->is_at_raster) { | |
| 399 // Image is at-raster, just return, this usage will be at-raster as well. | |
| 400 *task = nullptr; | |
| 401 return false; | |
| 402 } else if (image_data->decode.decode_failure) { | |
| 403 // We have already tried and failed to decode this image, so just return. | |
| 404 *task = nullptr; | |
| 405 return false; | |
| 406 } else if (image_data->upload.image()) { | |
| 407 // The image is already uploaded, ref and return. | |
| 408 RefImage(draw_image); | |
| 409 *task = nullptr; | |
| 410 return true; | |
| 411 } else if (image_data->upload.task) { | |
| 412 // We had an existing upload task, ref the image and return the task. | |
| 413 RefImage(draw_image); | |
| 414 *task = image_data->upload.task; | |
| 415 return true; | |
| 416 } | |
| 417 | |
| 418 // Ensure that the image we're about to decode/upload will fit in memory. | |
| 419 if (!EnsureCapacity(image_data->size)) { | |
| 420 // Image will not fit, do an at-raster decode. | |
| 421 *task = nullptr; | |
| 422 return false; | |
| 423 } | |
| 424 | |
| 425 // If we had to create new image data, add it to our map now that we know it | |
| 426 // will fit. | |
| 427 if (new_data) | |
| 428 persistent_cache_.Put(image_id, std::move(new_data)); | |
| 429 | |
| 430 // Ref image and create a upload and decode tasks. We will release this ref | |
| 431 // in UploadTaskCompleted. | |
| 432 RefImage(draw_image); | |
| 433 *task = make_scoped_refptr(new ImageUploadTaskImpl( | |
| 434 this, draw_image, GetImageDecodeTaskAndRef(draw_image, tracing_info), | |
| 435 tracing_info)); | |
| 436 image_data->upload.task = *task; | |
| 437 | |
| 438 // Ref the image again - this ref is owned by the caller, and it is their | |
| 439 // responsibility to release it by calling UnrefImage. | |
| 440 RefImage(draw_image); | |
| 441 return true; | |
| 442 } | |
| 443 | |
| 444 void GpuImageDecodeController::UnrefImage(const DrawImage& draw_image) { | |
| 445 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("cc.debug"), | |
| 446 "GpuImageDecodeController::UnrefImage"); | |
| 447 base::AutoLock lock(lock_); | |
| 448 UnrefImageInternal(draw_image); | |
| 449 } | |
| 450 | |
| 451 DecodedDrawImage GpuImageDecodeController::GetDecodedImageForDraw( | |
| 452 const DrawImage& draw_image) { | |
| 453 TRACE_EVENT0("cc", "GpuImageDecodeController::GetDecodedImageForDraw"); | |
| 454 | |
| 455 // We are being called during raster. The context lock must already be | |
| 456 // acquired by the caller. | |
| 457 context_->GetLock()->AssertAcquired(); | |
| 458 | |
| 459 if (SkipImage(draw_image)) | |
| 460 return DecodedDrawImage(nullptr, draw_image.filter_quality()); | |
| 461 | |
| 462 base::AutoLock lock(lock_); | |
| 463 ImageData* image_data = GetImageDataForDrawImage(draw_image); | |
| 464 if (!image_data) { | |
| 465 // We didn't find the image, create a new entry. | |
| 466 auto data = CreateImageData(draw_image); | |
| 467 image_data = data.get(); | |
| 468 persistent_cache_.Put(draw_image.image()->uniqueID(), std::move(data)); | |
| 469 } | |
| 470 | |
| 471 if (!image_data->upload.budgeted) { | |
| 472 // If image data is not budgeted by this point, it is at-raster. | |
| 473 image_data->is_at_raster = true; | |
| 474 } | |
| 475 | |
| 476 // Ref the image and decode so that they stay alive while we are | |
| 477 // decoding/uploading. | |
| 478 RefImage(draw_image); | |
| 479 RefImageDecode(draw_image); | |
| 480 | |
| 481 // We may or may not need to decode and upload the image we've found, the | |
| 482 // following functions early-out to if we already decoded. | |
| 483 DecodeImageIfNecessary(draw_image, image_data); | |
| 484 UploadImageIfNecessary(draw_image, image_data); | |
| 485 // Unref the image decode, but not the image. The image ref will be released | |
| 486 // in DrawWithImageFinished. | |
| 487 UnrefImageDecode(draw_image); | |
| 488 | |
| 489 sk_sp<SkImage> image = image_data->upload.image(); | |
| 490 image_data->upload.mark_used(); | |
| 491 DCHECK(image || image_data->decode.decode_failure); | |
| 492 | |
| 493 SkSize scale_factor = CalculateScaleFactorForMipLevel( | |
| 494 draw_image, image_data->upload_params.fPreScaleMipLevel); | |
| 495 DecodedDrawImage decoded_draw_image(std::move(image), SkSize(), scale_factor, | |
| 496 draw_image.filter_quality()); | |
| 497 decoded_draw_image.set_at_raster_decode(image_data->is_at_raster); | |
| 498 return decoded_draw_image; | |
| 499 } | |
| 500 | |
| 501 void GpuImageDecodeController::DrawWithImageFinished( | |
| 502 const DrawImage& draw_image, | |
| 503 const DecodedDrawImage& decoded_draw_image) { | |
| 504 TRACE_EVENT0("cc", "GpuImageDecodeController::DrawWithImageFinished"); | |
| 505 | |
| 506 // We are being called during raster. The context lock must already be | |
| 507 // acquired by the caller. | |
| 508 context_->GetLock()->AssertAcquired(); | |
| 509 | |
| 510 if (SkipImage(draw_image)) | |
| 511 return; | |
| 512 | |
| 513 base::AutoLock lock(lock_); | |
| 514 UnrefImageInternal(draw_image); | |
| 515 | |
| 516 // We are mid-draw and holding the context lock, ensure we clean up any | |
| 517 // textures (especially at-raster), which may have just been marked for | |
| 518 // deletion by UnrefImage. | |
| 519 DeletePendingImages(); | |
| 520 } | |
| 521 | |
| 522 void GpuImageDecodeController::ReduceCacheUsage() { | |
| 523 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("cc.debug"), | |
| 524 "GpuImageDecodeController::ReduceCacheUsage"); | |
| 525 base::AutoLock lock(lock_); | |
| 526 EnsureCapacity(0); | |
| 527 } | |
| 528 | |
| 529 void GpuImageDecodeController::SetShouldAggressivelyFreeResources( | |
| 530 bool aggressively_free_resources) { | |
| 531 TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("cc.debug"), | |
| 532 "GpuImageDecodeController::SetShouldAggressivelyFreeResources", | |
| 533 "agressive_free_resources", aggressively_free_resources); | |
| 534 if (aggressively_free_resources) { | |
| 535 ContextProvider::ScopedContextLock context_lock(context_); | |
| 536 base::AutoLock lock(lock_); | |
| 537 // We want to keep as little in our cache as possible. Set our memory limit | |
| 538 // to zero and EnsureCapacity to clean up memory. | |
| 539 cached_bytes_limit_ = kSuspendedOrInvisibleMaxGpuImageBytes; | |
| 540 EnsureCapacity(0); | |
| 541 | |
| 542 // We are holding the context lock, so finish cleaning up deleted images | |
| 543 // now. | |
| 544 DeletePendingImages(); | |
| 545 } else { | |
| 546 base::AutoLock lock(lock_); | |
| 547 cached_bytes_limit_ = normal_max_gpu_image_bytes_; | |
| 548 } | |
| 549 } | |
| 550 | |
| 551 bool GpuImageDecodeController::OnMemoryDump( | |
| 552 const base::trace_event::MemoryDumpArgs& args, | |
| 553 base::trace_event::ProcessMemoryDump* pmd) { | |
| 554 using base::trace_event::MemoryAllocatorDump; | |
| 555 using base::trace_event::MemoryAllocatorDumpGuid; | |
| 556 using base::trace_event::MemoryDumpLevelOfDetail; | |
| 557 | |
| 558 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("cc.debug"), | |
| 559 "GpuImageDecodeController::OnMemoryDump"); | |
| 560 | |
| 561 if (args.level_of_detail == MemoryDumpLevelOfDetail::BACKGROUND) { | |
| 562 std::string dump_name = | |
| 563 base::StringPrintf("cc/image_memory/controller_0x%" PRIXPTR, | |
| 564 reinterpret_cast<uintptr_t>(this)); | |
| 565 MemoryAllocatorDump* dump = pmd->CreateAllocatorDump(dump_name); | |
| 566 dump->AddScalar(MemoryAllocatorDump::kNameSize, | |
| 567 MemoryAllocatorDump::kUnitsBytes, bytes_used_); | |
| 568 | |
| 569 // Early out, no need for more detail in a BACKGROUND dump. | |
| 570 return true; | |
| 571 } | |
| 572 | |
| 573 for (const auto& image_pair : persistent_cache_) { | |
| 574 const ImageData* image_data = image_pair.second.get(); | |
| 575 const uint32_t image_id = image_pair.first; | |
| 576 | |
| 577 // If we have discardable decoded data, dump this here. | |
| 578 if (image_data->decode.data()) { | |
| 579 std::string discardable_dump_name = base::StringPrintf( | |
| 580 "cc/image_memory/controller_0x%" PRIXPTR "/discardable/image_%d", | |
| 581 reinterpret_cast<uintptr_t>(this), image_id); | |
| 582 MemoryAllocatorDump* dump = | |
| 583 image_data->decode.data()->CreateMemoryAllocatorDump( | |
| 584 discardable_dump_name.c_str(), pmd); | |
| 585 // If our image is locked, dump the "locked_size" as an additional | |
| 586 // column. | |
| 587 // This lets us see the amount of discardable which is contributing to | |
| 588 // memory pressure. | |
| 589 if (image_data->decode.is_locked()) { | |
| 590 dump->AddScalar("locked_size", MemoryAllocatorDump::kUnitsBytes, | |
| 591 image_data->size); | |
| 592 } | |
| 593 } | |
| 594 | |
| 595 // If we have an uploaded image (that is actually on the GPU, not just a | |
| 596 // CPU | |
| 597 // wrapper), upload it here. | |
| 598 if (image_data->upload.image() && | |
| 599 image_data->mode == DecodedDataMode::GPU) { | |
| 600 std::string gpu_dump_name = base::StringPrintf( | |
| 601 "cc/image_memory/controller_0x%" PRIXPTR "/gpu/image_%d", | |
| 602 reinterpret_cast<uintptr_t>(this), image_id); | |
| 603 MemoryAllocatorDump* dump = pmd->CreateAllocatorDump(gpu_dump_name); | |
| 604 dump->AddScalar(MemoryAllocatorDump::kNameSize, | |
| 605 MemoryAllocatorDump::kUnitsBytes, image_data->size); | |
| 606 | |
| 607 // Create a global shred GUID to associate this data with its GPU | |
| 608 // process | |
| 609 // counterpart. | |
| 610 GLuint gl_id = skia::GrBackendObjectToGrGLTextureInfo( | |
| 611 image_data->upload.image()->getTextureHandle( | |
| 612 false /* flushPendingGrContextIO */)) | |
| 613 ->fID; | |
| 614 MemoryAllocatorDumpGuid guid = gl::GetGLTextureClientGUIDForTracing( | |
| 615 context_->ContextSupport()->ShareGroupTracingGUID(), gl_id); | |
| 616 | |
| 617 // kImportance is somewhat arbitrary - we chose 3 to be higher than the | |
| 618 // value used in the GPU process (1), and Skia (2), causing us to appear | |
| 619 // as the owner in memory traces. | |
| 620 const int kImportance = 3; | |
| 621 pmd->CreateSharedGlobalAllocatorDump(guid); | |
| 622 pmd->AddOwnershipEdge(dump->guid(), guid, kImportance); | |
| 623 } | |
| 624 } | |
| 625 | |
| 626 return true; | |
| 627 } | |
| 628 | |
| 629 void GpuImageDecodeController::DecodeImage(const DrawImage& draw_image) { | |
| 630 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("cc.debug"), | |
| 631 "GpuImageDecodeController::DecodeImage"); | |
| 632 base::AutoLock lock(lock_); | |
| 633 ImageData* image_data = GetImageDataForDrawImage(draw_image); | |
| 634 DCHECK(image_data); | |
| 635 DCHECK(!image_data->is_at_raster); | |
| 636 DecodeImageIfNecessary(draw_image, image_data); | |
| 637 } | |
| 638 | |
| 639 void GpuImageDecodeController::UploadImage(const DrawImage& draw_image) { | |
| 640 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("cc.debug"), | |
| 641 "GpuImageDecodeController::UploadImage"); | |
| 642 ContextProvider::ScopedContextLock context_lock(context_); | |
| 643 base::AutoLock lock(lock_); | |
| 644 ImageData* image_data = GetImageDataForDrawImage(draw_image); | |
| 645 DCHECK(image_data); | |
| 646 DCHECK(!image_data->is_at_raster); | |
| 647 UploadImageIfNecessary(draw_image, image_data); | |
| 648 } | |
| 649 | |
| 650 void GpuImageDecodeController::OnImageDecodeTaskCompleted( | |
| 651 const DrawImage& draw_image) { | |
| 652 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("cc.debug"), | |
| 653 "GpuImageDecodeController::OnImageDecodeTaskCompleted"); | |
| 654 base::AutoLock lock(lock_); | |
| 655 // Decode task is complete, remove our reference to it. | |
| 656 ImageData* image_data = GetImageDataForDrawImage(draw_image); | |
| 657 DCHECK(image_data); | |
| 658 DCHECK(image_data->decode.task); | |
| 659 image_data->decode.task = nullptr; | |
| 660 | |
| 661 // While the decode task is active, we keep a ref on the decoded data. | |
| 662 // Release that ref now. | |
| 663 UnrefImageDecode(draw_image); | |
| 664 } | |
| 665 | |
| 666 void GpuImageDecodeController::OnImageUploadTaskCompleted( | |
| 667 const DrawImage& draw_image) { | |
| 668 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("cc.debug"), | |
| 669 "GpuImageDecodeController::OnImageUploadTaskCompleted"); | |
| 670 base::AutoLock lock(lock_); | |
| 671 // Upload task is complete, remove our reference to it. | |
| 672 ImageData* image_data = GetImageDataForDrawImage(draw_image); | |
| 673 DCHECK(image_data); | |
| 674 DCHECK(image_data->upload.task); | |
| 675 image_data->upload.task = nullptr; | |
| 676 | |
| 677 // While the upload task is active, we keep a ref on both the image it will be | |
| 678 // populating, as well as the decode it needs to populate it. Release these | |
| 679 // refs now. | |
| 680 UnrefImageDecode(draw_image); | |
| 681 UnrefImageInternal(draw_image); | |
| 682 } | |
| 683 | |
| 684 // Checks if an existing image decode exists. If not, returns a task to produce | |
| 685 // the requested decode. | |
| 686 scoped_refptr<TileTask> GpuImageDecodeController::GetImageDecodeTaskAndRef( | |
| 687 const DrawImage& draw_image, | |
| 688 const TracingInfo& tracing_info) { | |
| 689 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("cc.debug"), | |
| 690 "GpuImageDecodeController::GetImageDecodeTaskAndRef"); | |
| 691 lock_.AssertAcquired(); | |
| 692 | |
| 693 // This ref is kept alive while an upload task may need this decode. We | |
| 694 // release this ref in UploadTaskCompleted. | |
| 695 RefImageDecode(draw_image); | |
| 696 | |
| 697 ImageData* image_data = GetImageDataForDrawImage(draw_image); | |
| 698 DCHECK(image_data); | |
| 699 if (image_data->decode.is_locked()) { | |
| 700 // We should never be creating a decode task for an at raster image. | |
| 701 DCHECK(!image_data->is_at_raster); | |
| 702 // We should never be creating a decode for an already-uploaded image. | |
| 703 DCHECK(!image_data->upload.image()); | |
| 704 return nullptr; | |
| 705 } | |
| 706 | |
| 707 // We didn't have an existing locked image, create a task to lock or decode. | |
| 708 scoped_refptr<TileTask>& existing_task = image_data->decode.task; | |
| 709 if (!existing_task) { | |
| 710 // Ref image decode and create a decode task. This ref will be released in | |
| 711 // DecodeTaskCompleted. | |
| 712 RefImageDecode(draw_image); | |
| 713 existing_task = make_scoped_refptr( | |
| 714 new ImageDecodeTaskImpl(this, draw_image, tracing_info)); | |
| 715 } | |
| 716 return existing_task; | |
| 717 } | |
| 718 | |
| 719 void GpuImageDecodeController::RefImageDecode(const DrawImage& draw_image) { | |
| 720 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("cc.debug"), | |
| 721 "GpuImageDecodeController::RefImageDecode"); | |
| 722 lock_.AssertAcquired(); | |
| 723 auto found = in_use_cache_.find(GenerateInUseCacheKey(draw_image)); | |
| 724 DCHECK(found != in_use_cache_.end()); | |
| 725 ++found->second.ref_count; | |
| 726 ++found->second.image_data->decode.ref_count; | |
| 727 OwnershipChanged(draw_image, found->second.image_data.get()); | |
| 728 } | |
| 729 | |
| 730 void GpuImageDecodeController::UnrefImageDecode(const DrawImage& draw_image) { | |
| 731 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("cc.debug"), | |
| 732 "GpuImageDecodeController::UnrefImageDecode"); | |
| 733 lock_.AssertAcquired(); | |
| 734 auto found = in_use_cache_.find(GenerateInUseCacheKey(draw_image)); | |
| 735 DCHECK(found != in_use_cache_.end()); | |
| 736 DCHECK_GT(found->second.image_data->decode.ref_count, 0u); | |
| 737 DCHECK_GT(found->second.ref_count, 0u); | |
| 738 --found->second.ref_count; | |
| 739 --found->second.image_data->decode.ref_count; | |
| 740 OwnershipChanged(draw_image, found->second.image_data.get()); | |
| 741 if (found->second.ref_count == 0u) { | |
| 742 in_use_cache_.erase(found); | |
| 743 } | |
| 744 } | |
| 745 | |
| 746 void GpuImageDecodeController::RefImage(const DrawImage& draw_image) { | |
| 747 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("cc.debug"), | |
| 748 "GpuImageDecodeController::RefImage"); | |
| 749 lock_.AssertAcquired(); | |
| 750 InUseCacheKey key = GenerateInUseCacheKey(draw_image); | |
| 751 auto found = in_use_cache_.find(key); | |
| 752 | |
| 753 // If no secondary cache entry was found for the given |draw_image|, then | |
| 754 // the draw_image only exists in the |persistent_cache_|. Create an in-use | |
| 755 // cache entry now. | |
| 756 if (found == in_use_cache_.end()) { | |
| 757 auto found_image = persistent_cache_.Peek(draw_image.image()->uniqueID()); | |
| 758 DCHECK(found_image != persistent_cache_.end()); | |
| 759 DCHECK(found_image->second->upload_params.fPreScaleMipLevel <= | |
| 760 CalculateUploadScaleMipLevel(draw_image)); | |
| 761 found = in_use_cache_ | |
| 762 .insert(InUseCache::value_type( | |
| 763 key, InUseCacheEntry(found_image->second))) | |
| 764 .first; | |
| 765 } | |
| 766 | |
| 767 DCHECK(found != in_use_cache_.end()); | |
| 768 ++found->second.ref_count; | |
| 769 ++found->second.image_data->upload.ref_count; | |
| 770 OwnershipChanged(draw_image, found->second.image_data.get()); | |
| 771 } | |
| 772 | |
| 773 void GpuImageDecodeController::UnrefImageInternal(const DrawImage& draw_image) { | |
| 774 lock_.AssertAcquired(); | |
| 775 auto found = in_use_cache_.find(GenerateInUseCacheKey(draw_image)); | |
| 776 DCHECK(found != in_use_cache_.end()); | |
| 777 DCHECK_GT(found->second.image_data->upload.ref_count, 0u); | |
| 778 DCHECK_GT(found->second.ref_count, 0u); | |
| 779 --found->second.ref_count; | |
| 780 --found->second.image_data->upload.ref_count; | |
| 781 OwnershipChanged(draw_image, found->second.image_data.get()); | |
| 782 if (found->second.ref_count == 0u) { | |
| 783 in_use_cache_.erase(found); | |
| 784 } | |
| 785 } | |
| 786 | |
| 787 // Called any time an image or decode ref count changes. Takes care of any | |
| 788 // necessary memory budget book-keeping and cleanup. | |
| 789 void GpuImageDecodeController::OwnershipChanged(const DrawImage& draw_image, | |
| 790 ImageData* image_data) { | |
| 791 lock_.AssertAcquired(); | |
| 792 | |
| 793 bool has_any_refs = | |
| 794 image_data->upload.ref_count > 0 || image_data->decode.ref_count > 0; | |
| 795 | |
| 796 // Don't keep around completely empty images. This can happen if an image's | |
| 797 // decode/upload tasks were both cancelled before completing. | |
| 798 if (!has_any_refs && !image_data->upload.image() && | |
| 799 !image_data->decode.data()) { | |
| 800 auto found_persistent = | |
| 801 persistent_cache_.Peek(draw_image.image()->uniqueID()); | |
| 802 if (found_persistent != persistent_cache_.end()) | |
| 803 persistent_cache_.Erase(found_persistent); | |
| 804 } | |
| 805 | |
| 806 // Don't keep around orphaned images. | |
| 807 if (image_data->is_orphaned && !has_any_refs) { | |
| 808 images_pending_deletion_.push_back(std::move(image_data->upload.image())); | |
| 809 image_data->upload.SetImage(nullptr); | |
| 810 } | |
| 811 | |
| 812 // Don't keep CPU images if they are unused, these images can be recreated by | |
| 813 // re-locking discardable (rather than requiring a full upload like GPU | |
| 814 // images). | |
| 815 if (image_data->mode == DecodedDataMode::CPU && !has_any_refs) { | |
| 816 images_pending_deletion_.push_back(image_data->upload.image()); | |
| 817 image_data->upload.SetImage(nullptr); | |
| 818 } | |
| 819 | |
| 820 if (image_data->is_at_raster && !has_any_refs) { | |
| 821 // We have an at-raster image which has reached zero refs. If it won't fit | |
| 822 // in our cache, delete the image to allow it to fit. | |
| 823 if (image_data->upload.image() && !CanFitSize(image_data->size)) { | |
| 824 images_pending_deletion_.push_back(image_data->upload.image()); | |
| 825 image_data->upload.SetImage(nullptr); | |
| 826 } | |
| 827 | |
| 828 // We now have an at-raster image which will fit in our cache. Convert it | |
| 829 // to not-at-raster. | |
| 830 image_data->is_at_raster = false; | |
| 831 if (image_data->upload.image()) { | |
| 832 bytes_used_ += image_data->size; | |
| 833 image_data->upload.budgeted = true; | |
| 834 } | |
| 835 } | |
| 836 | |
| 837 // If we have image refs on a non-at-raster image, it must be budgeted, as it | |
| 838 // is either uploaded or pending upload. | |
| 839 if (image_data->upload.ref_count > 0 && !image_data->upload.budgeted && | |
| 840 !image_data->is_at_raster) { | |
| 841 // We should only be taking non-at-raster refs on images that fit in cache. | |
| 842 DCHECK(CanFitSize(image_data->size)); | |
| 843 | |
| 844 bytes_used_ += image_data->size; | |
| 845 image_data->upload.budgeted = true; | |
| 846 } | |
| 847 | |
| 848 // If we have no image refs on an image, it should only be budgeted if it has | |
| 849 // an uploaded image. If no image exists (upload was cancelled), we should | |
| 850 // un-budget the image. | |
| 851 if (image_data->upload.ref_count == 0 && image_data->upload.budgeted && | |
| 852 !image_data->upload.image()) { | |
| 853 DCHECK_GE(bytes_used_, image_data->size); | |
| 854 bytes_used_ -= image_data->size; | |
| 855 image_data->upload.budgeted = false; | |
| 856 } | |
| 857 | |
| 858 // We should unlock the discardable memory for the image in two cases: | |
| 859 // 1) The image is no longer being used (no decode or upload refs). | |
| 860 // 2) This is a GPU backed image that has already been uploaded (no decode | |
| 861 // refs). | |
| 862 bool should_unlock_discardable = | |
| 863 !has_any_refs || (image_data->mode == DecodedDataMode::GPU && | |
| 864 !image_data->decode.ref_count); | |
| 865 | |
| 866 if (should_unlock_discardable && image_data->decode.is_locked()) { | |
| 867 DCHECK(image_data->decode.data()); | |
| 868 image_data->decode.Unlock(); | |
| 869 } | |
| 870 | |
| 871 #if DCHECK_IS_ON() | |
| 872 // Sanity check the above logic. | |
| 873 if (image_data->upload.image()) { | |
| 874 DCHECK(image_data->is_at_raster || image_data->upload.budgeted); | |
| 875 if (image_data->mode == DecodedDataMode::CPU) | |
| 876 DCHECK(image_data->decode.is_locked()); | |
| 877 } else { | |
| 878 DCHECK(!image_data->upload.budgeted || image_data->upload.ref_count > 0); | |
| 879 } | |
| 880 #endif | |
| 881 } | |
| 882 | |
| 883 // Ensures that we can fit a new image of size |required_size| in our cache. In | |
| 884 // doing so, this function will free unreferenced image data as necessary to | |
| 885 // create rooom. | |
| 886 bool GpuImageDecodeController::EnsureCapacity(size_t required_size) { | |
| 887 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("cc.debug"), | |
| 888 "GpuImageDecodeController::EnsureCapacity"); | |
| 889 lock_.AssertAcquired(); | |
| 890 | |
| 891 if (CanFitSize(required_size) && !ExceedsPreferredCount()) | |
| 892 return true; | |
| 893 | |
| 894 // While we are over memory or preferred item capacity, we iterate through | |
| 895 // our set of cached image data in LRU order. For each image, we can do two | |
| 896 // things: 1) We can free the uploaded image, reducing the memory usage of | |
| 897 // the cache and 2) we can remove the entry entirely, reducing the count of | |
| 898 // elements in the cache. | |
| 899 for (auto it = persistent_cache_.rbegin(); it != persistent_cache_.rend();) { | |
| 900 if (it->second->decode.ref_count != 0 || | |
| 901 it->second->upload.ref_count != 0) { | |
| 902 ++it; | |
| 903 continue; | |
| 904 } | |
| 905 | |
| 906 // Current entry has no refs. Ensure it is not locked. | |
| 907 DCHECK(!it->second->decode.is_locked()); | |
| 908 | |
| 909 // If an image without refs is budgeted, it must have an associated image | |
| 910 // upload. | |
| 911 DCHECK(!it->second->upload.budgeted || it->second->upload.image()); | |
| 912 | |
| 913 // Free the uploaded image if possible. | |
| 914 if (it->second->upload.image()) { | |
| 915 DCHECK(it->second->upload.budgeted); | |
| 916 DCHECK_GE(bytes_used_, it->second->size); | |
| 917 bytes_used_ -= it->second->size; | |
| 918 images_pending_deletion_.push_back(it->second->upload.image()); | |
| 919 it->second->upload.SetImage(nullptr); | |
| 920 it->second->upload.budgeted = false; | |
| 921 } | |
| 922 | |
| 923 // Free the entire entry if necessary. | |
| 924 if (ExceedsPreferredCount()) { | |
| 925 it = persistent_cache_.Erase(it); | |
| 926 } else { | |
| 927 ++it; | |
| 928 } | |
| 929 | |
| 930 if (CanFitSize(required_size) && !ExceedsPreferredCount()) | |
| 931 return true; | |
| 932 } | |
| 933 | |
| 934 // Preferred count is only used as a guideline when triming the cache. Allow | |
| 935 // new elements to be added as long as we are below our size limit. | |
| 936 return CanFitSize(required_size); | |
| 937 } | |
| 938 | |
| 939 bool GpuImageDecodeController::CanFitSize(size_t size) const { | |
| 940 lock_.AssertAcquired(); | |
| 941 | |
| 942 size_t bytes_limit; | |
| 943 if (memory_state_ == base::MemoryState::NORMAL) { | |
| 944 bytes_limit = cached_bytes_limit_; | |
| 945 } else if (memory_state_ == base::MemoryState::THROTTLED) { | |
| 946 bytes_limit = cached_bytes_limit_ / kThrottledCacheSizeReductionFactor; | |
| 947 } else { | |
| 948 DCHECK_EQ(base::MemoryState::SUSPENDED, memory_state_); | |
| 949 bytes_limit = kSuspendedOrInvisibleMaxGpuImageBytes; | |
| 950 } | |
| 951 | |
| 952 base::CheckedNumeric<uint32_t> new_size(bytes_used_); | |
| 953 new_size += size; | |
| 954 return new_size.IsValid() && new_size.ValueOrDie() <= bytes_limit; | |
| 955 } | |
| 956 | |
| 957 bool GpuImageDecodeController::ExceedsPreferredCount() const { | |
| 958 lock_.AssertAcquired(); | |
| 959 | |
| 960 size_t items_limit; | |
| 961 if (memory_state_ == base::MemoryState::NORMAL) { | |
| 962 items_limit = kNormalMaxItemsInCache; | |
| 963 } else if (memory_state_ == base::MemoryState::THROTTLED) { | |
| 964 items_limit = kThrottledMaxItemsInCache; | |
| 965 } else { | |
| 966 DCHECK_EQ(base::MemoryState::SUSPENDED, memory_state_); | |
| 967 items_limit = kSuspendedMaxItemsInCache; | |
| 968 } | |
| 969 | |
| 970 return persistent_cache_.size() > items_limit; | |
| 971 } | |
| 972 | |
| 973 void GpuImageDecodeController::DecodeImageIfNecessary( | |
| 974 const DrawImage& draw_image, | |
| 975 ImageData* image_data) { | |
| 976 lock_.AssertAcquired(); | |
| 977 | |
| 978 DCHECK_GT(image_data->decode.ref_count, 0u); | |
| 979 | |
| 980 if (image_data->decode.decode_failure) { | |
| 981 // We have already tried and failed to decode this image. Don't try again. | |
| 982 return; | |
| 983 } | |
| 984 | |
| 985 if (image_data->upload.image()) { | |
| 986 // We already have an uploaded image, no reason to decode. | |
| 987 return; | |
| 988 } | |
| 989 | |
| 990 if (image_data->decode.data() && | |
| 991 (image_data->decode.is_locked() || image_data->decode.Lock())) { | |
| 992 // We already decoded this, or we just needed to lock, early out. | |
| 993 return; | |
| 994 } | |
| 995 | |
| 996 TRACE_EVENT0("cc", "GpuImageDecodeController::DecodeImage"); | |
| 997 | |
| 998 image_data->decode.ResetData(); | |
| 999 std::unique_ptr<base::DiscardableMemory> backing_memory; | |
| 1000 { | |
| 1001 base::AutoUnlock unlock(lock_); | |
| 1002 | |
| 1003 backing_memory = base::DiscardableMemoryAllocator::GetInstance() | |
| 1004 ->AllocateLockedDiscardableMemory(image_data->size); | |
| 1005 | |
| 1006 switch (image_data->mode) { | |
| 1007 case DecodedDataMode::CPU: { | |
| 1008 SkImageInfo image_info = CreateImageInfoForDrawImage( | |
| 1009 draw_image, image_data->upload_params.fPreScaleMipLevel); | |
| 1010 // In order to match GPU scaling quality (which uses mip-maps at high | |
| 1011 // quality), we want to use at most medium filter quality for the | |
| 1012 // scale. | |
| 1013 SkPixmap image_pixmap(image_info, backing_memory->data(), | |
| 1014 image_info.minRowBytes()); | |
| 1015 // Note that scalePixels falls back to readPixels if the sale is 1x, so | |
| 1016 // no need to special case that as an optimization. | |
| 1017 if (!draw_image.image()->scalePixels( | |
| 1018 image_pixmap, CalculateUploadScaleFilterQuality(draw_image), | |
| 1019 SkImage::kDisallow_CachingHint)) { | |
| 1020 backing_memory->Unlock(); | |
| 1021 backing_memory.reset(); | |
| 1022 } | |
| 1023 break; | |
| 1024 } | |
| 1025 case DecodedDataMode::GPU: { | |
| 1026 // TODO(crbug.com/649167): Params should not have changed since initial | |
| 1027 // sizing. Somehow this still happens. We should investigate and re-add | |
| 1028 // DCHECKs here to enforce this. | |
| 1029 | |
| 1030 if (!draw_image.image()->getDeferredTextureImageData( | |
| 1031 *context_threadsafe_proxy_.get(), &image_data->upload_params, 1, | |
| 1032 backing_memory->data())) { | |
| 1033 backing_memory->Unlock(); | |
| 1034 backing_memory.reset(); | |
| 1035 } | |
| 1036 break; | |
| 1037 } | |
| 1038 } | |
| 1039 } | |
| 1040 | |
| 1041 if (image_data->decode.data()) { | |
| 1042 // An at-raster task decoded this before us. Ingore our decode. | |
| 1043 return; | |
| 1044 } | |
| 1045 | |
| 1046 if (!backing_memory) { | |
| 1047 // If |backing_memory| was not populated, we had a non-decodable image. | |
| 1048 image_data->decode.decode_failure = true; | |
| 1049 return; | |
| 1050 } | |
| 1051 | |
| 1052 image_data->decode.SetLockedData(std::move(backing_memory)); | |
| 1053 } | |
| 1054 | |
| 1055 void GpuImageDecodeController::UploadImageIfNecessary( | |
| 1056 const DrawImage& draw_image, | |
| 1057 ImageData* image_data) { | |
| 1058 context_->GetLock()->AssertAcquired(); | |
| 1059 lock_.AssertAcquired(); | |
| 1060 | |
| 1061 if (image_data->decode.decode_failure) { | |
| 1062 // We were unnable to decode this image. Don't try to upload. | |
| 1063 return; | |
| 1064 } | |
| 1065 | |
| 1066 if (image_data->upload.image()) { | |
| 1067 // Someone has uploaded this image before us (at raster). | |
| 1068 return; | |
| 1069 } | |
| 1070 | |
| 1071 TRACE_EVENT0("cc", "GpuImageDecodeController::UploadImage"); | |
| 1072 DCHECK(image_data->decode.is_locked()); | |
| 1073 DCHECK_GT(image_data->decode.ref_count, 0u); | |
| 1074 DCHECK_GT(image_data->upload.ref_count, 0u); | |
| 1075 | |
| 1076 // We are about to upload a new image and are holding the context lock. | |
| 1077 // Ensure that any images which have been marked for deletion are actually | |
| 1078 // cleaned up so we don't exceed our memory limit during this upload. | |
| 1079 DeletePendingImages(); | |
| 1080 | |
| 1081 sk_sp<SkImage> uploaded_image; | |
| 1082 { | |
| 1083 base::AutoUnlock unlock(lock_); | |
| 1084 switch (image_data->mode) { | |
| 1085 case DecodedDataMode::CPU: { | |
| 1086 SkImageInfo image_info = CreateImageInfoForDrawImage( | |
| 1087 draw_image, image_data->upload_params.fPreScaleMipLevel); | |
| 1088 SkPixmap pixmap(image_info, image_data->decode.data()->data(), | |
| 1089 image_info.minRowBytes()); | |
| 1090 uploaded_image = | |
| 1091 SkImage::MakeFromRaster(pixmap, [](const void*, void*) {}, nullptr); | |
| 1092 break; | |
| 1093 } | |
| 1094 case DecodedDataMode::GPU: { | |
| 1095 uploaded_image = SkImage::MakeFromDeferredTextureImageData( | |
| 1096 context_->GrContext(), image_data->decode.data()->data(), | |
| 1097 SkBudgeted::kNo); | |
| 1098 break; | |
| 1099 } | |
| 1100 } | |
| 1101 } | |
| 1102 image_data->decode.mark_used(); | |
| 1103 DCHECK(uploaded_image); | |
| 1104 | |
| 1105 // At-raster may have decoded this while we were unlocked. If so, ignore our | |
| 1106 // result. | |
| 1107 if (!image_data->upload.image()) | |
| 1108 image_data->upload.SetImage(std::move(uploaded_image)); | |
| 1109 } | |
| 1110 | |
| 1111 scoped_refptr<GpuImageDecodeController::ImageData> | |
| 1112 GpuImageDecodeController::CreateImageData(const DrawImage& draw_image) { | |
| 1113 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("cc.debug"), | |
| 1114 "GpuImageDecodeController::CreateImageData"); | |
| 1115 lock_.AssertAcquired(); | |
| 1116 | |
| 1117 DecodedDataMode mode; | |
| 1118 int upload_scale_mip_level = CalculateUploadScaleMipLevel(draw_image); | |
| 1119 auto params = SkImage::DeferredTextureImageUsageParams( | |
| 1120 draw_image.matrix(), CalculateUploadScaleFilterQuality(draw_image), | |
| 1121 upload_scale_mip_level); | |
| 1122 size_t data_size = draw_image.image()->getDeferredTextureImageData( | |
| 1123 *context_threadsafe_proxy_.get(), ¶ms, 1, nullptr); | |
| 1124 | |
| 1125 if (data_size == 0) { | |
| 1126 // Can't upload image, too large or other failure. Try to use SW fallback. | |
| 1127 SkImageInfo image_info = | |
| 1128 CreateImageInfoForDrawImage(draw_image, upload_scale_mip_level); | |
| 1129 data_size = image_info.getSafeSize(image_info.minRowBytes()); | |
| 1130 mode = DecodedDataMode::CPU; | |
| 1131 } else { | |
| 1132 mode = DecodedDataMode::GPU; | |
| 1133 } | |
| 1134 | |
| 1135 return make_scoped_refptr(new ImageData(mode, data_size, params)); | |
| 1136 } | |
| 1137 | |
| 1138 void GpuImageDecodeController::DeletePendingImages() { | |
| 1139 context_->GetLock()->AssertAcquired(); | |
| 1140 lock_.AssertAcquired(); | |
| 1141 images_pending_deletion_.clear(); | |
| 1142 } | |
| 1143 | |
| 1144 SkImageInfo GpuImageDecodeController::CreateImageInfoForDrawImage( | |
| 1145 const DrawImage& draw_image, | |
| 1146 int upload_scale_mip_level) const { | |
| 1147 gfx::Size mip_size = | |
| 1148 CalculateSizeForMipLevel(draw_image, upload_scale_mip_level); | |
| 1149 return SkImageInfo::Make(mip_size.width(), mip_size.height(), | |
| 1150 ResourceFormatToClosestSkColorType(format_), | |
| 1151 kPremul_SkAlphaType); | |
| 1152 } | |
| 1153 | |
| 1154 // Tries to find an ImageData that can be used to draw the provided | |
| 1155 // |draw_image|. First looks for an exact entry in our |in_use_cache_|. If one | |
| 1156 // cannot be found, it looks for a compatible entry in our |persistent_cache_|. | |
| 1157 GpuImageDecodeController::ImageData* | |
| 1158 GpuImageDecodeController::GetImageDataForDrawImage( | |
| 1159 const DrawImage& draw_image) { | |
| 1160 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("cc.debug"), | |
| 1161 "GpuImageDecodeController::GetImageDataForDrawImage"); | |
| 1162 lock_.AssertAcquired(); | |
| 1163 auto found_in_use = in_use_cache_.find(GenerateInUseCacheKey(draw_image)); | |
| 1164 if (found_in_use != in_use_cache_.end()) | |
| 1165 return found_in_use->second.image_data.get(); | |
| 1166 | |
| 1167 auto found_persistent = persistent_cache_.Get(draw_image.image()->uniqueID()); | |
| 1168 if (found_persistent != persistent_cache_.end()) { | |
| 1169 ImageData* image_data = found_persistent->second.get(); | |
| 1170 if (IsCompatible(image_data, draw_image)) { | |
| 1171 return image_data; | |
| 1172 } else { | |
| 1173 found_persistent->second->is_orphaned = true; | |
| 1174 // Call OwnershipChanged before erasing the orphaned task from the | |
| 1175 // persistent cache. This ensures that if the orphaned task has 0 | |
| 1176 // references, it is cleaned up safely before it is deleted. | |
| 1177 OwnershipChanged(draw_image, image_data); | |
| 1178 persistent_cache_.Erase(found_persistent); | |
| 1179 } | |
| 1180 } | |
| 1181 | |
| 1182 return nullptr; | |
| 1183 } | |
| 1184 | |
| 1185 // Determines if we can draw the provided |draw_image| using the provided | |
| 1186 // |image_data|. This is true if the |image_data| is not scaled, or if it | |
| 1187 // is scaled at an equal or larger scale and equal or larger quality to | |
| 1188 // the provided |draw_image|. | |
| 1189 bool GpuImageDecodeController::IsCompatible(const ImageData* image_data, | |
| 1190 const DrawImage& draw_image) const { | |
| 1191 bool is_scaled = image_data->upload_params.fPreScaleMipLevel != 0; | |
| 1192 bool scale_is_compatible = CalculateUploadScaleMipLevel(draw_image) >= | |
| 1193 image_data->upload_params.fPreScaleMipLevel; | |
| 1194 bool quality_is_compatible = CalculateUploadScaleFilterQuality(draw_image) <= | |
| 1195 image_data->upload_params.fQuality; | |
| 1196 return !is_scaled || (scale_is_compatible && quality_is_compatible); | |
| 1197 } | |
| 1198 | |
| 1199 size_t GpuImageDecodeController::GetDrawImageSizeForTesting( | |
| 1200 const DrawImage& image) { | |
| 1201 base::AutoLock lock(lock_); | |
| 1202 scoped_refptr<ImageData> data = CreateImageData(image); | |
| 1203 return data->size; | |
| 1204 } | |
| 1205 | |
| 1206 void GpuImageDecodeController::SetImageDecodingFailedForTesting( | |
| 1207 const DrawImage& image) { | |
| 1208 base::AutoLock lock(lock_); | |
| 1209 auto found = persistent_cache_.Peek(image.image()->uniqueID()); | |
| 1210 DCHECK(found != persistent_cache_.end()); | |
| 1211 ImageData* image_data = found->second.get(); | |
| 1212 image_data->decode.decode_failure = true; | |
| 1213 } | |
| 1214 | |
| 1215 bool GpuImageDecodeController::DiscardableIsLockedForTesting( | |
| 1216 const DrawImage& image) { | |
| 1217 base::AutoLock lock(lock_); | |
| 1218 auto found = persistent_cache_.Peek(image.image()->uniqueID()); | |
| 1219 DCHECK(found != persistent_cache_.end()); | |
| 1220 ImageData* image_data = found->second.get(); | |
| 1221 return image_data->decode.is_locked(); | |
| 1222 } | |
| 1223 | |
| 1224 void GpuImageDecodeController::OnMemoryStateChange(base::MemoryState state) { | |
| 1225 switch (state) { | |
| 1226 case base::MemoryState::NORMAL: | |
| 1227 memory_state_ = state; | |
| 1228 break; | |
| 1229 case base::MemoryState::THROTTLED: | |
| 1230 case base::MemoryState::SUSPENDED: { | |
| 1231 memory_state_ = state; | |
| 1232 | |
| 1233 // We've just changed our memory state to a (potentially) more | |
| 1234 // restrictive one. Re-enforce cache limits. | |
| 1235 base::AutoLock lock(lock_); | |
| 1236 EnsureCapacity(0); | |
| 1237 break; | |
| 1238 } | |
| 1239 case base::MemoryState::UNKNOWN: | |
| 1240 // NOT_REACHED. | |
| 1241 break; | |
| 1242 } | |
| 1243 } | |
| 1244 | |
| 1245 } // namespace cc | |
| OLD | NEW |