blob: 58e588933f47747a94030f2a1d7ae2a0a2c801e3 [file] [log] [blame]
// Copyright 2021 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "media/renderers/video_frame_rgba_to_yuva_converter.h"
#include "base/logging.h"
#include "base/memory/ptr_util.h"
#include "components/viz/common/gpu/raster_context_provider.h"
#include "components/viz/common/resources/resource_format_utils.h"
#include "gpu/command_buffer/client/raster_interface.h"
#include "gpu/command_buffer/client/shared_image_interface.h"
#include "gpu/command_buffer/common/mailbox_holder.h"
#include "media/base/simple_sync_token_client.h"
#include "media/base/wait_and_replace_sync_token_client.h"
#include "media/renderers/video_frame_yuv_converter.h"
#include "media/renderers/video_frame_yuv_mailboxes_holder.h"
#include "skia/ext/rgba_to_yuva.h"
#include "third_party/skia/include/core/SkImage.h"
#include "third_party/skia/include/gpu/GrDirectContext.h"
#include "ui/gfx/gpu_memory_buffer.h"
namespace {
// Given a gpu::MailboxHolder and a viz::RasterContextProvider, create scoped
// access to the texture as an SkImage.
class ScopedAcceleratedSkImage {
public:
static std::unique_ptr<ScopedAcceleratedSkImage> Create(
viz::RasterContextProvider* provider,
viz::ResourceFormat format,
const gfx::Size& size,
const gfx::ColorSpace& color_space,
GrSurfaceOrigin surface_origin,
const gpu::MailboxHolder& mailbox_holder) {
auto* ri = provider->RasterInterface();
DCHECK(ri);
GrDirectContext* gr_context = provider->GrContext();
DCHECK(gr_context);
if (!mailbox_holder.mailbox.IsSharedImage()) {
DLOG(ERROR) << "Cannot created SkImage for non-SharedImage mailbox.";
return nullptr;
}
ri->WaitSyncTokenCHROMIUM(mailbox_holder.sync_token.GetConstData());
uint32_t texture_id =
ri->CreateAndConsumeForGpuRaster(mailbox_holder.mailbox);
if (!texture_id) {
DLOG(ERROR) << "Failed to create texture for mailbox.";
return nullptr;
}
ri->BeginSharedImageAccessDirectCHROMIUM(
texture_id, GL_SHARED_IMAGE_ACCESS_MODE_READ_CHROMIUM);
GrGLTextureInfo gl_info = {
mailbox_holder.texture_target,
texture_id,
viz::TextureStorageFormat(format),
};
GrBackendTexture backend_texture(size.width(), size.height(),
GrMipmapped::kNo, gl_info);
SkColorType color_type = viz::ResourceFormatToClosestSkColorType(
/*gpu_compositing=*/true, format);
sk_sp<SkImage> sk_image = SkImage::MakeFromTexture(
gr_context, backend_texture, surface_origin, color_type,
kOpaque_SkAlphaType, color_space.ToSkColorSpace());
if (!sk_image) {
DLOG(ERROR) << "Failed to SkImage for StaticBitmapImage.";
ri->EndSharedImageAccessDirectCHROMIUM(texture_id);
ri->DeleteGpuRasterTexture(texture_id);
return nullptr;
}
return base::WrapUnique<ScopedAcceleratedSkImage>(
new ScopedAcceleratedSkImage(provider, texture_id,
std::move(sk_image)));
}
~ScopedAcceleratedSkImage() {
auto* ri = provider_->RasterInterface();
DCHECK(ri);
GrDirectContext* gr_context = provider_->GrContext();
DCHECK(gr_context);
sk_image_ = nullptr;
if (texture_id_) {
ri->EndSharedImageAccessDirectCHROMIUM(texture_id_);
ri->DeleteGpuRasterTexture(texture_id_);
}
}
sk_sp<SkImage> sk_image() { return sk_image_; }
private:
ScopedAcceleratedSkImage(viz::RasterContextProvider* provider,
uint32_t texture_id,
sk_sp<SkImage> sk_image)
: provider_(provider), texture_id_(texture_id), sk_image_(sk_image) {}
viz::RasterContextProvider* const provider_;
uint32_t texture_id_ = 0;
sk_sp<SkImage> sk_image_;
};
} // namespace
namespace media {
bool CopyRGBATextureToVideoFrame(viz::RasterContextProvider* provider,
viz::ResourceFormat src_format,
const gfx::Size& src_size,
const gfx::ColorSpace& src_color_space,
GrSurfaceOrigin src_surface_origin,
const gpu::MailboxHolder& src_mailbox_holder,
VideoFrame* dst_video_frame,
gpu::SyncToken& completion_sync_token) {
DCHECK_EQ(dst_video_frame->format(), PIXEL_FORMAT_NV12);
auto* ri = provider->RasterInterface();
DCHECK(ri);
if (!provider->GrContext()) {
SkYUVAInfo yuva_info =
VideoFrameYUVMailboxesHolder::VideoFrameGetSkYUVAInfo(dst_video_frame);
gpu::Mailbox yuva_mailboxes[SkYUVAInfo::kMaxPlanes];
ri->WaitSyncTokenCHROMIUM(src_mailbox_holder.sync_token.GetConstData());
for (int plane = 0; plane < yuva_info.numPlanes(); ++plane) {
gpu::MailboxHolder dst_mailbox_holder =
dst_video_frame->mailbox_holder(plane);
ri->WaitSyncTokenCHROMIUM(dst_mailbox_holder.sync_token.GetConstData());
yuva_mailboxes[plane] = dst_mailbox_holder.mailbox;
}
ri->ConvertRGBAToYUVAMailboxes(
yuva_info.yuvColorSpace(), yuva_info.planeConfig(),
yuva_info.subsampling(), yuva_mailboxes, src_mailbox_holder.mailbox);
} else {
// Create an accelerated SkImage for the source.
auto scoped_sk_image = ScopedAcceleratedSkImage::Create(
provider, src_format, src_size, src_color_space, src_surface_origin,
src_mailbox_holder);
if (!scoped_sk_image) {
DLOG(ERROR) << "Failed to create accelerated SkImage for RGBA to YUVA "
"conversion.";
return false;
}
// Create SkSurfaces for the destination planes.
sk_sp<SkSurface> sk_surfaces[SkYUVAInfo::kMaxPlanes];
SkSurface* sk_surface_ptrs[SkYUVAInfo::kMaxPlanes] = {nullptr};
VideoFrameYUVMailboxesHolder holder;
if (!holder.VideoFrameToPlaneSkSurfaces(dst_video_frame, provider,
sk_surfaces)) {
DLOG(ERROR) << "Failed to create SkSurfaces for VideoFrame.";
return false;
}
// Make GrContext wait for `dst_video_frame`. Waiting on the mailbox tokens
// here ensures that all writes are completed in cases where the underlying
// GpuMemoryBuffer and SharedImage resources have been reused.
ri->Flush();
WaitAndReplaceSyncTokenClient client(ri);
for (int plane = 0; plane < holder.yuva_info().numPlanes(); ++plane) {
sk_surface_ptrs[plane] = sk_surfaces[plane].get();
dst_video_frame->UpdateMailboxHolderSyncToken(plane, &client);
}
// Do the blit.
skia::BlitRGBAToYUVA(scoped_sk_image->sk_image().get(), sk_surface_ptrs,
holder.yuva_info());
provider->GrContext()->flushAndSubmit(false);
}
ri->Flush();
const size_t num_planes = dst_video_frame->layout().num_planes();
// For shared memory GMBs on Windows we needed to explicitly request a copy
// from the shared image GPU texture to the GMB. Set `completion_sync_token`
// to mark the completion of the copy.
if (dst_video_frame->HasGpuMemoryBuffer() &&
dst_video_frame->GetGpuMemoryBuffer()->GetType() ==
gfx::SHARED_MEMORY_BUFFER) {
auto* sii = provider->SharedImageInterface();
gpu::SyncToken blit_done_sync_token;
ri->GenUnverifiedSyncTokenCHROMIUM(blit_done_sync_token.GetData());
for (size_t plane = 0; plane < num_planes; ++plane) {
const auto& mailbox = dst_video_frame->mailbox_holder(plane).mailbox;
sii->CopyToGpuMemoryBuffer(blit_done_sync_token, mailbox);
}
completion_sync_token = sii->GenVerifiedSyncToken();
} else {
ri->GenSyncTokenCHROMIUM(completion_sync_token.GetData());
}
// Make access to the `dst_video_frame` wait on copy completion. We also
// update the ReleaseSyncToken here since it's used when the underlying
// GpuMemoryBuffer and SharedImage resources are returned to the pool.
SimpleSyncTokenClient simple_client(completion_sync_token);
for (size_t plane = 0; plane < num_planes; ++plane)
dst_video_frame->UpdateMailboxHolderSyncToken(plane, &simple_client);
dst_video_frame->UpdateReleaseSyncToken(&simple_client);
return true;
}
} // namespace media