#include "gpu/ipc/service/image_decode_accelerator_stub.h"
#include <stddef.h>
#include <algorithm>
#include <new>
#include <utility>
#include <vector>
#include "base/containers/span.h"
#include "base/feature_list.h"
#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/location.h"
#include "base/logging.h"
#include "base/memory/raw_ptr.h"
#include "base/numerics/checked_math.h"
#include "base/numerics/safe_conversions.h"
#include "base/task/single_thread_task_runner.h"
#include "build/build_config.h"
#include "build/chromeos_buildflags.h"
#include "gpu/command_buffer/common/constants.h"
#include "gpu/command_buffer/common/context_result.h"
#include "gpu/command_buffer/common/discardable_handle.h"
#include "gpu/command_buffer/common/scheduling_priority.h"
#include "gpu/command_buffer/common/shared_image_usage.h"
#include "gpu/command_buffer/common/sync_token.h"
#include "gpu/command_buffer/service/context_group.h"
#include "gpu/command_buffer/service/decoder_context.h"
#include "gpu/command_buffer/service/gr_shader_cache.h"
#include "gpu/command_buffer/service/scheduler.h"
#include "gpu/command_buffer/service/service_transfer_cache.h"
#include "gpu/command_buffer/service/shared_context_state.h"
#include "gpu/command_buffer/service/shared_image/shared_image_factory.h"
#include "gpu/command_buffer/service/sync_point_manager.h"
#include "gpu/config/gpu_finch_features.h"
#include "gpu/ipc/common/command_buffer_id.h"
#include "gpu/ipc/common/surface_handle.h"
#include "gpu/ipc/service/command_buffer_stub.h"
#include "gpu/ipc/service/gpu_channel.h"
#include "gpu/ipc/service/gpu_channel_manager.h"
#include "gpu/ipc/service/shared_image_stub.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
#include "third_party/skia/include/core/SkColorSpace.h"
#include "third_party/skia/include/core/SkImage.h"
#include "third_party/skia/include/core/SkImageInfo.h"
#include "third_party/skia/include/core/SkRefCnt.h"
#include "third_party/skia/include/gpu/GrBackendSemaphore.h"
#include "third_party/skia/include/gpu/GrBackendSurface.h"
#include "third_party/skia/include/gpu/GrTypes.h"
#include "ui/gfx/buffer_format_util.h"
#include "ui/gfx/buffer_types.h"
#include "ui/gfx/color_space.h"
#include "ui/gfx/gpu_memory_buffer.h"
#if BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(ENABLE_HEIF_DECODER)
#include "ui/gfx/linux/native_pixmap_dmabuf.h"
#endif
namespace gpu {
class Buffer;
#if BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(ENABLE_HEIF_DECODER)
namespace {
struct CleanUpContext {
scoped_refptr<base::SingleThreadTaskRunner> main_task_runner;
raw_ptr<SharedContextState, ExperimentalAsh> shared_context_state = nullptr;
std::unique_ptr<SkiaImageRepresentation> skia_representation;
std::unique_ptr<SkiaImageRepresentation::ScopedReadAccess> skia_scoped_access;
};
#if BUILDFLAG(ENABLE_HEIF_DECODER)
void CleanUpResource(SkImage::ReleaseContext context) {
auto* clean_up_context = static_cast<CleanUpContext*>(context);
DCHECK(clean_up_context->main_task_runner->BelongsToCurrentThread());
DCHECK(
clean_up_context->shared_context_state->IsCurrent(nullptr));
clean_up_context->skia_scoped_access->ApplyBackendSurfaceEndState();
delete clean_up_context;
}
#endif
}
#endif
ImageDecodeAcceleratorStub::ImageDecodeAcceleratorStub(
ImageDecodeAcceleratorWorker* worker,
GpuChannel* channel,
int32_t route_id)
: worker_(worker),
channel_(channel),
sequence_(channel->scheduler()->CreateSequence(SchedulingPriority::kLow,
channel->task_runner())),
sync_point_client_state_(
channel->sync_point_manager()->CreateSyncPointClientState(
CommandBufferNamespace::GPU_IO,
CommandBufferIdFromChannelAndRoute(channel->client_id(),
route_id),
sequence_)),
main_task_runner_(channel->task_runner()),
io_task_runner_(channel->io_task_runner()) {
channel_->scheduler()->DisableSequence(sequence_);
}
void ImageDecodeAcceleratorStub::Shutdown() {
DCHECK(main_task_runner_->BelongsToCurrentThread());
base::AutoLock lock(lock_);
sync_point_client_state_->Destroy();
channel_->scheduler()->DestroySequence(sequence_);
channel_ = nullptr;
}
ImageDecodeAcceleratorStub::~ImageDecodeAcceleratorStub() {
DCHECK(!channel_);
}
void ImageDecodeAcceleratorStub::ScheduleImageDecode(
mojom::ScheduleImageDecodeParamsPtr params,
uint64_t release_count) {
DCHECK(io_task_runner_->BelongsToCurrentThread());
#if !BUILDFLAG(IS_OHOS)
if (!base::FeatureList::IsEnabled(
features::kVaapiJpegImageDecodeAcceleration) &&
!base::FeatureList::IsEnabled(
features::kVaapiWebPImageDecodeAcceleration)) {
return;
}
#endif
DCHECK(io_task_runner_->BelongsToCurrentThread());
base::AutoLock lock(lock_);
if (!channel_) {
return;
}
mojom::ScheduleImageDecodeParams& decode_params = *params;
worker_->Decode(
std::move(decode_params.encoded_data), decode_params.output_size,
base::BindOnce(&ImageDecodeAcceleratorStub::OnDecodeCompleted,
base::WrapRefCounted(this), decode_params.output_size));
const SyncToken discardable_handle_sync_token = SyncToken(
CommandBufferNamespace::GPU_IO,
CommandBufferIdFromChannelAndRoute(channel_->client_id(),
decode_params.raster_decoder_route_id),
decode_params.discardable_handle_release_count);
channel_->scheduler()->ScheduleTask(Scheduler::Task(
sequence_,
base::BindOnce(&ImageDecodeAcceleratorStub::ProcessCompletedDecode,
base::WrapRefCounted(this), std::move(params),
release_count),
{discardable_handle_sync_token} ));
}
void ImageDecodeAcceleratorStub::ProcessCompletedDecode(
mojom::ScheduleImageDecodeParamsPtr params_ptr,
uint64_t decode_release_count) {
DCHECK(main_task_runner_->BelongsToCurrentThread());
base::AutoLock lock(lock_);
if (!channel_) {
return;
}
mojom::ScheduleImageDecodeParams& params = *params_ptr;
DCHECK(!pending_completed_decodes_.empty());
std::unique_ptr<ImageDecodeAcceleratorWorker::DecodeResult> completed_decode =
std::move(pending_completed_decodes_.front());
pending_completed_decodes_.pop();
base::ScopedClosureRunner finalizer(
base::BindOnce(&ImageDecodeAcceleratorStub::FinishCompletedDecode,
base::Unretained(this), decode_release_count));
if (!completed_decode) {
DLOG(ERROR) << "The image could not be decoded";
return;
}
#if BUILDFLAG(ENABLE_HEIF_DECODER)
base::ScopedClosureRunner event_finalizer(
base::BindOnce(&ImageDecodeAcceleratorStub::ReleasePixmapData,
base::Unretained(this), completed_decode->event));
#endif
if (params.output_size.IsEmpty()) {
DLOG(ERROR) << "Output dimensions are too small";
return;
}
ContextResult context_result;
scoped_refptr<SharedContextState> shared_context_state =
channel_->gpu_channel_manager()->GetSharedContextState(&context_result);
if (context_result != ContextResult::kSuccess) {
DLOG(ERROR) << "Unable to obtain the SharedContextState";
return;
}
DCHECK(shared_context_state);
if (!shared_context_state->gr_context()) {
DLOG(ERROR) << "Could not get the GrContext";
return;
}
if (!shared_context_state->MakeCurrent(nullptr )) {
DLOG(ERROR) << "Could not MakeCurrent the shared context";
return;
}
std::vector<sk_sp<SkImage>> plane_sk_images;
absl::optional<base::ScopedClosureRunner> notify_gl_state_changed;
#if BUILDFLAG(IS_CHROMEOS_ASH)
DCHECK(completed_decode->buffer_format == gfx::BufferFormat::YVU_420 ||
completed_decode->buffer_format ==
gfx::BufferFormat::YUV_420_BIPLANAR);
DCHECK_EQ(
gfx::NumberOfPlanesForLinearBufferFormat(completed_decode->buffer_format),
completed_decode->handle.native_pixmap_handle.planes.size());
const gfx::Size y_plane_size = completed_decode->visible_size;
base::CheckedNumeric<int> safe_uv_width(y_plane_size.width());
base::CheckedNumeric<int> safe_uv_height(y_plane_size.height());
safe_uv_width += 1;
safe_uv_width /= 2;
safe_uv_height += 1;
safe_uv_height /= 2;
int uv_width;
int uv_height;
if (!safe_uv_width.AssignIfValid(&uv_width) ||
!safe_uv_height.AssignIfValid(&uv_height)) {
DLOG(ERROR) << "Could not calculate subsampled dimensions";
return;
}
gfx::Size uv_plane_size = gfx::Size(uv_width, uv_height);
notify_gl_state_changed.emplace(base::BindOnce(
[](scoped_refptr<SharedContextState> scs) {
scs->set_need_context_state_reset(true);
},
shared_context_state));
const size_t num_planes =
completed_decode->handle.native_pixmap_handle.planes.size();
plane_sk_images.resize(num_planes);
for (size_t plane = 0u; plane < num_planes; plane++) {
gfx::Size plane_size = plane == 0 ? y_plane_size : uv_plane_size;
gfx::GpuMemoryBufferHandle plane_handle;
plane_handle.type = completed_decode->handle.type;
plane_handle.native_pixmap_handle.planes.push_back(
std::move(completed_decode->handle.native_pixmap_handle.planes[plane]));
const bool is_nv12_chroma_plane = completed_decode->buffer_format ==
gfx::BufferFormat::YUV_420_BIPLANAR &&
plane == 1u;
const auto plane_format = is_nv12_chroma_plane ? gfx::BufferFormat::RG_88
: gfx::BufferFormat::R_8;
gpu::Mailbox mailbox = gpu::Mailbox::GenerateForSharedImage();
if (!channel_->shared_image_stub()->CreateSharedImage(
mailbox, std::move(plane_handle), plane_format,
gfx::BufferPlane::DEFAULT, plane_size, gfx::ColorSpace(),
kTopLeft_GrSurfaceOrigin, kOpaque_SkAlphaType,
SHARED_IMAGE_USAGE_RASTER | SHARED_IMAGE_USAGE_OOP_RASTERIZATION,
"ImageDecodeAccelerator")) {
DLOG(ERROR) << "Could not create SharedImage";
return;
}
auto skia_representation =
channel_->gpu_channel_manager()->shared_image_manager()->ProduceSkia(
mailbox, shared_context_state->memory_type_tracker(),
shared_context_state);
channel_->shared_image_stub()->factory()->DestroySharedImage(mailbox);
std::vector<GrBackendSemaphore> begin_semaphores;
std::vector<GrBackendSemaphore> end_semaphores;
auto skia_scoped_access = skia_representation->BeginScopedReadAccess(
&begin_semaphores, &end_semaphores);
if (!skia_scoped_access) {
DLOG(ERROR) << "Could not get scoped access to SkiaImageRepresentation";
return;
}
DCHECK(begin_semaphores.empty());
DCHECK(end_semaphores.empty());
CleanUpContext* resource = new CleanUpContext{};
resource->main_task_runner = channel_->task_runner();
resource->shared_context_state = shared_context_state.get();
resource->skia_representation = std::move(skia_representation);
resource->skia_scoped_access = std::move(skia_scoped_access);
plane_sk_images[plane] = resource->skia_scoped_access->CreateSkImage(
shared_context_state->gr_context(), CleanUpResource, resource);
if (!plane_sk_images[plane]) {
DLOG(ERROR) << "Could not create planar SkImage";
return;
}
}
CommandBufferStub* command_buffer =
channel_->LookupCommandBuffer(params.raster_decoder_route_id);
if (!command_buffer) {
DLOG(ERROR) << "Could not find the command buffer";
return;
}
scoped_refptr<Buffer> handle_buffer =
command_buffer->GetTransferBuffer(params.discardable_handle_shm_id);
if (!DiscardableHandleBase::ValidateParameters(
handle_buffer.get(), params.discardable_handle_shm_offset)) {
DLOG(ERROR) << "Could not validate the discardable handle parameters";
return;
}
DCHECK(command_buffer->decoder_context());
if (command_buffer->decoder_context()->GetRasterDecoderId() < 0) {
DLOG(ERROR) << "Could not get the raster decoder ID";
return;
}
{
auto* gr_shader_cache = channel_->gpu_channel_manager()->gr_shader_cache();
absl::optional<raster::GrShaderCache::ScopedCacheUse> cache_use;
if (gr_shader_cache)
cache_use.emplace(gr_shader_cache,
base::strict_cast<int32_t>(channel_->client_id()));
DCHECK(shared_context_state->transfer_cache());
SkYUVAInfo::PlaneConfig plane_config =
completed_decode->buffer_format == gfx::BufferFormat::YVU_420
? SkYUVAInfo::PlaneConfig::kY_V_U
: SkYUVAInfo::PlaneConfig::kY_UV;
if (!shared_context_state->transfer_cache()
->CreateLockedHardwareDecodedImageEntry(
command_buffer->decoder_context()->GetRasterDecoderId(),
params.transfer_cache_entry_id,
ServiceDiscardableHandle(std::move(handle_buffer),
params.discardable_handle_shm_offset,
params.discardable_handle_shm_id),
shared_context_state->gr_context(), std::move(plane_sk_images),
plane_config, SkYUVAInfo::Subsampling::k420,
completed_decode->yuv_color_space,
completed_decode->buffer_byte_size, params.needs_mips)) {
DLOG(ERROR) << "Could not create and insert the transfer cache entry";
return;
}
}
DCHECK(notify_gl_state_changed);
notify_gl_state_changed->RunAndReset();
#elif BUILDFLAG(ENABLE_HEIF_DECODER)
notify_gl_state_changed.emplace(base::BindOnce(
[](scoped_refptr<SharedContextState> scs) {
scs->set_need_context_state_reset(true);
},
shared_context_state));
const size_t num_planes =
completed_decode->handle.native_pixmap_handle.planes.size();
plane_sk_images.resize(num_planes);
for (size_t plane = 0u; plane < num_planes; plane++) {
gfx::Size plane_size = params.output_size;
gfx::GpuMemoryBufferHandle plane_handle;
plane_handle.type = completed_decode->handle.type;
plane_handle.native_pixmap_handle.planes.push_back(
std::move(completed_decode->handle.native_pixmap_handle.planes[plane]));
const auto plane_format = gfx::BufferFormat::RGBA_8888;
gpu::Mailbox mailbox = gpu::Mailbox::GenerateForSharedImage();
if (!channel_->shared_image_stub()->CreateSharedImage(
mailbox, std::move(plane_handle), plane_format,
gfx::BufferPlane::DEFAULT, plane_size, gfx::ColorSpace(),
kTopLeft_GrSurfaceOrigin, kOpaque_SkAlphaType,
SHARED_IMAGE_USAGE_RASTER | SHARED_IMAGE_USAGE_OOP_RASTERIZATION,
completed_decode->window_buffer)) {
LOG(ERROR) << "[HeifSupport] Could not create SharedImage";
return;
}
std::unique_ptr<SkiaImageRepresentation> skia_representation =
channel_->gpu_channel_manager()->shared_image_manager()->ProduceSkia(
mailbox, shared_context_state->memory_type_tracker(),
shared_context_state);
channel_->shared_image_stub()->factory()->DestroySharedImage(mailbox);
if (!skia_representation) {
DLOG(ERROR) << "Could not create a SkiaImageRepresentation";
return;
}
std::vector<GrBackendSemaphore> begin_semaphores;
std::vector<GrBackendSemaphore> end_semaphores;
auto skia_scoped_access = skia_representation->BeginScopedReadAccess(
&begin_semaphores, &end_semaphores);
if (!skia_scoped_access) {
LOG(ERROR) << "[HeifSupport] Could not get scoped access to SkiaImageRepresentation";
return;
}
DCHECK(begin_semaphores.empty());
DCHECK(end_semaphores.empty());
CleanUpContext* resource = new CleanUpContext{};
resource->main_task_runner = channel_->task_runner();
resource->shared_context_state = shared_context_state.get();
resource->skia_representation = std::move(skia_representation);
resource->skia_scoped_access = std::move(skia_scoped_access);
plane_sk_images[plane] = resource->skia_scoped_access->CreateSkImage(
shared_context_state->gr_context(), CleanUpResource, resource);
if (!plane_sk_images[plane]) {
LOG(ERROR) << "[HeifSupport] Could not create planar SkImage";
return;
}
}
CommandBufferStub* command_buffer =
channel_->LookupCommandBuffer(params.raster_decoder_route_id);
if (!command_buffer) {
LOG(ERROR) << "[HeifSupport] Could not find the command buffer";
return;
}
scoped_refptr<Buffer> handle_buffer =
command_buffer->GetTransferBuffer(params.discardable_handle_shm_id);
if (!DiscardableHandleBase::ValidateParameters(
handle_buffer.get(), params.discardable_handle_shm_offset)) {
LOG(ERROR) << "[HeifSupport] Could not validate the discardable handle parameters";
return;
}
DCHECK(command_buffer->decoder_context());
if (command_buffer->decoder_context()->GetRasterDecoderId() < 0) {
LOG(ERROR) << "[HeifSupport] Could not get the raster decoder ID";
return;
}
{
auto* gr_shader_cache = channel_->gpu_channel_manager()->gr_shader_cache();
absl::optional<raster::GrShaderCache::ScopedCacheUse> cache_use;
if (gr_shader_cache)
cache_use.emplace(gr_shader_cache,
base::strict_cast<int32_t>(channel_->client_id()));
DCHECK(shared_context_state->transfer_cache());
if (!shared_context_state->transfer_cache()
->CreateLockedRGBAHardwareDecodedImageEntry(
command_buffer->decoder_context()->GetRasterDecoderId(),
params.transfer_cache_entry_id,
ServiceDiscardableHandle(std::move(handle_buffer),
params.discardable_handle_shm_offset,
params.discardable_handle_shm_id),
shared_context_state->gr_context(), std::move(plane_sk_images),
completed_decode->buffer_byte_size)) {
LOG(ERROR) << "[HeifSupport] Could not create and insert the transfer cache entry";
return;
}
}
DCHECK(notify_gl_state_changed);
notify_gl_state_changed->RunAndReset();
#else
NOTIMPLEMENTED()
<< "Image decode acceleration is unsupported for this platform";
#endif
}
void ImageDecodeAcceleratorStub::FinishCompletedDecode(
uint64_t decode_release_count) {
DCHECK(main_task_runner_->BelongsToCurrentThread());
lock_.AssertAcquired();
sync_point_client_state_->ReleaseFenceSync(decode_release_count);
if (pending_completed_decodes_.empty())
channel_->scheduler()->DisableSequence(sequence_);
}
#if BUILDFLAG(ENABLE_HEIF_DECODER)
void ImageDecodeAcceleratorStub::ReleasePixmapData(
base::WaitableEvent* finish_event) {
DCHECK(main_task_runner_->BelongsToCurrentThread());
lock_.AssertAcquired();
worker_->ReleaseDecodedPixelMap();
if (finish_event) {
finish_event->Signal();
}
}
#endif
void ImageDecodeAcceleratorStub::OnDecodeCompleted(
gfx::Size expected_output_size,
std::unique_ptr<ImageDecodeAcceleratorWorker::DecodeResult> result) {
base::AutoLock lock(lock_);
if (!channel_) {
return;
}
DCHECK(!result || expected_output_size == result->visible_size);
pending_completed_decodes_.push(std::move(result));
if (pending_completed_decodes_.size() == 1u)
channel_->scheduler()->EnableSequence(sequence_);
}
}