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 WebKit::WebConsoleMessage;
     31 using WebKit::WebFrame;
     32 using WebKit::WebPluginContainer;
     33 using WebKit::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 GetCommandBuffer()->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::ViewWillInitiatePaint() {
    176 }
    177 
    178 void PPB_Graphics3D_Impl::ViewInitiatedPaint() {
    179   commit_pending_ = false;
    180 
    181   if (HasPendingSwap())
    182     SwapBuffersACK(PP_OK);
    183 }
    184 
    185 void PPB_Graphics3D_Impl::ViewFlushedPaint() {
    186 }
    187 
    188 gpu::CommandBuffer* PPB_Graphics3D_Impl::GetCommandBuffer() {
    189   return platform_context_->GetCommandBuffer();
    190 }
    191 
    192 int32 PPB_Graphics3D_Impl::DoSwapBuffers() {
    193   // We do not have a GLES2 implementation when using an OOP proxy.
    194   // The plugin-side proxy is responsible for adding the SwapBuffers command
    195   // to the command buffer in that case.
    196   if (gles2_impl())
    197     gles2_impl()->SwapBuffers();
    198 
    199   if (bound_to_instance_) {
    200     // If we are bound to the instance, we need to ask the compositor
    201     // to commit our backing texture so that the graphics appears on the page.
    202     // When the backing texture will be committed we get notified via
    203     // ViewFlushedPaint().
    204     //
    205     // Don't need to check for NULL from GetPluginInstance since when we're
    206     // bound, we know our instance is valid.
    207     HostGlobals::Get()->GetInstance(pp_instance())->CommitBackingTexture();
    208     commit_pending_ = true;
    209   } else {
    210     // Wait for the command to complete on the GPU to allow for throttling.
    211     platform_context_->Echo(base::Bind(&PPB_Graphics3D_Impl::OnSwapBuffers,
    212                                        weak_ptr_factory_.GetWeakPtr()));
    213   }
    214 
    215 
    216   return PP_OK_COMPLETIONPENDING;
    217 }
    218 
    219 bool PPB_Graphics3D_Impl::Init(PPB_Graphics3D_API* share_context,
    220                                const int32_t* attrib_list) {
    221   if (!InitRaw(share_context, attrib_list))
    222     return false;
    223 
    224   gpu::CommandBuffer* command_buffer = GetCommandBuffer();
    225   if (!command_buffer->Initialize())
    226     return false;
    227 
    228   gpu::gles2::GLES2Implementation* share_gles2 = NULL;
    229   if (share_context) {
    230     share_gles2 =
    231         static_cast<PPB_Graphics3D_Shared*>(share_context)->gles2_impl();
    232   }
    233 
    234   return CreateGLES2Impl(kCommandBufferSize, kTransferBufferSize,
    235                          share_gles2);
    236 }
    237 
    238 bool PPB_Graphics3D_Impl::InitRaw(PPB_Graphics3D_API* share_context,
    239                                   const int32_t* attrib_list) {
    240   PepperPluginInstanceImpl* plugin_instance =
    241       HostGlobals::Get()->GetInstance(pp_instance());
    242   if (!plugin_instance)
    243     return false;
    244 
    245   PlatformContext3D* share_platform_context = NULL;
    246   if (share_context) {
    247     PPB_Graphics3D_Impl* share_graphics =
    248         static_cast<PPB_Graphics3D_Impl*>(share_context);
    249     share_platform_context = share_graphics->platform_context();
    250   }
    251 
    252   // If accelerated compositing of plugins is disabled, fail to create a 3D
    253   // context, because it won't be visible. This allows graceful fallback in the
    254   // modules.
    255   const WebPreferences& prefs = static_cast<RenderViewImpl*>(plugin_instance->
    256       GetRenderView())->webkit_preferences();
    257   if (!prefs.accelerated_compositing_for_plugins_enabled)
    258     return false;
    259 
    260   platform_context_.reset(new PlatformContext3D);
    261   if (!platform_context_)
    262     return false;
    263 
    264   if (!platform_context_->Init(attrib_list, share_platform_context))
    265     return false;
    266 
    267   platform_context_->SetContextLostCallback(
    268       base::Bind(&PPB_Graphics3D_Impl::OnContextLost,
    269                  weak_ptr_factory_.GetWeakPtr()));
    270 
    271   platform_context_->SetOnConsoleMessageCallback(
    272       base::Bind(&PPB_Graphics3D_Impl::OnConsoleMessage,
    273                  weak_ptr_factory_.GetWeakPtr()));
    274   return true;
    275 }
    276 
    277 void PPB_Graphics3D_Impl::OnConsoleMessage(const std::string& message,
    278                                            int id) {
    279   if (!bound_to_instance_)
    280     return;
    281   WebPluginContainer* container =
    282       HostGlobals::Get()->GetInstance(pp_instance())->container();
    283   if (!container)
    284     return;
    285   WebFrame* frame = container->element().document().frame();
    286   if (!frame)
    287     return;
    288   WebConsoleMessage console_message = WebConsoleMessage(
    289       WebConsoleMessage::LevelError, WebString(UTF8ToUTF16(message)));
    290   frame->addMessageToConsole(console_message);
    291 }
    292 
    293 void PPB_Graphics3D_Impl::OnSwapBuffers() {
    294   if (HasPendingSwap()) {
    295     // If we're off-screen, no need to trigger and wait for compositing.
    296     // Just send the swap-buffers ACK to the plugin immediately.
    297     commit_pending_ = false;
    298     SwapBuffersACK(PP_OK);
    299   }
    300 }
    301 
    302 void PPB_Graphics3D_Impl::OnContextLost() {
    303   // Don't need to check for NULL from GetPluginInstance since when we're
    304   // bound, we know our instance is valid.
    305   if (bound_to_instance_) {
    306     HostGlobals::Get()->GetInstance(pp_instance())->BindGraphics(
    307         pp_instance(), 0);
    308   }
    309 
    310   // Send context lost to plugin. This may have been caused by a PPAPI call, so
    311   // avoid re-entering.
    312   base::MessageLoop::current()->PostTask(
    313       FROM_HERE,
    314       base::Bind(&PPB_Graphics3D_Impl::SendContextLost,
    315                  weak_ptr_factory_.GetWeakPtr()));
    316 }
    317 
    318 void PPB_Graphics3D_Impl::SendContextLost() {
    319   // By the time we run this, the instance may have been deleted, or in the
    320   // process of being deleted. Even in the latter case, we don't want to send a
    321   // callback after DidDestroy.
    322   PepperPluginInstanceImpl* instance =
    323       HostGlobals::Get()->GetInstance(pp_instance());
    324   if (!instance || !instance->container())
    325     return;
    326 
    327   // This PPB_Graphics3D_Impl could be deleted during the call to
    328   // GetPluginInterface (which sends a sync message in some cases). We still
    329   // send the Graphics3DContextLost to the plugin; the instance may care about
    330   // that event even though this context has been destroyed.
    331   PP_Instance this_pp_instance = pp_instance();
    332   const PPP_Graphics3D* ppp_graphics_3d =
    333       static_cast<const PPP_Graphics3D*>(
    334           instance->module()->GetPluginInterface(
    335               PPP_GRAPHICS_3D_INTERFACE));
    336   if (ppp_graphics_3d)
    337     ppp_graphics_3d->Graphics3DContextLost(this_pp_instance);
    338 }
    339 
    340 }  // namespace content
    341