ppb_graphics_3d_proxy.cc revision 424c4d7b64af9d0d8fd9624f381f469654d5e3d2
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 "ppapi/proxy/ppb_graphics_3d_proxy.h"
6
7#include "gpu/command_buffer/client/gles2_implementation.h"
8#include "gpu/command_buffer/common/command_buffer.h"
9#include "ppapi/c/pp_errors.h"
10#include "ppapi/proxy/enter_proxy.h"
11#include "ppapi/proxy/plugin_dispatcher.h"
12#include "ppapi/proxy/ppapi_command_buffer_proxy.h"
13#include "ppapi/proxy/ppapi_messages.h"
14#include "ppapi/shared_impl/ppapi_globals.h"
15#include "ppapi/shared_impl/proxy_lock.h"
16#include "ppapi/thunk/enter.h"
17#include "ppapi/thunk/resource_creation_api.h"
18#include "ppapi/thunk/thunk.h"
19
20using ppapi::thunk::EnterResourceNoLock;
21using ppapi::thunk::PPB_Graphics3D_API;
22using ppapi::thunk::ResourceCreationAPI;
23
24namespace ppapi {
25namespace proxy {
26
27namespace {
28
29const int32 kCommandBufferSize = 1024 * 1024;
30const int32 kTransferBufferSize = 1024 * 1024;
31
32base::SharedMemoryHandle TransportSHMHandleFromInt(Dispatcher* dispatcher,
33                                                   int shm_handle) {
34  // TODO(piman): Change trusted interface to return a PP_FileHandle, those
35  // casts are ugly.
36  base::PlatformFile source =
37#if defined(OS_WIN)
38      reinterpret_cast<HANDLE>(static_cast<intptr_t>(shm_handle));
39#elif defined(OS_POSIX)
40      shm_handle;
41#else
42  #error Not implemented.
43#endif
44  // Don't close the handle, it doesn't belong to us.
45  return dispatcher->ShareHandleWithRemote(source, false);
46}
47
48gpu::CommandBuffer::State GetErrorState() {
49  gpu::CommandBuffer::State error_state;
50  error_state.error = gpu::error::kGenericError;
51  return error_state;
52}
53
54}  // namespace
55
56// This class just wraps a CommandBuffer and optionally locks around every
57// method. This is used to ensure that we have the Proxy lock any time we enter
58// PpapiCommandBufferProxy.
59//
60// Note, for performance reasons, most of this code is not truly thread
61// safe in the sense of multiple threads concurrently rendering to the same
62// Graphics3D context; this isn't allowed, and will likely either crash or
63// result in undefined behavior.  It is assumed that the thread which creates
64// the Graphics3D context will be the thread on which subsequent gl rendering
65// will be done. This is why it is okay to read need_to_lock_ without the lock;
66// it should only ever be read and written on the same thread where the context
67// was created.
68//
69// TODO(nfullagar): At some point, allow multiple threads to concurrently render
70// each to its own context.  First step is to allow a single thread (either main
71// thread or background thread) to render to a single Graphics3D context.
72class Graphics3D::LockingCommandBuffer : public gpu::CommandBuffer {
73 public:
74  explicit LockingCommandBuffer(gpu::CommandBuffer* gpu_command_buffer)
75      : gpu_command_buffer_(gpu_command_buffer), need_to_lock_(true) {
76  }
77  virtual ~LockingCommandBuffer() {
78  }
79  void set_need_to_lock(bool need_to_lock) { need_to_lock_ = need_to_lock; }
80  bool need_to_lock() const { return need_to_lock_; }
81
82 private:
83  // MaybeLock acquires the proxy lock on construction if and only if
84  // need_to_lock is true. If it acquired the lock, it releases it on
85  // destruction. If need_to_lock is false, then the lock must already be held.
86  struct MaybeLock {
87    explicit MaybeLock(bool need_to_lock) : locked_(need_to_lock) {
88      if (need_to_lock)
89        ppapi::ProxyLock::Acquire();
90      else
91        ppapi::ProxyLock::AssertAcquired();
92    }
93    ~MaybeLock() {
94      if (locked_)
95        ppapi::ProxyLock::Release();
96    }
97   private:
98    bool locked_;
99  };
100
101  // gpu::CommandBuffer implementation:
102  virtual bool Initialize() OVERRIDE {
103    MaybeLock lock(need_to_lock_);
104    return gpu_command_buffer_->Initialize();
105  }
106  virtual State GetState() OVERRIDE {
107    MaybeLock lock(need_to_lock_);
108    return gpu_command_buffer_->GetState();
109  }
110  virtual State GetLastState() OVERRIDE {
111    // During a normal scene, the vast majority of calls are to GetLastState().
112    // We don't allow multi-threaded rendering on the same contex, so for
113    // performance reasons, avoid the global lock for this entry point.  We can
114    // get away with this here because the underlying implementation of
115    // GetLastState() is trivial and does not involve global or shared state
116    // between other contexts.
117    // TODO(nfullagar): We can probably skip MaybeLock for other methods, but
118    // the performance gain may not be worth it.
119    //
120    // MaybeLock lock(need_to_lock_);
121    return gpu_command_buffer_->GetLastState();
122  }
123  virtual int32 GetLastToken() OVERRIDE {
124    return GetLastState().token;
125  }
126  virtual void Flush(int32 put_offset) OVERRIDE {
127    MaybeLock lock(need_to_lock_);
128    gpu_command_buffer_->Flush(put_offset);
129  }
130  virtual State FlushSync(int32 put_offset, int32 last_known_get) OVERRIDE {
131    MaybeLock lock(need_to_lock_);
132    return gpu_command_buffer_->FlushSync(put_offset, last_known_get);
133  }
134  virtual void SetGetBuffer(int32 transfer_buffer_id) OVERRIDE {
135    MaybeLock lock(need_to_lock_);
136    gpu_command_buffer_->SetGetBuffer(transfer_buffer_id);
137  }
138  virtual void SetGetOffset(int32 get_offset) OVERRIDE {
139    MaybeLock lock(need_to_lock_);
140    gpu_command_buffer_->SetGetOffset(get_offset);
141  }
142  virtual gpu::Buffer CreateTransferBuffer(size_t size,
143                                           int32* id) OVERRIDE {
144    MaybeLock lock(need_to_lock_);
145    return gpu_command_buffer_->CreateTransferBuffer(size, id);
146  }
147  virtual void DestroyTransferBuffer(int32 id) OVERRIDE {
148    MaybeLock lock(need_to_lock_);
149    gpu_command_buffer_->DestroyTransferBuffer(id);
150  }
151  virtual gpu::Buffer GetTransferBuffer(int32 id) OVERRIDE {
152    MaybeLock lock(need_to_lock_);
153    return gpu_command_buffer_->GetTransferBuffer(id);
154  }
155  virtual void SetToken(int32 token) OVERRIDE {
156    MaybeLock lock(need_to_lock_);
157    gpu_command_buffer_->SetToken(token);
158  }
159  virtual void SetParseError(gpu::error::Error error) OVERRIDE {
160    MaybeLock lock(need_to_lock_);
161    gpu_command_buffer_->SetParseError(error);
162  }
163  virtual void SetContextLostReason(
164      gpu::error::ContextLostReason reason) OVERRIDE {
165    MaybeLock lock(need_to_lock_);
166    gpu_command_buffer_->SetContextLostReason(reason);
167  }
168  virtual uint32 InsertSyncPoint() OVERRIDE {
169    MaybeLock lock(need_to_lock_);
170    return gpu_command_buffer_->InsertSyncPoint();
171  }
172
173  // Weak pointer - see class Graphics3D for the scopted_ptr.
174  gpu::CommandBuffer* gpu_command_buffer_;
175
176  bool need_to_lock_;
177};
178
179Graphics3D::Graphics3D(const HostResource& resource)
180    : PPB_Graphics3D_Shared(resource),
181      num_already_locked_calls_(0) {
182}
183
184Graphics3D::~Graphics3D() {
185  if (gles2_impl())
186    DestroyGLES2Impl();
187}
188
189bool Graphics3D::Init(gpu::gles2::GLES2Implementation* share_gles2) {
190  PluginDispatcher* dispatcher = PluginDispatcher::GetForResource(this);
191  if (!dispatcher)
192    return false;
193
194  command_buffer_.reset(
195      new PpapiCommandBufferProxy(host_resource(), dispatcher));
196  locking_command_buffer_.reset(
197      new LockingCommandBuffer(command_buffer_.get()));
198
199  ScopedNoLocking already_locked(this);
200  return CreateGLES2Impl(kCommandBufferSize, kTransferBufferSize,
201                         share_gles2);
202}
203
204PP_Bool Graphics3D::SetGetBuffer(int32_t /* transfer_buffer_id */) {
205  return PP_FALSE;
206}
207
208gpu::CommandBuffer::State Graphics3D::GetState() {
209  return GetErrorState();
210}
211
212PP_Bool Graphics3D::Flush(int32_t put_offset) {
213  return PP_FALSE;
214}
215
216gpu::CommandBuffer::State Graphics3D::FlushSync(int32_t put_offset) {
217  return GetErrorState();
218}
219
220int32_t Graphics3D::CreateTransferBuffer(uint32_t size) {
221  return PP_FALSE;
222}
223
224PP_Bool Graphics3D::DestroyTransferBuffer(int32_t id) {
225  return PP_FALSE;
226}
227
228PP_Bool Graphics3D::GetTransferBuffer(int32_t id,
229                                      int* shm_handle,
230                                      uint32_t* shm_size) {
231  return PP_FALSE;
232}
233
234gpu::CommandBuffer::State Graphics3D::FlushSyncFast(int32_t put_offset,
235                                                    int32_t last_known_get) {
236  return GetErrorState();
237}
238
239uint32_t Graphics3D::InsertSyncPoint() {
240  NOTREACHED();
241  return 0;
242}
243
244gpu::CommandBuffer* Graphics3D::GetCommandBuffer() {
245  return locking_command_buffer_.get();
246}
247
248int32 Graphics3D::DoSwapBuffers() {
249  // gles2_impl()->SwapBuffers() results in CommandBuffer calls, and we already
250  // have the proxy lock.
251  ScopedNoLocking already_locked(this);
252
253  gles2_impl()->SwapBuffers();
254  IPC::Message* msg = new PpapiHostMsg_PPBGraphics3D_SwapBuffers(
255      API_ID_PPB_GRAPHICS_3D, host_resource());
256  msg->set_unblock(true);
257  PluginDispatcher::GetForResource(this)->Send(msg);
258
259  return PP_OK_COMPLETIONPENDING;
260}
261
262void Graphics3D::PushAlreadyLocked() {
263  ppapi::ProxyLock::AssertAcquired();
264  if (!locking_command_buffer_) {
265    NOTREACHED();
266    return;
267  }
268  if (num_already_locked_calls_ == 0)
269    locking_command_buffer_->set_need_to_lock(false);
270  ++num_already_locked_calls_;
271}
272
273void Graphics3D::PopAlreadyLocked() {
274  // We must have Pushed before we can Pop.
275  DCHECK(!locking_command_buffer_->need_to_lock());
276  DCHECK_GT(num_already_locked_calls_, 0);
277  ppapi::ProxyLock::AssertAcquired();
278  if (!locking_command_buffer_) {
279    NOTREACHED();
280    return;
281  }
282  --num_already_locked_calls_;
283  if (num_already_locked_calls_ == 0)
284    locking_command_buffer_->set_need_to_lock(true);
285}
286
287PPB_Graphics3D_Proxy::PPB_Graphics3D_Proxy(Dispatcher* dispatcher)
288    : InterfaceProxy(dispatcher),
289      callback_factory_(this) {
290}
291
292PPB_Graphics3D_Proxy::~PPB_Graphics3D_Proxy() {
293}
294
295// static
296PP_Resource PPB_Graphics3D_Proxy::CreateProxyResource(
297    PP_Instance instance,
298    PP_Resource share_context,
299    const int32_t* attrib_list) {
300  PluginDispatcher* dispatcher = PluginDispatcher::GetForInstance(instance);
301  if (!dispatcher)
302    return PP_ERROR_BADARGUMENT;
303
304  HostResource share_host;
305  gpu::gles2::GLES2Implementation* share_gles2 = NULL;
306  if (share_context != 0) {
307    EnterResourceNoLock<PPB_Graphics3D_API> enter(share_context, true);
308    if (enter.failed())
309      return PP_ERROR_BADARGUMENT;
310
311    PPB_Graphics3D_Shared* share_graphics =
312        static_cast<PPB_Graphics3D_Shared*>(enter.object());
313    share_host = share_graphics->host_resource();
314    share_gles2 = share_graphics->gles2_impl();
315  }
316
317  std::vector<int32_t> attribs;
318  if (attrib_list) {
319    for (const int32_t* attr = attrib_list;
320         attr[0] != PP_GRAPHICS3DATTRIB_NONE;
321         attr += 2) {
322      attribs.push_back(attr[0]);
323      attribs.push_back(attr[1]);
324    }
325  }
326  attribs.push_back(PP_GRAPHICS3DATTRIB_NONE);
327
328  HostResource result;
329  dispatcher->Send(new PpapiHostMsg_PPBGraphics3D_Create(
330      API_ID_PPB_GRAPHICS_3D, instance, share_host, attribs, &result));
331  if (result.is_null())
332    return 0;
333
334  scoped_refptr<Graphics3D> graphics_3d(new Graphics3D(result));
335  if (!graphics_3d->Init(share_gles2))
336    return 0;
337  return graphics_3d->GetReference();
338}
339
340bool PPB_Graphics3D_Proxy::OnMessageReceived(const IPC::Message& msg) {
341  bool handled = true;
342  IPC_BEGIN_MESSAGE_MAP(PPB_Graphics3D_Proxy, msg)
343#if !defined(OS_NACL)
344    IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBGraphics3D_Create,
345                        OnMsgCreate)
346    IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBGraphics3D_SetGetBuffer,
347                        OnMsgSetGetBuffer)
348    IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBGraphics3D_GetState,
349                        OnMsgGetState)
350    IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBGraphics3D_Flush,
351                        OnMsgFlush)
352    IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBGraphics3D_AsyncFlush,
353                        OnMsgAsyncFlush)
354    IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBGraphics3D_CreateTransferBuffer,
355                        OnMsgCreateTransferBuffer)
356    IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBGraphics3D_DestroyTransferBuffer,
357                        OnMsgDestroyTransferBuffer)
358    IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBGraphics3D_GetTransferBuffer,
359                        OnMsgGetTransferBuffer)
360    IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBGraphics3D_SwapBuffers,
361                        OnMsgSwapBuffers)
362    IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBGraphics3D_InsertSyncPoint,
363                        OnMsgInsertSyncPoint)
364#endif  // !defined(OS_NACL)
365
366    IPC_MESSAGE_HANDLER(PpapiMsg_PPBGraphics3D_SwapBuffersACK,
367                        OnMsgSwapBuffersACK)
368    IPC_MESSAGE_UNHANDLED(handled = false)
369
370  IPC_END_MESSAGE_MAP()
371  // FIXME(brettw) handle bad messages!
372  return handled;
373}
374
375#if !defined(OS_NACL)
376void PPB_Graphics3D_Proxy::OnMsgCreate(PP_Instance instance,
377                                       HostResource share_context,
378                                       const std::vector<int32_t>& attribs,
379                                       HostResource* result) {
380  if (attribs.empty() ||
381      attribs.back() != PP_GRAPHICS3DATTRIB_NONE ||
382      !(attribs.size() & 1))
383    return;  // Bad message.
384
385  thunk::EnterResourceCreation enter(instance);
386
387  if (enter.succeeded()) {
388    result->SetHostResource(
389      instance,
390      enter.functions()->CreateGraphics3DRaw(instance,
391                                             share_context.host_resource(),
392                                             &attribs.front()));
393  }
394}
395
396void PPB_Graphics3D_Proxy::OnMsgSetGetBuffer(
397    const HostResource& context,
398    int32 transfer_buffer_id) {
399  EnterHostFromHostResource<PPB_Graphics3D_API> enter(context);
400  if (enter.succeeded())
401    enter.object()->SetGetBuffer(transfer_buffer_id);
402}
403
404void PPB_Graphics3D_Proxy::OnMsgGetState(const HostResource& context,
405                                         gpu::CommandBuffer::State* state,
406                                         bool* success) {
407  EnterHostFromHostResource<PPB_Graphics3D_API> enter(context);
408  if (enter.failed()) {
409    *success = false;
410    return;
411  }
412  *state = enter.object()->GetState();
413  *success = true;
414}
415
416void PPB_Graphics3D_Proxy::OnMsgFlush(const HostResource& context,
417                                      int32 put_offset,
418                                      int32 last_known_get,
419                                      gpu::CommandBuffer::State* state,
420                                      bool* success) {
421  EnterHostFromHostResource<PPB_Graphics3D_API> enter(context);
422  if (enter.failed()) {
423    *success = false;
424    return;
425  }
426  *state = enter.object()->FlushSyncFast(put_offset, last_known_get);
427  *success = true;
428}
429
430void PPB_Graphics3D_Proxy::OnMsgAsyncFlush(const HostResource& context,
431                                           int32 put_offset) {
432  EnterHostFromHostResource<PPB_Graphics3D_API> enter(context);
433  if (enter.succeeded())
434    enter.object()->Flush(put_offset);
435}
436
437void PPB_Graphics3D_Proxy::OnMsgCreateTransferBuffer(
438    const HostResource& context,
439    uint32 size,
440    int32* id) {
441  EnterHostFromHostResource<PPB_Graphics3D_API> enter(context);
442  if (enter.succeeded())
443    *id = enter.object()->CreateTransferBuffer(size);
444  else
445    *id = -1;
446}
447
448void PPB_Graphics3D_Proxy::OnMsgDestroyTransferBuffer(
449    const HostResource& context,
450    int32 id) {
451  EnterHostFromHostResource<PPB_Graphics3D_API> enter(context);
452  if (enter.succeeded())
453    enter.object()->DestroyTransferBuffer(id);
454}
455
456void PPB_Graphics3D_Proxy::OnMsgGetTransferBuffer(
457    const HostResource& context,
458    int32 id,
459    ppapi::proxy::SerializedHandle* transfer_buffer) {
460  transfer_buffer->set_null_shmem();
461
462  EnterHostFromHostResource<PPB_Graphics3D_API> enter(context);
463  int shm_handle = 0;
464  uint32_t shm_size = 0;
465  if (enter.succeeded() &&
466      enter.object()->GetTransferBuffer(id, &shm_handle, &shm_size)) {
467    transfer_buffer->set_shmem(
468        TransportSHMHandleFromInt(dispatcher(), shm_handle),
469        shm_size);
470  }
471}
472
473void PPB_Graphics3D_Proxy::OnMsgSwapBuffers(const HostResource& context) {
474  EnterHostFromHostResourceForceCallback<PPB_Graphics3D_API> enter(
475      context, callback_factory_,
476      &PPB_Graphics3D_Proxy::SendSwapBuffersACKToPlugin, context);
477  if (enter.succeeded())
478    enter.SetResult(enter.object()->SwapBuffers(enter.callback()));
479}
480
481void PPB_Graphics3D_Proxy::OnMsgInsertSyncPoint(const HostResource& context,
482                                                uint32* sync_point) {
483  *sync_point = 0;
484  EnterHostFromHostResource<PPB_Graphics3D_API> enter(context);
485  if (enter.succeeded())
486    *sync_point = enter.object()->InsertSyncPoint();
487}
488#endif  // !defined(OS_NACL)
489
490void PPB_Graphics3D_Proxy::OnMsgSwapBuffersACK(const HostResource& resource,
491                                              int32_t pp_error) {
492  EnterPluginFromHostResource<PPB_Graphics3D_API> enter(resource);
493  if (enter.succeeded())
494    static_cast<Graphics3D*>(enter.object())->SwapBuffersACK(pp_error);
495}
496
497#if !defined(OS_NACL)
498void PPB_Graphics3D_Proxy::SendSwapBuffersACKToPlugin(
499    int32_t result,
500    const HostResource& context) {
501  dispatcher()->Send(new PpapiMsg_PPBGraphics3D_SwapBuffersACK(
502      API_ID_PPB_GRAPHICS_3D, context, result));
503}
504#endif  // !defined(OS_NACL)
505
506}  // namespace proxy
507}  // namespace ppapi
508
509