Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / content / renderer / pepper / ppb_graphics_3d_impl.cc
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