Skip to content

Commit

Permalink
Blink: Implement WebP YUV decoding and conditionally go down the path.
Browse files Browse the repository at this point in the history
This change defines the logic to do WebP YUV decoding only
for lossy, non-alpha images without color profiles and only when
the kDecodeLossyWebPImagesToYUV flag is on. For now, we continue to
veto YUV decoding until all data is received because supporting
"incremental" YUV decoding in Chromium is a separate task
crbug.com/943519. It also adds a few initialization calls to
ImageFrameGenerator to set up plane sizes.

The YUV decoding path will initially be implemented for in-process
GPU rasterization and later for OOPR (Out of Process Rasterization).

See bit.ly/webp-decoding-into-yuv for the design document and
crrev.com/c/1338461 for a prototype CL summarizing all changes.

Bug: 900589
Change-Id: I7f40517273a62288a9bf0060176f864bf30d5aca
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1393003
Commit-Queue: Madeleine Barowsky <mbarowsky@chromium.org>
Reviewed-by: Khushal <khushalsagar@chromium.org>
Reviewed-by: Leon Scroggins <scroggo@chromium.org>
Reviewed-by: Kenneth Russell <kbr@chromium.org>
Cr-Commit-Position: refs/heads/master@{#660545}
  • Loading branch information
Madeleine Barowsky authored and Commit Bot committed May 16, 2019
1 parent 000c3a4 commit a106ff5
Show file tree
Hide file tree
Showing 10 changed files with 315 additions and 71 deletions.
28 changes: 25 additions & 3 deletions cc/tiles/gpu_image_decode_cache.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2107,6 +2107,7 @@ void GpuImageDecodeCache::UnlockImage(ImageData* image_data) {
images_pending_unlock_.push_back(image_data->upload.y_image().get());
images_pending_unlock_.push_back(image_data->upload.u_image().get());
images_pending_unlock_.push_back(image_data->upload.v_image().get());
yuv_images_pending_unlock_.push_back(image_data->upload.image());
} else {
images_pending_unlock_.push_back(image_data->upload.image().get());
}
Expand Down Expand Up @@ -2137,11 +2138,32 @@ void GpuImageDecodeCache::UnlockImage(ImageData* image_data) {
}
}

// YUV images are handled slightly differently because they are not themselves
// registered with the discardable memory system. We cannot use
// GlIdFromSkImage on these YUV SkImages to flush pending operations because
// doing so will flatten it to RGB.
void GpuImageDecodeCache::FlushYUVImages(
std::vector<sk_sp<SkImage>>* yuv_images) {
CheckContextLockAcquiredIfNecessary();
lock_.AssertAcquired();
for (auto& image : *yuv_images) {
image->flush(context_->GrContext());
}
yuv_images->clear();
}

// We always run pending operations in the following order:
// Lock > Unlock > Delete
// > Lock
// > Flush YUV images that will be unlocked
// > Unlock
// > Flush YUV images that will be deleted
// > Delete
// This ensures that:
// a) We never fully unlock an image that's pending lock (lock before unlock)
// b) We never delete an image that has pending locks/unlocks.
// c) We never unlock or delete the underlying texture planes for a YUV
// image before all operations referencing it have completed.
//
// As this can be run at-raster, to unlock/delete an image that was just used,
// we need to call GlIdFromSkImage, which flushes pending IO on the image,
// rather than just using a cached GL ID.
Expand All @@ -2159,6 +2181,7 @@ void GpuImageDecodeCache::RunPendingContextThreadOperations() {
}
images_pending_complete_lock_.clear();

FlushYUVImages(&yuv_images_pending_unlock_);
for (auto* image : images_pending_unlock_) {
context_->ContextGL()->UnlockDiscardableTextureCHROMIUM(
GlIdFromSkImage(image));
Expand All @@ -2171,8 +2194,7 @@ void GpuImageDecodeCache::RunPendingContextThreadOperations() {
}
ids_pending_unlock_.clear();

yuv_images_pending_deletion_.clear();

FlushYUVImages(&yuv_images_pending_deletion_);
for (auto& image : images_pending_deletion_) {
uint32_t texture_id = GlIdFromSkImage(image.get());
if (context_->ContextGL()->LockDiscardableTextureCHROMIUM(texture_id)) {
Expand Down
7 changes: 7 additions & 0 deletions cc/tiles/gpu_image_decode_cache.h
Original file line number Diff line number Diff line change
Expand Up @@ -593,6 +593,10 @@ class CC_EXPORT GpuImageDecodeCache
void UploadImageIfNecessary(const DrawImage& draw_image,
ImageData* image_data);

// Flush pending operations on context_->GrContext() for each element of
// |yuv_images| and then clear the vector.
void FlushYUVImages(std::vector<sk_sp<SkImage>>* yuv_images);

// Runs pending operations that required the |context_| lock to be held, but
// were queued up during a time when the |context_| lock was unavailable.
// These including deleting, unlocking, and locking textures.
Expand Down Expand Up @@ -660,7 +664,10 @@ class CC_EXPORT GpuImageDecodeCache
std::vector<sk_sp<SkImage>> images_pending_deletion_;
// Images that are backed by planar textures must be handled differently
// to avoid inadvertently flattening to RGB and creating additional textures.
// See comment in RunPendingContextThreadOperations().
std::vector<sk_sp<SkImage>> yuv_images_pending_deletion_;
std::vector<sk_sp<SkImage>> yuv_images_pending_unlock_;
const sk_sp<SkColorSpace> target_color_space_;

std::vector<uint32_t> ids_pending_unlock_;
std::vector<uint32_t> ids_pending_deletion_;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@
#include "third_party/blink/renderer/platform/graphics/decoding_image_generator.h"

#include <utility>

#include <memory>

#include "third_party/blink/renderer/platform/graphics/image_frame_generator.h"
#include "third_party/blink/renderer/platform/graphics/skia/skia_utils.h"
#include "third_party/blink/renderer/platform/image-decoders/image_decoder.h"
Expand Down Expand Up @@ -231,6 +231,7 @@ bool DecodingImageGenerator::QueryYUVA8(
indices[SkYUVAIndex::kV_Index] = {2, SkColorChannel::kR};
indices[SkYUVAIndex::kA_Index] = {-1, SkColorChannel::kR};

DCHECK(all_data_received_);
return frame_generator_->GetYUVComponentSizes(data_.get(), size_info);
}

Expand All @@ -239,8 +240,8 @@ bool DecodingImageGenerator::GetYUVA8Planes(const SkYUVASizeInfo& size_info,
void* planes[3],
size_t frame_index,
uint32_t lazy_pixel_ref) {
// YUV decoding does not currently support progressive decoding. See comment
// in ImageFrameGenerator.h.
// TODO(crbug.com/943519): YUV decoding does not currently support incremental
// decoding. See comment in image_frame_generator.h.
DCHECK(can_yuv_decode_);
DCHECK(all_data_received_);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,17 @@ sk_sp<PaintImageGenerator> DeferredImageDecoder::CreateGenerator(size_t index) {
ReportIncrementalDecodeNeeded(all_data_received_, image_type);
}
}
DCHECK(incremental_decode_needed_.has_value());

// TODO(crbug.com/943519):
// If we haven't received all data, we might veto YUV and begin doing
// incremental RGB decoding until all data were received. Then the final
// decode would be in YUV (but from the beginning of the image).
//
// The memory/speed tradeoffs of mixing RGB and YUV decoding are unclear due
// to caching at various levels. Additionally, incremental decoding is less
// common, so we avoid worrying about this with the line below.
can_yuv_decode_ &= !incremental_decode_needed_.value();

auto generator = DecodingImageGenerator::Create(
frame_generator_, info, std::move(segment_reader), std::move(frames),
Expand Down Expand Up @@ -406,8 +417,6 @@ void DeferredImageDecoder::PrepareLazyDecodedFrames() {
metadata_decoder_->FrameIsReceivedAtIndex(last_frame);
}

// YUV decoding does not currently support progressive decoding. See comment
// in image_frame_generator.h.
can_yuv_decode_ = RuntimeEnabledFeatures::DecodeToYUVEnabled() &&
metadata_decoder_->CanDecodeToYUV() && all_data_received_ &&
!frame_generator_->IsMultiFrame();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,12 +42,16 @@ static bool UpdateYUVComponentSizes(ImageDecoder* decoder,
SkISize component_sizes[4],
size_t component_width_bytes[4]) {
DCHECK(decoder->CanDecodeToYUV());
// Initialize sizes for decoder if not already set.
bool size_available = decoder->IsSizeAvailable();
DCHECK(size_available);

for (int yuv_index = 0; yuv_index < 3; ++yuv_index) {
IntSize size = decoder->DecodedYUVSize(yuv_index);
component_sizes[yuv_index].set(size.Width(), size.Height());
component_width_bytes[yuv_index] = decoder->DecodedYUVWidthBytes(yuv_index);
}
// TODO(crbug/910276): Alpha plane is currently unsupported.
component_sizes[3] = SkISize::MakeEmpty();
component_width_bytes[3] = 0;

Expand All @@ -74,6 +78,7 @@ ImageFrameGenerator::ImageFrameGenerator(const SkISize& full_size,
}

ImageFrameGenerator::~ImageFrameGenerator() {
// We expect all image decoders to be unlocked and catch with DCHECKs if not.
ImageDecodingStore::Instance().RemoveCacheIndexedByGenerator(this);
}

Expand Down Expand Up @@ -155,26 +160,29 @@ bool ImageFrameGenerator::DecodeToYUV(SegmentReader* data,
// TODO (scroggo): The only interesting thing this uses from the
// ImageFrameGenerator is m_decodeFailed. Move this into
// DecodingImageGenerator, which is the only class that calls it.
if (decode_failed_)
if (decode_failed_ || yuv_decoding_failed_)
return false;

if (!planes || !planes[0] || !planes[1] || !planes[2] || !row_bytes ||
!row_bytes[0] || !row_bytes[1] || !row_bytes[2]) {
return false;
}

const bool data_complete = true;
const bool all_data_received = true;
std::unique_ptr<ImageDecoder> decoder = ImageDecoder::Create(
data, data_complete, ImageDecoder::kAlphaPremultiplied,
data, all_data_received, ImageDecoder::kAlphaPremultiplied,
ImageDecoder::kDefaultBitDepth, decoder_color_behavior_);
// getYUVComponentSizes was already called and was successful, so
// ImageDecoder::create must succeed.
DCHECK(decoder);

std::unique_ptr<ImagePlanes> image_planes =
std::make_unique<ImagePlanes>(planes, row_bytes);
// TODO(crbug.com/943519): Don't forget to initialize planes to black or
// transparent for incremental decoding.
decoder->SetImagePlanes(std::move(image_planes));

DCHECK(decoder->CanDecodeToYUV());

{
// This is the YUV analog of ImageFrameGenerator::decode.
TRACE_EVENT0("blink,benchmark", "ImageFrameGenerator::decodeToYUV");
Expand Down Expand Up @@ -221,20 +229,19 @@ bool ImageFrameGenerator::GetYUVComponentSizes(SegmentReader* data,

if (yuv_decoding_failed_)
return false;

const bool data_complete = true;
std::unique_ptr<ImageDecoder> decoder = ImageDecoder::Create(
data, data_complete, ImageDecoder::kAlphaPremultiplied,
data, true /* data_complete */, ImageDecoder::kAlphaPremultiplied,
ImageDecoder::kDefaultBitDepth, decoder_color_behavior_);
if (!decoder)
return false;
DCHECK(decoder);

// Setting a dummy ImagePlanes object signals to the decoder that we want to
// do YUV decoding.
std::unique_ptr<ImagePlanes> dummy_image_planes =
std::make_unique<ImagePlanes>();
decoder->SetImagePlanes(std::move(dummy_image_planes));

DCHECK(decoder->CanDecodeToYUV());

return UpdateYUVComponentSizes(decoder.get(), size_info->fSizes,
size_info->fWidthBytes);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,10 +88,10 @@ class PLATFORM_EXPORT ImageFrameGenerator final
cc::PaintImage::GeneratorClientId);

// Decodes YUV components directly into the provided memory planes. Must not
// be called unless getYUVComponentSizes has been called and returned true.
// YUV decoding does not currently support progressive decoding. In order to
// support it, ImageDecoder needs something analagous to its ImageFrame cache
// to hold partial planes, and the GPU code needs to handle them.
// be called unless GetYUVComponentSizes has been called and returned true.
// TODO(crbug.com/943519): In order to support incremental YUV decoding,
// ImageDecoder needs something analogous to its ImageFrame cache to hold
// partial planes, and the GPU code needs to handle them.
bool DecodeToYUV(SegmentReader*,
size_t index,
const SkISize component_sizes[3],
Expand All @@ -110,9 +110,7 @@ class PLATFORM_EXPORT ImageFrameGenerator final

bool HasAlpha(size_t index);

// Must not be called unless the SkROBuffer has all the data. YUV decoding
// does not currently support progressive decoding. See comment above on
// decodeToYUV().
// TODO(crbug.com/943519): Do not call unless the SkROBuffer has all the data.
bool GetYUVComponentSizes(SegmentReader*, SkYUVASizeInfo*);

private:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -362,7 +362,7 @@ void ImageDecoder::ClearFrameBuffer(size_t frame_index) {
}

Vector<size_t> ImageDecoder::FindFramesToDecode(size_t index) const {
DCHECK(index < frame_buffer_cache_.size());
DCHECK_LT(index, frame_buffer_cache_.size());

Vector<size_t> frames_to_decode;
do {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ class PLATFORM_EXPORT ImagePlanes final {

public:
ImagePlanes();
// TODO(crbug/910276): To support YUVA, ImagePlanes needs to support a
// variable number of planes.
ImagePlanes(void* planes[3], const size_t row_bytes[3]);

void* Plane(int);
Expand Down
Loading

0 comments on commit a106ff5

Please sign in to comment.