blob: 6a79e45389a4b232f26ecdb68a795c9579b8c21c [file] [log] [blame]
// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "gpu/ipc/service/gpu_channel_shared_image_interface.h"
#include "base/process/memory.h"
#include "base/synchronization/waitable_event.h"
#include "build/build_config.h"
#include "components/viz/common/resources/shared_image_format_utils.h"
#include "gpu/command_buffer/client/client_shared_image.h"
#include "gpu/command_buffer/common/shared_image_usage.h"
#include "gpu/command_buffer/common/sync_token.h"
#include "gpu/command_buffer/service/scheduler.h"
#include "gpu/command_buffer/service/shared_image/shared_image_factory.h"
#include "gpu/ipc/service/gpu_channel.h"
#include "gpu/ipc/service/gpu_channel_manager.h"
#include "ui/gfx/buffer_format_util.h"
#if BUILDFLAG(IS_ANDROID)
#include "gpu/command_buffer/service/ref_counted_lock.h"
#include "gpu/command_buffer/service/shared_image/android_video_image_backing.h"
#include "gpu/command_buffer/service/stream_texture_shared_image_interface.h"
#endif
namespace gpu {
namespace {
// Generates unique IDs for GpuChannelSharedImageInterface.
base::AtomicSequenceNumber g_next_id;
} // namespace
GpuChannelSharedImageInterface::GpuChannelSharedImageInterface(
base::WeakPtr<SharedImageStub> shared_image_stub)
: shared_image_stub_(shared_image_stub),
command_buffer_id_(CommandBufferIdFromChannelAndRoute(
shared_image_stub->channel()->client_id(),
g_next_id.GetNext() + 1)),
scheduler_(shared_image_stub->channel()->scheduler()),
sequence_(scheduler_->CreateSequence(
SchedulingPriority::kLow,
shared_image_stub->channel()->task_runner())),
sync_point_client_state_(
shared_image_stub->channel()
->sync_point_manager()
->CreateSyncPointClientState(
CommandBufferNamespace::GPU_CHANNEL_SHARED_IMAGE_INTERFACE,
command_buffer_id_,
sequence_)),
shared_image_capabilities_(
shared_image_stub->factory()->MakeCapabilities()) {
DETACH_FROM_SEQUENCE(gpu_sequence_checker_);
}
GpuChannelSharedImageInterface::~GpuChannelSharedImageInterface() {
scheduler_->DestroySequence(sequence_);
sync_point_client_state_->Destroy();
}
const SharedImageCapabilities&
GpuChannelSharedImageInterface::GetCapabilities() {
return shared_image_capabilities_;
}
// Public functions specific to GpuChannelSharedImageInterface:
#if BUILDFLAG(IS_ANDROID)
scoped_refptr<ClientSharedImage>
GpuChannelSharedImageInterface::CreateSharedImageForAndroidVideo(
const gfx::Size& size,
const gfx::ColorSpace& color_space,
scoped_refptr<StreamTextureSharedImageInterface> image,
scoped_refptr<RefCountedLock> drdc_lock) {
DCHECK_CALLED_ON_VALID_SEQUENCE(gpu_sequence_checker_);
if (!shared_image_stub_) {
return nullptr;
}
auto mailbox = Mailbox::Generate();
scoped_refptr<SharedContextState> shared_context =
shared_image_stub_->shared_context_state();
if (shared_context->context_lost()) {
return nullptr;
}
auto shared_image_backing = AndroidVideoImageBacking::Create(
mailbox, size, color_space, kTopLeft_GrSurfaceOrigin, kPremul_SkAlphaType,
/*debug_label=*/"SIForAndroidVideo", std::move(image),
std::move(shared_context), std::move(drdc_lock));
SharedImageMetadata metadata{shared_image_backing->format(),
shared_image_backing->size(),
shared_image_backing->color_space(),
shared_image_backing->surface_origin(),
shared_image_backing->alpha_type(),
shared_image_backing->usage()};
// Register it with shared image mailbox. This keeps |shared_image_backing|
// around until its destruction cb is called.
DCHECK(shared_image_stub_->channel()
->gpu_channel_manager()
->shared_image_manager());
shared_image_stub_->factory()->RegisterBacking(
std::move(shared_image_backing));
return base::WrapRefCounted<ClientSharedImage>(
new ClientSharedImage(mailbox, metadata, GenVerifiedSyncToken(), holder_,
GL_TEXTURE_EXTERNAL_OES));
}
#endif
#if BUILDFLAG(IS_WIN)
scoped_refptr<ClientSharedImage>
GpuChannelSharedImageInterface::CreateSharedImageForD3D11Video(
const SharedImageInfo& si_info,
Microsoft::WRL::ComPtr<ID3D11Texture2D> texture,
scoped_refptr<gpu::DXGISharedHandleState> dxgi_shared_handle_state,
size_t array_slice,
const bool is_thread_safe) {
DCHECK_CALLED_ON_VALID_SEQUENCE(gpu_sequence_checker_);
if (!shared_image_stub_) {
return nullptr;
}
auto mailbox = Mailbox::Generate();
auto metadata = si_info.meta;
auto caps = shared_image_stub_->shared_context_state()->GetGLFormatCaps();
// The target must be GL_TEXTURE_EXTERNAL_OES as the texture is not created
// with D3D11_BIND_RENDER_TARGET bind flag and so it cannot be bound to the
// framebuffer. To prevent Skia trying to bind it for read pixels, we need
// it to be GL_TEXTURE_EXTERNAL_OES.
std::unique_ptr<gpu::SharedImageBacking> backing =
gpu::D3DImageBacking::Create(
mailbox, metadata.format, metadata.size, metadata.color_space,
metadata.surface_origin, metadata.alpha_type, metadata.usage,
si_info.debug_label, texture, /*dcomp_texture=*/nullptr,
std::move(dxgi_shared_handle_state), caps, GL_TEXTURE_EXTERNAL_OES,
array_slice, /*use_update_subresource1=*/false, is_thread_safe);
if (!backing) {
return nullptr;
}
// Need to clear the backing since the D3D11 Video Decoder will initialize
// the textures.
backing->SetCleared();
DCHECK(shared_image_stub_->channel()
->gpu_channel_manager()
->shared_image_manager());
shared_image_stub_->factory()->RegisterBacking(std::move(backing));
return base::WrapRefCounted<ClientSharedImage>(
new ClientSharedImage(mailbox, metadata, GenVerifiedSyncToken(), holder_,
GL_TEXTURE_EXTERNAL_OES));
}
#endif
bool GpuChannelSharedImageInterface::MakeContextCurrent(bool needs_gl) {
DCHECK_CALLED_ON_VALID_SEQUENCE(gpu_sequence_checker_);
if (!shared_image_stub_) {
return false;
}
return shared_image_stub_->MakeContextCurrent(needs_gl);
}
void GpuChannelSharedImageInterface::ReleaseFenceSync(uint64_t release) {
DCHECK_CALLED_ON_VALID_SEQUENCE(gpu_sequence_checker_);
sync_point_client_state_->ReleaseFenceSync(release);
}
scoped_refptr<ClientSharedImage>
GpuChannelSharedImageInterface::CreateSharedImage(
const SharedImageInfo& si_info,
gpu::SurfaceHandle surface_handle) {
DCHECK(gpu::IsValidClientUsage(si_info.meta.usage));
auto mailbox = Mailbox::Generate();
{
base::AutoLock lock(lock_);
ScheduleGpuTask(
base::BindOnce(
&GpuChannelSharedImageInterface::CreateSharedImageOnGpuThread, this,
mailbox, si_info, surface_handle, next_fence_sync_release_++),
{});
}
return base::MakeRefCounted<ClientSharedImage>(mailbox, si_info.meta,
GenVerifiedSyncToken(),
holder_, gfx::EMPTY_BUFFER);
}
void GpuChannelSharedImageInterface::CreateSharedImageOnGpuThread(
const Mailbox& mailbox,
SharedImageInfo si_info,
gpu::SurfaceHandle surface_handle,
uint64_t release) {
DCHECK_CALLED_ON_VALID_SEQUENCE(gpu_sequence_checker_);
if (!MakeContextCurrent()) {
return;
}
DCHECK(shared_image_stub_->factory());
if (!shared_image_stub_->factory()->CreateSharedImage(
mailbox, si_info.meta.format, si_info.meta.size,
si_info.meta.color_space, si_info.meta.surface_origin,
si_info.meta.alpha_type, surface_handle, si_info.meta.usage,
std::string(si_info.debug_label))) {
shared_image_stub_->shared_context_state()->MarkContextLost();
return;
}
ReleaseFenceSync(release);
}
scoped_refptr<ClientSharedImage>
GpuChannelSharedImageInterface::CreateSharedImage(
const SharedImageInfo& si_info,
base::span<const uint8_t> pixel_data) {
DCHECK(gpu::IsValidClientUsage(si_info.meta.usage));
auto mailbox = Mailbox::Generate();
std::vector<uint8_t> pixel_data_copy(pixel_data.begin(), pixel_data.end());
{
base::AutoLock lock(lock_);
ScheduleGpuTask(
base::BindOnce(&GpuChannelSharedImageInterface::
CreateSharedImageWithDataOnGpuThread,
this, mailbox, si_info, std::move(pixel_data_copy),
next_fence_sync_release_++),
{});
}
return base::MakeRefCounted<ClientSharedImage>(mailbox, si_info.meta,
GenVerifiedSyncToken(),
holder_, gfx::EMPTY_BUFFER);
}
void GpuChannelSharedImageInterface::CreateSharedImageWithDataOnGpuThread(
const Mailbox& mailbox,
SharedImageInfo si_info,
std::vector<uint8_t> pixel_data,
uint64_t release) {
DCHECK_CALLED_ON_VALID_SEQUENCE(gpu_sequence_checker_);
if (!MakeContextCurrent()) {
return;
}
DCHECK(shared_image_stub_->factory());
if (!shared_image_stub_->factory()->CreateSharedImage(
mailbox, si_info.meta.format, si_info.meta.size,
si_info.meta.color_space, si_info.meta.surface_origin,
si_info.meta.alpha_type, si_info.meta.usage,
std::move(si_info.debug_label), pixel_data)) {
shared_image_stub_->shared_context_state()->MarkContextLost();
return;
}
ReleaseFenceSync(release);
}
scoped_refptr<ClientSharedImage>
GpuChannelSharedImageInterface::CreateSharedImage(
const SharedImageInfo& si_info,
SurfaceHandle surface_handle,
gfx::BufferUsage buffer_usage) {
DCHECK(gpu::IsValidClientUsage(si_info.meta.usage));
auto mailbox = Mailbox::Generate();
{
base::AutoLock lock(lock_);
ScheduleGpuTask(
base::BindOnce(&GpuChannelSharedImageInterface::
CreateSharedImageWithBufferUsageOnGpuThread,
this, mailbox, si_info, surface_handle, buffer_usage,
next_fence_sync_release_++),
{});
}
return base::MakeRefCounted<ClientSharedImage>(
mailbox, si_info.meta, GenVerifiedSyncToken(),
GetGpuMemoryBufferHandleInfo(mailbox), holder_);
}
void GpuChannelSharedImageInterface::
CreateSharedImageWithBufferUsageOnGpuThread(const Mailbox& mailbox,
SharedImageInfo si_info,
SurfaceHandle surface_handle,
gfx::BufferUsage buffer_usage,
uint64_t release) {
DCHECK_CALLED_ON_VALID_SEQUENCE(gpu_sequence_checker_);
if (!MakeContextCurrent()) {
return;
}
DCHECK(shared_image_stub_->factory());
if (!shared_image_stub_->factory()->CreateSharedImage(
mailbox, si_info.meta.format, si_info.meta.size,
si_info.meta.color_space, si_info.meta.surface_origin,
si_info.meta.alpha_type, surface_handle, si_info.meta.usage,
std::move(si_info.debug_label), buffer_usage)) {
shared_image_stub_->shared_context_state()->MarkContextLost();
return;
}
ReleaseFenceSync(release);
}
GpuMemoryBufferHandleInfo
GpuChannelSharedImageInterface::GetGpuMemoryBufferHandleInfo(
const Mailbox& mailbox) {
base::WaitableEvent completion(
base::WaitableEvent::ResetPolicy::MANUAL,
base::WaitableEvent::InitialState::NOT_SIGNALED);
gfx::GpuMemoryBufferHandle handle;
viz::SharedImageFormat format;
gfx::Size size;
gfx::BufferUsage buffer_usage;
ScheduleGpuTask(base::BindOnce(&GpuChannelSharedImageInterface::
GetGpuMemoryBufferHandleInfoOnGpuThread,
this, mailbox, &handle, &format, &size,
&buffer_usage, &completion),
{});
completion.Wait();
return GpuMemoryBufferHandleInfo(std::move(handle), format, size,
buffer_usage);
}
void GpuChannelSharedImageInterface::GetGpuMemoryBufferHandleInfoOnGpuThread(
const Mailbox& mailbox,
gfx::GpuMemoryBufferHandle* handle,
viz::SharedImageFormat* format,
gfx::Size* size,
gfx::BufferUsage* buffer_usage,
base::WaitableEvent* completion) {
DCHECK_CALLED_ON_VALID_SEQUENCE(gpu_sequence_checker_);
base::ScopedClosureRunner completion_runner(base::BindOnce(
[](base::WaitableEvent* completion) { completion->Signal(); },
completion));
DCHECK(shared_image_stub_->factory());
if (!shared_image_stub_->factory()->GetGpuMemoryBufferHandleInfo(
mailbox, *handle, *format, *size, *buffer_usage)) {
LOG(ERROR) << "GpuChannelSharedImageInterface: Unable to get "
"GpuMemoryBufferHandle";
}
}
scoped_refptr<ClientSharedImage>
GpuChannelSharedImageInterface::CreateSharedImage(
const SharedImageInfo& si_info,
gpu::SurfaceHandle surface_handle,
gfx::BufferUsage buffer_usage,
gfx::GpuMemoryBufferHandle buffer_handle) {
DCHECK(gpu::IsValidClientUsage(si_info.meta.usage));
#if BUILDFLAG(IS_APPLE) || BUILDFLAG(IS_WIN)
CHECK(!si_info.meta.format.PrefersExternalSampler());
#endif
auto client_buffer_handle = buffer_handle.Clone();
auto mailbox = Mailbox::Generate();
{
base::AutoLock lock(lock_);
ScheduleGpuTask(
base::BindOnce(&GpuChannelSharedImageInterface::
CreateSharedImageWithBufferOnGpuThread,
this, mailbox, si_info, std::move(buffer_handle),
next_fence_sync_release_++),
{});
}
return base::MakeRefCounted<ClientSharedImage>(
mailbox, si_info.meta, GenVerifiedSyncToken(),
GpuMemoryBufferHandleInfo(std::move(client_buffer_handle),
si_info.meta.format, si_info.meta.size,
buffer_usage),
holder_);
}
scoped_refptr<ClientSharedImage>
GpuChannelSharedImageInterface::CreateSharedImage(
const SharedImageInfo& si_info,
gfx::GpuMemoryBufferHandle buffer_handle) {
DCHECK(gpu::IsValidClientUsage(si_info.meta.usage));
#if BUILDFLAG(IS_APPLE) || BUILDFLAG(IS_WIN)
CHECK(!si_info.meta.format.PrefersExternalSampler());
#endif
auto mailbox = Mailbox::Generate();
auto gmb_type = buffer_handle.type;
{
base::AutoLock lock(lock_);
ScheduleGpuTask(
base::BindOnce(&GpuChannelSharedImageInterface::
CreateSharedImageWithBufferOnGpuThread,
this, mailbox, si_info, std::move(buffer_handle),
next_fence_sync_release_++),
{});
}
return base::MakeRefCounted<ClientSharedImage>(
mailbox, si_info.meta, GenVerifiedSyncToken(), holder_, gmb_type);
}
SharedImageInterface::SharedImageMapping
GpuChannelSharedImageInterface::CreateSharedImage(
const SharedImageInfo& si_info) {
DCHECK(gpu::IsValidClientUsage(si_info.meta.usage));
DCHECK(si_info.meta.usage ==
gpu::SharedImageUsageSet(gpu::SHARED_IMAGE_USAGE_CPU_WRITE));
#if BUILDFLAG(IS_APPLE) || BUILDFLAG(IS_WIN)
CHECK(!si_info.meta.format.PrefersExternalSampler());
#endif
SharedImageInterface::SharedImageMapping shared_image_mapping;
gfx::BufferFormat buffer_format =
viz::SinglePlaneSharedImageFormatToBufferFormat(si_info.meta.format);
const size_t buffer_size =
gfx::BufferSizeForBufferFormat(si_info.meta.size, buffer_format);
auto shared_memory_region =
base::UnsafeSharedMemoryRegion::Create(buffer_size);
if (!shared_memory_region.IsValid()) {
DLOG(ERROR) << "base::UnsafeSharedMemoryRegion::Create() for SharedImage "
"with SHARED_IMAGE_USAGE_CPU_WRITE fails!";
base::TerminateBecauseOutOfMemory(buffer_size);
}
shared_image_mapping.mapping = shared_memory_region.Map();
if (!shared_image_mapping.mapping.IsValid()) {
DLOG(ERROR)
<< "shared_memory_region.Map() for SHARED_IMAGE_USAGE_CPU_WRITE fails!";
base::TerminateBecauseOutOfMemory(buffer_size);
}
gfx::GpuMemoryBufferHandle handle;
handle.type = gfx::SHARED_MEMORY_BUFFER;
handle.offset = 0;
handle.stride = static_cast<int32_t>(
gfx::RowSizeForBufferFormat(si_info.meta.size.width(), buffer_format, 0));
handle.region = std::move(shared_memory_region);
auto mailbox = Mailbox::Generate();
{
base::AutoLock lock(lock_);
ScheduleGpuTask(base::BindOnce(&GpuChannelSharedImageInterface::
CreateSharedImageWithBufferOnGpuThread,
this, mailbox, si_info, std::move(handle),
next_fence_sync_release_++),
{});
}
shared_image_mapping.shared_image = base::MakeRefCounted<ClientSharedImage>(
mailbox, si_info.meta, GenVerifiedSyncToken(), holder_,
gfx::SHARED_MEMORY_BUFFER);
return shared_image_mapping;
}
void GpuChannelSharedImageInterface::CreateSharedImageWithBufferOnGpuThread(
const Mailbox& mailbox,
SharedImageInfo si_info,
gfx::GpuMemoryBufferHandle buffer_handle,
uint64_t release) {
DCHECK_CALLED_ON_VALID_SEQUENCE(gpu_sequence_checker_);
if (!MakeContextCurrent()) {
return;
}
DCHECK(shared_image_stub_->factory());
if (!shared_image_stub_->factory()->CreateSharedImage(
mailbox, si_info.meta.format, si_info.meta.size,
si_info.meta.color_space, si_info.meta.surface_origin,
si_info.meta.alpha_type, si_info.meta.usage,
std::move(si_info.debug_label), std::move(buffer_handle))) {
shared_image_stub_->shared_context_state()->MarkContextLost();
return;
}
ReleaseFenceSync(release);
}
SharedImageInterface::SwapChainSharedImages
GpuChannelSharedImageInterface::CreateSwapChain(
viz::SharedImageFormat format,
const gfx::Size& size,
const gfx::ColorSpace& color_space,
GrSurfaceOrigin surface_origin,
SkAlphaType alpha_type,
SharedImageUsageSet usage) {
NOTREACHED_IN_MIGRATION();
return GpuChannelSharedImageInterface::SwapChainSharedImages(nullptr,
nullptr);
}
void GpuChannelSharedImageInterface::PresentSwapChain(
const SyncToken& sync_token,
const Mailbox& mailbox) {
NOTREACHED_IN_MIGRATION();
}
#if BUILDFLAG(IS_FUCHSIA)
void GpuChannelSharedImageInterface::RegisterSysmemBufferCollection(
zx::eventpair service_handle,
zx::channel sysmem_token,
const viz::SharedImageFormat& format,
gfx::BufferUsage usage,
bool register_with_image_pipe) {
NOTREACHED_IN_MIGRATION();
}
#endif // BUILDFLAG(IS_FUCHSIA)
void GpuChannelSharedImageInterface::UpdateSharedImage(
const SyncToken& sync_token,
const Mailbox& mailbox) {
UpdateSharedImage(sync_token, nullptr, mailbox);
}
void GpuChannelSharedImageInterface::UpdateSharedImage(
const SyncToken& sync_token,
std::unique_ptr<gfx::GpuFence> acquire_fence,
const Mailbox& mailbox) {
DCHECK(!acquire_fence);
base::AutoLock lock(lock_);
ScheduleGpuTask(
base::BindOnce(
&GpuChannelSharedImageInterface::UpdateSharedImageOnGpuThread, this,
mailbox, next_fence_sync_release_++),
{sync_token});
}
void GpuChannelSharedImageInterface::UpdateSharedImageOnGpuThread(
const Mailbox& mailbox,
uint64_t release) {
DCHECK_CALLED_ON_VALID_SEQUENCE(gpu_sequence_checker_);
if (!MakeContextCurrent()) {
return;
}
if (!shared_image_stub_->factory() ||
!shared_image_stub_->factory()->UpdateSharedImage(mailbox)) {
shared_image_stub_->shared_context_state()->MarkContextLost();
return;
}
ReleaseFenceSync(release);
}
void GpuChannelSharedImageInterface::DestroySharedImage(
const SyncToken& sync_token,
const Mailbox& mailbox) {
// Use sync token dependency to ensure that the destroy task does not run
// before sync token is released.
ScheduleGpuTask(
base::BindOnce(
&GpuChannelSharedImageInterface::DestroySharedImageOnGpuThread, this,
mailbox),
{sync_token});
}
void GpuChannelSharedImageInterface::DestroySharedImage(
const SyncToken& sync_token,
scoped_refptr<ClientSharedImage> client_shared_image) {
CHECK(client_shared_image->HasOneRef());
client_shared_image->UpdateDestructionSyncToken(sync_token);
}
void GpuChannelSharedImageInterface::DestroySharedImageOnGpuThread(
const Mailbox& mailbox) {
DCHECK_CALLED_ON_VALID_SEQUENCE(gpu_sequence_checker_);
if (!MakeContextCurrent()) {
return;
}
if (!shared_image_stub_->factory() ||
!shared_image_stub_->factory()->DestroySharedImage(mailbox)) {
shared_image_stub_->shared_context_state()->MarkContextLost();
}
}
SyncToken GpuChannelSharedImageInterface::GenUnverifiedSyncToken() {
base::AutoLock lock(lock_);
return MakeSyncToken(next_fence_sync_release_ - 1);
}
SyncToken GpuChannelSharedImageInterface::GenVerifiedSyncToken() {
base::AutoLock lock(lock_);
SyncToken sync_token = MakeSyncToken(next_fence_sync_release_ - 1);
VerifySyncToken(sync_token);
return sync_token;
}
void GpuChannelSharedImageInterface::VerifySyncToken(SyncToken& sync_token) {
sync_token.SetVerifyFlush();
}
void GpuChannelSharedImageInterface::WaitSyncToken(
const SyncToken& sync_token) {
base::AutoLock lock(lock_);
ScheduleGpuTask(
base::BindOnce(&GpuChannelSharedImageInterface::ReleaseFenceSync, this,
next_fence_sync_release_++),
{sync_token});
}
void GpuChannelSharedImageInterface::Flush() {
// No need to flush in this implementation.
}
scoped_refptr<gfx::NativePixmap>
GpuChannelSharedImageInterface::GetNativePixmap(const gpu::Mailbox& mailbox) {
NOTREACHED_IN_MIGRATION();
return nullptr;
}
void GpuChannelSharedImageInterface::ScheduleGpuTask(
base::OnceClosure task,
std::vector<SyncToken> sync_token_fences) {
scheduler_->ScheduleTask(
gpu::Scheduler::Task(sequence_, std::move(task), {}));
}
scoped_refptr<ClientSharedImage>
GpuChannelSharedImageInterface::ImportSharedImage(
const ExportedSharedImage& exported_shared_image) {
NOTREACHED_IN_MIGRATION();
return nullptr;
}
} // namespace gpu