#include "gpu/ipc/client/image_decode_accelerator_proxy.h"
#include <memory>
#include <utility>
#include <vector>
#include "base/check_op.h"
#include "base/containers/contains.h"
#include "base/notreached.h"
#include "base/ranges/algorithm.h"
#include "build/enable_heif_buildflags.h"
#include "cc/paint/paint_image.h"
#include "gpu/command_buffer/common/constants.h"
#include "gpu/config/gpu_info.h"
#include "gpu/ipc/client/gpu_channel_host.h"
#include "gpu/ipc/common/command_buffer_id.h"
#include "gpu/ipc/common/gpu_channel.mojom.h"
#include "ui/gfx/color_space.h"
#include "ui/gfx/geometry/size.h"
namespace gpu {
namespace {
bool IsSupportedImageSize(
const cc::ImageHeaderMetadata* image_data,
const ImageDecodeAcceleratorSupportedProfile& supported_profile) {
DCHECK(image_data);
#if BUILDFLAG(ENABLE_HEIF_DECODER)
if(image_data->image_type == cc::ImageType::kHEIF) {
LOG(INFO) << "[HeifSupport] Heif type, no need to check size.";
return true;
}
#endif
gfx::Size image_size;
if (image_data->coded_size.has_value())
image_size = image_data->coded_size.value();
else
image_size = image_data->image_size;
DCHECK(!image_size.IsEmpty());
return image_size.width() >=
supported_profile.min_encoded_dimensions.width() &&
image_size.height() >=
supported_profile.min_encoded_dimensions.height() &&
image_size.width() <=
supported_profile.max_encoded_dimensions.width() &&
image_size.height() <=
supported_profile.max_encoded_dimensions.height();
}
bool IsSupportedJpegImage(
const cc::ImageHeaderMetadata* image_data,
const ImageDecodeAcceleratorSupportedProfile& supported_profile) {
DCHECK(image_data);
DCHECK_EQ(cc::ImageType::kJPEG, image_data->image_type);
DCHECK_EQ(ImageDecodeAcceleratorType::kJpeg, supported_profile.image_type);
DCHECK(image_data->jpeg_is_progressive.has_value());
if (image_data->jpeg_is_progressive.value())
return false;
static_assert(
static_cast<int>(ImageDecodeAcceleratorSubsampling::kMaxValue) == 2,
"IsSupportedJpegImage() must be adapted to support all subsampling "
"factors in ImageDecodeAcceleratorSubsampling");
ImageDecodeAcceleratorSubsampling subsampling;
switch (image_data->yuv_subsampling) {
case cc::YUVSubsampling::k420:
subsampling = ImageDecodeAcceleratorSubsampling::k420;
break;
case cc::YUVSubsampling::k422:
subsampling = ImageDecodeAcceleratorSubsampling::k422;
break;
case cc::YUVSubsampling::k444:
subsampling = ImageDecodeAcceleratorSubsampling::k444;
break;
default:
return false;
}
return base::Contains(supported_profile.subsamplings, subsampling);
}
}
ImageDecodeAcceleratorProxy::ImageDecodeAcceleratorProxy(GpuChannelHost* host,
int32_t route_id)
: host_(host), route_id_(route_id) {}
ImageDecodeAcceleratorProxy::~ImageDecodeAcceleratorProxy() {}
bool ImageDecodeAcceleratorProxy::IsImageSupported(
const cc::ImageHeaderMetadata* image_metadata) const {
DCHECK(host_);
if (!image_metadata)
return false;
if (!image_metadata->all_data_received_prior_to_decode)
return false;
if (image_metadata->has_embedded_color_profile)
return false;
#if BUILDFLAG(ENABLE_HEIF_DECODER)
static_assert(static_cast<int>(ImageDecodeAcceleratorType::kMaxValue) == 3,
#else
static_assert(static_cast<int>(ImageDecodeAcceleratorType::kMaxValue) == 2,
#endif
"IsImageSupported() must be adapted to support all image types "
"in ImageDecodeAcceleratorType");
ImageDecodeAcceleratorType image_type = ImageDecodeAcceleratorType::kUnknown;
switch (image_metadata->image_type) {
case cc::ImageType::kJPEG:
image_type = ImageDecodeAcceleratorType::kJpeg;
break;
case cc::ImageType::kWEBP:
image_type = ImageDecodeAcceleratorType::kWebP;
break;
#if BUILDFLAG(ENABLE_HEIF_DECODER)
case cc::ImageType::kHEIF:
image_type = ImageDecodeAcceleratorType::kHeif;
break;
#endif
default:
return false;
}
const std::vector<ImageDecodeAcceleratorSupportedProfile>& profiles =
host_->gpu_info().image_decode_accelerator_supported_profiles;
auto profile_it =
base::ranges::find(profiles, image_type,
&ImageDecodeAcceleratorSupportedProfile::image_type);
if (profile_it == profiles.cend())
return false;
if (!IsSupportedImageSize(image_metadata, *profile_it))
return false;
switch (image_type) {
case ImageDecodeAcceleratorType::kJpeg:
return IsSupportedJpegImage(image_metadata, *profile_it);
case ImageDecodeAcceleratorType::kWebP:
DCHECK(image_metadata->webp_is_non_extended_lossy.has_value());
return image_metadata->webp_is_non_extended_lossy.value();
#if BUILDFLAG(ENABLE_HEIF_DECODER)
case ImageDecodeAcceleratorType::kHeif:
return true;
#endif
case ImageDecodeAcceleratorType::kUnknown:
NOTREACHED();
break;
}
return false;
}
bool ImageDecodeAcceleratorProxy::IsJpegDecodeAccelerationSupported() const {
const auto& profiles =
host_->gpu_info().image_decode_accelerator_supported_profiles;
for (const auto& profile : profiles) {
if (profile.image_type == ImageDecodeAcceleratorType::kJpeg)
return true;
}
return false;
}
bool ImageDecodeAcceleratorProxy::IsWebPDecodeAccelerationSupported() const {
const auto& profiles =
host_->gpu_info().image_decode_accelerator_supported_profiles;
for (const auto& profile : profiles) {
if (profile.image_type == ImageDecodeAcceleratorType::kWebP)
return true;
}
return false;
}
SyncToken ImageDecodeAcceleratorProxy::ScheduleImageDecode(
base::span<const uint8_t> encoded_data,
const gfx::Size& output_size,
CommandBufferId raster_decoder_command_buffer_id,
uint32_t transfer_cache_entry_id,
int32_t discardable_handle_shm_id,
uint32_t discardable_handle_shm_offset,
uint64_t discardable_handle_release_count,
const gfx::ColorSpace& target_color_space,
bool needs_mips) {
DCHECK(host_);
DCHECK_EQ(host_->channel_id(),
ChannelIdFromCommandBufferId(raster_decoder_command_buffer_id));
auto params = mojom::ScheduleImageDecodeParams::New();
params->encoded_data.assign(encoded_data.begin(), encoded_data.end());
params->output_size = output_size;
params->raster_decoder_route_id =
RouteIdFromCommandBufferId(raster_decoder_command_buffer_id);
params->transfer_cache_entry_id = transfer_cache_entry_id;
params->discardable_handle_shm_id = discardable_handle_shm_id;
params->discardable_handle_shm_offset = discardable_handle_shm_offset;
params->discardable_handle_release_count = discardable_handle_release_count;
params->target_color_space = target_color_space;
params->needs_mips = needs_mips;
base::AutoLock lock(lock_);
const uint64_t release_count = ++next_release_count_;
host_->EnsureFlush(UINT32_MAX);
host_->GetGpuChannel().ScheduleImageDecode(std::move(params), release_count);
return SyncToken(
CommandBufferNamespace::GPU_IO,
CommandBufferIdFromChannelAndRoute(host_->channel_id(), route_id_),
release_count);
}
}