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
28using ppapi::thunk::EnterResourceNoLock;
29using ppapi::thunk::PPB_Graphics3D_API;
30using blink::WebConsoleMessage;
31using blink::WebFrame;
32using blink::WebPluginContainer;
33using blink::WebString;
34
35namespace content {
36
37namespace {
38const int32 kCommandBufferSize = 1024 * 1024;
39const int32 kTransferBufferSize = 1024 * 1024;
40
41PP_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
60PPB_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
67PPB_Graphics3D_Impl::~PPB_Graphics3D_Impl() {
68  DestroyGLES2Impl();
69}
70
71// static
72PP_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
80PP_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
100PP_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
119PP_Bool PPB_Graphics3D_Impl::SetGetBuffer(int32_t transfer_buffer_id) {
120  GetCommandBuffer()->SetGetBuffer(transfer_buffer_id);
121  return PP_TRUE;
122}
123
124gpu::CommandBuffer::State PPB_Graphics3D_Impl::GetState() {
125  return GetCommandBuffer()->GetState();
126}
127
128int32_t PPB_Graphics3D_Impl::CreateTransferBuffer(uint32_t size) {
129  int32_t id = -1;
130  GetCommandBuffer()->CreateTransferBuffer(size, &id);
131  return id;
132}
133
134PP_Bool PPB_Graphics3D_Impl::DestroyTransferBuffer(int32_t id) {
135  GetCommandBuffer()->DestroyTransferBuffer(id);
136  return PP_TRUE;
137}
138
139PP_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
146PP_Bool PPB_Graphics3D_Impl::Flush(int32_t put_offset) {
147  GetCommandBuffer()->Flush(put_offset);
148  return PP_TRUE;
149}
150
151gpu::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
156gpu::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
162uint32_t PPB_Graphics3D_Impl::InsertSyncPoint() {
163  return platform_context_->GetGpuControl()->InsertSyncPoint();
164}
165
166bool PPB_Graphics3D_Impl::BindToInstance(bool bind) {
167  bound_to_instance_ = bind;
168  return true;
169}
170
171bool PPB_Graphics3D_Impl::IsOpaque() {
172  return platform_context_->IsOpaque();
173}
174
175void PPB_Graphics3D_Impl::ViewInitiatedPaint() {
176  commit_pending_ = false;
177
178  if (HasPendingSwap())
179    SwapBuffersACK(PP_OK);
180}
181
182void PPB_Graphics3D_Impl::ViewFlushedPaint() {
183}
184
185gpu::CommandBuffer* PPB_Graphics3D_Impl::GetCommandBuffer() {
186  return platform_context_->GetCommandBuffer();
187}
188
189gpu::GpuControl* PPB_Graphics3D_Impl::GetGpuControl() {
190  return platform_context_->GetGpuControl();
191}
192
193int32 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
224bool 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
243bool 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
282void 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
298void 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
307void 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
323void 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