Commit 85bf8ed5 authored by Vikas Soni's avatar Vikas Soni Committed by Commit Bot

Vulkan backed Skia representation of the SharedImageVideo.

1. Implement vulkan representation of the SharedImageVideo.

2. Skia promise image requires sampler conversion information ycbcr
in the vulkan mode. This information is needed during the promise
image creation time which happens before BeginReadAccess. This is
done by passing this info to promise image via
VideoFrame->StreamVideoDrawQuad->ResourceMetadata.

3. Add ycbcr info to the video frame. Update required mojom and struct
traits.

4. Add ycbcr info to the StreamVideoDrawQuad and update required mojom
and struct traits.

Bug: 900963
Change-Id: Ie4537604efe446492377d3615ed7c3cfde8c2130
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1582774
Commit-Queue: vikas soni <vikassoni@chromium.org>
Reviewed-by: default avatarDaniel Cheng <dcheng@chromium.org>
Reviewed-by: default avatarFrank Liberato <liberato@chromium.org>
Reviewed-by: default avatarEric Karl <ericrk@chromium.org>
Cr-Commit-Position: refs/heads/master@{#664802}
parent ea8bab4e
......@@ -250,6 +250,7 @@ viz_component("common") {
"//gpu/command_buffer/client:gles2_interface",
"//gpu/command_buffer/client:raster",
"//gpu/command_buffer/client:raster_interface",
"//gpu/ipc/common:common",
"//gpu/vulkan:buildflags",
"//mojo/public/cpp/base",
"//mojo/public/cpp/system",
......
......@@ -10,6 +10,7 @@ include_rules = [
"+third_party/skia",
"+ui/gl/dc_renderer_layer_params.h",
"+ui/latency",
"+gpu/ipc/common/vulkan_ycbcr_info.h",
]
specific_include_rules = {
......
......@@ -12,15 +12,20 @@
namespace viz {
StreamVideoDrawQuad::StreamVideoDrawQuad() = default;
StreamVideoDrawQuad::~StreamVideoDrawQuad() = default;
StreamVideoDrawQuad::StreamVideoDrawQuad(const StreamVideoDrawQuad& quad) =
default;
void StreamVideoDrawQuad::SetNew(const SharedQuadState* shared_quad_state,
const gfx::Rect& rect,
const gfx::Rect& visible_rect,
bool needs_blending,
unsigned resource_id,
gfx::Size resource_size_in_pixels,
const gfx::PointF& uv_top_left,
const gfx::PointF& uv_bottom_right) {
void StreamVideoDrawQuad::SetNew(
const SharedQuadState* shared_quad_state,
const gfx::Rect& rect,
const gfx::Rect& visible_rect,
bool needs_blending,
unsigned resource_id,
gfx::Size resource_size_in_pixels,
const gfx::PointF& uv_top_left,
const gfx::PointF& uv_bottom_right,
const base::Optional<gpu::VulkanYCbCrInfo>& ycbcr_info) {
DrawQuad::SetAll(shared_quad_state, DrawQuad::Material::kStreamVideoContent,
rect, visible_rect, needs_blending);
resources.ids[kResourceIdIndex] = resource_id;
......@@ -28,6 +33,7 @@ void StreamVideoDrawQuad::SetNew(const SharedQuadState* shared_quad_state,
resources.count = 1;
this->uv_top_left = uv_top_left;
this->uv_bottom_right = uv_bottom_right;
this->ycbcr_info = ycbcr_info;
}
void StreamVideoDrawQuad::SetAll(const SharedQuadState* shared_quad_state,
......
......@@ -11,6 +11,7 @@
#include "components/viz/common/quads/draw_quad.h"
#include "components/viz/common/viz_common_export.h"
#include "gpu/ipc/common/vulkan_ycbcr_info.h"
#include "ui/gfx/geometry/point_f.h"
namespace viz {
......@@ -20,15 +21,19 @@ class VIZ_COMMON_EXPORT StreamVideoDrawQuad : public DrawQuad {
static const size_t kResourceIdIndex = 0;
StreamVideoDrawQuad();
void SetNew(const SharedQuadState* shared_quad_state,
const gfx::Rect& rect,
const gfx::Rect& visible_rect,
bool needs_blending,
unsigned resource_id,
gfx::Size resource_size_in_pixels,
const gfx::PointF& uv_top_left,
const gfx::PointF& uv_bottom_right);
~StreamVideoDrawQuad() override;
StreamVideoDrawQuad(const StreamVideoDrawQuad& quad);
void SetNew(
const SharedQuadState* shared_quad_state,
const gfx::Rect& rect,
const gfx::Rect& visible_rect,
bool needs_blending,
unsigned resource_id,
gfx::Size resource_size_in_pixels,
const gfx::PointF& uv_top_left,
const gfx::PointF& uv_bottom_right,
const base::Optional<gpu::VulkanYCbCrInfo>& ycbcr_info = base::nullopt);
void SetAll(const SharedQuadState* shared_quad_state,
const gfx::Rect& rect,
......@@ -48,6 +53,9 @@ class VIZ_COMMON_EXPORT StreamVideoDrawQuad : public DrawQuad {
};
OverlayResources overlay_resources;
// Sampler conversion information which is used in vulkan context for android.
base::Optional<gpu::VulkanYCbCrInfo> ycbcr_info;
static const StreamVideoDrawQuad* MaterialCast(const DrawQuad*);
ResourceId resource_id() const { return resources.ids[kResourceIdIndex]; }
......
......@@ -198,6 +198,7 @@ viz_component("service") {
# Note that dependency on //gpu/ipc/client is for GpuMemoryBufferImpl. This
# dependency should not be in public_deps.
"//gpu/ipc/client",
"//gpu/ipc/common:common",
"//gpu/ipc/common:surface_handle_type",
"//gpu/ipc/service",
"//gpu/skia_bindings:skia_bindings",
......
......@@ -22,6 +22,7 @@ include_rules = [
"+ui/gl/ca_renderer_layer_params.h",
"+ui/gl/dc_renderer_layer_params.h",
"+ui/gl/trace_util.h",
"+gpu/ipc/common/vulkan_ycbcr_info.h",
]
specific_include_rules = {
......
......@@ -5,11 +5,13 @@
#ifndef COMPONENTS_VIZ_SERVICE_DISPLAY_RESOURCE_METADATA_H_
#define COMPONENTS_VIZ_SERVICE_DISPLAY_RESOURCE_METADATA_H_
#include "base/optional.h"
#include "components/viz/common/resources/resource_format.h"
#include "components/viz/common/resources/resource_id.h"
#include "components/viz/service/viz_service_export.h"
#include "gpu/command_buffer/common/mailbox_holder.h"
#include "gpu/command_buffer/common/sync_token.h"
#include "gpu/ipc/common/vulkan_ycbcr_info.h"
#include "third_party/skia/include/gpu/GrTypes.h"
#include "ui/gfx/color_space.h"
#include "ui/gfx/geometry/size.h"
......@@ -44,6 +46,10 @@ struct VIZ_SERVICE_EXPORT ResourceMetadata {
// If the SkImage should use top-left or bottom-left for (0,0) uv
GrSurfaceOrigin origin;
// Sampler conversion information which is used in vulkan context for android
// video.
base::Optional<gpu::VulkanYCbCrInfo> ycbcr_info;
};
} // namespace viz
......
......@@ -451,6 +451,11 @@ class SkiaRenderer::ScopedSkImageBuilder {
ResourceId resource_id,
SkAlphaType alpha_type = kPremul_SkAlphaType,
GrSurfaceOrigin origin = kTopLeft_GrSurfaceOrigin);
ScopedSkImageBuilder(SkiaRenderer* skia_renderer,
ResourceId resource_id,
base::Optional<gpu::VulkanYCbCrInfo> ycbcr_info,
SkAlphaType alpha_type = kPremul_SkAlphaType,
GrSurfaceOrigin origin = kTopLeft_GrSurfaceOrigin);
~ScopedSkImageBuilder() = default;
const SkImage* sk_image() const { return sk_image_; }
......@@ -466,6 +471,18 @@ SkiaRenderer::ScopedSkImageBuilder::ScopedSkImageBuilder(
SkiaRenderer* skia_renderer,
ResourceId resource_id,
SkAlphaType alpha_type,
GrSurfaceOrigin origin)
: SkiaRenderer::ScopedSkImageBuilder(skia_renderer,
resource_id,
base::nullopt,
alpha_type,
origin) {}
SkiaRenderer::ScopedSkImageBuilder::ScopedSkImageBuilder(
SkiaRenderer* skia_renderer,
ResourceId resource_id,
base::Optional<gpu::VulkanYCbCrInfo> ycbcr_info,
SkAlphaType alpha_type,
GrSurfaceOrigin origin) {
if (!resource_id)
return;
......@@ -486,6 +503,7 @@ SkiaRenderer::ScopedSkImageBuilder::ScopedSkImageBuilder(
skia_renderer->lock_set_for_external_use_->LockResource(resource_id);
metadata.alpha_type = alpha_type;
metadata.origin = origin;
metadata.ycbcr_info = ycbcr_info;
image = skia_renderer->skia_output_surface_->MakePromiseSkImage(metadata);
LOG_IF(ERROR, !image) << "Failed to create the promise sk image.";
}
......@@ -1267,7 +1285,8 @@ void SkiaRenderer::DrawSolidColorQuad(const SolidColorDrawQuad* quad,
void SkiaRenderer::DrawStreamVideoQuad(const StreamVideoDrawQuad* quad,
DrawQuadParams* params) {
DCHECK(!MustFlushBatchedQuads(quad, *params));
ScopedSkImageBuilder builder(this, quad->resource_id(),
ScopedSkImageBuilder builder(this, quad->resource_id(), quad->ycbcr_info,
kUnpremul_SkAlphaType);
const SkImage* image = builder.sk_image();
if (!image)
......
......@@ -224,8 +224,10 @@ sk_sp<SkImage> SkiaOutputSurfaceImpl::MakePromiseSkImage(
image_context = std::make_unique<ImageContext>(metadata);
SkColorType color_type = ResourceFormatToClosestSkColorType(
true /* gpu_compositing */, metadata.resource_format);
GrBackendFormat backend_format = GetGrBackendFormatForTexture(
metadata.resource_format, metadata.mailbox_holder.texture_target);
metadata.resource_format, metadata.mailbox_holder.texture_target,
metadata.ycbcr_info);
image_context->image = recorder_->makePromiseTexture(
backend_format, metadata.size.width(), metadata.size.height(),
GrMipMapped::kNo, metadata.origin, color_type, metadata.alpha_type,
......@@ -594,8 +596,10 @@ void SkiaOutputSurfaceImpl::ScheduleGpuTask(
GrBackendFormat SkiaOutputSurfaceImpl::GetGrBackendFormatForTexture(
ResourceFormat resource_format,
uint32_t gl_texture_target) {
uint32_t gl_texture_target,
base::Optional<gpu::VulkanYCbCrInfo> ycbcr_info) {
if (!is_using_vulkan_) {
DCHECK(!ycbcr_info);
// Convert internal format from GLES2 to platform GL.
const auto* version_info = impl_on_gpu_->gl_version_info();
unsigned int texture_storage_format = TextureStorageFormat(resource_format);
......@@ -607,7 +611,20 @@ GrBackendFormat SkiaOutputSurfaceImpl::GetGrBackendFormatForTexture(
gl_texture_target);
} else {
#if BUILDFLAG(ENABLE_VULKAN)
return GrBackendFormat::MakeVk(ToVkFormat(resource_format));
if (!ycbcr_info)
return GrBackendFormat::MakeVk(ToVkFormat(resource_format));
GrVkYcbcrConversionInfo fYcbcrConversionInfo(
static_cast<VkSamplerYcbcrModelConversion>(
ycbcr_info->suggested_ycbcr_model),
static_cast<VkSamplerYcbcrRange>(ycbcr_info->suggested_ycbcr_range),
static_cast<VkChromaLocation>(ycbcr_info->suggested_xchroma_offset),
static_cast<VkChromaLocation>(ycbcr_info->suggested_ychroma_offset),
VK_FILTER_LINEAR, // VkFilter
0, // VkBool32 forceExplicitReconstruction,
ycbcr_info->external_format,
static_cast<VkFormatFeatureFlags>(ycbcr_info->format_features));
return GrBackendFormat::MakeVk(fYcbcrConversionInfo);
#else
NOTREACHED();
return GrBackendFormat();
......
......@@ -16,6 +16,7 @@
#include "components/viz/service/viz_service_export.h"
#include "gpu/command_buffer/common/sync_token.h"
#include "gpu/ipc/common/surface_handle.h"
#include "gpu/ipc/common/vulkan_ycbcr_info.h"
#include "gpu/ipc/in_process_command_buffer.h"
#include "third_party/skia/include/core/SkDeferredDisplayListRecorder.h"
#include "third_party/skia/include/core/SkOverdrawCanvas.h"
......@@ -107,8 +108,10 @@ class VIZ_SERVICE_EXPORT SkiaOutputSurfaceImpl : public SkiaOutputSurfaceBase {
void BufferPresented(const gfx::PresentationFeedback& feedback);
void ScheduleGpuTask(base::OnceClosure callback,
std::vector<gpu::SyncToken> sync_tokens);
GrBackendFormat GetGrBackendFormatForTexture(ResourceFormat resource_format,
uint32_t gl_texture_target);
GrBackendFormat GetGrBackendFormatForTexture(
ResourceFormat resource_format,
uint32_t gl_texture_target,
base::Optional<gpu::VulkanYCbCrInfo> ycbcr_info = base::nullopt);
uint64_t sync_fence_release_ = 0;
GpuServiceImpl* const gpu_service_;
......
......@@ -311,6 +311,7 @@ jumbo_source_set("base") {
deps = [
"//base/allocator:buildflags",
"//gpu/command_buffer/common",
"//gpu/ipc/common:common",
"//skia",
"//third_party/libyuv",
"//third_party/widevine/cdm:headers",
......@@ -319,6 +320,7 @@ jumbo_source_set("base") {
"//ui/events:events_base",
"//url:url",
]
libs = []
configs += [
"//build/config:precompiled_headers",
......
......@@ -22,11 +22,13 @@
#include "base/memory/shared_memory.h"
#include "base/memory/shared_memory_handle.h"
#include "base/memory/unsafe_shared_memory_region.h"
#include "base/optional.h"
#include "base/synchronization/lock.h"
#include "base/thread_annotations.h"
#include "base/unguessable_token.h"
#include "build/build_config.h"
#include "gpu/command_buffer/common/mailbox_holder.h"
#include "gpu/ipc/common/vulkan_ycbcr_info.h"
#include "media/base/video_frame_layout.h"
#include "media/base/video_frame_metadata.h"
#include "media/base/video_types.h"
......@@ -434,6 +436,10 @@ class MEDIA_EXPORT VideoFrame : public base::RefCountedThreadSafe<VideoFrame> {
return data_[plane];
}
const base::Optional<gpu::VulkanYCbCrInfo>& ycbcr_info() const {
return ycbcr_info_;
}
// Returns pointer to the data in the visible region of the frame, for
// IsMappable() storage types. The returned pointer is offsetted into the
// plane buffer specified by visible_rect().origin(). Memory is owned by
......@@ -537,6 +543,11 @@ class MEDIA_EXPORT VideoFrame : public base::RefCountedThreadSafe<VideoFrame> {
// Returns the number of bits per channel.
size_t BitDepth() const;
// Provide the sampler conversion information for the frame.
void set_ycbcr_info(const base::Optional<gpu::VulkanYCbCrInfo>& ycbcr_info) {
ycbcr_info_ = ycbcr_info;
}
protected:
friend class base::RefCountedThreadSafe<VideoFrame>;
......@@ -678,6 +689,9 @@ class MEDIA_EXPORT VideoFrame : public base::RefCountedThreadSafe<VideoFrame> {
gfx::ColorSpace color_space_;
// Sampler conversion information which is used in vulkan context for android.
base::Optional<gpu::VulkanYCbCrInfo> ycbcr_info_;
DISALLOW_IMPLICIT_CONSTRUCTORS(VideoFrame);
};
......
......@@ -5,6 +5,7 @@
import("//build/buildflag_header.gni")
import("//build/config/features.gni")
import("//build/config/ui.gni")
import("//gpu/vulkan/features.gni")
import("//media/gpu/args.gni")
import("//media/media_options.gni")
import("//testing/test.gni")
......@@ -148,6 +149,9 @@ component("gpu") {
"//services/service_manager/public/cpp:cpp",
"//ui/gl:gl_jni_headers",
]
if (enable_vulkan) {
deps += [ "//gpu/vulkan:vulkan" ]
}
# TODO(crbug.com/789435): This is needed for AVDA to access the CDM
# directly. Remove this dependency after VDAs are also running as part of
......
......@@ -13,6 +13,7 @@ include_rules = [
"+ui/display/manager",
"+ui/display/types",
"+ui/platform_window",
"+components/viz/common/gpu/vulkan_context_provider.h",
# media/gpu is not part of "media" target and should not use MEDIA_EXPORT.
"-media/base/media_export.h",
......
......@@ -69,8 +69,10 @@ class ImageReaderGLOwner::ScopedHardwareBufferImpl
}
void SetReadFence(base::ScopedFD fence_fd, bool has_context) final {
DCHECK(!read_fence_.is_valid());
read_fence_ = std::move(fence_fd);
// Client can call this method multiple times for a hardware buffer. Hence
// all the client provided sync_fd should be merged. Eg: BeginReadAccess()
// can be called multiple times for a SharedImageVideo representation.
read_fence_ = gl::MergeFDs(std::move(read_fence_), std::move(fence_fd));
}
private:
......
......@@ -6,6 +6,9 @@
#include <utility>
#include "base/android/scoped_hardware_buffer_fence_sync.h"
#include "base/android/scoped_hardware_buffer_handle.h"
#include "components/viz/common/gpu/vulkan_context_provider.h"
#include "components/viz/common/resources/resource_format_utils.h"
#include "components/viz/common/resources/resource_sizes.h"
#include "gpu/command_buffer/common/shared_image_usage.h"
......@@ -16,6 +19,11 @@
#include "gpu/command_buffer/service/shared_image_representation.h"
#include "gpu/command_buffer/service/skia_utils.h"
#include "gpu/command_buffer/service/texture_manager.h"
#include "gpu/vulkan/vulkan_device_queue.h"
#include "gpu/vulkan/vulkan_fence_helper.h"
#include "gpu/vulkan/vulkan_function_pointers.h"
#include "gpu/vulkan/vulkan_implementation.h"
#include "gpu/vulkan/vulkan_util.h"
#include "media/gpu/android/codec_image.h"
#include "third_party/skia/include/core/SkPromiseImageTexture.h"
#include "third_party/skia/include/gpu/GrBackendSemaphore.h"
......@@ -23,12 +31,90 @@
namespace media {
namespace {
sk_sp<SkPromiseImageTexture> CreatePromiseTexture(
viz::VulkanContextProvider* context_provider,
base::android::ScopedHardwareBufferHandle ahb_handle,
gfx::Size size,
viz::ResourceFormat format) {
gpu::VulkanImplementation* vk_implementation =
context_provider->GetVulkanImplementation();
VkDevice vk_device = context_provider->GetDeviceQueue()->GetVulkanDevice();
VkPhysicalDevice vk_physical_device =
context_provider->GetDeviceQueue()->GetVulkanPhysicalDevice();
// Create a VkImage and import AHB.
VkImage vk_image;
VkImageCreateInfo vk_image_info;
VkDeviceMemory vk_device_memory;
VkDeviceSize mem_allocation_size;
gpu::VulkanYCbCrInfo ycbcr_info;
if (!vk_implementation->CreateVkImageAndImportAHB(
vk_device, vk_physical_device, size, std::move(ahb_handle), &vk_image,
&vk_image_info, &vk_device_memory, &mem_allocation_size,
&ycbcr_info)) {
return nullptr;
}
GrVkYcbcrConversionInfo fYcbcrConversionInfo(
static_cast<VkSamplerYcbcrModelConversion>(
ycbcr_info.suggested_ycbcr_model),
static_cast<VkSamplerYcbcrRange>(ycbcr_info.suggested_ycbcr_range),
static_cast<VkChromaLocation>(ycbcr_info.suggested_xchroma_offset),
static_cast<VkChromaLocation>(ycbcr_info.suggested_ychroma_offset),
VK_FILTER_LINEAR, // VkFilter
0, // VkBool32 forceExplicitReconstruction
ycbcr_info.external_format,
static_cast<VkFormatFeatureFlags>(ycbcr_info.format_features));
// Create backend texture from the VkImage.
GrVkAlloc alloc = {vk_device_memory, 0, mem_allocation_size, 0};
GrVkImageInfo vk_info = {vk_image,
alloc,
vk_image_info.tiling,
vk_image_info.initialLayout,
vk_image_info.format,
vk_image_info.mipLevels,
VK_QUEUE_FAMILY_EXTERNAL,
fYcbcrConversionInfo};
// TODO(bsalomon): Determine whether it makes sense to attempt to reuse this
// if the vk_info stays the same on subsequent calls.
auto promise_texture = SkPromiseImageTexture::Make(
GrBackendTexture(size.width(), size.height(), vk_info));
if (!promise_texture) {
vkDestroyImage(vk_device, vk_image, nullptr);
vkFreeMemory(vk_device, vk_device_memory, nullptr);
return nullptr;
}
return promise_texture;
}
void DestroyVkPromiseTexture(viz::VulkanContextProvider* context_provider,
sk_sp<SkPromiseImageTexture> promise_texture) {
DCHECK(promise_texture);
DCHECK(promise_texture->unique());
GrVkImageInfo vk_image_info;
bool result =
promise_texture->backendTexture().getVkImageInfo(&vk_image_info);
DCHECK(result);
gpu::VulkanFenceHelper* fence_helper =
context_provider->GetDeviceQueue()->GetFenceHelper();
fence_helper->EnqueueImageCleanupForSubmittedWork(
vk_image_info.fImage, vk_image_info.fAlloc.fMemory);
}
} // namespace
SharedImageVideo::SharedImageVideo(
const gpu::Mailbox& mailbox,
const gfx::ColorSpace color_space,
scoped_refptr<CodecImage> codec_image,
std::unique_ptr<gpu::gles2::AbstractTexture> abstract_texture,
scoped_refptr<gpu::SharedContextState> shared_context_state,
scoped_refptr<gpu::SharedContextState> context_state,
bool is_thread_safe)
: SharedImageBacking(
mailbox,
......@@ -42,19 +128,19 @@ SharedImageVideo::SharedImageVideo(
is_thread_safe),
codec_image_(std::move(codec_image)),
abstract_texture_(std::move(abstract_texture)),
shared_context_state_(std::move(shared_context_state)) {
context_state_(std::move(context_state)) {
DCHECK(codec_image_);
DCHECK(shared_context_state_);
DCHECK(context_state_);
// Currently this backing is not thread safe.
DCHECK(!is_thread_safe);
shared_context_state_->AddContextLostObserver(this);
context_state_->AddContextLostObserver(this);
}
SharedImageVideo::~SharedImageVideo() {
codec_image_->ReleaseCodecBuffer();
if (shared_context_state_)
shared_context_state_->RemoveContextLostObserver(this);
if (context_state_)
context_state_->RemoveContextLostObserver(this);
}
bool SharedImageVideo::IsCleared() const {
......@@ -86,11 +172,41 @@ void SharedImageVideo::OnContextLost() {
// texture owner's texture was created on shared context. Once shared context
// is lost, no one should try to use that texture.
codec_image_->ReleaseCodecBuffer();
shared_context_state_->RemoveContextLostObserver(this);
shared_context_state_ = nullptr;
context_state_->RemoveContextLostObserver(this);
context_state_ = nullptr;
}
base::Optional<gpu::VulkanYCbCrInfo> SharedImageVideo::GetYcbcrInfo() {
// For non-vulkan context, return null.
if (!context_state_->GrContextIsVulkan())
return base::nullopt;
// Render the codec image.
codec_image_->RenderToFrontBuffer();
// Get the AHB from the latest image.
auto scoped_hardware_buffer =
codec_image_->texture_owner()->GetAHardwareBuffer();
if (!scoped_hardware_buffer) {
return base::nullopt;
}
DCHECK(scoped_hardware_buffer->buffer());
auto* context_provider = context_state_->vk_context_provider();
gpu::VulkanImplementation* vk_implementation =
context_provider->GetVulkanImplementation();
VkDevice vk_device = context_provider->GetDeviceQueue()->GetVulkanDevice();
gpu::VulkanYCbCrInfo ycbcr_info;
if (!vk_implementation->GetSamplerYcbcrConversionInfo(
vk_device, scoped_hardware_buffer->TakeBuffer(), &ycbcr_info)) {
LOG(ERROR) << "Failed to get the ycbcr info.";
return base::nullopt;
}
return base::Optional<gpu::VulkanYCbCrInfo>(ycbcr_info);
}
// Representation of a SharedImageCodecImage as a GL Texture.
// Representation of SharedImageVideo as a GL Texture.
class SharedImageRepresentationGLTextureVideo
: public gpu::SharedImageRepresentationGLTexture {
public:
......@@ -152,34 +268,190 @@ class SharedImageRepresentationVideoSkiaGL
sk_sp<SkPromiseImageTexture> BeginReadAccess(
std::vector<GrBackendSemaphore>* begin_semaphores,
std::vector<GrBackendSemaphore>* end_semaphores) override {
if (!promise_texture_) {
if (promise_texture_)
return promise_texture_;
auto* video_backing = static_cast<SharedImageVideo*>(backing());
DCHECK(video_backing);
auto* codec_image = video_backing->codec_image_.get();
auto* texture_owner = codec_image->texture_owner().get();
// Render the codec image.
codec_image->RenderToFrontBuffer();
// Bind the tex image if it's not already bound.
if (!texture_owner->binds_texture_on_update())
texture_owner->EnsureTexImageBound();
GrBackendTexture backend_texture;
if (!gpu::GetGrBackendTexture(gl::GLContext::GetCurrent()->GetVersionInfo(),
GL_TEXTURE_EXTERNAL_OES, size(),
texture_owner->GetTextureId(), format(),
&backend_texture)) {
return nullptr;
}
promise_texture_ = SkPromiseImageTexture::Make(backend_texture);
return promise_texture_;
}
void EndReadAccess() override {}
private:
sk_sp<SkPromiseImageTexture> promise_texture_;
};
// Vulkan backed Skia representation of SharedImageVideo.
class SharedImageRepresentationVideoSkiaVk
: public gpu::SharedImageRepresentationSkia {
public:
SharedImageRepresentationVideoSkiaVk(
gpu::SharedImageManager* manager,
gpu::SharedImageBacking* backing,
scoped_refptr<gpu::SharedContextState> context_state,
gpu::MemoryTypeTracker* tracker)
: gpu::SharedImageRepresentationSkia(manager, backing, tracker),
context_state_(std::move(context_state)) {
DCHECK(context_state_);
DCHECK(context_state_->vk_context_provider());
}
~SharedImageRepresentationVideoSkiaVk() override {
DCHECK(end_access_semaphore_ == VK_NULL_HANDLE);
// |promise_texture_| could be null if we never being read.
if (!promise_texture_)
return;
DestroyVkPromiseTexture(context_state_->vk_context_provider(),
std::move(promise_texture_));
}
sk_sp<SkSurface> BeginWriteAccess(
int final_msaa_count,
const SkSurfaceProps& surface_props,
std::vector<GrBackendSemaphore>* begin_semaphores,
std::vector<GrBackendSemaphore>* end_semaphores) override {
// Writes are not intended to used for video backed representations.
NOTIMPLEMENTED();
return nullptr;
}
void EndWriteAccess(sk_sp<SkSurface> surface) override { NOTIMPLEMENTED(); }
sk_sp<SkPromiseImageTexture> BeginReadAccess(
std::vector<GrBackendSemaphore>* begin_semaphores,
std::vector<GrBackendSemaphore>* end_semaphores) override {
if (!scoped_hardware_buffer_) {
auto* video_backing = static_cast<SharedImageVideo*>(backing());
DCHECK(video_backing);
auto* codec_image = video_backing->codec_image_.get();
auto* texture_owner = codec_image->texture_owner().get();
// Render the codec image.
// Render the codec image and get AHB from latest image.
codec_image->RenderToFrontBuffer();
// Bind the tex image if it's not already bound.
if (!texture_owner->binds_texture_on_update())
texture_owner->EnsureTexImageBound();
GrBackendTexture backend_texture;
if (!gpu::GetGrBackendTexture(
gl::GLContext::GetCurrent()->GetVersionInfo(),
GL_TEXTURE_EXTERNAL_OES, size(), texture_owner->GetTextureId(),
format(), &backend_texture)) {
scoped_hardware_buffer_ = texture_owner->GetAHardwareBuffer();
if (!scoped_hardware_buffer_) {
LOG(ERROR) << "Failed to get the hardware buffer.";
return nullptr;
}
promise_texture_ = SkPromiseImageTexture::Make(backend_texture);
}
DCHECK(scoped_hardware_buffer_->buffer());
// Wait on the sync fd attached to the buffer to make sure buffer is
// ready before the read. This is done by inserting the sync fd semaphore
// into begin_semaphore vector which client will wait on.
base::ScopedFD sync_fd = scoped_hardware_buffer_->TakeFence();
if (!BeginRead(begin_semaphores, end_semaphores, std::move(sync_fd))) {
return nullptr;
}
if (!promise_texture_) {
// Create the promise texture.
promise_texture_ = CreatePromiseTexture(
context_state_->vk_context_provider(),
scoped_hardware_buffer_->TakeBuffer(), size(), format());
}
return promise_texture_;
}
void EndReadAccess() override {}
void EndReadAccess() override {
DCHECK(end_access_semaphore_ != VK_NULL_HANDLE);
gpu::SemaphoreHandle semaphore_handle =
vk_implementation()->GetSemaphoreHandle(vk_device(),
end_access_semaphore_);
auto sync_fd = semaphore_handle.TakeHandle();
DCHECK(sync_fd.is_valid());
// Pass the end access sync fd to the scoped hardware buffer. This will make
// sure that the AImage associated with the hardware buffer will be deleted
// only when the read access is ending.
scoped_hardware_buffer_->SetReadFence(std::move(sync_fd), true);
fence_helper()->EnqueueSemaphoreCleanupForSubmittedWork(
end_access_semaphore_);
end_access_semaphore_ = VK_NULL_HANDLE;
}
private:
bool BeginRead(std::vector<GrBackendSemaphore>* begin_semaphores,
std::vector<GrBackendSemaphore>* end_semaphores,
base::ScopedFD sync_fd) {
DCHECK(begin_semaphores);
DCHECK(end_semaphores);
DCHECK(end_access_semaphore_ == VK_NULL_HANDLE);
VkSemaphore begin_access_semaphore = VK_NULL_HANDLE;
if (sync_fd.is_valid()) {
begin_access_semaphore = vk_implementation()->ImportSemaphoreHandle(
vk_device(),
gpu::SemaphoreHandle(VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT,
std::move(sync_fd)));
if (begin_access_semaphore == VK_NULL_HANDLE) {
DLOG(ERROR) << "Failed to import semaphore from sync_fd.";
return false;
}
}
end_access_semaphore_ =
vk_implementation()->CreateExternalSemaphore(vk_device());
if (end_access_semaphore_ == VK_NULL_HANDLE) {
DLOG(ERROR) << "Failed to create the external semaphore.";
if (begin_access_semaphore != VK_NULL_HANDLE) {
vkDestroySemaphore(vk_device(), begin_access_semaphore,
nullptr /* pAllocator */);
}
return false;
}
end_semaphores->emplace_back();
end_semaphores->back().initVulkan(end_access_semaphore_);
if (begin_access_semaphore != VK_NULL_HANDLE) {
begin_semaphores->emplace_back();
begin_semaphores->back().initVulkan(begin_access_semaphore);
}
return true;
}
VkDevice vk_device() {
return context_state_->vk_context_provider()
->GetDeviceQueue()
->GetVulkanDevice();
}
gpu::VulkanImplementation* vk_implementation() {
return context_state_->vk_context_provider()->GetVulkanImplementation();
}
gpu::VulkanFenceHelper* fence_helper() {
return context_state_->vk_context_provider()
->GetDeviceQueue()
->GetFenceHelper();
}
sk_sp<SkPromiseImageTexture> promise_texture_;
scoped_refptr<gpu::SharedContextState> context_state_;
std::unique_ptr<base::android::ScopedHardwareBufferFenceSync>
scoped_hardware_buffer_;
VkSemaphore end_access_semaphore_ = VK_NULL_HANDLE;
};
// TODO(vikassoni): Currently GLRenderer doesn't support overlays with shared
......@@ -209,19 +481,18 @@ SharedImageVideo::ProduceSkia(
scoped_refptr<gpu::SharedContextState> context_state) {
DCHECK(context_state);
// Right now we only support GL mode.
// TODO(vikassoni): Land follow up patches to enable vulkan mode.
if (!context_state->GrContextIsGL()) {
DLOG(ERROR) << "SharedImage video path not yet supported in Vulkan.";
return nullptr;
}
// For (old) overlays, we don't have a texture owner, but overlay promotion
// might not happen for some reasons. In that case, it will try to draw
// which should result in no image.
if (!codec_image_->texture_owner())
return nullptr;
if (context_state->GrContextIsVulkan()) {
return std::make_unique<SharedImageRepresentationVideoSkiaVk>(
manager, this, std::move(context_state), tracker);
}
DCHECK(context_state->GrContextIsGL());
// In GL mode, use the texture id of the TextureOwner.
return std::make_unique<SharedImageRepresentationVideoSkiaGL>(manager, this,
tracker);
......
......@@ -8,8 +8,10 @@
#include <memory>
#include "base/memory/scoped_refptr.h"
#include "base/optional.h"
#include "gpu/command_buffer/service/shared_context_state.h"
#include "gpu/command_buffer/service/shared_image_backing.h"
#include "gpu/ipc/common/vulkan_ycbcr_info.h"
#include "media/gpu/media_gpu_export.h"
namespace gpu {
......@@ -53,6 +55,10 @@ class MEDIA_GPU_EXPORT SharedImageVideo
// SharedContextState::ContextLostObserver implementation.
void OnContextLost() override;
// Returns ycbcr information. This is only valid in vulkan context and
// nullopt for other context.
base::Optional<gpu::VulkanYCbCrInfo> GetYcbcrInfo();
protected:
std::unique_ptr<gpu::SharedImageRepresentationGLTexture> ProduceGLTexture(
gpu::SharedImageManager* manager,
......@@ -69,12 +75,13 @@ class MEDIA_GPU_EXPORT SharedImageVideo
private:
friend class SharedImageRepresentationGLTextureVideo;
friend class SharedImageRepresentationVideoSkiaGL;
friend class SharedImageRepresentationVideoSkiaVk;
scoped_refptr<CodecImage> codec_image_;
// |abstract_texture_| is only used for legacy mailbox.
std::unique_ptr<gpu::gles2::AbstractTexture> abstract_texture_;
scoped_refptr<gpu::SharedContextState> shared_context_state_;
scoped_refptr<gpu::SharedContextState> context_state_;
DISALLOW_COPY_AND_ASSIGN(SharedImageVideo);
};
......
......@@ -7,6 +7,7 @@
#include "base/callback.h"
#include "gpu/command_buffer/service/mailbox_manager.h"
#include "gpu/ipc/common/vulkan_ycbcr_info.h"
#include "media/gpu/android/codec_image_group.h"
#include "media/gpu/android/promotion_hint_aggregator.h"
#include "media/gpu/media_gpu_export.h"
......@@ -50,6 +51,9 @@ class MEDIA_GPU_EXPORT SharedImageVideoProvider {
// Mailbox to which this shared image is bound.
gpu::Mailbox mailbox;
// Sampler conversion information which is used in vulkan context.
base::Optional<gpu::VulkanYCbCrInfo> ycbcr_info;
// Release callback. When this is called (or dropped), the image will be
// considered unused.
ReleaseCB release_cb;
......
......@@ -284,6 +284,7 @@ void VideoFrameFactoryImpl::OnImageReady(
pixel_format, mailbox_holders, VideoFrame::ReleaseMailboxCB(), coded_size,
visible_rect, natural_size, timestamp);
frame->set_ycbcr_info(record.ycbcr_info);
// If, for some reason, we failed to create a frame, then fail. Note that we
// don't need to call |release_cb|; dropping it is okay since the api says so.
if (!frame) {
......@@ -432,6 +433,7 @@ void GpuSharedImageVideoFactory::CreateImage(
SharedImageVideoProvider::ImageRecord record;
record.mailbox = mailbox;
record.release_cb = std::move(release_cb);
record.ycbcr_info = ycbcr_info_;
std::move(image_ready_cb).Run(std::move(record), std::move(codec_image));
}
......@@ -499,7 +501,10 @@ bool GpuSharedImageVideoFactory::CreateImageInternal(
std::move(texture), std::move(shared_context),
false /* is_thread_safe */);
// Register it with shared image mailbox as well as legacy mailbox. This
if (!ycbcr_info_)
ycbcr_info_ = shared_image->GetYcbcrInfo();
// Register it with shared image mailbox as well as legacy mailbox. This
// keeps |shared_image| around until its destruction cb is called.
// NOTE: Currently none of the video mailbox consumer uses shared image
// mailbox.
......
......@@ -13,6 +13,7 @@
#include "gpu/command_buffer/service/gles2_cmd_decoder.h"
#include "gpu/command_buffer/service/shared_image_representation.h"
#include "gpu/command_buffer/service/texture_manager.h"
#include "gpu/ipc/common/vulkan_ycbcr_info.h"
#include "gpu/ipc/service/command_buffer_stub.h"
#include "media/base/video_frame.h"
#include "media/gpu/android/codec_image.h"
......@@ -36,7 +37,7 @@ class MEDIA_GPU_EXPORT VideoFrameFactoryImpl : public VideoFrameFactory {
public:
// Callback used to return a mailbox and release callback for an image. The
// release callback may be dropped without being run, and the image will be
// cleaned up properly. The releae callback may be called from any thread.
// cleaned up properly. The release callback may be called from any thread.
using ImageReadyCB =
base::OnceCallback<void(gpu::Mailbox mailbox,
VideoFrame::ReleaseMailboxCB release_cb)>;
......@@ -164,6 +165,10 @@ class GpuSharedImageVideoFactory
// replace this when SetImageGroup() is called.
scoped_refptr<CodecImageGroup> image_group_;
// Sampler conversion information which is used in vulkan context. This is
// constant for all the frames in a video and hence we cache it.
base::Optional<gpu::VulkanYCbCrInfo> ycbcr_info_;
THREAD_CHECKER(thread_checker_);
base::WeakPtrFactory<GpuSharedImageVideoFactory> weak_factory_;
DISALLOW_COPY_AND_ASSIGN(GpuSharedImageVideoFactory);
......
......@@ -5,6 +5,7 @@
module media.mojom;
import "gpu/ipc/common/mailbox_holder.mojom";
import "gpu/ipc/common/vulkan_ycbcr_info.mojom";
import "mojo/public/mojom/base/time.mojom";
import "mojo/public/mojom/base/values.mojom";
import "ui/gfx/geometry/mojo/geometry.mojom";
......@@ -298,6 +299,7 @@ struct SharedBufferVideoFrameData {
struct MailboxVideoFrameData {
// Size must be kept in sync with media::VideoFrame::kMaxPlanes.
array<gpu.mojom.MailboxHolder, 4> mailbox_holder;
gpu.mojom.VulkanYCbCrInfo? ycbcr_data;
};
struct PipelineStatistics {
......
......@@ -53,7 +53,8 @@ media::mojom::VideoFrameDataPtr MakeVideoFrameData(
for (size_t i = 0; i < num_planes; i++)
mailbox_holder[i] = input->mailbox_holder(i);
return media::mojom::VideoFrameData::NewMailboxData(
media::mojom::MailboxVideoFrameData::New(std::move(mailbox_holder)));
media::mojom::MailboxVideoFrameData::New(
std::move(mailbox_holder), std::move(input->ycbcr_info())));
}
NOTREACHED() << "Unsupported VideoFrame conversion";
......@@ -133,9 +134,14 @@ bool StructTraits<media::mojom::VideoFrameDataView,
for (size_t i = 0; i < media::VideoFrame::kMaxPlanes; i++)
mailbox_holder_array[i] = mailbox_holder[i];
base::Optional<gpu::VulkanYCbCrInfo> ycbcr_info;
if (!mailbox_data.ReadYcbcrData(&ycbcr_info))
return false;
frame = media::VideoFrame::WrapNativeTextures(
format, mailbox_holder_array, media::VideoFrame::ReleaseMailboxCB(),
coded_size, visible_rect, natural_size, timestamp);
frame->set_ycbcr_info(ycbcr_info);
} else {
// TODO(sandersd): Switch on the union tag to avoid this ugliness?
NOTREACHED();
......
......@@ -6,8 +6,10 @@
#define MEDIA_MOJO_INTERFACES_VIDEO_FRAME_STRUCT_TRAITS_H_
#include "base/memory/ref_counted.h"
#include "base/optional.h"
#include "base/values.h"
#include "gpu/ipc/common/mailbox_holder_struct_traits.h"
#include "gpu/ipc/common/vulkan_ycbcr_info_mojom_traits.h"
#include "media/base/ipc/media_param_traits_macros.h"
#include "media/base/video_frame.h"
#include "media/mojo/interfaces/media_types.mojom.h"
......@@ -60,6 +62,11 @@ struct StructTraits<media::mojom::VideoFrameDataView,
return input->ColorSpace();
}
static const base::Optional<gpu::VulkanYCbCrInfo>& ycbcr_info(
const scoped_refptr<media::VideoFrame>& input) {
return input->ycbcr_info();
}
static media::mojom::VideoFrameDataPtr data(
const scoped_refptr<media::VideoFrame>& input);
......
......@@ -639,7 +639,7 @@ void VideoResourceUpdater::AppendQuads(viz::RenderPass* render_pass,
stream_video_quad->SetNew(shared_quad_state, quad_rect, visible_quad_rect,
needs_blending, frame_resources_[0].id,
frame_resources_[0].size_in_pixels, uv_top_left,
uv_bottom_right);
uv_bottom_right, frame->ycbcr_info());
for (viz::ResourceId resource_id : stream_video_quad->resources) {
resource_provider_->ValidateResource(resource_id);
}
......
......@@ -6,4 +6,5 @@ include_rules = [
"+third_party/skia/include",
"+ui/gfx",
"+ui/latency/mojo",
"+gpu/ipc/common/vulkan_ycbcr_info.h"
]
......@@ -109,7 +109,8 @@ bool StructTraits<viz::mojom::StreamVideoQuadStateDataView, viz::DrawQuad>::
&quad->overlay_resources.size_in_pixels
[viz::StreamVideoDrawQuad::kResourceIdIndex]) &&
data.ReadUvTopLeft(&quad->uv_top_left) &&
data.ReadUvBottomRight(&quad->uv_bottom_right);
data.ReadUvBottomRight(&quad->uv_bottom_right) &&
data.ReadYcbcrInfo(&quad->ycbcr_info);
}
// static
......
......@@ -18,6 +18,8 @@
#include "components/viz/common/quads/tile_draw_quad.h"
#include "components/viz/common/quads/video_hole_draw_quad.h"
#include "components/viz/common/quads/yuv_video_draw_quad.h"
#include "gpu/ipc/common/vulkan_ycbcr_info.h"
#include "gpu/ipc/common/vulkan_ycbcr_info_mojom_traits.h"
#include "services/viz/public/cpp/compositing/filter_operation_struct_traits.h"
#include "services/viz/public/cpp/compositing/filter_operations_struct_traits.h"
#include "services/viz/public/cpp/compositing/shared_quad_state_struct_traits.h"
......@@ -300,6 +302,13 @@ struct StructTraits<viz::mojom::StreamVideoQuadStateDataView, viz::DrawQuad> {
return quad->uv_bottom_right;
}
static const base::Optional<gpu::VulkanYCbCrInfo>& ycbcr_info(
const viz::DrawQuad& input) {
const viz::StreamVideoDrawQuad* quad =
viz::StreamVideoDrawQuad::MaterialCast(&input);
return quad->ycbcr_info;
}
static bool Read(viz::mojom::StreamVideoQuadStateDataView data,
viz::DrawQuad* out);
};
......
......@@ -4,6 +4,7 @@
module viz.mojom;
import "gpu/ipc/common/vulkan_ycbcr_info.mojom";
import "mojo/public/mojom/base/unguessable_token.mojom";
import "services/viz/public/interfaces/compositing/surface_range.mojom";
import "services/viz/public/interfaces/compositing/shared_quad_state.mojom";
......@@ -61,6 +62,7 @@ struct StreamVideoQuadState {
gfx.mojom.Size resource_size_in_pixels;
gfx.mojom.PointF uv_top_left;
gfx.mojom.PointF uv_bottom_right;
gpu.mojom.VulkanYCbCrInfo? ycbcr_info;
};
struct SurfaceQuadState {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment