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