Home | History | Annotate | Download | only in pepper
      1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "content/renderer/pepper/ppb_graphics_3d_impl.h"
      6 
      7 #include "base/bind.h"
      8 #include "base/command_line.h"
      9 #include "base/message_loop/message_loop.h"
     10 #include "base/strings/utf_string_conversions.h"
     11 #include "content/public/common/content_switches.h"
     12 #include "content/renderer/pepper/host_globals.h"
     13 #include "content/renderer/pepper/pepper_platform_context_3d.h"
     14 #include "content/renderer/pepper/pepper_plugin_instance_impl.h"
     15 #include "content/renderer/pepper/plugin_module.h"
     16 #include "content/renderer/render_view_impl.h"
     17 #include "gpu/command_buffer/client/gles2_implementation.h"
     18 #include "ppapi/c/ppp_graphics_3d.h"
     19 #include "ppapi/thunk/enter.h"
     20 #include "third_party/WebKit/public/platform/WebString.h"
     21 #include "third_party/WebKit/public/web/WebConsoleMessage.h"
     22 #include "third_party/WebKit/public/web/WebDocument.h"
     23 #include "third_party/WebKit/public/web/WebElement.h"
     24 #include "third_party/WebKit/public/web/WebFrame.h"
     25 #include "third_party/WebKit/public/web/WebPluginContainer.h"
     26 #include "webkit/common/webpreferences.h"
     27 
     28 using ppapi::thunk::EnterResourceNoLock;
     29 using ppapi::thunk::PPB_Graphics3D_API;
     30 using blink::WebConsoleMessage;
     31 using blink::WebFrame;
     32 using blink::WebPluginContainer;
     33 using blink::WebString;
     34 
     35 namespace content {
     36 
     37 namespace {
     38 const int32 kCommandBufferSize = 1024 * 1024;
     39 const int32 kTransferBufferSize = 1024 * 1024;
     40 
     41 PP_Bool ShmToHandle(base::SharedMemory* shm,
     42                     size_t size,
     43                     int* shm_handle,
     44                     uint32_t* shm_size) {
     45   if (!shm || !shm_handle || !shm_size)
     46     return PP_FALSE;
     47 #if defined(OS_POSIX)
     48   *shm_handle = shm->handle().fd;
     49 #elif defined(OS_WIN)
     50   *shm_handle = reinterpret_cast<int>(shm->handle());
     51 #else
     52   #error "Platform not supported."
     53 #endif
     54   *shm_size = size;
     55   return PP_TRUE;
     56 }
     57 
     58 }  // namespace.
     59 
     60 PPB_Graphics3D_Impl::PPB_Graphics3D_Impl(PP_Instance instance)
     61     : PPB_Graphics3D_Shared(instance),
     62       bound_to_instance_(false),
     63       commit_pending_(false),
     64       weak_ptr_factory_(this) {
     65 }
     66 
     67 PPB_Graphics3D_Impl::~PPB_Graphics3D_Impl() {
     68   DestroyGLES2Impl();
     69 }
     70 
     71 // static
     72 PP_Bool PPB_Graphics3D_Impl::IsGpuBlacklisted() {
     73   CommandLine* command_line = CommandLine::ForCurrentProcess();
     74   if (command_line)
     75     return PP_FromBool(command_line->HasSwitch(switches::kDisablePepper3d));
     76   return PP_TRUE;
     77 }
     78 
     79 // static
     80 PP_Resource PPB_Graphics3D_Impl::Create(PP_Instance instance,
     81                                         PP_Resource share_context,
     82                                         const int32_t* attrib_list) {
     83   PPB_Graphics3D_API* share_api = NULL;
     84   if (IsGpuBlacklisted())
     85     return 0;
     86   if (share_context) {
     87     EnterResourceNoLock<PPB_Graphics3D_API> enter(share_context, true);
     88     if (enter.failed())
     89       return 0;
     90     share_api = enter.object();
     91   }
     92   scoped_refptr<PPB_Graphics3D_Impl> graphics_3d(
     93       new PPB_Graphics3D_Impl(instance));
     94   if (!graphics_3d->Init(share_api, attrib_list))
     95     return 0;
     96   return graphics_3d->GetReference();
     97 }
     98 
     99 // static
    100 PP_Resource PPB_Graphics3D_Impl::CreateRaw(PP_Instance instance,
    101                                            PP_Resource share_context,
    102                                            const int32_t* attrib_list) {
    103   PPB_Graphics3D_API* share_api = NULL;
    104   if (IsGpuBlacklisted())
    105     return 0;
    106   if (share_context) {
    107     EnterResourceNoLock<PPB_Graphics3D_API> enter(share_context, true);
    108     if (enter.failed())
    109       return 0;
    110     share_api = enter.object();
    111   }
    112   scoped_refptr<PPB_Graphics3D_Impl> graphics_3d(
    113       new PPB_Graphics3D_Impl(instance));
    114   if (!graphics_3d->InitRaw(share_api, attrib_list))
    115     return 0;
    116   return graphics_3d->GetReference();
    117 }
    118 
    119 PP_Bool PPB_Graphics3D_Impl::SetGetBuffer(int32_t transfer_buffer_id) {
    120   GetCommandBuffer()->SetGetBuffer(transfer_buffer_id);
    121   return PP_TRUE;
    122 }
    123 
    124 gpu::CommandBuffer::State PPB_Graphics3D_Impl::GetState() {
    125   return GetCommandBuffer()->GetState();
    126 }
    127 
    128 int32_t PPB_Graphics3D_Impl::CreateTransferBuffer(uint32_t size) {
    129   int32_t id = -1;
    130   GetCommandBuffer()->CreateTransferBuffer(size, &id);
    131   return id;
    132 }
    133 
    134 PP_Bool PPB_Graphics3D_Impl::DestroyTransferBuffer(int32_t id) {
    135   GetCommandBuffer()->DestroyTransferBuffer(id);
    136   return PP_TRUE;
    137 }
    138 
    139 PP_Bool PPB_Graphics3D_Impl::GetTransferBuffer(int32_t id,
    140                                                int* shm_handle,
    141                                                uint32_t* shm_size) {
    142   gpu::Buffer buffer = GetCommandBuffer()->GetTransferBuffer(id);
    143   return ShmToHandle(buffer.shared_memory, buffer.size, shm_handle, shm_size);
    144 }
    145 
    146 PP_Bool PPB_Graphics3D_Impl::Flush(int32_t put_offset) {
    147   GetCommandBuffer()->Flush(put_offset);
    148   return PP_TRUE;
    149 }
    150 
    151 gpu::CommandBuffer::State PPB_Graphics3D_Impl::FlushSync(int32_t put_offset) {
    152   gpu::CommandBuffer::State state = GetCommandBuffer()->GetState();
    153   return GetCommandBuffer()->FlushSync(put_offset, state.get_offset);
    154 }
    155 
    156 gpu::CommandBuffer::State PPB_Graphics3D_Impl::FlushSyncFast(
    157     int32_t put_offset,
    158     int32_t last_known_get) {
    159   return GetCommandBuffer()->FlushSync(put_offset, last_known_get);
    160 }
    161 
    162 uint32_t PPB_Graphics3D_Impl::InsertSyncPoint() {
    163   return platform_context_->GetGpuControl()->InsertSyncPoint();
    164 }
    165 
    166 bool PPB_Graphics3D_Impl::BindToInstance(bool bind) {
    167   bound_to_instance_ = bind;
    168   return true;
    169 }
    170 
    171 bool PPB_Graphics3D_Impl::IsOpaque() {
    172   return platform_context_->IsOpaque();
    173 }
    174 
    175 void PPB_Graphics3D_Impl::ViewInitiatedPaint() {
    176   commit_pending_ = false;
    177 
    178   if (HasPendingSwap())
    179     SwapBuffersACK(PP_OK);
    180 }
    181 
    182 void PPB_Graphics3D_Impl::ViewFlushedPaint() {
    183 }
    184 
    185 gpu::CommandBuffer* PPB_Graphics3D_Impl::GetCommandBuffer() {
    186   return platform_context_->GetCommandBuffer();
    187 }
    188 
    189 gpu::GpuControl* PPB_Graphics3D_Impl::GetGpuControl() {
    190   return platform_context_->GetGpuControl();
    191 }
    192 
    193 int32 PPB_Graphics3D_Impl::DoSwapBuffers() {
    194   // We do not have a GLES2 implementation when using an OOP proxy.
    195   // The plugin-side proxy is responsible for adding the SwapBuffers command
    196   // to the command buffer in that case.
    197   if (gles2_impl())
    198     gles2_impl()->SwapBuffers();
    199 
    200   // Since the backing texture has been updated, a new sync point should be
    201   // inserted.
    202   platform_context_->InsertSyncPointForBackingMailbox();
    203 
    204   if (bound_to_instance_) {
    205     // If we are bound to the instance, we need to ask the compositor
    206     // to commit our backing texture so that the graphics appears on the page.
    207     // When the backing texture will be committed we get notified via
    208     // ViewFlushedPaint().
    209     //
    210     // Don't need to check for NULL from GetPluginInstance since when we're
    211     // bound, we know our instance is valid.
    212     HostGlobals::Get()->GetInstance(pp_instance())->CommitBackingTexture();
    213     commit_pending_ = true;
    214   } else {
    215     // Wait for the command to complete on the GPU to allow for throttling.
    216     platform_context_->Echo(base::Bind(&PPB_Graphics3D_Impl::OnSwapBuffers,
    217                                        weak_ptr_factory_.GetWeakPtr()));
    218   }
    219 
    220 
    221   return PP_OK_COMPLETIONPENDING;
    222 }
    223 
    224 bool PPB_Graphics3D_Impl::Init(PPB_Graphics3D_API* share_context,
    225                                const int32_t* attrib_list) {
    226   if (!InitRaw(share_context, attrib_list))
    227     return false;
    228 
    229   gpu::CommandBuffer* command_buffer = GetCommandBuffer();
    230   if (!command_buffer->Initialize())
    231     return false;
    232 
    233   gpu::gles2::GLES2Implementation* share_gles2 = NULL;
    234   if (share_context) {
    235     share_gles2 =
    236         static_cast<PPB_Graphics3D_Shared*>(share_context)->gles2_impl();
    237   }
    238 
    239   return CreateGLES2Impl(kCommandBufferSize, kTransferBufferSize,
    240                          share_gles2);
    241 }
    242 
    243 bool PPB_Graphics3D_Impl::InitRaw(PPB_Graphics3D_API* share_context,
    244                                   const int32_t* attrib_list) {
    245   PepperPluginInstanceImpl* plugin_instance =
    246       HostGlobals::Get()->GetInstance(pp_instance());
    247   if (!plugin_instance)
    248     return false;
    249 
    250   PlatformContext3D* share_platform_context = NULL;
    251   if (share_context) {
    252     PPB_Graphics3D_Impl* share_graphics =
    253         static_cast<PPB_Graphics3D_Impl*>(share_context);
    254     share_platform_context = share_graphics->platform_context();
    255   }
    256 
    257   // If accelerated compositing of plugins is disabled, fail to create a 3D
    258   // context, because it won't be visible. This allows graceful fallback in the
    259   // modules.
    260   const WebPreferences& prefs = static_cast<RenderViewImpl*>(plugin_instance->
    261       GetRenderView())->webkit_preferences();
    262   if (!prefs.accelerated_compositing_for_plugins_enabled)
    263     return false;
    264 
    265   platform_context_.reset(new PlatformContext3D);
    266   if (!platform_context_)
    267     return false;
    268 
    269   if (!platform_context_->Init(attrib_list, share_platform_context))
    270     return false;
    271 
    272   platform_context_->SetContextLostCallback(
    273       base::Bind(&PPB_Graphics3D_Impl::OnContextLost,
    274                  weak_ptr_factory_.GetWeakPtr()));
    275 
    276   platform_context_->SetOnConsoleMessageCallback(
    277       base::Bind(&PPB_Graphics3D_Impl::OnConsoleMessage,
    278                  weak_ptr_factory_.GetWeakPtr()));
    279   return true;
    280 }
    281 
    282 void PPB_Graphics3D_Impl::OnConsoleMessage(const std::string& message,
    283                                            int id) {
    284   if (!bound_to_instance_)
    285     return;
    286   WebPluginContainer* container =
    287       HostGlobals::Get()->GetInstance(pp_instance())->container();
    288   if (!container)
    289     return;
    290   WebFrame* frame = container->element().document().frame();
    291   if (!frame)
    292     return;
    293   WebConsoleMessage console_message = WebConsoleMessage(
    294       WebConsoleMessage::LevelError, WebString(UTF8ToUTF16(message)));
    295   frame->addMessageToConsole(console_message);
    296 }
    297 
    298 void PPB_Graphics3D_Impl::OnSwapBuffers() {
    299   if (HasPendingSwap()) {
    300     // If we're off-screen, no need to trigger and wait for compositing.
    301     // Just send the swap-buffers ACK to the plugin immediately.
    302     commit_pending_ = false;
    303     SwapBuffersACK(PP_OK);
    304   }
    305 }
    306 
    307 void PPB_Graphics3D_Impl::OnContextLost() {
    308   // Don't need to check for NULL from GetPluginInstance since when we're
    309   // bound, we know our instance is valid.
    310   if (bound_to_instance_) {
    311     HostGlobals::Get()->GetInstance(pp_instance())->BindGraphics(
    312         pp_instance(), 0);
    313   }
    314 
    315   // Send context lost to plugin. This may have been caused by a PPAPI call, so
    316   // avoid re-entering.
    317   base::MessageLoop::current()->PostTask(
    318       FROM_HERE,
    319       base::Bind(&PPB_Graphics3D_Impl::SendContextLost,
    320                  weak_ptr_factory_.GetWeakPtr()));
    321 }
    322 
    323 void PPB_Graphics3D_Impl::SendContextLost() {
    324   // By the time we run this, the instance may have been deleted, or in the
    325   // process of being deleted. Even in the latter case, we don't want to send a
    326   // callback after DidDestroy.
    327   PepperPluginInstanceImpl* instance =
    328       HostGlobals::Get()->GetInstance(pp_instance());
    329   if (!instance || !instance->container())
    330     return;
    331 
    332   // This PPB_Graphics3D_Impl could be deleted during the call to
    333   // GetPluginInterface (which sends a sync message in some cases). We still
    334   // send the Graphics3DContextLost to the plugin; the instance may care about
    335   // that event even though this context has been destroyed.
    336   PP_Instance this_pp_instance = pp_instance();
    337   const PPP_Graphics3D* ppp_graphics_3d =
    338       static_cast<const PPP_Graphics3D*>(
    339           instance->module()->GetPluginInterface(
    340               PPP_GRAPHICS_3D_INTERFACE));
    341   // We have to check *again* that the instance exists, because it could have
    342   // been deleted during GetPluginInterface(). Even the PluginModule could be
    343   // deleted, but in that case, the instance should also be gone, so the
    344   // GetInstance check covers both cases.
    345   if (ppp_graphics_3d && HostGlobals::Get()->GetInstance(this_pp_instance))
    346     ppp_graphics_3d->Graphics3DContextLost(this_pp_instance);
    347 }
    348 
    349 }  // namespace content
    350