• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
PPB_Graphics3D_Impl(PP_Instance instance)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 
~PPB_Graphics3D_Impl()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
Create(PP_Instance instance,PP_Resource share_context,const int32_t * attrib_list)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
CreateRaw(PP_Instance instance,PP_Resource share_context,const int32_t * attrib_list,base::SharedMemoryHandle * shared_state_handle)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 
SetGetBuffer(int32_t transfer_buffer_id)103 PP_Bool PPB_Graphics3D_Impl::SetGetBuffer(int32_t transfer_buffer_id) {
104   GetCommandBuffer()->SetGetBuffer(transfer_buffer_id);
105   return PP_TRUE;
106 }
107 
CreateTransferBuffer(uint32_t size,int32_t * id)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 
DestroyTransferBuffer(int32_t id)114 PP_Bool PPB_Graphics3D_Impl::DestroyTransferBuffer(int32_t id) {
115   GetCommandBuffer()->DestroyTransferBuffer(id);
116   return PP_TRUE;
117 }
118 
Flush(int32_t put_offset)119 PP_Bool PPB_Graphics3D_Impl::Flush(int32_t put_offset) {
120   GetCommandBuffer()->Flush(put_offset);
121   return PP_TRUE;
122 }
123 
WaitForTokenInRange(int32_t start,int32_t end)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 
WaitForGetOffsetInRange(int32_t start,int32_t end)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 
InsertSyncPoint()138 uint32_t PPB_Graphics3D_Impl::InsertSyncPoint() {
139   return command_buffer_->InsertSyncPoint();
140 }
141 
InsertFutureSyncPoint()142 uint32_t PPB_Graphics3D_Impl::InsertFutureSyncPoint() {
143   return command_buffer_->InsertFutureSyncPoint();
144 }
145 
RetireSyncPoint(uint32_t sync_point)146 void PPB_Graphics3D_Impl::RetireSyncPoint(uint32_t sync_point) {
147   return command_buffer_->RetireSyncPoint(sync_point);
148 }
149 
BindToInstance(bool bind)150 bool PPB_Graphics3D_Impl::BindToInstance(bool bind) {
151   bound_to_instance_ = bind;
152   return true;
153 }
154 
IsOpaque()155 bool PPB_Graphics3D_Impl::IsOpaque() { return !has_alpha_; }
156 
ViewInitiatedPaint()157 void PPB_Graphics3D_Impl::ViewInitiatedPaint() {
158   commit_pending_ = false;
159 
160   if (HasPendingSwap())
161     SwapBuffersACK(PP_OK);
162 }
163 
ViewFlushedPaint()164 void PPB_Graphics3D_Impl::ViewFlushedPaint() {}
165 
GetCommandBufferRouteId()166 int PPB_Graphics3D_Impl::GetCommandBufferRouteId() {
167   DCHECK(command_buffer_);
168   return command_buffer_->GetRouteID();
169 }
170 
GetCommandBuffer()171 gpu::CommandBuffer* PPB_Graphics3D_Impl::GetCommandBuffer() {
172   return command_buffer_;
173 }
174 
GetGpuControl()175 gpu::GpuControl* PPB_Graphics3D_Impl::GetGpuControl() {
176   return command_buffer_;
177 }
178 
DoSwapBuffers()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 
Init(PPB_Graphics3D_API * share_context,const int32_t * attrib_list)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 
InitRaw(PPB_Graphics3D_API * share_context,const int32_t * attrib_list,base::SharedMemoryHandle * shared_state_handle)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 
OnConsoleMessage(const std::string & message,int id)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 
OnSwapBuffers()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 
OnContextLost()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 
SendContextLost()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