• 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/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 
ShmToHandle(base::SharedMemory * shm,size_t size,int * shm_handle,uint32_t * shm_size)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 
PPB_Graphics3D_Impl(PP_Instance instance)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 
~PPB_Graphics3D_Impl()67 PPB_Graphics3D_Impl::~PPB_Graphics3D_Impl() {
68   DestroyGLES2Impl();
69 }
70 
71 // static
IsGpuBlacklisted()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
Create(PP_Instance instance,PP_Resource share_context,const int32_t * attrib_list)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
CreateRaw(PP_Instance instance,PP_Resource share_context,const int32_t * attrib_list)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 
SetGetBuffer(int32_t transfer_buffer_id)119 PP_Bool PPB_Graphics3D_Impl::SetGetBuffer(int32_t transfer_buffer_id) {
120   GetCommandBuffer()->SetGetBuffer(transfer_buffer_id);
121   return PP_TRUE;
122 }
123 
GetState()124 gpu::CommandBuffer::State PPB_Graphics3D_Impl::GetState() {
125   return GetCommandBuffer()->GetState();
126 }
127 
CreateTransferBuffer(uint32_t size)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 
DestroyTransferBuffer(int32_t id)134 PP_Bool PPB_Graphics3D_Impl::DestroyTransferBuffer(int32_t id) {
135   GetCommandBuffer()->DestroyTransferBuffer(id);
136   return PP_TRUE;
137 }
138 
GetTransferBuffer(int32_t id,int * shm_handle,uint32_t * shm_size)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 
Flush(int32_t put_offset)146 PP_Bool PPB_Graphics3D_Impl::Flush(int32_t put_offset) {
147   GetCommandBuffer()->Flush(put_offset);
148   return PP_TRUE;
149 }
150 
FlushSync(int32_t put_offset)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 
FlushSyncFast(int32_t put_offset,int32_t last_known_get)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 
InsertSyncPoint()162 uint32_t PPB_Graphics3D_Impl::InsertSyncPoint() {
163   return platform_context_->GetGpuControl()->InsertSyncPoint();
164 }
165 
BindToInstance(bool bind)166 bool PPB_Graphics3D_Impl::BindToInstance(bool bind) {
167   bound_to_instance_ = bind;
168   return true;
169 }
170 
IsOpaque()171 bool PPB_Graphics3D_Impl::IsOpaque() {
172   return platform_context_->IsOpaque();
173 }
174 
ViewInitiatedPaint()175 void PPB_Graphics3D_Impl::ViewInitiatedPaint() {
176   commit_pending_ = false;
177 
178   if (HasPendingSwap())
179     SwapBuffersACK(PP_OK);
180 }
181 
ViewFlushedPaint()182 void PPB_Graphics3D_Impl::ViewFlushedPaint() {
183 }
184 
GetCommandBuffer()185 gpu::CommandBuffer* PPB_Graphics3D_Impl::GetCommandBuffer() {
186   return platform_context_->GetCommandBuffer();
187 }
188 
GetGpuControl()189 gpu::GpuControl* PPB_Graphics3D_Impl::GetGpuControl() {
190   return platform_context_->GetGpuControl();
191 }
192 
DoSwapBuffers()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 
Init(PPB_Graphics3D_API * share_context,const int32_t * attrib_list)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 
InitRaw(PPB_Graphics3D_API * share_context,const int32_t * attrib_list)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 
OnConsoleMessage(const std::string & message,int id)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 
OnSwapBuffers()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 
OnContextLost()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 
SendContextLost()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