#include "gpu/ipc/service/shared_image_stub.h"
#include <inttypes.h>
#include <memory>
#include <utility>
#include "base/memory/ptr_util.h"
#include "base/trace_event/trace_event.h"
#include "build/build_config.h"
#include "components/viz/common/resources/shared_image_format.h"
#include "gpu/command_buffer/service/scheduler.h"
#include "gpu/command_buffer/service/shared_image/shared_image_factory.h"
#include "gpu/command_buffer/service/shared_image/shared_image_representation.h"
#include "gpu/ipc/common/command_buffer_id.h"
#include "gpu/ipc/common/gpu_peak_memory.h"
#include "gpu/ipc/service/gpu_channel.h"
#include "gpu/ipc/service/gpu_channel_manager.h"
#include "gpu/ipc/service/gpu_memory_buffer_factory.h"
#include "ui/gfx/buffer_format_util.h"
#include "ui/gl/gl_context.h"
namespace gpu {
SharedImageStub::SharedImageStub(GpuChannel* channel, int32_t route_id)
: channel_(channel),
command_buffer_id_(
CommandBufferIdFromChannelAndRoute(channel->client_id(), route_id)),
sequence_(channel->scheduler()->CreateSequence(SchedulingPriority::kLow,
channel_->task_runner())),
sync_point_client_state_(
channel->sync_point_manager()->CreateSyncPointClientState(
CommandBufferNamespace::GPU_IO,
command_buffer_id_,
sequence_)) {}
SharedImageStub::~SharedImageStub() {
channel_->scheduler()->DestroySequence(sequence_);
sync_point_client_state_->Destroy();
if (factory_ && factory_->HasImages()) {
bool have_context = MakeContextCurrent();
factory_->DestroyAllSharedImages(have_context);
}
}
std::unique_ptr<SharedImageStub> SharedImageStub::Create(GpuChannel* channel,
int32_t route_id) {
auto stub = base::WrapUnique(new SharedImageStub(channel, route_id));
ContextResult result = stub->MakeContextCurrentAndCreateFactory();
if (result == ContextResult::kSuccess)
return stub;
if (result != ContextResult::kTransientFailure)
return nullptr;
if (stub->MakeContextCurrentAndCreateFactory() != ContextResult::kSuccess)
return nullptr;
return stub;
}
void SharedImageStub::ExecuteDeferredRequest(
mojom::DeferredSharedImageRequestPtr request) {
switch (request->which()) {
case mojom::DeferredSharedImageRequest::Tag::kNop:
break;
case mojom::DeferredSharedImageRequest::Tag::kCreateSharedImage:
OnCreateSharedImage(std::move(request->get_create_shared_image()));
break;
case mojom::DeferredSharedImageRequest::Tag::kCreateSharedImageWithData:
OnCreateSharedImageWithData(
std::move(request->get_create_shared_image_with_data()));
break;
case mojom::DeferredSharedImageRequest::Tag::kCreateSharedImageWithBuffer:
OnCreateSharedImageWithBuffer(
std::move(request->get_create_shared_image_with_buffer()));
break;
case mojom::DeferredSharedImageRequest::Tag::kCreateGmbSharedImage:
OnCreateGMBSharedImage(std::move(request->get_create_gmb_shared_image()));
break;
case mojom::DeferredSharedImageRequest::Tag::kRegisterUploadBuffer:
OnRegisterSharedImageUploadBuffer(
std::move(request->get_register_upload_buffer()));
break;
case mojom::DeferredSharedImageRequest::Tag::kUpdateSharedImage: {
auto& update = *request->get_update_shared_image();
OnUpdateSharedImage(update.mailbox, update.release_id,
std::move(update.in_fence_handle));
break;
}
case mojom::DeferredSharedImageRequest::Tag::kAddReferenceToSharedImage: {
const auto& add_ref = *request->get_add_reference_to_shared_image();
OnAddReference(add_ref.mailbox, add_ref.release_id);
break;
}
case mojom::DeferredSharedImageRequest::Tag::kDestroySharedImage:
OnDestroySharedImage(request->get_destroy_shared_image());
break;
#if BUILDFLAG(IS_WIN)
case mojom::DeferredSharedImageRequest::Tag::kCopyToGpuMemoryBuffer: {
auto& params = *request->get_copy_to_gpu_memory_buffer();
OnCopyToGpuMemoryBuffer(params.mailbox, params.release_id);
break;
}
case mojom::DeferredSharedImageRequest::Tag::kCreateSwapChain:
OnCreateSwapChain(std::move(request->get_create_swap_chain()));
break;
case mojom::DeferredSharedImageRequest::Tag::kPresentSwapChain:
OnPresentSwapChain(request->get_present_swap_chain()->mailbox,
request->get_present_swap_chain()->release_id);
break;
#endif
}
}
#if BUILDFLAG(ENABLE_HEIF_DECODER)
bool SharedImageStub::CreateSharedImage(const Mailbox& mailbox,
gfx::GpuMemoryBufferHandle handle,
gfx::BufferFormat format,
gfx::BufferPlane plane,
const gfx::Size& size,
const gfx::ColorSpace& color_space,
GrSurfaceOrigin surface_origin,
SkAlphaType alpha_type,
uint32_t usage,
void* window_buffer) {
TRACE_EVENT2("gpu", "SharedImageStub::CreateSharedImage", "width",
size.width(), "height", size.height());
if (!mailbox.IsSharedImage()) {
LOG(ERROR) << "[HeifSupport] SharedImageStub: Trying to create a SharedImage with a "
"non-SharedImage mailbox.";
OnError();
return false;
}
if (!MakeContextCurrent()) {
OnError();
return false;
}
if (!factory_->CreateSharedImage(mailbox, std::move(handle), format, plane,
size, color_space, surface_origin,
alpha_type, usage, window_buffer)) {
LOG(ERROR) << "[HeifSupport] SharedImageStub: Unable to create shared image";
OnError();
return false;
}
return true;
}
#endif
bool SharedImageStub::CreateSharedImage(const Mailbox& mailbox,
gfx::GpuMemoryBufferHandle handle,
gfx::BufferFormat format,
gfx::BufferPlane plane,
const gfx::Size& size,
const gfx::ColorSpace& color_space,
GrSurfaceOrigin surface_origin,
SkAlphaType alpha_type,
uint32_t usage,
std::string debug_label) {
TRACE_EVENT2("gpu", "SharedImageStub::CreateSharedImage", "width",
size.width(), "height", size.height());
if (!mailbox.IsSharedImage()) {
LOG(ERROR) << "SharedImageStub: Trying to create a SharedImage with a "
"non-SharedImage mailbox.";
OnError();
return false;
}
if (!MakeContextCurrent()) {
OnError();
return false;
}
if (!factory_->CreateSharedImage(mailbox, std::move(handle), format, plane,
size, color_space, surface_origin,
alpha_type, usage, GetLabel(debug_label))) {
LOG(ERROR) << "SharedImageStub: Unable to create shared image";
OnError();
return false;
}
return true;
}
bool SharedImageStub::CreateSharedImage(const Mailbox& mailbox,
gfx::GpuMemoryBufferHandle handle,
viz::SharedImageFormat format,
const gfx::Size& size,
const gfx::ColorSpace& color_space,
GrSurfaceOrigin surface_origin,
SkAlphaType alpha_type,
uint32_t usage,
std::string debug_label) {
TRACE_EVENT2("gpu", "SharedImageStub::CreateSharedImage", "width",
size.width(), "height", size.height());
if (!format.is_multi_plane()) {
LOG(ERROR) << "SharedImageStub: Incompatible format.";
OnError();
return false;
}
if (!mailbox.IsSharedImage()) {
LOG(ERROR) << "SharedImageStub: Trying to create a SharedImage with a "
"non-SharedImage mailbox and multiplanar format";
OnError();
return false;
}
if (!MakeContextCurrent()) {
OnError();
return false;
}
if (!factory_->CreateSharedImage(mailbox, format, size, color_space,
surface_origin, alpha_type, usage,
GetLabel(debug_label), std::move(handle))) {
LOG(ERROR) << "SharedImageStub: Unable to create shared image with "
"multiplanar format";
OnError();
return false;
}
return true;
}
bool SharedImageStub::UpdateSharedImage(const Mailbox& mailbox,
gfx::GpuFenceHandle in_fence_handle) {
TRACE_EVENT0("gpu", "SharedImageStub::UpdateSharedImage");
std::unique_ptr<gfx::GpuFence> in_fence;
if (!in_fence_handle.is_null())
in_fence = std::make_unique<gfx::GpuFence>(std::move(in_fence_handle));
if (!mailbox.IsSharedImage()) {
LOG(ERROR) << "SharedImageStub: Trying to access a SharedImage with a "
"non-SharedImage mailbox.";
OnError();
return false;
}
if (!MakeContextCurrent()) {
OnError();
return false;
}
if (!factory_->UpdateSharedImage(mailbox, std::move(in_fence))) {
LOG(ERROR) << "SharedImageStub: Unable to update shared image";
OnError();
return false;
}
return true;
}
void SharedImageStub::OnCreateSharedImage(
mojom::CreateSharedImageParamsPtr params) {
TRACE_EVENT2("gpu", "SharedImageStub::OnCreateSharedImage", "width",
params->size.width(), "height", params->size.height());
if (!params->mailbox.IsSharedImage()) {
LOG(ERROR) << "SharedImageStub: Trying to create a SharedImage with a "
"non-SharedImage mailbox.";
OnError();
return;
}
if (!MakeContextCurrent(true)) {
OnError();
return;
}
if (!factory_->CreateSharedImage(
params->mailbox, params->format, params->size, params->color_space,
params->surface_origin, params->alpha_type, gpu::kNullSurfaceHandle,
params->usage, GetLabel(params->debug_label))) {
LOG(ERROR) << "SharedImageStub: Unable to create shared image";
OnError();
return;
}
sync_point_client_state_->ReleaseFenceSync(params->release_id);
}
void SharedImageStub::OnCreateSharedImageWithData(
mojom::CreateSharedImageWithDataParamsPtr params) {
TRACE_EVENT2("gpu", "SharedImageStub::OnCreateSharedImageWithData", "width",
params->size.width(), "height", params->size.height());
if (!params->mailbox.IsSharedImage()) {
LOG(ERROR) << "SharedImageStub: Trying to create a SharedImage with a "
"non-SharedImage mailbox.";
OnError();
return;
}
if (!MakeContextCurrent()) {
OnError();
return;
}
base::CheckedNumeric<size_t> safe_required_span_size =
params->pixel_data_offset;
safe_required_span_size += params->pixel_data_size;
size_t required_span_size;
if (!safe_required_span_size.AssignIfValid(&required_span_size)) {
LOG(ERROR) << "SharedImageStub: upload data size and offset is invalid";
OnError();
return;
}
auto memory =
upload_memory_mapping_.GetMemoryAsSpan<uint8_t>(required_span_size);
if (memory.empty()) {
LOG(ERROR) << "SharedImageStub: upload data does not have expected size";
OnError();
return;
}
auto subspan =
memory.subspan(params->pixel_data_offset, params->pixel_data_size);
if (!factory_->CreateSharedImage(
params->mailbox, params->format, params->size, params->color_space,
params->surface_origin, params->alpha_type, params->usage,
GetLabel(params->debug_label), subspan)) {
LOG(ERROR) << "SharedImageStub: Unable to create shared image";
OnError();
return;
}
if (params->done_with_shm) {
upload_memory_mapping_ = base::ReadOnlySharedMemoryMapping();
upload_memory_ = base::ReadOnlySharedMemoryRegion();
}
sync_point_client_state_->ReleaseFenceSync(params->release_id);
}
void SharedImageStub::OnCreateSharedImageWithBuffer(
mojom::CreateSharedImageWithBufferParamsPtr params) {
TRACE_EVENT2("gpu", "SharedImageStub::OnCreateSharedImageWithBuffer", "width",
params->size.width(), "height", params->size.height());
if (!CreateSharedImage(params->mailbox, std::move(params->buffer_handle),
params->format, params->size, params->color_space,
params->surface_origin, params->alpha_type,
params->usage, GetLabel(params->debug_label))) {
return;
}
sync_point_client_state_->ReleaseFenceSync(params->release_id);
}
void SharedImageStub::OnCreateGMBSharedImage(
mojom::CreateGMBSharedImageParamsPtr params) {
TRACE_EVENT2("gpu", "SharedImageStub::OnCreateGMBSharedImage", "width",
params->size.width(), "height", params->size.height());
if (!CreateSharedImage(params->mailbox, std::move(params->buffer_handle),
params->format, params->plane, params->size,
params->color_space, params->surface_origin,
params->alpha_type, params->usage,
GetLabel(params->debug_label))) {
return;
}
sync_point_client_state_->ReleaseFenceSync(params->release_id);
}
void SharedImageStub::OnUpdateSharedImage(const Mailbox& mailbox,
uint32_t release_id,
gfx::GpuFenceHandle in_fence_handle) {
TRACE_EVENT0("gpu", "SharedImageStub::OnUpdateSharedImage");
if (!UpdateSharedImage(mailbox, std::move(in_fence_handle)))
return;
sync_point_client_state_->ReleaseFenceSync(release_id);
}
void SharedImageStub::OnAddReference(const Mailbox& mailbox,
uint32_t release_id) {
TRACE_EVENT0("gpu", "SharedImageStub::OnUpdateSharedImage");
if (!mailbox.IsSharedImage()) {
LOG(ERROR)
<< "SharedImageStub: Trying to add reference to SharedImage with a "
"non-SharedImage mailbox.";
OnError();
return;
}
if (!factory_->AddSecondaryReference(mailbox)) {
LOG(ERROR) << "SharedImageStub: Unable to add secondary reference";
OnError();
return;
}
sync_point_client_state_->ReleaseFenceSync(release_id);
}
void SharedImageStub::OnDestroySharedImage(const Mailbox& mailbox) {
TRACE_EVENT0("gpu", "SharedImageStub::OnDestroySharedImage");
if (!mailbox.IsSharedImage()) {
LOG(ERROR) << "SharedImageStub: Trying to destroy a SharedImage with a "
"non-SharedImage mailbox.";
OnError();
return;
}
if (!MakeContextCurrent()) {
OnError();
return;
}
if (!factory_->DestroySharedImage(mailbox)) {
LOG(ERROR) << "SharedImageStub: Unable to destroy shared image";
OnError();
return;
}
}
#if BUILDFLAG(IS_WIN)
void SharedImageStub::OnCopyToGpuMemoryBuffer(const Mailbox& mailbox,
uint32_t release_id) {
if (!mailbox.IsSharedImage()) {
DLOG(ERROR) << "SharedImageStub: Trying to access a SharedImage with a "
"non-SharedImage mailbox.";
OnError();
return;
}
if (!MakeContextCurrent()) {
OnError();
return;
}
if (!factory_->CopyToGpuMemoryBuffer(mailbox)) {
DLOG(ERROR) << "SharedImageStub: Unable to update shared GMB";
OnError();
return;
}
sync_point_client_state_->ReleaseFenceSync(release_id);
}
void SharedImageStub::OnCreateSwapChain(
mojom::CreateSwapChainParamsPtr params) {
TRACE_EVENT0("gpu", "SharedImageStub::OnCreateSwapChain");
if (!params->front_buffer_mailbox.IsSharedImage() ||
!params->back_buffer_mailbox.IsSharedImage()) {
DLOG(ERROR) << "SharedImageStub: Trying to access SharedImage with a "
"non-SharedImage mailbox.";
OnError();
return;
}
if (!MakeContextCurrent()) {
OnError();
return;
}
if (!factory_->CreateSwapChain(
params->front_buffer_mailbox, params->back_buffer_mailbox,
params->format, params->size, params->color_space,
params->surface_origin, params->alpha_type, params->usage)) {
DLOG(ERROR) << "SharedImageStub: Unable to create swap chain";
OnError();
return;
}
sync_point_client_state_->ReleaseFenceSync(params->release_id);
}
void SharedImageStub::OnPresentSwapChain(const Mailbox& mailbox,
uint32_t release_id) {
TRACE_EVENT0("gpu", "SharedImageStub::OnPresentSwapChain");
if (!mailbox.IsSharedImage()) {
DLOG(ERROR) << "SharedImageStub: Trying to access a SharedImage with a "
"non-SharedImage mailbox.";
OnError();
return;
}
if (!MakeContextCurrent()) {
OnError();
return;
}
if (!factory_->PresentSwapChain(mailbox)) {
DLOG(ERROR) << "SharedImageStub: Unable to present swap chain";
OnError();
return;
}
sync_point_client_state_->ReleaseFenceSync(release_id);
}
#endif
#if BUILDFLAG(IS_FUCHSIA)
void SharedImageStub::RegisterSysmemBufferCollection(
zx::eventpair service_handle,
zx::channel sysmem_token,
gfx::BufferFormat format,
gfx::BufferUsage usage,
bool register_with_image_pipe) {
if (!service_handle || !sysmem_token) {
OnError();
return;
}
factory_->RegisterSysmemBufferCollection(std::move(service_handle),
std::move(sysmem_token), format,
usage, register_with_image_pipe);
}
#endif
void SharedImageStub::OnRegisterSharedImageUploadBuffer(
base::ReadOnlySharedMemoryRegion shm) {
TRACE_EVENT0("gpu", "SharedImageStub::OnRegisterSharedImageUploadBuffer");
upload_memory_ = std::move(shm);
upload_memory_mapping_ = upload_memory_.Map();
if (!upload_memory_mapping_.IsValid()) {
LOG(ERROR)
<< "SharedImageStub: Unable to map shared memory for upload data";
OnError();
return;
}
}
bool SharedImageStub::MakeContextCurrent(bool needs_gl) {
DCHECK(context_state_);
if (context_state_->context_lost()) {
LOG(ERROR) << "SharedImageStub: context already lost";
return false;
}
auto* context = context_state_->real_context();
if (context->IsCurrent(nullptr))
return !context_state_->CheckResetStatus(needs_gl);
return context_state_->MakeCurrent(nullptr, needs_gl);
}
ContextResult SharedImageStub::MakeContextCurrentAndCreateFactory() {
auto* channel_manager = channel_->gpu_channel_manager();
DCHECK(!context_state_);
ContextResult result;
context_state_ = channel_manager->GetSharedContextState(&result);
if (result != ContextResult::kSuccess) {
LOG(ERROR) << "SharedImageStub: unable to create context";
context_state_ = nullptr;
return result;
}
DCHECK(context_state_);
DCHECK(!context_state_->context_lost());
if (!MakeContextCurrent(true)) {
context_state_ = nullptr;
return ContextResult::kTransientFailure;
}
factory_ = std::make_unique<SharedImageFactory>(
channel_manager->gpu_preferences(),
channel_manager->gpu_driver_bug_workarounds(),
channel_manager->gpu_feature_info(), context_state_.get(),
channel_manager->shared_image_manager(), this,
false);
return ContextResult::kSuccess;
}
void SharedImageStub::OnError() {
channel_->OnChannelError();
}
void SharedImageStub::TrackMemoryAllocatedChange(int64_t delta) {
DCHECK(delta >= 0 || size_ >= static_cast<uint64_t>(-delta));
uint64_t old_size = size_;
size_ += delta;
channel_->gpu_channel_manager()
->peak_memory_monitor()
->OnMemoryAllocatedChange(
command_buffer_id_, old_size, size_,
GpuPeakMemoryAllocationSource::SHARED_IMAGE_STUB);
}
uint64_t SharedImageStub::GetSize() const {
return size_;
}
uint64_t SharedImageStub::ClientTracingId() const {
return channel_->client_tracing_id();
}
int SharedImageStub::ClientId() const {
return channel_->client_id();
}
uint64_t SharedImageStub::ContextGroupTracingId() const {
return sync_point_client_state_->command_buffer_id().GetUnsafeValue();
}
SharedImageStub::SharedImageDestructionCallback
SharedImageStub::GetSharedImageDestructionCallback(const Mailbox& mailbox) {
return base::BindOnce(&SharedImageStub::DestroySharedImage,
weak_factory_.GetWeakPtr(), mailbox);
}
void SharedImageStub::DestroySharedImage(const Mailbox& mailbox,
const SyncToken& sync_token) {
if (!sync_token.HasData()) {
OnDestroySharedImage(mailbox);
return;
}
auto done_cb = base::BindOnce(&SharedImageStub::OnDestroySharedImage,
weak_factory_.GetWeakPtr(), mailbox);
channel_->scheduler()->ScheduleTask(
gpu::Scheduler::Task(sequence_, std::move(done_cb),
std::vector<gpu::SyncToken>({sync_token})));
}
std::string SharedImageStub::GetLabel(const std::string& debug_label) const {
return debug_label + "_Cid:" + base::NumberToString(channel_->client_id()) +
"_Pid:" + base::NumberToString(channel_->client_pid());
}
}