blob: cdd7d6c78f4c48f76b77b81696682ae49289c2e4 [file] [log] [blame]
// Copyright (c) 2011 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 "webkit/plugins/ppapi/ppb_graphics_3d_impl.h"
#include "base/message_loop.h"
#include "gpu/command_buffer/client/gles2_implementation.h"
#include "webkit/plugins/ppapi/plugin_module.h"
#include "webkit/plugins/ppapi/ppapi_plugin_instance.h"
#include "webkit/plugins/ppapi/resource_helper.h"
using ppapi::thunk::PPB_Graphics3D_API;
namespace webkit {
namespace ppapi {
namespace {
const int32 kCommandBufferSize = 1024 * 1024;
const int32 kTransferBufferSize = 1024 * 1024;
PP_Bool ShmToHandle(base::SharedMemory* shm,
size_t size,
int* shm_handle,
uint32_t* shm_size) {
if (!shm || !shm_handle || !shm_size)
return PP_FALSE;
#if defined(OS_POSIX)
*shm_handle = shm->handle().fd;
#elif defined(OS_WIN)
*shm_handle = reinterpret_cast<int>(shm->handle());
#else
#error "Platform not supported."
#endif
*shm_size = size;
return PP_TRUE;
}
PP_Graphics3DTrustedState PPStateFromGPUState(
const gpu::CommandBuffer::State& s) {
PP_Graphics3DTrustedState state = {
s.num_entries,
s.get_offset,
s.put_offset,
s.token,
static_cast<PPB_Graphics3DTrustedError>(s.error),
s.generation
};
return state;
}
} // namespace.
PPB_Graphics3D_Impl::PPB_Graphics3D_Impl(PP_Instance instance)
: Resource(instance),
bound_to_instance_(false),
commit_pending_(false),
callback_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)),
method_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) {
}
PPB_Graphics3D_Impl::~PPB_Graphics3D_Impl() {
DestroyGLES2Impl();
}
// static
PP_Resource PPB_Graphics3D_Impl::Create(PP_Instance instance,
PP_Resource share_context,
const int32_t* attrib_list) {
scoped_refptr<PPB_Graphics3D_Impl> graphics_3d(
new PPB_Graphics3D_Impl(instance));
if (!graphics_3d->Init(share_context, attrib_list))
return 0;
return graphics_3d->GetReference();
}
PP_Resource PPB_Graphics3D_Impl::CreateRaw(PP_Instance instance,
PP_Resource share_context,
const int32_t* attrib_list) {
scoped_refptr<PPB_Graphics3D_Impl> graphics_3d(
new PPB_Graphics3D_Impl(instance));
if (!graphics_3d->InitRaw(share_context, attrib_list))
return 0;
return graphics_3d->GetReference();
}
PPB_Graphics3D_API* PPB_Graphics3D_Impl::AsPPB_Graphics3D_API() {
return this;
}
PP_Bool PPB_Graphics3D_Impl::InitCommandBuffer(int32_t size) {
return PP_FromBool(GetCommandBuffer()->Initialize(size));
}
PP_Bool PPB_Graphics3D_Impl::GetRingBuffer(int* shm_handle,
uint32_t* shm_size) {
gpu::Buffer buffer = GetCommandBuffer()->GetRingBuffer();
return ShmToHandle(buffer.shared_memory, buffer.size, shm_handle, shm_size);
}
PP_Graphics3DTrustedState PPB_Graphics3D_Impl::GetState() {
return PPStateFromGPUState(GetCommandBuffer()->GetState());
}
int32_t PPB_Graphics3D_Impl::CreateTransferBuffer(uint32_t size) {
return GetCommandBuffer()->CreateTransferBuffer(size, -1);
}
PP_Bool PPB_Graphics3D_Impl::DestroyTransferBuffer(int32_t id) {
GetCommandBuffer()->DestroyTransferBuffer(id);
return PP_TRUE;
}
PP_Bool PPB_Graphics3D_Impl::GetTransferBuffer(int32_t id,
int* shm_handle,
uint32_t* shm_size) {
gpu::Buffer buffer = GetCommandBuffer()->GetTransferBuffer(id);
return ShmToHandle(buffer.shared_memory, buffer.size, shm_handle, shm_size);
}
PP_Bool PPB_Graphics3D_Impl::Flush(int32_t put_offset) {
GetCommandBuffer()->Flush(put_offset);
return PP_TRUE;
}
PP_Graphics3DTrustedState PPB_Graphics3D_Impl::FlushSync(int32_t put_offset) {
gpu::CommandBuffer::State state = GetCommandBuffer()->GetState();
return PPStateFromGPUState(
GetCommandBuffer()->FlushSync(put_offset, state.get_offset));
}
PP_Graphics3DTrustedState PPB_Graphics3D_Impl::FlushSyncFast(
int32_t put_offset,
int32_t last_known_get) {
return PPStateFromGPUState(
GetCommandBuffer()->FlushSync(put_offset, last_known_get));
}
bool PPB_Graphics3D_Impl::BindToInstance(bool bind) {
bound_to_instance_ = bind;
return true;
}
unsigned int PPB_Graphics3D_Impl::GetBackingTextureId() {
return platform_context_->GetBackingTextureId();
}
void PPB_Graphics3D_Impl::ViewInitiatedPaint() {
}
void PPB_Graphics3D_Impl::ViewFlushedPaint() {
commit_pending_ = false;
if (HasPendingSwap())
SwapBuffersACK(PP_OK);
}
gpu::CommandBuffer* PPB_Graphics3D_Impl::GetCommandBuffer() {
return platform_context_->GetCommandBuffer();
}
int32 PPB_Graphics3D_Impl::DoSwapBuffers() {
// We do not have a GLES2 implementation when using an OOP proxy.
// The plugin-side proxy is responsible for adding the SwapBuffers command
// to the command buffer in that case.
if (gles2_impl())
gles2_impl()->SwapBuffers();
platform_context_->Echo(method_factory_.NewRunnableMethod(
&PPB_Graphics3D_Impl::OnSwapBuffers));
return PP_OK_COMPLETIONPENDING;
}
bool PPB_Graphics3D_Impl::Init(PP_Resource share_context,
const int32_t* attrib_list) {
if (!InitRaw(share_context, attrib_list))
return false;
gpu::CommandBuffer* command_buffer = GetCommandBuffer();
if (!command_buffer->Initialize(kCommandBufferSize))
return false;
return CreateGLES2Impl(kCommandBufferSize, kTransferBufferSize);
}
bool PPB_Graphics3D_Impl::InitRaw(PP_Resource share_context,
const int32_t* attrib_list) {
PluginInstance* plugin_instance = ResourceHelper::GetPluginInstance(this);
if (!plugin_instance)
return false;
// TODO(alokp): Support shared context.
DCHECK_EQ(0, share_context);
if (share_context != 0)
return false;
platform_context_.reset(plugin_instance->CreateContext3D());
if (!platform_context_.get())
return false;
if (!platform_context_->Init(attrib_list))
return false;
platform_context_->SetContextLostCallback(
callback_factory_.NewCallback(&PPB_Graphics3D_Impl::OnContextLost));
return true;
}
void PPB_Graphics3D_Impl::OnSwapBuffers() {
if (bound_to_instance_) {
// If we are bound to the instance, we need to ask the compositor
// to commit our backing texture so that the graphics appears on the page.
// When the backing texture will be committed we get notified via
// ViewFlushedPaint().
//
// Don't need to check for NULL from GetPluginInstance since when we're
// bound, we know our instance is valid.
ResourceHelper::GetPluginInstance(this)->CommitBackingTexture();
commit_pending_ = true;
} else if (HasPendingSwap()) {
// If we're off-screen, no need to trigger and wait for compositing.
// Just send the swap-buffers ACK to the plugin immediately.
commit_pending_ = false;
SwapBuffersACK(PP_OK);
}
}
void PPB_Graphics3D_Impl::OnContextLost() {
// Don't need to check for NULL from GetPluginInstance since when we're
// bound, we know our instance is valid.
if (bound_to_instance_)
ResourceHelper::GetPluginInstance(this)->BindGraphics(pp_instance(), 0);
// Send context lost to plugin. This may have been caused by a PPAPI call, so
// avoid re-entering.
MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod(
this, &PPB_Graphics3D_Impl::SendContextLost));
}
void PPB_Graphics3D_Impl::SendContextLost() {
// By the time we run this, the instance may have been deleted, or in the
// process of being deleted. Even in the latter case, we don't want to send a
// callback after DidDestroy.
PluginInstance* instance = ResourceHelper::GetPluginInstance(this);
if (!instance || !instance->container())
return;
const PPP_Graphics3D* ppp_graphics_3d =
static_cast<const PPP_Graphics3D*>(
instance->module()->GetPluginInterface(
PPP_GRAPHICS_3D_INTERFACE));
if (ppp_graphics_3d)
ppp_graphics_3d->Graphics3DContextLost(pp_instance());
}
} // namespace ppapi
} // namespace webkit