Chromium Code Reviews| Index: cc/tiles/software_image_decode_controller.cc |
| diff --git a/cc/tiles/software_image_decode_controller.cc b/cc/tiles/software_image_decode_controller.cc |
| index c61a0c6b5732448a0e6d9c85536183851e3f8849..dbfca685d5d2328861240f9bcded6a6cd2c7b2e8 100644 |
| --- a/cc/tiles/software_image_decode_controller.cc |
| +++ b/cc/tiles/software_image_decode_controller.cc |
| @@ -6,7 +6,9 @@ |
| #include <stdint.h> |
| +#include <algorithm> |
| #include <functional> |
| +#include <limits> |
| #include "base/macros.h" |
| #include "base/memory/discardable_memory.h" |
| @@ -107,36 +109,6 @@ SkFilterQuality GetDecodedFilterQuality(const ImageDecodeControllerKey& key) { |
| return std::min(key.filter_quality(), kLow_SkFilterQuality); |
| } |
| -SkColorType SkColorTypeForDecoding(ResourceFormat format) { |
|
vmpstr
2016/03/29 18:38:13
Why this change?
cblume
2016/03/29 18:53:44
Oh sorry. I branched off my other branch after I h
|
| - // Use kN32_SkColorType if there is no corresponding SkColorType. |
| - switch (format) { |
| - case RGBA_4444: |
| - return kARGB_4444_SkColorType; |
| - case RGBA_8888: |
| - case BGRA_8888: |
| - return kN32_SkColorType; |
| - case ALPHA_8: |
| - return kAlpha_8_SkColorType; |
| - case RGB_565: |
| - return kRGB_565_SkColorType; |
| - case LUMINANCE_8: |
| - return kGray_8_SkColorType; |
| - case ETC1: |
| - case RED_8: |
| - case LUMINANCE_F16: |
| - return kN32_SkColorType; |
| - } |
| - NOTREACHED(); |
| - return kN32_SkColorType; |
| -} |
| - |
| -SkImageInfo CreateImageInfo(size_t width, |
| - size_t height, |
| - ResourceFormat format) { |
| - return SkImageInfo::Make(width, height, SkColorTypeForDecoding(format), |
| - kPremul_SkAlphaType); |
| -} |
| - |
| } // namespace |
| SoftwareImageDecodeController::SoftwareImageDecodeController( |
| @@ -174,25 +146,6 @@ bool SoftwareImageDecodeController::GetTaskForImageAndRef( |
| return false; |
| } |
| - // If we're not going to do a scale, we will just create a task to preroll the |
|
vmpstr
2016/03/29 18:38:13
\o/
|
| - // image the first time we see it. This doesn't need to account for memory. |
| - // TODO(vmpstr): We can also lock the original sized image, in which case it |
| - // does require memory bookkeeping. |
| - if (!CanHandleImage(key)) { |
| - base::AutoLock lock(lock_); |
| - if (prerolled_images_.count(key.image_id()) == 0) { |
| - scoped_refptr<ImageDecodeTask>& existing_task = pending_image_tasks_[key]; |
| - if (!existing_task) { |
| - existing_task = make_scoped_refptr( |
| - new ImageDecodeTaskImpl(this, key, image, prepare_tiles_id)); |
| - } |
| - *task = existing_task; |
| - } else { |
| - *task = nullptr; |
| - } |
| - return false; |
| - } |
| - |
| base::AutoLock lock(lock_); |
| // If we already have the image in cache, then we can return it. |
| @@ -264,7 +217,6 @@ void SoftwareImageDecodeController::UnrefImage(const DrawImage& image) { |
| // it yet (or failed to decode it). |
| // 2b. Unlock the image but keep it in list. |
| const ImageKey& key = ImageKey::FromDrawImage(image); |
| - DCHECK(CanHandleImage(key)); |
| TRACE_EVENT1("disabled-by-default-cc.debug", |
| "SoftwareImageDecodeController::UnrefImage", "key", |
| key.ToString()); |
| @@ -295,20 +247,6 @@ void SoftwareImageDecodeController::DecodeImage(const ImageKey& key, |
| const DrawImage& image) { |
| TRACE_EVENT1("cc", "SoftwareImageDecodeController::DecodeImage", "key", |
| key.ToString()); |
| - if (!CanHandleImage(key)) { |
| - image.image()->preroll(); |
| - |
| - base::AutoLock lock(lock_); |
| - prerolled_images_.insert(key.image_id()); |
| - // Erase the pending task from the queue, since the task won't be doing |
| - // anything useful after this function terminates. Since we don't preroll |
| - // images twice, this is actually not necessary but it behaves similar to |
| - // the other code path: when this function finishes, the task isn't in the |
| - // pending_image_tasks_ list. |
| - pending_image_tasks_.erase(key); |
| - return; |
| - } |
| - |
| base::AutoLock lock(lock_); |
| AutoRemoveKeyFromTaskMap remove_key_from_task_map(&pending_image_tasks_, key); |
| @@ -365,120 +303,113 @@ void SoftwareImageDecodeController::DecodeImage(const ImageKey& key, |
| } |
| scoped_ptr<SoftwareImageDecodeController::DecodedImage> |
| -SoftwareImageDecodeController::DecodeImageInternal( |
| - const ImageKey& key, |
| - const DrawImage& draw_image) { |
| - TRACE_EVENT1("disabled-by-default-cc.debug", |
| - "SoftwareImageDecodeController::DecodeImageInternal", "key", |
| - key.ToString()); |
| - const SkImage* image = draw_image.image(); |
| +SoftwareImageDecodeController::DecodeImageNoneLowQuality(const ImageKey& key, |
| + const SkImage& image) { |
| + DCHECK(key.can_use_original_decode()); |
| - // If we can use the original decode, then we don't need to do scaling. We can |
| - // just read pixels into the final memory. |
| - if (key.can_use_original_decode()) { |
| - SkImageInfo decoded_info = |
| - CreateImageInfo(image->width(), image->height(), format_); |
| - scoped_ptr<base::DiscardableMemory> decoded_pixels; |
| - { |
| - TRACE_EVENT0( |
| - "disabled-by-default-cc.debug", |
| - "SoftwareImageDecodeController::DecodeImageInternal - allocate " |
| - "decoded pixels"); |
| - decoded_pixels = |
| - base::DiscardableMemoryAllocator::GetInstance() |
| - ->AllocateLockedDiscardableMemory(decoded_info.minRowBytes() * |
| - decoded_info.height()); |
| - } |
| - { |
| - TRACE_EVENT0( |
| - "disabled-by-default-cc.debug", |
| - "SoftwareImageDecodeController::DecodeImageInternal - read pixels"); |
| - bool result = image->readPixels(decoded_info, decoded_pixels->data(), |
| - decoded_info.minRowBytes(), 0, 0, |
| - SkImage::kDisallow_CachingHint); |
| + return AttemptToUseOriginalImage(key, image); |
| +} |
| - if (!result) { |
| - decoded_pixels->Unlock(); |
| - return nullptr; |
| +scoped_ptr<SoftwareImageDecodeController::DecodedImage> |
| +SoftwareImageDecodeController::DecodeImageMediumQuality(const ImageKey& key, |
| + const SkImage& image) { |
| + // We need an original decode to either do a scale or extract a subrect |
| + // from the image. |
| + auto decoded_image_result = DecodeImageOrUseCache(key, image); |
| + if (!decoded_image_result.decoded_pixmap_.addr()) { |
| + return nullptr; |
| + } |
| + |
| + // Generate a key and scaled image for each mipmap level. |
| + gfx::Rect src_rect = key.src_rect(); |
| + int src_height = src_rect.height(); |
| + int src_width = src_rect.width(); |
| + int largest_axis = std::max(src_height, src_width); |
| + |
| + // Make sure our signed int fits inside a uint32_t |
| + if (largest_axis > numeric_limits<uint32_t>::max()) { |
|
vmpstr
2016/03/29 18:38:13
Do you know which systems we have that have a 64 b
cblume
2016/03/29 18:53:44
I agree with your comments. Actually, when I origi
|
| + return nullptr; |
| + } |
| + int leading_zeros = SkCLZ(static_cast<uint32_t>(largest_axis)); |
|
cblume
2016/03/29 18:53:44
Oops. I meant to make this the new CLZ function.
A
|
| + // If the value 00011010 has 3 leading 0s then it had 5 significant bits |
| + // (the bits which are not leading zeros) |
| + int significant_bits = (sizeof(uint32_t) * 8) - leading_zeros; |
|
vmpstr
2016/03/29 18:38:13
sizeof(uint32_t) == 4 on all platforms that we sup
cblume
2016/03/29 18:53:44
As far as I know, yes.
Maybe make it a DCHECK and
|
| + // This is making the assumption taht the size of a byte is 8 bits |
|
vmpstr
2016/03/29 18:38:13
s/taht/that/
I think that sizeof(char) is defined
cblume
2016/03/29 18:53:44
Oh holy cow, I didn't know about CHAR_BIT.
We cou
|
| + // and that sizeof(uint32_t)'s implementation-defined behavior is 4. |
| + int mip_level_count = significant_bits; |
| + |
| + scoped_ptr<DecodedImage> last_mip_scaled_image = nullptr; |
|
vmpstr
2016/03/29 18:38:13
you don't need to initialize scoped_ptrs, they are
cblume
2016/03/29 18:53:44
Done.
|
| + scoped_ptr<DecodedImage> target_mip_image = nullptr; |
| + for (int current_mip_level = 0; current_mip_level < mip_level_count; |
| + current_mip_level++) { |
| + int mip_height = std::max(1, src_height / (1 << current_mip_level)); |
| + int mip_width = std::max(1, src_width / (1 << current_mip_level)); |
| + SkScalar y_scale = static_cast<float>(mip_height) / src_height; |
| + SkScalar x_scale = static_cast<float>(mip_width) / src_width; |
| + |
| + // Generate an image and key to cache it |
| + DrawImage mip_image(*image, src_rect, kMedium_SkFilterQuality, |
| + SkMatrix::MakeScale(x_scale, y_scale)); |
| + ImageKey mip_key = ImageKey::FromDrawImage(mip_image); |
| + scoped_ptr<DecodedImage> mip_scaled_image = |
| + ScaleImage(mip_key, decoded_image_result); |
| + |
| + // Check if this is the first mipmap smaller than target. |
| + // If so, use the mip level below on the stack. |
| + // This effectively always uses the larger image and always scales down. |
| + if (mip_height < key.target_size().height() || |
| + mip_width < key.target_size().width()) { |
| + if (target_mip_image == nullptr) { |
| + target_mip_image = last_mip_scaled_image; |
|
vmpstr
2016/03/29 18:38:13
scoped_ptrs assign like this? last_mip_scaled_imag
cblume
2016/03/29 18:53:44
The outcome is a build error. Whoops. Fixing.
|
| } |
| } |
| - |
| - return make_scoped_ptr(new DecodedImage( |
| - decoded_info, std::move(decoded_pixels), SkSize::Make(0, 0))); |
| + last_mip_scaled_image = mip_scaled_image; |
| } |
| - // If we get here, that means we couldn't use the original sized decode for |
| - // whatever reason. However, in all cases we do need an original decode to |
| - // either do a scale or to extract a subrect from the image. So, what we can |
| - // do is construct a key that would require a full sized decode, then get that |
| - // decode via GetDecodedImageForDrawInternal(), use it, and unref it. This |
| - // ensures that if the original sized decode is already available in any of |
| - // the caches, we reuse that. We also ensure that all the proper locking takes |
| - // place. If, on the other hand, the decode was not available, |
| - // GetDecodedImageForDrawInternal() would decode the image, and unreffing it |
| - // later ensures that we will store the discardable memory unlocked in the |
| - // cache to be used by future requests. |
| - gfx::Rect full_image_rect(image->width(), image->height()); |
| - DrawImage original_size_draw_image(image, gfx::RectToSkIRect(full_image_rect), |
| - kNone_SkFilterQuality, SkMatrix::I()); |
| - ImageKey original_size_key = |
| - ImageKey::FromDrawImage(original_size_draw_image); |
| - // Sanity checks. |
| - DCHECK(original_size_key.can_use_original_decode()); |
| - DCHECK(full_image_rect.size() == original_size_key.target_size()); |
| + return target_mip_image; |
| +} |
| - auto decoded_draw_image = GetDecodedImageForDrawInternal( |
| - original_size_key, original_size_draw_image); |
| - if (!decoded_draw_image.image()) { |
| - DrawWithImageFinished(original_size_draw_image, decoded_draw_image); |
| +scoped_ptr<SoftwareImageDecodeController::DecodedImage> |
| +SoftwareImageDecodeController::DecodeImageHighQuality(const ImageKey& key, |
| + const SkImage& image) { |
| + // We need an original decode to either do a scale or to extract a subrect |
| + // from the image. |
| + auto decoded_image_result = DecodeImageOrUseCache(key, image); |
| + if (!decoded_image_result.decoded_pixmap_.addr()) { |
| return nullptr; |
| } |
| - SkPixmap decoded_pixmap; |
| - bool result = decoded_draw_image.image()->peekPixels(&decoded_pixmap); |
| - DCHECK(result); |
| - if (key.src_rect() != full_image_rect) { |
| - result = decoded_pixmap.extractSubset(&decoded_pixmap, |
| - gfx::RectToSkIRect(key.src_rect())); |
| - DCHECK(result); |
| - } |
| // Now we have a decoded_pixmap which represents the src_rect at the |
| // original scale. All we need to do is scale it. |
| - DCHECK(!key.target_size().IsEmpty()); |
| - SkImageInfo scaled_info = CreateImageInfo( |
| - key.target_size().width(), key.target_size().height(), format_); |
| - scoped_ptr<base::DiscardableMemory> scaled_pixels; |
| - { |
| - TRACE_EVENT0( |
| - "disabled-by-default-cc.debug", |
| - "SoftwareImageDecodeController::DecodeImageInternal - allocate " |
| - "scaled pixels"); |
| - scaled_pixels = base::DiscardableMemoryAllocator::GetInstance() |
| - ->AllocateLockedDiscardableMemory( |
| - scaled_info.minRowBytes() * scaled_info.height()); |
| - } |
| - SkPixmap scaled_pixmap(scaled_info, scaled_pixels->data(), |
| - scaled_info.minRowBytes()); |
| - // TODO(vmpstr): Start handling more than just high filter quality. |
| - DCHECK_EQ(kHigh_SkFilterQuality, key.filter_quality()); |
| - { |
| - TRACE_EVENT0( |
| - "disabled-by-default-cc.debug", |
| - "SoftwareImageDecodeController::DecodeImageInternal - scale pixels"); |
| - bool result = |
| - decoded_pixmap.scalePixels(scaled_pixmap, key.filter_quality()); |
| - DCHECK(result); |
| - } |
| + return ScaleImage(key, decoded_image_result); |
| +} |
| - // Release the original sized decode. Any other intermediate result to release |
| - // would be the subrect memory. However, that's in a scoped_ptr and will be |
| - // deleted automatically when we return. |
| - DrawWithImageFinished(original_size_draw_image, decoded_draw_image); |
| +scoped_ptr<SoftwareImageDecodeController::DecodedImage> |
| +SoftwareImageDecodeController::DecodeImageInternal( |
| + const ImageKey& key, |
| + const DrawImage& draw_image) { |
| + TRACE_EVENT1("disabled-by-default-cc.debug", |
| + "SoftwareImageDecodeController::DecodeImageInternal", "key", |
| + key.ToString()); |
| + const SkImage* image = draw_image.image(); |
| + if (!image) { |
| + return nullptr; |
| + } |
| - return make_scoped_ptr( |
| - new DecodedImage(scaled_info, std::move(scaled_pixels), |
| - SkSize::Make(-key.src_rect().x(), -key.src_rect().y()))); |
| + switch (key.filter_quality()) { |
| + case kNone_SkFilterQuality: |
| + // fall through |
| + case kLow_SkFilterQuality: |
| + return DecodeImageNoneLowQuality(key, *image); |
| + case kMedium_SkFilterQuality: |
| + return DecodeImageMediumQuality(key, *image); |
| + case kHigh_SkFilterQuality: |
| + return DecodeImageHighQuality(key, *image); |
| + default: |
| + NOTREACHED(); |
| + return nullptr; |
| + } |
| } |
| DecodedDrawImage SoftwareImageDecodeController::GetDecodedImageForDraw( |
| @@ -491,9 +422,6 @@ DecodedDrawImage SoftwareImageDecodeController::GetDecodedImageForDraw( |
| if (key.target_size().IsEmpty()) |
| return DecodedDrawImage(nullptr, kNone_SkFilterQuality); |
| - if (!CanHandleImage(key)) |
| - return DecodedDrawImage(draw_image.image(), draw_image.filter_quality()); |
| - |
| return GetDecodedImageForDrawInternal(key, draw_image); |
| } |
| @@ -549,7 +477,11 @@ DecodedDrawImage SoftwareImageDecodeController::GetDecodedImageForDrawInternal( |
| // on the compositor thread. This means holding on to the lock might stall |
| // the compositor thread for the duration of the decode! |
| base::AutoUnlock unlock(lock_); |
| - scoped_decoded_image = DecodeImageInternal(key, draw_image); |
| + const SkImage* image = draw_image.image(); |
| + if (!image) { |
| + return DecodedDrawImage(nullptr, kNone_SkFilterQuality); |
| + } |
| + scoped_decoded_image = AttemptToUseOriginalImage(key, *image); |
| decoded_image = scoped_decoded_image.get(); |
| // Skip the image if we couldn't decode it. |
| @@ -590,6 +522,128 @@ DecodedDrawImage SoftwareImageDecodeController::GetDecodedImageForDrawInternal( |
| return decoded_draw_image; |
| } |
| +SoftwareImageDecodeController::DecodedImageResult::DecodedImageResult( |
| + SkPixmap decoded_pixmap, |
| + DrawImage original_size_draw_image, |
| + DecodedDrawImage decoded_draw_image) |
| + : decoded_pixmap_(decoded_pixmap), |
| + original_size_draw_image_(original_size_draw_image), |
| + decoded_draw_image_(decoded_draw_image) {} |
| + |
| +scoped_ptr<SoftwareImageDecodeController::DecodedImage> |
| +SoftwareImageDecodeController::AttemptToUseOriginalImage(const ImageKey& key, |
| + const SkImage& image) { |
| + TRACE_EVENT1("disabled-by-default-cc.debug", |
| + "SoftwareImageDecodeController::AttemptToUseOriginalImage", |
| + "key", key.ToString()); |
| + if (!key.can_use_original_decode()) { |
| + return nullptr; |
| + } else { |
| + SkImageInfo decoded_info = |
| + SkImageInfo::MakeN32Premul(image.width(), image.height()); |
| + scoped_ptr<base::DiscardableMemory> decoded_pixels; |
| + { |
| + TRACE_EVENT0("disabled-by-default-cc.debug", |
| + "SoftwareImageDecodeController::AttemptToUseOriginalImage - " |
| + "allocate decoded pixels"); |
| + decoded_pixels = |
| + base::DiscardableMemoryAllocator::GetInstance() |
| + ->AllocateLockedDiscardableMemory(decoded_info.minRowBytes() * |
| + decoded_info.height()); |
| + } |
| + { |
| + TRACE_EVENT0("disabled-by-default-cc.debug", |
| + "SoftwareImageDecodeController::AttemptToUseOriginalImage - " |
| + "read pixels"); |
| + bool result = image.readPixels(decoded_info, decoded_pixels->data(), |
| + decoded_info.minRowBytes(), 0, 0, |
| + SkImage::kDisallow_CachingHint); |
| + |
| + if (!result) { |
| + decoded_pixels->Unlock(); |
| + return nullptr; |
| + } |
| + } |
| + |
| + return make_scoped_ptr(new DecodedImage( |
| + decoded_info, std::move(decoded_pixels), SkSize::Make(0, 0))); |
| + } |
| +} |
| + |
| +SoftwareImageDecodeController::DecodedImageResult |
| +SoftwareImageDecodeController::DecodeImageOrUseCache(const ImageKey& key, |
| + const SkImage& image) { |
| + // Construct a key to use in GetDecodedImageForDrawInternal(). |
| + // This allows us to reuse an image in any cache if available. |
| + gfx::Rect full_image_rect(image.width(), image.height()); |
| + DrawImage original_size_draw_image(&image, |
| + gfx::RectToSkIRect(full_image_rect), |
| + kNone_SkFilterQuality, SkMatrix::I()); |
| + ImageKey original_size_key = |
| + ImageKey::FromDrawImage(original_size_draw_image); |
| + // Sanity checks. |
| + DCHECK(original_size_key.can_use_original_decode()); |
| + DCHECK(full_image_rect.size() == original_size_key.target_size()); |
| + |
| + auto decoded_draw_image = GetDecodedImageForDrawInternal( |
| + original_size_key, original_size_draw_image); |
| + |
| + if (!decoded_draw_image.image()) { |
| + DrawWithImageFinished(original_size_draw_image, decoded_draw_image); |
| + return DecodedImageResult(SkPixmap(), DrawImage(), |
| + DecodedDrawImage(nullptr, kNone_SkFilterQuality)); |
| + } |
| + |
| + SkPixmap decoded_pixmap; |
| + bool result = decoded_draw_image.image()->peekPixels(&decoded_pixmap); |
| + DCHECK(result); |
| + if (key.src_rect() != full_image_rect) { |
| + result = decoded_pixmap.extractSubset(&decoded_pixmap, |
| + gfx::RectToSkIRect(key.src_rect())); |
| + DCHECK(result); |
| + } |
| + |
| + return DecodedImageResult(decoded_pixmap, original_size_draw_image, |
| + decoded_draw_image); |
| +} |
| + |
| +scoped_ptr<SoftwareImageDecodeController::DecodedImage> |
| +SoftwareImageDecodeController::ScaleImage( |
| + const ImageKey& key, |
| + const DecodedImageResult& decoded_image_result) { |
| + DCHECK(!key.target_size().IsEmpty()); |
| + SkImageInfo scaled_info = SkImageInfo::MakeN32Premul( |
| + key.target_size().width(), key.target_size().height()); |
| + scoped_ptr<base::DiscardableMemory> scaled_pixels; |
| + { |
| + TRACE_EVENT0( |
| + "disabled-by-default-cc.debug", |
| + "SoftwareImageDecodeController::ScaleImage - allocate scaled pixels"); |
| + scaled_pixels = base::DiscardableMemoryAllocator::GetInstance() |
| + ->AllocateLockedDiscardableMemory( |
| + scaled_info.minRowBytes() * scaled_info.height()); |
| + } |
| + SkPixmap scaled_pixmap(scaled_info, scaled_pixels->data(), |
| + scaled_info.minRowBytes()); |
| + { |
| + TRACE_EVENT0("disabled-by-default-cc.debug", |
| + "SoftwareImageDecodeController::ScaleImage - scale pixels"); |
| + bool result = decoded_image_result.decoded_pixmap_.scalePixels( |
| + scaled_pixmap, key.filter_quality()); |
| + DCHECK(result); |
| + } |
| + |
| + // Release the original sized decode. Any other intermediate result to release |
| + // would be the subrect memory. However, that's in a scoped_ptr and will be |
| + // deleted automatically when we return. |
| + DrawWithImageFinished(decoded_image_result.original_size_draw_image_, |
| + decoded_image_result.decoded_draw_image_); |
| + |
| + return make_scoped_ptr(new SoftwareImageDecodeController::DecodedImage( |
| + scaled_info, std::move(scaled_pixels), |
| + SkSize::Make(-key.src_rect().x(), -key.src_rect().y()))); |
| +} |
| + |
| void SoftwareImageDecodeController::DrawWithImageFinished( |
| const DrawImage& image, |
| const DecodedDrawImage& decoded_image) { |
| @@ -597,7 +651,7 @@ void SoftwareImageDecodeController::DrawWithImageFinished( |
| "SoftwareImageDecodeController::DrawWithImageFinished", "key", |
| ImageKey::FromDrawImage(image).ToString()); |
| ImageKey key = ImageKey::FromDrawImage(image); |
| - if (!decoded_image.image() || !CanHandleImage(key)) |
| + if (!decoded_image.image()) |
| return; |
| if (decoded_image.is_at_raster_decode()) |
| @@ -664,11 +718,6 @@ void SoftwareImageDecodeController::UnrefAtRasterImage(const ImageKey& key) { |
| } |
| } |
| -bool SoftwareImageDecodeController::CanHandleImage(const ImageKey& key) { |
| - // TODO(vmpstr): Start handling medium filter quality as well. |
| - return key.filter_quality() != kMedium_SkFilterQuality; |
| -} |
| - |
| void SoftwareImageDecodeController::ReduceCacheUsage() { |
| TRACE_EVENT0("cc", "SoftwareImageDecodeController::ReduceCacheUsage"); |
| base::AutoLock lock(lock_); |