1 // Copyright 2021 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "media/renderers/video_frame_rgba_to_yuva_converter.h"
7 #include "base/logging.h"
8 #include "base/memory/ptr_util.h"
9 #include "base/memory/raw_ptr.h"
10 #include "components/viz/common/gpu/raster_context_provider.h"
11 #include "components/viz/common/resources/shared_image_format_utils.h"
12 #include "gpu/command_buffer/client/raster_interface.h"
13 #include "gpu/command_buffer/client/shared_image_interface.h"
14 #include "gpu/command_buffer/common/mailbox_holder.h"
15 #include "gpu/command_buffer/common/shared_image_capabilities.h"
16 #include "media/base/simple_sync_token_client.h"
17 #include "media/base/wait_and_replace_sync_token_client.h"
18 #include "media/renderers/video_frame_yuv_converter.h"
19 #include "media/renderers/video_frame_yuv_mailboxes_holder.h"
20 #include "skia/ext/rgba_to_yuva.h"
21 #include "third_party/skia/include/core/SkColorSpace.h"
22 #include "third_party/skia/include/core/SkImage.h"
23 #include "third_party/skia/include/gpu/GrDirectContext.h"
24 #include "third_party/skia/include/gpu/GrTypes.h"
25 #include "third_party/skia/include/gpu/ganesh/SkImageGanesh.h"
26 #include "third_party/skia/include/gpu/ganesh/gl/GrGLBackendSurface.h"
27 #include "third_party/skia/include/gpu/gl/GrGLTypes.h"
28 #include "ui/gfx/gpu_memory_buffer.h"
32 // Given a gpu::MailboxHolder and a viz::RasterContextProvider, create scoped
33 // access to the texture as an SkImage.
34 class ScopedAcceleratedSkImage {
36 static std::unique_ptr<ScopedAcceleratedSkImage> Create(
37 viz::RasterContextProvider* provider,
38 viz::SharedImageFormat format,
39 const gfx::Size& size,
40 const gfx::ColorSpace& color_space,
41 GrSurfaceOrigin surface_origin,
42 const gpu::MailboxHolder& mailbox_holder) {
43 auto* ri = provider->RasterInterface();
45 GrDirectContext* gr_context = provider->GrContext();
48 if (!mailbox_holder.mailbox.IsSharedImage()) {
49 DLOG(ERROR) << "Cannot created SkImage for non-SharedImage mailbox.";
53 ri->WaitSyncTokenCHROMIUM(mailbox_holder.sync_token.GetConstData());
56 ri->CreateAndConsumeForGpuRaster(mailbox_holder.mailbox);
58 DLOG(ERROR) << "Failed to create texture for mailbox.";
61 ri->BeginSharedImageAccessDirectCHROMIUM(
62 texture_id, GL_SHARED_IMAGE_ACCESS_MODE_READ_CHROMIUM);
64 GrGLTextureInfo gl_info = {
65 mailbox_holder.texture_target,
67 provider->GetGrGLTextureFormat(format),
69 auto backend_texture = GrBackendTextures::MakeGL(
70 size.width(), size.height(), skgpu::Mipmapped::kNo, gl_info);
72 SkColorType color_type = viz::ToClosestSkColorType(
73 /*gpu_compositing=*/true, format);
74 sk_sp<SkImage> sk_image = SkImages::BorrowTextureFrom(
75 gr_context, backend_texture, surface_origin, color_type,
76 kOpaque_SkAlphaType, color_space.ToSkColorSpace());
78 DLOG(ERROR) << "Failed to SkImage for StaticBitmapImage.";
79 ri->EndSharedImageAccessDirectCHROMIUM(texture_id);
80 ri->DeleteGpuRasterTexture(texture_id);
84 return base::WrapUnique<ScopedAcceleratedSkImage>(
85 new ScopedAcceleratedSkImage(provider, texture_id,
86 std::move(sk_image)));
89 ~ScopedAcceleratedSkImage() {
90 auto* ri = provider_->RasterInterface();
92 GrDirectContext* gr_context = provider_->GrContext();
97 ri->EndSharedImageAccessDirectCHROMIUM(texture_id_);
98 ri->DeleteGpuRasterTexture(texture_id_);
102 sk_sp<SkImage> sk_image() { return sk_image_; }
105 ScopedAcceleratedSkImage(viz::RasterContextProvider* provider,
107 sk_sp<SkImage> sk_image)
108 : provider_(provider), texture_id_(texture_id), sk_image_(sk_image) {}
110 const raw_ptr<viz::RasterContextProvider> provider_;
111 uint32_t texture_id_ = 0;
112 sk_sp<SkImage> sk_image_;
119 bool CopyRGBATextureToVideoFrame(viz::RasterContextProvider* provider,
120 viz::SharedImageFormat src_format,
121 const gfx::Size& src_size,
122 const gfx::ColorSpace& src_color_space,
123 GrSurfaceOrigin src_surface_origin,
124 const gpu::MailboxHolder& src_mailbox_holder,
125 VideoFrame* dst_video_frame) {
126 DCHECK_EQ(dst_video_frame->format(), PIXEL_FORMAT_NV12);
128 auto* ri = provider->RasterInterface();
131 // If context is lost for any reason e.g. creating shared image failed, we
132 // cannot distinguish between OOP and non-OOP raster based on GrContext().
133 if (ri->GetGraphicsResetStatusKHR() != GL_NO_ERROR) {
134 DLOG(ERROR) << "Raster context lost.";
138 // With OOP raster, if RGB->YUV conversion is unsupported, the CopySharedImage
139 // calls will fail on the service side with no ability to detect failure on
140 // the client side. Check for support here and early out if it's unsupported.
141 if (!provider->GrContext() &&
142 !provider->ContextCapabilities().supports_yuv_rgb_conversion) {
143 DVLOG(1) << "RGB->YUV conversion not supported";
147 #if BUILDFLAG(IS_WIN)
148 // CopyToGpuMemoryBuffer is only supported for D3D shared images on Windows.
149 if (!provider->SharedImageInterface()->GetCapabilities().shared_image_d3d) {
150 DVLOG(1) << "CopyToGpuMemoryBuffer not supported.";
153 #endif // BUILDFLAG(IS_WIN)
155 if (provider->ContextCapabilities().supports_yuv_rgb_conversion &&
156 src_mailbox_holder.mailbox.IsSharedImage()) {
157 ri->WaitSyncTokenCHROMIUM(src_mailbox_holder.sync_token.GetConstData());
158 if (dst_video_frame->shared_image_format_type() ==
159 SharedImageFormatType::kLegacy) {
160 SkYUVAInfo yuva_info =
161 VideoFrameYUVMailboxesHolder::VideoFrameGetSkYUVAInfo(
163 gpu::Mailbox yuva_mailboxes[SkYUVAInfo::kMaxPlanes];
164 for (int plane = 0; plane < yuva_info.numPlanes(); ++plane) {
165 gpu::MailboxHolder dst_mailbox_holder =
166 dst_video_frame->mailbox_holder(plane);
167 ri->WaitSyncTokenCHROMIUM(dst_mailbox_holder.sync_token.GetConstData());
168 yuva_mailboxes[plane] = dst_mailbox_holder.mailbox;
170 ri->ConvertRGBAToYUVAMailboxes(
171 yuva_info.yuvColorSpace(), yuva_info.planeConfig(),
172 yuva_info.subsampling(), yuva_mailboxes, src_mailbox_holder.mailbox);
174 gpu::MailboxHolder dst_mailbox_holder =
175 dst_video_frame->mailbox_holder(0);
176 ri->WaitSyncTokenCHROMIUM(dst_mailbox_holder.sync_token.GetConstData());
178 // `unpack_flip_y` should be set if the surface origin of the source
179 // doesn't match that of the destination, which is created with
180 // kTopLeft_GrSurfaceOrigin.
181 // TODO(crbug.com/1453515): If this codepath is used with destinations
182 // that are created with other surface origins, will need to generalize
184 bool unpack_flip_y = (src_surface_origin != kTopLeft_GrSurfaceOrigin);
186 // Note: the destination video frame can have a coded size that is larger
187 // than that of the source video to account for alignment needs. In this
188 // case, both this codepath and the the legacy codepath above stretch to
189 // fill the destination. Cropping would clearly be more correct, but
190 // implementing that behavior in CopySharedImage() for the MultiplanarSI
191 // case resulted in pixeltest failures due to pixel bleeding around image
192 // borders that we weren't able to resolve (see crbug.com/1451025 for
194 // TODO(crbug.com/1451025): Update this comment when we resolve that bug
195 // and change CopySharedImage() to crop rather than stretch.
196 ri->CopySharedImage(src_mailbox_holder.mailbox,
197 dst_mailbox_holder.mailbox, GL_TEXTURE_2D, 0, 0, 0, 0,
198 src_size.width(), src_size.height(), unpack_flip_y,
199 /*unpack_premultiply_alpha=*/false);
202 // We shouldn't be here with OOP-raster since supports_yuv_rgb_conversion
203 // should be true in that case. We can end up here with non-OOP raster when
204 // dealing with legacy mailbox when YUV-RGB conversion is unsupported by GL.
205 CHECK(provider->GrContext());
206 // Create an accelerated SkImage for the source.
207 auto scoped_sk_image = ScopedAcceleratedSkImage::Create(
208 provider, src_format, src_size, src_color_space, src_surface_origin,
210 if (!scoped_sk_image) {
211 DLOG(ERROR) << "Failed to create accelerated SkImage for RGBA to YUVA "
216 // Create SkSurfaces for the destination planes.
217 sk_sp<SkSurface> sk_surfaces[SkYUVAInfo::kMaxPlanes];
218 SkSurface* sk_surface_ptrs[SkYUVAInfo::kMaxPlanes] = {nullptr};
219 VideoFrameYUVMailboxesHolder holder;
220 if (!holder.VideoFrameToPlaneSkSurfaces(dst_video_frame, provider,
222 DLOG(ERROR) << "Failed to create SkSurfaces for VideoFrame.";
226 // Make GrContext wait for `dst_video_frame`. Waiting on the mailbox tokens
227 // here ensures that all writes are completed in cases where the underlying
228 // GpuMemoryBuffer and SharedImage resources have been reused.
230 WaitAndReplaceSyncTokenClient client(ri);
231 for (int plane = 0; plane < holder.yuva_info().numPlanes(); ++plane) {
232 sk_surface_ptrs[plane] = sk_surfaces[plane].get();
233 dst_video_frame->UpdateMailboxHolderSyncToken(plane, &client);
237 skia::BlitRGBAToYUVA(scoped_sk_image->sk_image().get(), sk_surface_ptrs,
239 provider->GrContext()->flushAndSubmit(GrSyncCpu::kNo);
243 #if BUILDFLAG(IS_WIN)
244 // For shared memory GMBs on Windows we needed to explicitly request a copy
245 // from the shared image GPU texture to the GMB.
246 DCHECK(dst_video_frame->HasGpuMemoryBuffer());
247 DCHECK_EQ(dst_video_frame->GetGpuMemoryBuffer()->GetType(),
248 gfx::SHARED_MEMORY_BUFFER);
250 gpu::SyncToken blit_done_sync_token;
251 ri->GenUnverifiedSyncTokenCHROMIUM(blit_done_sync_token.GetData());
253 auto* sii = provider->SharedImageInterface();
254 for (size_t plane = 0; plane < dst_video_frame->NumTextures(); ++plane) {
255 const auto& mailbox = dst_video_frame->mailbox_holder(plane).mailbox;
256 sii->CopyToGpuMemoryBuffer(blit_done_sync_token, mailbox);
259 // Synchronize RasterInterface with SharedImageInterface. We want to generate
260 // the final SyncToken from the RasterInterface since callers might be using
261 // RasterInterface::Finish() to ensure synchronization in cases where
262 // SignalSyncToken can't be used (e.g. webrtc video frame adapter).
263 auto copy_to_gmb_done_sync_token = sii->GenUnverifiedSyncToken();
264 ri->WaitSyncTokenCHROMIUM(copy_to_gmb_done_sync_token.GetData());
265 #endif // BUILDFLAG(IS_WIN)
267 // Make access to the `dst_video_frame` wait on copy completion. We also
268 // update the ReleaseSyncToken here since it's used when the underlying
269 // GpuMemoryBuffer and SharedImage resources are returned to the pool.
270 gpu::SyncToken completion_sync_token;
271 ri->GenSyncTokenCHROMIUM(completion_sync_token.GetData());
272 SimpleSyncTokenClient simple_client(completion_sync_token);
273 for (size_t plane = 0; plane < dst_video_frame->NumTextures(); ++plane) {
274 dst_video_frame->UpdateMailboxHolderSyncToken(plane, &simple_client);
276 dst_video_frame->UpdateReleaseSyncToken(&simple_client);