blob: da8ba724596d658ed49fa4ffbac0e17e0728dcdd [file] [log] [blame]
// Copyright 2019 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 "gpu/command_buffer/service/shared_image_video.h"
#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"
#include "gpu/command_buffer/service/abstract_texture.h"
#include "gpu/command_buffer/service/ahardwarebuffer_utils.h"
#include "gpu/command_buffer/service/mailbox_manager.h"
#include "gpu/command_buffer/service/memory_tracking.h"
#include "gpu/command_buffer/service/shared_context_state.h"
#include "gpu/command_buffer/service/shared_image_representation.h"
#include "gpu/command_buffer/service/shared_image_representation_skia_gl.h"
#include "gpu/command_buffer/service/shared_image_representation_skia_vk_android.h"
#include "gpu/command_buffer/service/skia_utils.h"
#include "gpu/command_buffer/service/texture_manager.h"
#include "gpu/command_buffer/service/texture_owner.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_image.h"
#include "gpu/vulkan/vulkan_implementation.h"
#include "gpu/vulkan/vulkan_util.h"
#include "third_party/skia/include/core/SkPromiseImageTexture.h"
#include "third_party/skia/include/gpu/GrBackendSemaphore.h"
#include "third_party/skia/include/gpu/GrBackendSurface.h"
namespace gpu {
SharedImageVideo::SharedImageVideo(
const Mailbox& mailbox,
const gfx::Size& size,
const gfx::ColorSpace color_space,
GrSurfaceOrigin surface_origin,
SkAlphaType alpha_type,
scoped_refptr<StreamTextureSharedImageInterface> stream_texture_sii,
std::unique_ptr<gles2::AbstractTexture> abstract_texture,
scoped_refptr<SharedContextState> context_state,
bool is_thread_safe)
: SharedImageBackingAndroid(
mailbox,
viz::RGBA_8888,
size,
color_space,
surface_origin,
alpha_type,
(SHARED_IMAGE_USAGE_DISPLAY | SHARED_IMAGE_USAGE_GLES2),
viz::ResourceSizes::UncheckedSizeInBytes<size_t>(size,
viz::RGBA_8888),
is_thread_safe,
base::ScopedFD()),
stream_texture_sii_(std::move(stream_texture_sii)),
abstract_texture_(std::move(abstract_texture)),
context_state_(std::move(context_state)) {
DCHECK(stream_texture_sii_);
DCHECK(context_state_);
// Currently this backing is not thread safe.
DCHECK(!is_thread_safe);
context_state_->AddContextLostObserver(this);
}
SharedImageVideo::~SharedImageVideo() {
stream_texture_sii_->ReleaseResources();
if (context_state_)
context_state_->RemoveContextLostObserver(this);
}
gfx::Rect SharedImageVideo::ClearedRect() const {
// SharedImageVideo objects are always created from pre-initialized textures
// provided by the media decoder. Always treat these as cleared (return the
// full rectangle).
return gfx::Rect(size());
}
void SharedImageVideo::SetClearedRect(const gfx::Rect& cleared_rect) {}
void SharedImageVideo::Update(std::unique_ptr<gfx::GpuFence> in_fence) {
DCHECK(!in_fence);
}
bool SharedImageVideo::ProduceLegacyMailbox(MailboxManager* mailbox_manager) {
DCHECK(abstract_texture_);
mailbox_manager->ProduceTexture(mailbox(),
abstract_texture_->GetTextureBase());
return true;
}
size_t SharedImageVideo::EstimatedSizeForMemTracking() const {
// This backing contributes to gpu memory only if its bound to the texture and
// not when the backing is created.
return stream_texture_sii_->IsUsingGpuMemory() ? estimated_size() : 0;
}
void SharedImageVideo::OnContextLost() {
// We release codec buffers when shared image context is lost. This is because
// texture owner's texture was created on shared context. Once shared context
// is lost, no one should try to use that texture.
stream_texture_sii_->ReleaseResources();
context_state_->RemoveContextLostObserver(this);
context_state_ = nullptr;
}
base::Optional<VulkanYCbCrInfo> SharedImageVideo::GetYcbcrInfo(
TextureOwner* texture_owner,
scoped_refptr<SharedContextState> context_state) {
// For non-vulkan context, return null.
if (!context_state->GrContextIsVulkan())
return base::nullopt;
// GetAHardwareBuffer() renders the latest image and gets AHardwareBuffer
// from it.
auto scoped_hardware_buffer = texture_owner->GetAHardwareBuffer();
if (!scoped_hardware_buffer) {
return base::nullopt;
}
DCHECK(scoped_hardware_buffer->buffer());
auto* context_provider = context_state->vk_context_provider();
VulkanImplementation* vk_implementation =
context_provider->GetVulkanImplementation();
VkDevice vk_device = context_provider->GetDeviceQueue()->GetVulkanDevice();
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<VulkanYCbCrInfo>(ycbcr_info);
}
std::unique_ptr<base::android::ScopedHardwareBufferFenceSync>
SharedImageVideo::GetAHardwareBuffer() {
DCHECK(stream_texture_sii_);
return stream_texture_sii_->GetAHardwareBuffer();
}
// Representation of SharedImageVideo as a GL Texture.
class SharedImageRepresentationGLTextureVideo
: public SharedImageRepresentationGLTexture {
public:
SharedImageRepresentationGLTextureVideo(SharedImageManager* manager,
SharedImageVideo* backing,
MemoryTypeTracker* tracker,
gles2::Texture* texture)
: SharedImageRepresentationGLTexture(manager, backing, tracker),
texture_(texture) {}
gles2::Texture* GetTexture() override { return texture_; }
bool BeginAccess(GLenum mode) override {
// This representation should only be called for read or overlay.
DCHECK(mode == GL_SHARED_IMAGE_ACCESS_MODE_READ_CHROMIUM ||
mode == GL_SHARED_IMAGE_ACCESS_MODE_OVERLAY_CHROMIUM);
auto* video_backing = static_cast<SharedImageVideo*>(backing());
video_backing->BeginGLReadAccess();
return true;
}
void EndAccess() override {}
private:
gles2::Texture* texture_;
DISALLOW_COPY_AND_ASSIGN(SharedImageRepresentationGLTextureVideo);
};
// Representation of SharedImageVideo as a GL Texture.
class SharedImageRepresentationGLTexturePassthroughVideo
: public SharedImageRepresentationGLTexturePassthrough {
public:
SharedImageRepresentationGLTexturePassthroughVideo(
SharedImageManager* manager,
SharedImageVideo* backing,
MemoryTypeTracker* tracker,
scoped_refptr<gles2::TexturePassthrough> texture)
: SharedImageRepresentationGLTexturePassthrough(manager,
backing,
tracker),
texture_(std::move(texture)) {
// TODO(https://crbug.com/1172769): Remove this CHECK.
CHECK(texture_);
}
const scoped_refptr<gles2::TexturePassthrough>& GetTexturePassthrough()
override {
return texture_;
}
bool BeginAccess(GLenum mode) override {
// This representation should only be called for read or overlay.
DCHECK(mode == GL_SHARED_IMAGE_ACCESS_MODE_READ_CHROMIUM ||
mode == GL_SHARED_IMAGE_ACCESS_MODE_OVERLAY_CHROMIUM);
auto* video_backing = static_cast<SharedImageVideo*>(backing());
video_backing->BeginGLReadAccess();
return true;
}
void EndAccess() override {}
private:
scoped_refptr<gles2::TexturePassthrough> texture_;
DISALLOW_COPY_AND_ASSIGN(SharedImageRepresentationGLTexturePassthroughVideo);
};
class SharedImageRepresentationVideoSkiaVk
: public SharedImageRepresentationSkiaVkAndroid {
public:
SharedImageRepresentationVideoSkiaVk(
SharedImageManager* manager,
SharedImageBackingAndroid* backing,
scoped_refptr<SharedContextState> context_state,
MemoryTypeTracker* tracker)
: SharedImageRepresentationSkiaVkAndroid(manager,
backing,
std::move(context_state),
tracker) {}
sk_sp<SkSurface> BeginWriteAccess(
int final_msaa_count,
const SkSurfaceProps& surface_props,
std::vector<GrBackendSemaphore>* begin_semaphores,
std::vector<GrBackendSemaphore>* end_semaphores,
std::unique_ptr<GrBackendSurfaceMutableState>* end_state) 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,
std::unique_ptr<GrBackendSurfaceMutableState>* end_state) override {
DCHECK(!scoped_hardware_buffer_);
auto* video_backing = static_cast<SharedImageVideo*>(backing());
DCHECK(video_backing);
auto* stream_texture_sii = video_backing->stream_texture_sii_.get();
// GetAHardwareBuffer() renders the latest image and gets AHardwareBuffer
// from it.
scoped_hardware_buffer_ = stream_texture_sii->GetAHardwareBuffer();
if (!scoped_hardware_buffer_) {
LOG(ERROR) << "Failed to get the hardware buffer.";
return nullptr;
}
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.
init_read_fence_ = scoped_hardware_buffer_->TakeFence();
if (!vulkan_image_) {
DCHECK(!promise_texture_);
vulkan_image_ =
CreateVkImageFromAhbHandle(scoped_hardware_buffer_->TakeBuffer(),
context_state(), size(), format());
if (!vulkan_image_)
return nullptr;
// We always use VK_IMAGE_TILING_OPTIMAL while creating the vk image in
// VulkanImplementationAndroid::CreateVkImageAndImportAHB. Hence pass the
// tiling parameter as VK_IMAGE_TILING_OPTIMAL to below call rather than
// passing |vk_image_info.tiling|. This is also to ensure that the promise
// image created here at [1] as well the fullfil image created via the
// current function call are consistent and both are using
// VK_IMAGE_TILING_OPTIMAL. [1] -
// https://cs.chromium.org/chromium/src/components/viz/service/display_embedder/skia_output_surface_impl.cc?rcl=db5ffd448ba5d66d9d3c5c099754e5067c752465&l=789.
DCHECK_EQ(static_cast<int32_t>(vulkan_image_->image_tiling()),
static_cast<int32_t>(VK_IMAGE_TILING_OPTIMAL));
// TODO(bsalomon): Determine whether it makes sense to attempt to reuse
// this if the vk_info stays the same on subsequent calls.
promise_texture_ = SkPromiseImageTexture::Make(
GrBackendTexture(size().width(), size().height(),
CreateGrVkImageInfo(vulkan_image_.get())));
DCHECK(promise_texture_);
}
return SharedImageRepresentationSkiaVkAndroid::BeginReadAccess(
begin_semaphores, end_semaphores, end_state);
}
void EndReadAccess() override {
DCHECK(scoped_hardware_buffer_);
SharedImageRepresentationSkiaVkAndroid::EndReadAccess();
// Pass the end read 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(android_backing()->TakeReadFence(),
true);
scoped_hardware_buffer_ = nullptr;
}
private:
std::unique_ptr<base::android::ScopedHardwareBufferFenceSync>
scoped_hardware_buffer_;
};
// TODO(vikassoni): Currently GLRenderer doesn't support overlays with shared
// image. Add support for overlays in GLRenderer as well as overlay
// representations of shared image.
std::unique_ptr<SharedImageRepresentationGLTexture>
SharedImageVideo::ProduceGLTexture(SharedImageManager* manager,
MemoryTypeTracker* tracker) {
// 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 (!stream_texture_sii_->HasTextureOwner())
return nullptr;
// TODO(vikassoni): We would want to give the TextureOwner's underlying
// Texture, but it was not set with the correct size. The AbstractTexture,
// that we use for legacy mailbox, is correctly set.
auto* texture =
gles2::Texture::CheckedCast(abstract_texture_->GetTextureBase());
DCHECK(texture);
return std::make_unique<SharedImageRepresentationGLTextureVideo>(
manager, this, tracker, texture);
}
// TODO(vikassoni): Currently GLRenderer doesn't support overlays with shared
// image. Add support for overlays in GLRenderer as well as overlay
// representations of shared image.
std::unique_ptr<SharedImageRepresentationGLTexturePassthrough>
SharedImageVideo::ProduceGLTexturePassthrough(SharedImageManager* manager,
MemoryTypeTracker* tracker) {
// 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 (!stream_texture_sii_->HasTextureOwner())
return nullptr;
// TODO(vikassoni): We would want to give the TextureOwner's underlying
// Texture, but it was not set with the correct size. The AbstractTexture,
// that we use for legacy mailbox, is correctly set.
scoped_refptr<gles2::TexturePassthrough> texture =
gles2::TexturePassthrough::CheckedCast(
abstract_texture_->GetTextureBase());
DCHECK(texture);
return std::make_unique<SharedImageRepresentationGLTexturePassthroughVideo>(
manager, this, tracker, std::move(texture));
}
// Currently SkiaRenderer doesn't support overlays.
std::unique_ptr<SharedImageRepresentationSkia> SharedImageVideo::ProduceSkia(
SharedImageManager* manager,
MemoryTypeTracker* tracker,
scoped_refptr<SharedContextState> context_state) {
DCHECK(context_state);
// 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 (!stream_texture_sii_->HasTextureOwner())
return nullptr;
if (context_state->GrContextIsVulkan()) {
return std::make_unique<SharedImageRepresentationVideoSkiaVk>(
manager, this, std::move(context_state), tracker);
}
DCHECK(context_state->GrContextIsGL());
auto* texture_base = stream_texture_sii_->GetTextureBase();
DCHECK(texture_base);
// In GL mode, create the SharedImageRepresentationGLTexture*Video
// representation to use with SharedImageRepresentationVideoSkiaGL.
std::unique_ptr<gpu::SharedImageRepresentationGLTextureBase>
gl_representation;
if (texture_base->GetType() == gpu::TextureBase::Type::kValidated) {
gl_representation =
std::make_unique<SharedImageRepresentationGLTextureVideo>(
manager, this, tracker, gles2::Texture::CheckedCast(texture_base));
} else {
gl_representation =
std::make_unique<SharedImageRepresentationGLTexturePassthroughVideo>(
manager, this, tracker,
gles2::TexturePassthrough::CheckedCast(texture_base));
}
return SharedImageRepresentationSkiaGL::Create(std::move(gl_representation),
std::move(context_state),
manager, this, tracker);
}
void SharedImageVideo::BeginGLReadAccess() {
// Render the codec image.
stream_texture_sii_->UpdateAndBindTexImage();
}
// Representation of SharedImageVideo as an overlay plane.
class SharedImageRepresentationOverlayVideo
: public gpu::SharedImageRepresentationOverlay {
public:
SharedImageRepresentationOverlayVideo(gpu::SharedImageManager* manager,
SharedImageVideo* backing,
gpu::MemoryTypeTracker* tracker)
: gpu::SharedImageRepresentationOverlay(manager, backing, tracker),
stream_image_(backing->stream_texture_sii_) {}
protected:
bool BeginReadAccess(std::vector<gfx::GpuFence>* acquire_fences) override {
// A |CodecImage| is already in a SurfaceView, render content to the
// overlay.
if (!stream_image_->HasTextureOwner()) {
TRACE_EVENT0("media",
"SharedImageRepresentationOverlayVideo::BeginReadAccess");
stream_image_->RenderToOverlay();
}
return true;
}
void EndReadAccess(gfx::GpuFenceHandle release_fence) override {
DCHECK(release_fence.is_null());
}
gl::GLImage* GetGLImage() override {
DCHECK(stream_image_->HasTextureOwner())
<< "The backing is already in a SurfaceView!";
return stream_image_.get();
}
void NotifyOverlayPromotion(bool promotion,
const gfx::Rect& bounds) override {
stream_image_->NotifyOverlayPromotion(promotion, bounds);
}
private:
scoped_refptr<StreamTextureSharedImageInterface> stream_image_;
DISALLOW_COPY_AND_ASSIGN(SharedImageRepresentationOverlayVideo);
};
std::unique_ptr<gpu::SharedImageRepresentationOverlay>
SharedImageVideo::ProduceOverlay(gpu::SharedImageManager* manager,
gpu::MemoryTypeTracker* tracker) {
return std::make_unique<SharedImageRepresentationOverlayVideo>(manager, this,
tracker);
}
} // namespace gpu