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/common/gpu/client/command_buffer_proxy_impl.h"
     12 #include "content/common/gpu/client/gpu_channel_host.h"
     13 #include "content/public/common/content_switches.h"
     14 #include "content/public/common/web_preferences.h"
     15 #include "content/renderer/pepper/host_globals.h"
     16 #include "content/renderer/pepper/pepper_plugin_instance_impl.h"
     17 #include "content/renderer/pepper/plugin_module.h"
     18 #include "content/renderer/render_thread_impl.h"
     19 #include "content/renderer/render_view_impl.h"
     20 #include "gpu/command_buffer/client/gles2_implementation.h"
     21 #include "ppapi/c/ppp_graphics_3d.h"
     22 #include "ppapi/thunk/enter.h"
     23 #include "third_party/WebKit/public/platform/WebString.h"
     24 #include "third_party/WebKit/public/web/WebConsoleMessage.h"
     25 #include "third_party/WebKit/public/web/WebDocument.h"
     26 #include "third_party/WebKit/public/web/WebElement.h"
     27 #include "third_party/WebKit/public/web/WebLocalFrame.h"
     28 #include "third_party/WebKit/public/web/WebPluginContainer.h"
     29 
     30 using ppapi::thunk::EnterResourceNoLock;
     31 using ppapi::thunk::PPB_Graphics3D_API;
     32 using blink::WebConsoleMessage;
     33 using blink::WebLocalFrame;
     34 using blink::WebPluginContainer;
     35 using blink::WebString;
     36 
     37 namespace content {
     38 
     39 namespace {
     40 const int32 kCommandBufferSize = 1024 * 1024;
     41 const int32 kTransferBufferSize = 1024 * 1024;
     42 
     43 }  // namespace.
     44 
     45 PPB_Graphics3D_Impl::PPB_Graphics3D_Impl(PP_Instance instance)
     46     : PPB_Graphics3D_Shared(instance),
     47       bound_to_instance_(false),
     48       commit_pending_(false),
     49       sync_point_(0),
     50       has_alpha_(false),
     51       command_buffer_(NULL),
     52       weak_ptr_factory_(this) {}
     53 
     54 PPB_Graphics3D_Impl::~PPB_Graphics3D_Impl() {
     55   DestroyGLES2Impl();
     56   if (command_buffer_) {
     57     DCHECK(channel_.get());
     58     channel_->DestroyCommandBuffer(command_buffer_);
     59     command_buffer_ = NULL;
     60   }
     61 
     62   channel_ = NULL;
     63 }
     64 
     65 // static
     66 PP_Resource PPB_Graphics3D_Impl::Create(PP_Instance instance,
     67                                         PP_Resource share_context,
     68                                         const int32_t* attrib_list) {
     69   PPB_Graphics3D_API* share_api = NULL;
     70   if (share_context) {
     71     EnterResourceNoLock<PPB_Graphics3D_API> enter(share_context, true);
     72     if (enter.failed())
     73       return 0;
     74     share_api = enter.object();
     75   }
     76   scoped_refptr<PPB_Graphics3D_Impl> graphics_3d(
     77       new PPB_Graphics3D_Impl(instance));
     78   if (!graphics_3d->Init(share_api, attrib_list))
     79     return 0;
     80   return graphics_3d->GetReference();
     81 }
     82 
     83 // static
     84 PP_Resource PPB_Graphics3D_Impl::CreateRaw(
     85     PP_Instance instance,
     86     PP_Resource share_context,
     87     const int32_t* attrib_list,
     88     base::SharedMemoryHandle* shared_state_handle) {
     89   PPB_Graphics3D_API* share_api = NULL;
     90   if (share_context) {
     91     EnterResourceNoLock<PPB_Graphics3D_API> enter(share_context, true);
     92     if (enter.failed())
     93       return 0;
     94     share_api = enter.object();
     95   }
     96   scoped_refptr<PPB_Graphics3D_Impl> graphics_3d(
     97       new PPB_Graphics3D_Impl(instance));
     98   if (!graphics_3d->InitRaw(share_api, attrib_list, shared_state_handle))
     99     return 0;
    100   return graphics_3d->GetReference();
    101 }
    102 
    103 PP_Bool PPB_Graphics3D_Impl::SetGetBuffer(int32_t transfer_buffer_id) {
    104   GetCommandBuffer()->SetGetBuffer(transfer_buffer_id);
    105   return PP_TRUE;
    106 }
    107 
    108 scoped_refptr<gpu::Buffer> PPB_Graphics3D_Impl::CreateTransferBuffer(
    109     uint32_t size,
    110     int32_t* id) {
    111   return GetCommandBuffer()->CreateTransferBuffer(size, id);
    112 }
    113 
    114 PP_Bool PPB_Graphics3D_Impl::DestroyTransferBuffer(int32_t id) {
    115   GetCommandBuffer()->DestroyTransferBuffer(id);
    116   return PP_TRUE;
    117 }
    118 
    119 PP_Bool PPB_Graphics3D_Impl::Flush(int32_t put_offset) {
    120   GetCommandBuffer()->Flush(put_offset);
    121   return PP_TRUE;
    122 }
    123 
    124 gpu::CommandBuffer::State PPB_Graphics3D_Impl::WaitForTokenInRange(
    125     int32_t start,
    126     int32_t end) {
    127   GetCommandBuffer()->WaitForTokenInRange(start, end);
    128   return GetCommandBuffer()->GetLastState();
    129 }
    130 
    131 gpu::CommandBuffer::State PPB_Graphics3D_Impl::WaitForGetOffsetInRange(
    132     int32_t start,
    133     int32_t end) {
    134   GetCommandBuffer()->WaitForGetOffsetInRange(start, end);
    135   return GetCommandBuffer()->GetLastState();
    136 }
    137 
    138 uint32_t PPB_Graphics3D_Impl::InsertSyncPoint() {
    139   return command_buffer_->InsertSyncPoint();
    140 }
    141 
    142 uint32_t PPB_Graphics3D_Impl::InsertFutureSyncPoint() {
    143   return command_buffer_->InsertFutureSyncPoint();
    144 }
    145 
    146 void PPB_Graphics3D_Impl::RetireSyncPoint(uint32_t sync_point) {
    147   return command_buffer_->RetireSyncPoint(sync_point);
    148 }
    149 
    150 bool PPB_Graphics3D_Impl::BindToInstance(bool bind) {
    151   bound_to_instance_ = bind;
    152   return true;
    153 }
    154 
    155 bool PPB_Graphics3D_Impl::IsOpaque() { return !has_alpha_; }
    156 
    157 void PPB_Graphics3D_Impl::ViewInitiatedPaint() {
    158   commit_pending_ = false;
    159 
    160   if (HasPendingSwap())
    161     SwapBuffersACK(PP_OK);
    162 }
    163 
    164 void PPB_Graphics3D_Impl::ViewFlushedPaint() {}
    165 
    166 int PPB_Graphics3D_Impl::GetCommandBufferRouteId() {
    167   DCHECK(command_buffer_);
    168   return command_buffer_->GetRouteID();
    169 }
    170 
    171 gpu::CommandBuffer* PPB_Graphics3D_Impl::GetCommandBuffer() {
    172   return command_buffer_;
    173 }
    174 
    175 gpu::GpuControl* PPB_Graphics3D_Impl::GetGpuControl() {
    176   return command_buffer_;
    177 }
    178 
    179 int32 PPB_Graphics3D_Impl::DoSwapBuffers() {
    180   DCHECK(command_buffer_);
    181   // We do not have a GLES2 implementation when using an OOP proxy.
    182   // The plugin-side proxy is responsible for adding the SwapBuffers command
    183   // to the command buffer in that case.
    184   if (gles2_impl())
    185     gles2_impl()->SwapBuffers();
    186 
    187   // Since the backing texture has been updated, a new sync point should be
    188   // inserted.
    189   sync_point_ = command_buffer_->InsertSyncPoint();
    190 
    191   if (bound_to_instance_) {
    192     // If we are bound to the instance, we need to ask the compositor
    193     // to commit our backing texture so that the graphics appears on the page.
    194     // When the backing texture will be committed we get notified via
    195     // ViewFlushedPaint().
    196     //
    197     // Don't need to check for NULL from GetPluginInstance since when we're
    198     // bound, we know our instance is valid.
    199     HostGlobals::Get()->GetInstance(pp_instance())->CommitBackingTexture();
    200     commit_pending_ = true;
    201   } else {
    202     // Wait for the command to complete on the GPU to allow for throttling.
    203     command_buffer_->Echo(base::Bind(&PPB_Graphics3D_Impl::OnSwapBuffers,
    204                                      weak_ptr_factory_.GetWeakPtr()));
    205   }
    206 
    207   return PP_OK_COMPLETIONPENDING;
    208 }
    209 
    210 bool PPB_Graphics3D_Impl::Init(PPB_Graphics3D_API* share_context,
    211                                const int32_t* attrib_list) {
    212   if (!InitRaw(share_context, attrib_list, NULL))
    213     return false;
    214 
    215   gpu::gles2::GLES2Implementation* share_gles2 = NULL;
    216   if (share_context) {
    217     share_gles2 =
    218         static_cast<PPB_Graphics3D_Shared*>(share_context)->gles2_impl();
    219   }
    220 
    221   return CreateGLES2Impl(kCommandBufferSize, kTransferBufferSize, share_gles2);
    222 }
    223 
    224 bool PPB_Graphics3D_Impl::InitRaw(
    225     PPB_Graphics3D_API* share_context,
    226     const int32_t* attrib_list,
    227     base::SharedMemoryHandle* shared_state_handle) {
    228   PepperPluginInstanceImpl* plugin_instance =
    229       HostGlobals::Get()->GetInstance(pp_instance());
    230   if (!plugin_instance)
    231     return false;
    232 
    233   const WebPreferences& prefs =
    234       static_cast<RenderViewImpl*>(plugin_instance->GetRenderView())
    235           ->webkit_preferences();
    236   // 3D access might be disabled or blacklisted.
    237   if (!prefs.pepper_3d_enabled)
    238     return false;
    239 
    240   RenderThreadImpl* render_thread = RenderThreadImpl::current();
    241   if (!render_thread)
    242     return false;
    243 
    244   channel_ = render_thread->EstablishGpuChannelSync(
    245       CAUSE_FOR_GPU_LAUNCH_PEPPERPLATFORMCONTEXT3DIMPL_INITIALIZE);
    246   if (!channel_.get())
    247     return false;
    248 
    249   gfx::Size surface_size;
    250   std::vector<int32> attribs;
    251   gfx::GpuPreference gpu_preference = gfx::PreferDiscreteGpu;
    252   // TODO(alokp): Change GpuChannelHost::CreateOffscreenCommandBuffer()
    253   // interface to accept width and height in the attrib_list so that
    254   // we do not need to filter for width and height here.
    255   if (attrib_list) {
    256     for (const int32_t* attr = attrib_list; attr[0] != PP_GRAPHICS3DATTRIB_NONE;
    257          attr += 2) {
    258       switch (attr[0]) {
    259         case PP_GRAPHICS3DATTRIB_WIDTH:
    260           surface_size.set_width(attr[1]);
    261           break;
    262         case PP_GRAPHICS3DATTRIB_HEIGHT:
    263           surface_size.set_height(attr[1]);
    264           break;
    265         case PP_GRAPHICS3DATTRIB_GPU_PREFERENCE:
    266           gpu_preference =
    267               (attr[1] == PP_GRAPHICS3DATTRIB_GPU_PREFERENCE_LOW_POWER)
    268                   ? gfx::PreferIntegratedGpu
    269                   : gfx::PreferDiscreteGpu;
    270           break;
    271         case PP_GRAPHICS3DATTRIB_ALPHA_SIZE:
    272           has_alpha_ = attr[1] > 0;
    273         // fall-through
    274         default:
    275           attribs.push_back(attr[0]);
    276           attribs.push_back(attr[1]);
    277           break;
    278       }
    279     }
    280     attribs.push_back(PP_GRAPHICS3DATTRIB_NONE);
    281   }
    282 
    283   CommandBufferProxyImpl* share_buffer = NULL;
    284   if (share_context) {
    285     PPB_Graphics3D_Impl* share_graphics =
    286         static_cast<PPB_Graphics3D_Impl*>(share_context);
    287     share_buffer = share_graphics->command_buffer_;
    288   }
    289 
    290   command_buffer_ = channel_->CreateOffscreenCommandBuffer(
    291       surface_size, share_buffer, attribs, GURL::EmptyGURL(), gpu_preference);
    292   if (!command_buffer_)
    293     return false;
    294   if (!command_buffer_->Initialize())
    295     return false;
    296   if (shared_state_handle)
    297     *shared_state_handle = command_buffer_->GetSharedStateHandle();
    298   mailbox_ = gpu::Mailbox::Generate();
    299   if (!command_buffer_->ProduceFrontBuffer(mailbox_))
    300     return false;
    301   sync_point_ = command_buffer_->InsertSyncPoint();
    302 
    303   command_buffer_->SetChannelErrorCallback(base::Bind(
    304       &PPB_Graphics3D_Impl::OnContextLost, weak_ptr_factory_.GetWeakPtr()));
    305 
    306   command_buffer_->SetOnConsoleMessageCallback(base::Bind(
    307       &PPB_Graphics3D_Impl::OnConsoleMessage, weak_ptr_factory_.GetWeakPtr()));
    308   return true;
    309 }
    310 
    311 void PPB_Graphics3D_Impl::OnConsoleMessage(const std::string& message, int id) {
    312   if (!bound_to_instance_)
    313     return;
    314   WebPluginContainer* container =
    315       HostGlobals::Get()->GetInstance(pp_instance())->container();
    316   if (!container)
    317     return;
    318   WebLocalFrame* frame = container->element().document().frame();
    319   if (!frame)
    320     return;
    321   WebConsoleMessage console_message = WebConsoleMessage(
    322       WebConsoleMessage::LevelError, WebString(base::UTF8ToUTF16(message)));
    323   frame->addMessageToConsole(console_message);
    324 }
    325 
    326 void PPB_Graphics3D_Impl::OnSwapBuffers() {
    327   if (HasPendingSwap()) {
    328     // If we're off-screen, no need to trigger and wait for compositing.
    329     // Just send the swap-buffers ACK to the plugin immediately.
    330     commit_pending_ = false;
    331     SwapBuffersACK(PP_OK);
    332   }
    333 }
    334 
    335 void PPB_Graphics3D_Impl::OnContextLost() {
    336   // Don't need to check for NULL from GetPluginInstance since when we're
    337   // bound, we know our instance is valid.
    338   if (bound_to_instance_) {
    339     HostGlobals::Get()->GetInstance(pp_instance())->BindGraphics(pp_instance(),
    340                                                                  0);
    341   }
    342 
    343   // Send context lost to plugin. This may have been caused by a PPAPI call, so
    344   // avoid re-entering.
    345   base::MessageLoop::current()->PostTask(
    346       FROM_HERE,
    347       base::Bind(&PPB_Graphics3D_Impl::SendContextLost,
    348                  weak_ptr_factory_.GetWeakPtr()));
    349 }
    350 
    351 void PPB_Graphics3D_Impl::SendContextLost() {
    352   // By the time we run this, the instance may have been deleted, or in the
    353   // process of being deleted. Even in the latter case, we don't want to send a
    354   // callback after DidDestroy.
    355   PepperPluginInstanceImpl* instance =
    356       HostGlobals::Get()->GetInstance(pp_instance());
    357   if (!instance || !instance->container())
    358     return;
    359 
    360   // This PPB_Graphics3D_Impl could be deleted during the call to
    361   // GetPluginInterface (which sends a sync message in some cases). We still
    362   // send the Graphics3DContextLost to the plugin; the instance may care about
    363   // that event even though this context has been destroyed.
    364   PP_Instance this_pp_instance = pp_instance();
    365   const PPP_Graphics3D* ppp_graphics_3d = static_cast<const PPP_Graphics3D*>(
    366       instance->module()->GetPluginInterface(PPP_GRAPHICS_3D_INTERFACE));
    367   // We have to check *again* that the instance exists, because it could have
    368   // been deleted during GetPluginInterface(). Even the PluginModule could be
    369   // deleted, but in that case, the instance should also be gone, so the
    370   // GetInstance check covers both cases.
    371   if (ppp_graphics_3d && HostGlobals::Get()->GetInstance(this_pp_instance))
    372     ppp_graphics_3d->Graphics3DContextLost(this_pp_instance);
    373 }
    374 
    375 }  // namespace content
    376