Chromium Code Reviews| 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 |
| index 8a76daf9cd20278e89c9aed82e6aeb61f9fe6846..2e8318f5666a7f3905504a080ea84d377f4dc61d 100644 |
| --- a/cc/tiles/gpu_image_decode_controller.cc |
| +++ b/cc/tiles/gpu_image_decode_controller.cc |
| @@ -14,6 +14,7 @@ |
| #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" |
| @@ -46,16 +47,75 @@ bool SkipImage(const DrawImage& draw_image) { |
| } |
| SkImage::DeferredTextureImageUsageParams ParamsFromDrawImage( |
| - const DrawImage& draw_image) { |
| + const DrawImage& draw_image, |
| + int pre_scale_mip_level) { |
| SkImage::DeferredTextureImageUsageParams params; |
| params.fMatrix = draw_image.matrix(); |
| params.fQuality = draw_image.filter_quality(); |
| + params.fPreScaleMipLevel = pre_scale_mip_level; |
| return params; |
| } |
| +// Calculate the mp level to pre-scale the image to before uploading. We use mip |
|
vmpstr
2016/06/21 20:07:08
s/mp/mip/
ericrk
2016/06/22 18:56:38
Done.
|
| +// levels rather than exact scales to increase re-use of scaled images. |
| +int CalculatePreScaleMipLevel(const DrawImage& draw_image) { |
|
vmpstr
2016/06/21 20:07:08
Can we rename "Pre" to "Upload"
ericrk
2016/06/22 18:56:37
Done.
|
| + if (draw_image.src_rect() != draw_image.image()->bounds()) { |
| + // Images which are being clipped will have color-bleeding if scaled. |
|
vmpstr
2016/06/21 20:07:07
Move the comment before the if, please
ericrk
2016/06/22 18:56:38
Done.
|
| + // TODO(ericrk): Investigate uploading clipped images to handle this case |
| + // and provide further optimization. crbug.com/620899 |
| + 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. |
| + gfx::Size scaled_size = gfx::ScaleToCeiledSize( |
| + base_size, draw_image.scale().width(), draw_image.scale().height()); |
| + |
| + return MipMapUtil::GetLevelForSize(base_size, scaled_size); |
| +} |
| + |
| +SkSize CalculatePreScaleFactor(const DrawImage& draw_image, int mip_level) { |
|
vmpstr
2016/06/21 20:07:07
Put a comment before the function please
ericrk
2016/06/22 18:56:38
Done.
|
| + gfx::Size base_size(draw_image.image()->width(), |
| + draw_image.image()->height()); |
| + return MipMapUtil::GetScaleAdjustmentForLevel(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 = draw_image.filter_quality(); |
| + 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 = CalculatePreScaleMipLevel(draw_image); |
| + DCHECK_LT(mip_level, 32); |
| + |
| + return (static_cast<uint64_t>(draw_image.image()->uniqueID()) << 32) | |
| + (mip_level << 16) | draw_image.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 { |
| @@ -175,12 +235,12 @@ void GpuImageDecodeController::DecodedImageData::ResetData() { |
| } |
| 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 |
| + // lock_count │ used │ result state |
| + // ═══════════╪═══════╪══════════════════ |
|
vmpstr
2016/06/21 20:07:08
nice!
ericrk
2016/06/22 18:56:38
Done.
|
| + // 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 { |
| @@ -231,18 +291,33 @@ void GpuImageDecodeController::UploadedImageData::ReportUsageStats() const { |
| usage_stats_.first_ref_wasted); |
| } |
| -GpuImageDecodeController::ImageData::ImageData(DecodedDataMode mode, |
| - size_t size) |
| - : mode(mode), size(size) {} |
| - |
| -GpuImageDecodeController::ImageData::~ImageData() = default; |
| +GpuImageDecodeController::ImageData::ImageData( |
| + DecodedDataMode mode, |
| + size_t size, |
| + int pre_scale_mip_level, |
| + SkFilterQuality pre_scale_filter_quality) |
| + : mode(mode), |
| + size(size), |
| + pre_scale_mip_level(pre_scale_mip_level), |
| + pre_scale_filter_quality(pre_scale_filter_quality) {} |
| + |
| +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), |
| - image_data_(ImageDataMRUCache::NO_AUTO_EVICT), |
| + persistent_cache_(ImageDataMRUCache::NO_AUTO_EVICT), |
| cached_items_limit_(kMaxDiscardableItems), |
| cached_bytes_limit_(max_gpu_image_bytes), |
| bytes_used_(0), |
| @@ -285,10 +360,8 @@ bool GpuImageDecodeController::GetTaskForImageAndRef( |
| base::AutoLock lock(lock_); |
| const auto image_id = draw_image.image()->uniqueID(); |
| - |
| - auto found = image_data_.Get(image_id); |
| - if (found != image_data_.end()) { |
| - ImageData* image_data = found->second.get(); |
| + ImageData* image_data = GetImageDataForDrawImage(draw_image); |
| + if (image_data) { |
| if (image_data->is_at_raster) { |
| // Image is at-raster, just return, this usage will be at-raster as well. |
| *task = nullptr; |
| @@ -307,32 +380,28 @@ bool GpuImageDecodeController::GetTaskForImageAndRef( |
| *task = nullptr; |
| return true; |
| } |
| - } |
| - // We didn't have a pre-uploaded image, so we need an upload task. Try to find |
| - // an existing one. |
| - scoped_refptr<TileTask>& existing_task = |
| - pending_image_upload_tasks_[image_id]; |
| - if (existing_task) { |
| - // We had an existing upload task, ref the image and return the task. |
| - RefImage(draw_image); |
| - *task = existing_task; |
| - return true; |
| + // We didn't have a pre-uploaded image, so we need an upload task. Try to |
| + // find an existing one. |
| + 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; |
| + } else { |
| + } |
| } |
| // We will be creating a new upload task. If necessary, create a placeholder |
| // ImageData to hold the result. |
| - std::unique_ptr<ImageData> new_data; |
| - ImageData* data; |
| - if (found == image_data_.end()) { |
| + scoped_refptr<ImageData> new_data; |
| + if (!image_data) { |
| new_data = CreateImageData(draw_image); |
| - data = new_data.get(); |
| - } else { |
| - data = found->second.get(); |
| + image_data = new_data.get(); |
| } |
| // Ensure that the image we're about to decode/upload will fit in memory. |
| - if (!EnsureCapacity(data->size)) { |
| + if (!EnsureCapacity(image_data->size)) { |
| // Image will not fit, do an at-raster decode. |
| *task = nullptr; |
| return false; |
| @@ -341,19 +410,19 @@ bool GpuImageDecodeController::GetTaskForImageAndRef( |
| // If we had to create new image data, add it to our map now that we know it |
| // will fit. |
| if (new_data) |
| - found = image_data_.Put(image_id, std::move(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); |
| - existing_task = make_scoped_refptr(new ImageUploadTaskImpl( |
| + *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); |
| - *task = existing_task; |
| return true; |
| } |
| @@ -374,16 +443,14 @@ DecodedDrawImage GpuImageDecodeController::GetDecodedImageForDraw( |
| TRACE_EVENT0("cc", "GpuImageDecodeController::GetDecodedImageForDraw"); |
| base::AutoLock lock(lock_); |
| - const uint32_t unique_id = draw_image.image()->uniqueID(); |
| - auto found = image_data_.Peek(unique_id); |
| - if (found == image_data_.end()) { |
| + ImageData* image_data = GetImageDataForDrawImage(draw_image); |
| + if (!image_data) { |
| // We didn't find the image, create a new entry. |
| auto data = CreateImageData(draw_image); |
| - found = image_data_.Put(unique_id, std::move(data)); |
| + image_data = data.get(); |
| + persistent_cache_.Put(draw_image.image()->uniqueID(), std::move(data)); |
| } |
| - ImageData* image_data = found->second.get(); |
| - |
| if (!image_data->upload.budgeted) { |
| // If image data is not budgeted by this point, it is at-raster. |
| image_data->is_at_raster = true; |
| @@ -406,7 +473,9 @@ DecodedDrawImage GpuImageDecodeController::GetDecodedImageForDraw( |
| image_data->upload.mark_used(); |
| DCHECK(image || image_data->decode.decode_failure); |
| - DecodedDrawImage decoded_draw_image(std::move(image), |
| + SkSize scale_factor = |
| + CalculatePreScaleFactor(draw_image, image_data->pre_scale_mip_level); |
| + 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; |
| @@ -423,6 +492,7 @@ void GpuImageDecodeController::DrawWithImageFinished( |
| return; |
| base::AutoLock lock(lock_); |
| + TRACE_EVENT0("cc", "GpuImageDecodeController::DrawWithImageFinished"); |
| UnrefImageInternal(draw_image); |
| // We are mid-draw and holding the context lock, ensure we clean up any |
| @@ -458,7 +528,7 @@ void GpuImageDecodeController::SetShouldAggressivelyFreeResources( |
| bool GpuImageDecodeController::OnMemoryDump( |
| const base::trace_event::MemoryDumpArgs& args, |
| base::trace_event::ProcessMemoryDump* pmd) { |
| - for (const auto& image_pair : image_data_) { |
| + for (const auto& image_pair : persistent_cache_) { |
| const ImageData* image_data = image_pair.second.get(); |
| const uint32_t image_id = image_pair.first; |
| @@ -469,7 +539,6 @@ bool GpuImageDecodeController::OnMemoryDump( |
| base::trace_event::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. |
| @@ -516,26 +585,29 @@ bool GpuImageDecodeController::OnMemoryDump( |
| void GpuImageDecodeController::DecodeImage(const DrawImage& draw_image) { |
| base::AutoLock lock(lock_); |
| - auto found = image_data_.Peek(draw_image.image()->uniqueID()); |
| - DCHECK(found != image_data_.end()); |
| - DCHECK(!found->second->is_at_raster); |
| - DecodeImageIfNecessary(draw_image, found->second.get()); |
| + 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) { |
| ContextProvider::ScopedContextLock context_lock(context_); |
| base::AutoLock lock(lock_); |
| - auto found = image_data_.Peek(draw_image.image()->uniqueID()); |
| - DCHECK(found != image_data_.end()); |
| - DCHECK(!found->second->is_at_raster); |
| - UploadImageIfNecessary(draw_image, found->second.get()); |
| + 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) { |
| base::AutoLock lock(lock_); |
| - // Decode task is complete, remove it from our list of pending tasks. |
| - pending_image_decode_tasks_.erase(draw_image.image()->uniqueID()); |
| + // 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. |
| @@ -545,8 +617,11 @@ void GpuImageDecodeController::OnImageDecodeTaskCompleted( |
| void GpuImageDecodeController::OnImageUploadTaskCompleted( |
| const DrawImage& draw_image) { |
| base::AutoLock lock(lock_); |
| - // Upload task is complete, remove it from our list of pending tasks. |
| - pending_image_upload_tasks_.erase(draw_image.image()->uniqueID()); |
| + // Upload task is complete, remove our referene to it. |
|
vmpstr
2016/06/21 20:07:08
s/ne/nce/
ericrk
2016/06/22 18:56:37
Done.
|
| + 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 |
| @@ -562,24 +637,22 @@ scoped_refptr<TileTask> GpuImageDecodeController::GetImageDecodeTaskAndRef( |
| const TracingInfo& tracing_info) { |
| lock_.AssertAcquired(); |
| - const uint32_t image_id = draw_image.image()->uniqueID(); |
| - |
| // This ref is kept alive while an upload task may need this decode. We |
| // release this ref in UploadTaskCompleted. |
| RefImageDecode(draw_image); |
| - auto found = image_data_.Peek(image_id); |
| - if (found != image_data_.end() && found->second->decode.is_locked()) { |
| + 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(!found->second->is_at_raster); |
| + DCHECK(!image_data->is_at_raster); |
| // We should never be creating a decode for an already-uploaded image. |
| - DCHECK(!found->second->upload.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 = |
| - pending_image_decode_tasks_[image_id]; |
| + 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. |
| @@ -592,38 +665,64 @@ scoped_refptr<TileTask> GpuImageDecodeController::GetImageDecodeTaskAndRef( |
| void GpuImageDecodeController::RefImageDecode(const DrawImage& draw_image) { |
| lock_.AssertAcquired(); |
| - auto found = image_data_.Peek(draw_image.image()->uniqueID()); |
| - DCHECK(found != image_data_.end()); |
| - ++found->second->decode.ref_count; |
| - RefCountChanged(found->second.get()); |
| + 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; |
| + RefCountChanged(found->second.image_data.get()); |
| } |
| void GpuImageDecodeController::UnrefImageDecode(const DrawImage& draw_image) { |
| lock_.AssertAcquired(); |
| - auto found = image_data_.Peek(draw_image.image()->uniqueID()); |
| - DCHECK(found != image_data_.end()); |
| - DCHECK_GT(found->second->decode.ref_count, 0u); |
| - --found->second->decode.ref_count; |
| - RefCountChanged(found->second.get()); |
| + 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; |
| + RefCountChanged(found->second.image_data.get()); |
| + if (found->second.ref_count == 0u) { |
| + in_use_cache_.erase(found); |
| + } |
| } |
| void GpuImageDecodeController::RefImage(const DrawImage& draw_image) { |
| lock_.AssertAcquired(); |
| - auto found = image_data_.Peek(draw_image.image()->uniqueID()); |
| - DCHECK(found != image_data_.end()); |
| - ++found->second->upload.ref_count; |
| - RefCountChanged(found->second.get()); |
| + 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->pre_scale_mip_level <= |
| + CalculatePreScaleMipLevel(draw_image)); |
| + found = |
| + in_use_cache_ |
| + .insert(std::make_pair(key, InUseCacheEntry(found_image->second))) |
|
vmpstr
2016/06/21 20:07:08
make_pair here will make a std::pair<InUseCacheKey
ericrk
2016/06/22 18:56:37
Emplace works on windows - will update this again
|
| + .first; |
| + } |
| + |
| + DCHECK(found != in_use_cache_.end()); |
| + ++found->second.ref_count; |
| + ++found->second.image_data->upload.ref_count; |
| + RefCountChanged(found->second.image_data.get()); |
| } |
| void GpuImageDecodeController::UnrefImageInternal(const DrawImage& draw_image) { |
| lock_.AssertAcquired(); |
| - auto found = image_data_.Peek(draw_image.image()->uniqueID()); |
| - DCHECK(found != image_data_.end()); |
| - DCHECK_GT(found->second->upload.ref_count, 0u); |
| - --found->second->upload.ref_count; |
| - if (found->second->upload.ref_count == 0) |
| - found->second->upload.notify_ref_reached_zero(); |
| - RefCountChanged(found->second.get()); |
| + 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; |
| + RefCountChanged(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 |
| @@ -634,6 +733,12 @@ void GpuImageDecodeController::RefCountChanged(ImageData* image_data) { |
| bool has_any_refs = |
| image_data->upload.ref_count > 0 || image_data->decode.ref_count > 0; |
| + // 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). |
| @@ -719,7 +824,7 @@ bool GpuImageDecodeController::EnsureCapacity(size_t required_size) { |
| // 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 = image_data_.rbegin(); it != image_data_.rend();) { |
| + for (auto it = persistent_cache_.rbegin(); it != persistent_cache_.rend();) { |
| if (it->second->decode.ref_count != 0 || |
| it->second->upload.ref_count != 0) { |
| ++it; |
| @@ -745,7 +850,7 @@ bool GpuImageDecodeController::EnsureCapacity(size_t required_size) { |
| // Free the entire entry if necessary. |
| if (ExceedsPreferredCount()) { |
| - it = image_data_.Erase(it); |
| + it = persistent_cache_.Erase(it); |
| } else { |
| ++it; |
| } |
| @@ -770,7 +875,7 @@ bool GpuImageDecodeController::CanFitSize(size_t size) const { |
| bool GpuImageDecodeController::ExceedsPreferredCount() const { |
| lock_.AssertAcquired(); |
| - return image_data_.size() > cached_items_limit_; |
| + return persistent_cache_.size() > cached_items_limit_; |
| } |
| void GpuImageDecodeController::DecodeImageIfNecessary( |
| @@ -807,10 +912,19 @@ void GpuImageDecodeController::DecodeImageIfNecessary( |
| backing_memory = |
| base::DiscardableMemoryAllocator::GetInstance() |
| ->AllocateLockedDiscardableMemory(image_data->size); |
| - SkImageInfo image_info = CreateImageInfoForDrawImage(draw_image); |
| - if (!draw_image.image()->readPixels(image_info, backing_memory->data(), |
| - image_info.minRowBytes(), 0, 0, |
| - SkImage::kDisallow_CachingHint)) { |
| + SkImageInfo image_info = CreateImageInfoForDrawImage( |
| + draw_image, image_data->pre_scale_mip_level); |
| + // 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. |
| + SkFilterQuality scale_quality = |
| + std::max(kMedium_SkFilterQuality, draw_image.filter_quality()); |
| + 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, scale_quality, |
| + SkImage::kDisallow_CachingHint)) { |
| backing_memory.reset(); |
| } |
| break; |
| @@ -819,7 +933,8 @@ void GpuImageDecodeController::DecodeImageIfNecessary( |
| backing_memory = |
| base::DiscardableMemoryAllocator::GetInstance() |
| ->AllocateLockedDiscardableMemory(image_data->size); |
| - auto params = ParamsFromDrawImage(draw_image); |
| + auto params = |
| + ParamsFromDrawImage(draw_image, image_data->pre_scale_mip_level); |
| if (!draw_image.image()->getDeferredTextureImageData( |
| *context_threadsafe_proxy_.get(), ¶ms, 1, |
| backing_memory->data())) { |
| @@ -875,7 +990,8 @@ void GpuImageDecodeController::UploadImageIfNecessary( |
| base::AutoUnlock unlock(lock_); |
| switch (image_data->mode) { |
| case DecodedDataMode::CPU: { |
| - SkImageInfo image_info = CreateImageInfoForDrawImage(draw_image); |
| + SkImageInfo image_info = CreateImageInfoForDrawImage( |
| + draw_image, image_data->pre_scale_mip_level); |
| SkPixmap pixmap(image_info, image_data->decode.data()->data(), |
| image_info.minRowBytes()); |
| uploaded_image = |
| @@ -899,26 +1015,29 @@ void GpuImageDecodeController::UploadImageIfNecessary( |
| image_data->upload.SetImage(std::move(uploaded_image)); |
| } |
| -std::unique_ptr<GpuImageDecodeController::ImageData> |
| +scoped_refptr<GpuImageDecodeController::ImageData> |
| GpuImageDecodeController::CreateImageData(const DrawImage& draw_image) { |
| lock_.AssertAcquired(); |
| DecodedDataMode mode; |
| - SkImageInfo info = CreateImageInfoForDrawImage(draw_image); |
| + int pre_scale_mip_level = CalculatePreScaleMipLevel(draw_image); |
| SkImage::DeferredTextureImageUsageParams params = |
| - ParamsFromDrawImage(draw_image); |
| + ParamsFromDrawImage(draw_image, pre_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. |
| - data_size = info.getSafeSize(info.minRowBytes()); |
| + SkImageInfo image_info = |
| + CreateImageInfoForDrawImage(draw_image, pre_scale_mip_level); |
| + data_size = image_info.getSafeSize(image_info.minRowBytes()); |
| mode = DecodedDataMode::CPU; |
| } else { |
| mode = DecodedDataMode::GPU; |
| } |
| - return base::WrapUnique(new ImageData(mode, data_size)); |
| + return make_scoped_refptr(new ImageData(mode, data_size, pre_scale_mip_level, |
| + draw_image.filter_quality())); |
| } |
| void GpuImageDecodeController::DeletePendingImages() { |
| @@ -928,17 +1047,74 @@ void GpuImageDecodeController::DeletePendingImages() { |
| } |
| SkImageInfo GpuImageDecodeController::CreateImageInfoForDrawImage( |
| - const DrawImage& draw_image) const { |
| + const DrawImage& draw_image, |
| + int pre_scale_mip_level) const { |
| + DrawImage scaled_draw_image = draw_image.ApplyScale( |
| + CalculatePreScaleFactor(draw_image, pre_scale_mip_level)); |
| return SkImageInfo::Make( |
| - draw_image.image()->width(), draw_image.image()->height(), |
| + scaled_draw_image.image()->width() * scaled_draw_image.scale().width(), |
| + scaled_draw_image.image()->height() * scaled_draw_image.scale().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) { |
| + lock_.AssertAcquired(); |
| + { |
| + auto found = in_use_cache_.find(GenerateInUseCacheKey(draw_image)); |
|
vmpstr
2016/06/21 20:07:08
I prefer if you just name a variable a different t
ericrk
2016/06/22 18:56:37
Done.
|
| + if (found != in_use_cache_.end()) { |
|
vmpstr
2016/06/21 20:07:08
no braces
ericrk
2016/06/22 18:56:38
Done.
|
| + return found->second.image_data.get(); |
| + } |
| + } |
| + { |
| + auto found = persistent_cache_.Get(draw_image.image()->uniqueID()); |
| + if (found != persistent_cache_.end()) { |
| + if (IsCompatibleWithDrawImage(found->second.get(), draw_image)) { |
| + return found->second.get(); |
| + } else { |
| + found->second->is_orphaned = true; |
| + // Call RefCountChanged before erasing the orphaned task to ensure |
| + // that we clean up any SkImage safely. |
| + RefCountChanged(found->second.get()); |
|
vmpstr
2016/06/21 20:07:07
Where did we change the ref count? Can this be cur
ericrk
2016/06/22 18:56:37
RefCountChanged really now means "ownership change
|
| + persistent_cache_.Erase(found); |
| + } |
| + } |
| + } |
| + |
| + 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::IsCompatibleWithDrawImage( |
| + const ImageData* image_data, |
| + const DrawImage& draw_image) const { |
| + bool not_scaled = image_data->pre_scale_mip_level == 0; |
|
vmpstr
2016/06/21 20:07:08
can you name bools in the positive? ie "is_scaled"
ericrk
2016/06/22 18:56:38
Done.
|
| + bool scale_is_compatible = |
| + CalculatePreScaleMipLevel(draw_image) >= image_data->pre_scale_mip_level; |
| + bool quality_is_compatible = |
| + draw_image.filter_quality() <= image_data->pre_scale_filter_quality; |
| + return not_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 = image_data_.Peek(image.image()->uniqueID()); |
| - DCHECK(found != image_data_.end()); |
| + 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; |
| } |
| @@ -946,8 +1122,8 @@ void GpuImageDecodeController::SetImageDecodingFailedForTesting( |
| bool GpuImageDecodeController::DiscardableIsLockedForTesting( |
| const DrawImage& image) { |
| base::AutoLock lock(lock_); |
| - auto found = image_data_.Peek(image.image()->uniqueID()); |
| - DCHECK(found != image_data_.end()); |
| + 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(); |
| } |