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