// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifdef UNSAFE_BUFFERS_BUILD
// TODO(crbug.com/351564777): Remove this and convert code to safer constructs.
#pragma allow_unsafe_buffers
#endif
#include "ppapi/proxy/ppb_graphics_3d_proxy.h"
#include <memory>
#include "base/numerics/safe_conversions.h"
#include "build/build_config.h"
#include "gpu/command_buffer/client/gles2_implementation.h"
#include "gpu/command_buffer/common/command_buffer.h"
#include "gpu/command_buffer/common/sync_token.h"
#include "ppapi/c/pp_errors.h"
#include "ppapi/proxy/enter_proxy.h"
#include "ppapi/proxy/plugin_dispatcher.h"
#include "ppapi/proxy/ppapi_command_buffer_proxy.h"
#include "ppapi/proxy/ppapi_messages.h"
#include "ppapi/shared_impl/ppapi_globals.h"
#include "ppapi/thunk/enter.h"
#include "ppapi/thunk/resource_creation_api.h"
#include "ppapi/thunk/thunk.h"
using ppapi::thunk::EnterResourceNoLock;
using ppapi::thunk::PPB_Graphics3D_API;
using ppapi::thunk::ResourceCreationAPI;
namespace ppapi {
namespace proxy {
namespace {
#if !BUILDFLAG(IS_NACL)
base::UnsafeSharedMemoryRegion TransportSHMHandle(
Dispatcher* dispatcher,
const base::UnsafeSharedMemoryRegion& region) {
return dispatcher->ShareUnsafeSharedMemoryRegionWithRemote(region);
}
#endif // !BUILDFLAG(IS_NACL)
gpu::CommandBuffer::State GetErrorState() {
gpu::CommandBuffer::State error_state;
error_state.error = gpu::error::kGenericError;
return error_state;
}
} // namespace
Graphics3D::Graphics3D(const HostResource& resource, const gfx::Size& size)
: PPB_Graphics3D_Shared(resource, size) {}
Graphics3D::~Graphics3D() {
DestroyGLES2Impl();
}
bool Graphics3D::Init(gpu::gles2::GLES2Implementation* share_gles2,
const gpu::Capabilities& capabilities,
const gpu::GLCapabilities& gl_capabilities,
SerializedHandle shared_state,
gpu::CommandBufferId command_buffer_id) {
PluginDispatcher* dispatcher = PluginDispatcher::GetForResource(this);
if (!dispatcher)
return false;
InstanceData* data = dispatcher->GetInstanceData(host_resource().instance());
DCHECK(data);
command_buffer_ = std::make_unique<PpapiCommandBufferProxy>(
host_resource(), &data->flush_info, dispatcher, capabilities,
gl_capabilities, std::move(shared_state), command_buffer_id);
return CreateGLES2Impl(share_gles2);
}
PP_Bool Graphics3D::SetGetBuffer(int32_t /* transfer_buffer_id */) {
return PP_FALSE;
}
PP_Bool Graphics3D::Flush(int32_t put_offset) {
return PP_FALSE;
}
scoped_refptr<gpu::Buffer> Graphics3D::CreateTransferBuffer(
uint32_t size,
int32_t* id) {
*id = -1;
return nullptr;
}
PP_Bool Graphics3D::DestroyTransferBuffer(int32_t id) {
return PP_FALSE;
}
gpu::CommandBuffer::State Graphics3D::WaitForTokenInRange(int32_t start,
int32_t end) {
return GetErrorState();
}
gpu::CommandBuffer::State Graphics3D::WaitForGetOffsetInRange(
uint32_t set_get_buffer_count,
int32_t start,
int32_t end) {
return GetErrorState();
}
void Graphics3D::EnsureWorkVisible() {
NOTREACHED();
}
void Graphics3D::ResolveAndDetachFramebuffer() {
NOTREACHED();
}
gpu::CommandBuffer* Graphics3D::GetCommandBuffer() {
return command_buffer_.get();
}
gpu::GpuControl* Graphics3D::GetGpuControl() {
return command_buffer_.get();
}
int32_t Graphics3D::DoSwapBuffers(const gpu::SyncToken& sync_token,
const gfx::Size& size) {
// A valid sync token would indicate a swap buffer already happened somehow.
DCHECK(!sync_token.HasData());
gpu::gles2::GLES2Implementation* gl = gles2_impl();
// Flush current GL commands.
gl->ShallowFlushCHROMIUM();
// Make sure we resolved and detached our frame buffer
PluginDispatcher::GetForResource(this)->Send(
new PpapiHostMsg_PPBGraphics3D_ResolveAndDetachFramebuffer(
API_ID_PPB_GRAPHICS_3D, host_resource()));
gpu::SyncToken new_sync_token;
gl->GenSyncTokenCHROMIUM(new_sync_token.GetData());
IPC::Message* msg = new PpapiHostMsg_PPBGraphics3D_SwapBuffers(
API_ID_PPB_GRAPHICS_3D, host_resource(), new_sync_token, size);
msg->set_unblock(true);
PluginDispatcher::GetForResource(this)->Send(msg);
return PP_OK_COMPLETIONPENDING;
}
void Graphics3D::DoResize(gfx::Size size) {
// Flush current GL commands.
gles2_impl()->ShallowFlushCHROMIUM();
PluginDispatcher::GetForResource(this)->Send(
new PpapiHostMsg_PPBGraphics3D_Resize(API_ID_PPB_GRAPHICS_3D,
host_resource(), size));
}
PPB_Graphics3D_Proxy::PPB_Graphics3D_Proxy(Dispatcher* dispatcher)
: InterfaceProxy(dispatcher),
callback_factory_(this) {
}
PPB_Graphics3D_Proxy::~PPB_Graphics3D_Proxy() {
}
// static
PP_Resource PPB_Graphics3D_Proxy::CreateProxyResource(
PP_Instance instance,
PP_Resource share_context,
const int32_t* attrib_list) {
PluginDispatcher* dispatcher = PluginDispatcher::GetForInstance(instance);
if (!dispatcher)
return PP_ERROR_BADARGUMENT;
HostResource share_host;
gpu::gles2::GLES2Implementation* share_gles2 = nullptr;
if (share_context != 0) {
EnterResourceNoLock<PPB_Graphics3D_API> enter(share_context, true);
if (enter.failed())
return PP_ERROR_BADARGUMENT;
PPB_Graphics3D_Shared* share_graphics =
static_cast<PPB_Graphics3D_Shared*>(enter.object());
share_host = share_graphics->host_resource();
share_gles2 = share_graphics->gles2_impl();
}
Graphics3DContextAttribs attrib_helper;
if (attrib_list) {
for (const int32_t* attr = attrib_list; attr[0] != PP_GRAPHICS3DATTRIB_NONE;
attr += 2) {
int32_t key = attr[0];
int32_t value = attr[1];
switch (key) {
case PP_GRAPHICS3DATTRIB_ALPHA_SIZE:
attrib_helper.alpha_size = value;
break;
case PP_GRAPHICS3DATTRIB_DEPTH_SIZE:
attrib_helper.depth_size = value;
break;
case PP_GRAPHICS3DATTRIB_STENCIL_SIZE:
attrib_helper.stencil_size = value;
break;
case PP_GRAPHICS3DATTRIB_SAMPLES:
attrib_helper.samples = value;
break;
case PP_GRAPHICS3DATTRIB_SAMPLE_BUFFERS:
attrib_helper.sample_buffers = value;
break;
case PP_GRAPHICS3DATTRIB_SWAP_BEHAVIOR:
attrib_helper.buffer_preserved =
value == PP_GRAPHICS3DATTRIB_BUFFER_PRESERVED;
break;
case PP_GRAPHICS3DATTRIB_WIDTH:
attrib_helper.offscreen_framebuffer_size.set_width(value);
break;
case PP_GRAPHICS3DATTRIB_HEIGHT:
attrib_helper.offscreen_framebuffer_size.set_height(value);
break;
case PP_GRAPHICS3DATTRIB_SINGLE_BUFFER:
attrib_helper.single_buffer = !!value;
break;
// These attributes are valid, but ignored.
case PP_GRAPHICS3DATTRIB_RED_SIZE:
case PP_GRAPHICS3DATTRIB_BLUE_SIZE:
case PP_GRAPHICS3DATTRIB_GREEN_SIZE:
case PP_GRAPHICS3DATTRIB_GPU_PREFERENCE:
break;
default:
DLOG(ERROR) << "Invalid context creation attribute: " << attr[0];
return 0;
}
}
}
HostResource result;
gpu::Capabilities capabilities;
gpu::GLCapabilities gl_capabilities;
ppapi::proxy::SerializedHandle shared_state;
gpu::CommandBufferId command_buffer_id;
dispatcher->Send(new PpapiHostMsg_PPBGraphics3D_Create(
API_ID_PPB_GRAPHICS_3D, instance, share_host, attrib_helper, &result,
&capabilities, &gl_capabilities, &shared_state, &command_buffer_id));
if (result.is_null())
return 0;
scoped_refptr<Graphics3D> graphics_3d(
new Graphics3D(result, attrib_helper.offscreen_framebuffer_size));
if (!graphics_3d->Init(share_gles2, capabilities, gl_capabilities,
std::move(shared_state), command_buffer_id)) {
return 0;
}
return graphics_3d->GetReference();
}
bool PPB_Graphics3D_Proxy::OnMessageReceived(const IPC::Message& msg) {
bool handled = true;
IPC_BEGIN_MESSAGE_MAP(PPB_Graphics3D_Proxy, msg)
#if !BUILDFLAG(IS_NACL)
IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBGraphics3D_Create,
OnMsgCreate)
IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBGraphics3D_SetGetBuffer,
OnMsgSetGetBuffer)
IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBGraphics3D_WaitForTokenInRange,
OnMsgWaitForTokenInRange)
IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBGraphics3D_WaitForGetOffsetInRange,
OnMsgWaitForGetOffsetInRange)
IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBGraphics3D_AsyncFlush, OnMsgAsyncFlush)
IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBGraphics3D_CreateTransferBuffer,
OnMsgCreateTransferBuffer)
IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBGraphics3D_DestroyTransferBuffer,
OnMsgDestroyTransferBuffer)
IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBGraphics3D_SwapBuffers,
OnMsgSwapBuffers)
IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBGraphics3D_ResolveAndDetachFramebuffer,
OnMsgResolveAndDetachFramebuffer)
IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBGraphics3D_Resize, OnMsgResize)
IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBGraphics3D_EnsureWorkVisible,
OnMsgEnsureWorkVisible)
#endif // !BUILDFLAG(IS_NACL)
IPC_MESSAGE_HANDLER(PpapiMsg_PPBGraphics3D_SwapBuffersACK,
OnMsgSwapBuffersACK)
IPC_MESSAGE_UNHANDLED(handled = false)
IPC_END_MESSAGE_MAP()
// FIXME(brettw) handle bad messages!
return handled;
}
#if !BUILDFLAG(IS_NACL)
void PPB_Graphics3D_Proxy::OnMsgCreate(
PP_Instance instance,
HostResource share_context,
const Graphics3DContextAttribs& context_attribs,
HostResource* result,
gpu::Capabilities* capabilities,
gpu::GLCapabilities* gl_capabilities,
SerializedHandle* shared_state,
gpu::CommandBufferId* command_buffer_id) {
shared_state->set_null_shmem_region();
thunk::EnterResourceCreation enter(instance);
if (!enter.succeeded())
return;
const base::UnsafeSharedMemoryRegion* region = nullptr;
result->SetHostResource(
instance, enter.functions()->CreateGraphics3DRaw(
instance, share_context.host_resource(), context_attribs,
capabilities, gl_capabilities, ®ion, command_buffer_id));
if (!result->is_null()) {
shared_state->set_shmem_region(
base::UnsafeSharedMemoryRegion::TakeHandleForSerialization(
TransportSHMHandle(dispatcher(), *region)));
}
}
void PPB_Graphics3D_Proxy::OnMsgSetGetBuffer(const HostResource& context,
int32_t transfer_buffer_id) {
EnterHostFromHostResource<PPB_Graphics3D_API> enter(context);
if (enter.succeeded())
enter.object()->SetGetBuffer(transfer_buffer_id);
}
void PPB_Graphics3D_Proxy::OnMsgWaitForTokenInRange(
const HostResource& context,
int32_t start,
int32_t end,
gpu::CommandBuffer::State* state,
bool* success) {
EnterHostFromHostResource<PPB_Graphics3D_API> enter(context);
if (enter.failed()) {
*success = false;
return;
}
*state = enter.object()->WaitForTokenInRange(start, end);
*success = true;
}
void PPB_Graphics3D_Proxy::OnMsgWaitForGetOffsetInRange(
const HostResource& context,
uint32_t set_get_buffer_count,
int32_t start,
int32_t end,
gpu::CommandBuffer::State* state,
bool* success) {
EnterHostFromHostResource<PPB_Graphics3D_API> enter(context);
if (enter.failed()) {
*success = false;
return;
}
*state =
enter.object()->WaitForGetOffsetInRange(set_get_buffer_count, start, end);
*success = true;
}
void PPB_Graphics3D_Proxy::OnMsgAsyncFlush(const HostResource& context,
int32_t put_offset) {
EnterHostFromHostResource<PPB_Graphics3D_API> enter(context);
if (enter.succeeded())
enter.object()->Flush(put_offset);
}
void PPB_Graphics3D_Proxy::OnMsgCreateTransferBuffer(
const HostResource& context,
uint32_t size,
int32_t* id,
SerializedHandle* transfer_buffer) {
transfer_buffer->set_null_shmem_region();
EnterHostFromHostResource<PPB_Graphics3D_API> enter(context);
if (enter.succeeded()) {
scoped_refptr<gpu::Buffer> buffer =
enter.object()->CreateTransferBuffer(size, id);
if (!buffer.get())
return;
gpu::SharedMemoryBufferBacking* backing =
static_cast<gpu::SharedMemoryBufferBacking*>(buffer->backing());
DCHECK(backing && backing->shared_memory_region().IsValid());
transfer_buffer->set_shmem_region(
base::UnsafeSharedMemoryRegion::TakeHandleForSerialization(
TransportSHMHandle(dispatcher(), backing->shared_memory_region())));
} else {
*id = -1;
}
}
void PPB_Graphics3D_Proxy::OnMsgDestroyTransferBuffer(
const HostResource& context,
int32_t id) {
EnterHostFromHostResource<PPB_Graphics3D_API> enter(context);
if (enter.succeeded())
enter.object()->DestroyTransferBuffer(id);
}
void PPB_Graphics3D_Proxy::OnMsgSwapBuffers(const HostResource& context,
const gpu::SyncToken& sync_token,
const gfx::Size& size) {
EnterHostFromHostResourceForceCallback<PPB_Graphics3D_API> enter(
context, callback_factory_,
&PPB_Graphics3D_Proxy::SendSwapBuffersACKToPlugin, context);
if (enter.succeeded())
enter.SetResult(enter.object()->SwapBuffersWithSyncToken(
enter.callback(), sync_token, size));
}
void PPB_Graphics3D_Proxy::OnMsgResolveAndDetachFramebuffer(
const HostResource& context) {
EnterHostFromHostResource<PPB_Graphics3D_API> enter(context);
if (enter.succeeded())
enter.object()->ResolveAndDetachFramebuffer();
}
void PPB_Graphics3D_Proxy::OnMsgResize(const HostResource& context,
gfx::Size size) {
EnterHostFromHostResource<PPB_Graphics3D_API> enter(context);
if (enter.succeeded())
enter.object()->ResizeBuffers(size.width(), size.height());
}
void PPB_Graphics3D_Proxy::OnMsgEnsureWorkVisible(const HostResource& context) {
EnterHostFromHostResource<PPB_Graphics3D_API> enter(context);
if (enter.succeeded())
enter.object()->EnsureWorkVisible();
}
#endif // !BUILDFLAG(IS_NACL)
void PPB_Graphics3D_Proxy::OnMsgSwapBuffersACK(const HostResource& resource,
int32_t pp_error) {
EnterPluginFromHostResource<PPB_Graphics3D_API> enter(resource);
if (enter.succeeded())
static_cast<Graphics3D*>(enter.object())->SwapBuffersACK(pp_error);
}
#if !BUILDFLAG(IS_NACL)
void PPB_Graphics3D_Proxy::SendSwapBuffersACKToPlugin(
int32_t result,
const HostResource& context) {
dispatcher()->Send(new PpapiMsg_PPBGraphics3D_SwapBuffersACK(
API_ID_PPB_GRAPHICS_3D, context, result));
}
#endif // !BUILDFLAG(IS_NACL)
} // namespace proxy
} // namespace ppapi