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.
5 #include "ppapi/proxy/ppb_graphics_3d_proxy.h"
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"
20 using ppapi::thunk::EnterResourceNoLock
;
21 using ppapi::thunk::PPB_Graphics3D_API
;
22 using ppapi::thunk::ResourceCreationAPI
;
29 const int32 kCommandBufferSize
= 1024 * 1024;
30 const int32 kTransferBufferSize
= 1024 * 1024;
32 base::SharedMemoryHandle
TransportSHMHandleFromInt(Dispatcher
* dispatcher
,
34 // TODO(piman): Change trusted interface to return a PP_FileHandle, those
36 base::PlatformFile source
=
38 reinterpret_cast<HANDLE
>(static_cast<intptr_t>(shm_handle
));
39 #elif defined(OS_POSIX)
42 #error Not implemented.
44 // Don't close the handle, it doesn't belong to us.
45 return dispatcher
->ShareHandleWithRemote(source
, false);
48 gpu::CommandBuffer::State
GetErrorState() {
49 gpu::CommandBuffer::State error_state
;
50 error_state
.error
= gpu::error::kGenericError
;
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.
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
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.
72 class Graphics3D::LockingCommandBuffer
: public gpu::CommandBuffer
{
74 explicit LockingCommandBuffer(gpu::CommandBuffer
* gpu_command_buffer
)
75 : gpu_command_buffer_(gpu_command_buffer
), need_to_lock_(true) {
77 virtual ~LockingCommandBuffer() {
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_
; }
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.
87 explicit MaybeLock(bool need_to_lock
) : locked_(need_to_lock
) {
89 ppapi::ProxyLock::Acquire();
91 ppapi::ProxyLock::AssertAcquired();
95 ppapi::ProxyLock::Release();
101 // gpu::CommandBuffer implementation:
102 virtual bool Initialize() OVERRIDE
{
103 MaybeLock
lock(need_to_lock_
);
104 return gpu_command_buffer_
->Initialize();
106 virtual State
GetState() OVERRIDE
{
107 MaybeLock
lock(need_to_lock_
);
108 return gpu_command_buffer_
->GetState();
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.
120 // MaybeLock lock(need_to_lock_);
121 return gpu_command_buffer_
->GetLastState();
123 virtual int32
GetLastToken() OVERRIDE
{
124 return GetLastState().token
;
126 virtual void Flush(int32 put_offset
) OVERRIDE
{
127 MaybeLock
lock(need_to_lock_
);
128 gpu_command_buffer_
->Flush(put_offset
);
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
);
134 virtual void SetGetBuffer(int32 transfer_buffer_id
) OVERRIDE
{
135 MaybeLock
lock(need_to_lock_
);
136 gpu_command_buffer_
->SetGetBuffer(transfer_buffer_id
);
138 virtual void SetGetOffset(int32 get_offset
) OVERRIDE
{
139 MaybeLock
lock(need_to_lock_
);
140 gpu_command_buffer_
->SetGetOffset(get_offset
);
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
);
147 virtual void DestroyTransferBuffer(int32 id
) OVERRIDE
{
148 MaybeLock
lock(need_to_lock_
);
149 gpu_command_buffer_
->DestroyTransferBuffer(id
);
151 virtual gpu::Buffer
GetTransferBuffer(int32 id
) OVERRIDE
{
152 MaybeLock
lock(need_to_lock_
);
153 return gpu_command_buffer_
->GetTransferBuffer(id
);
155 virtual void SetToken(int32 token
) OVERRIDE
{
156 MaybeLock
lock(need_to_lock_
);
157 gpu_command_buffer_
->SetToken(token
);
159 virtual void SetParseError(gpu::error::Error error
) OVERRIDE
{
160 MaybeLock
lock(need_to_lock_
);
161 gpu_command_buffer_
->SetParseError(error
);
163 virtual void SetContextLostReason(
164 gpu::error::ContextLostReason reason
) OVERRIDE
{
165 MaybeLock
lock(need_to_lock_
);
166 gpu_command_buffer_
->SetContextLostReason(reason
);
168 virtual uint32
InsertSyncPoint() OVERRIDE
{
169 MaybeLock
lock(need_to_lock_
);
170 return gpu_command_buffer_
->InsertSyncPoint();
173 // Weak pointer - see class Graphics3D for the scopted_ptr.
174 gpu::CommandBuffer
* gpu_command_buffer_
;
179 Graphics3D::Graphics3D(const HostResource
& resource
)
180 : PPB_Graphics3D_Shared(resource
),
181 num_already_locked_calls_(0) {
184 Graphics3D::~Graphics3D() {
189 bool Graphics3D::Init(gpu::gles2::GLES2Implementation
* share_gles2
) {
190 PluginDispatcher
* dispatcher
= PluginDispatcher::GetForResource(this);
194 command_buffer_
.reset(
195 new PpapiCommandBufferProxy(host_resource(), dispatcher
));
196 locking_command_buffer_
.reset(
197 new LockingCommandBuffer(command_buffer_
.get()));
199 ScopedNoLocking
already_locked(this);
200 return CreateGLES2Impl(kCommandBufferSize
, kTransferBufferSize
,
204 PP_Bool
Graphics3D::SetGetBuffer(int32_t /* transfer_buffer_id */) {
208 gpu::CommandBuffer::State
Graphics3D::GetState() {
209 return GetErrorState();
212 PP_Bool
Graphics3D::Flush(int32_t put_offset
) {
216 gpu::CommandBuffer::State
Graphics3D::FlushSync(int32_t put_offset
) {
217 return GetErrorState();
220 int32_t Graphics3D::CreateTransferBuffer(uint32_t size
) {
224 PP_Bool
Graphics3D::DestroyTransferBuffer(int32_t id
) {
228 PP_Bool
Graphics3D::GetTransferBuffer(int32_t id
,
230 uint32_t* shm_size
) {
234 gpu::CommandBuffer::State
Graphics3D::FlushSyncFast(int32_t put_offset
,
235 int32_t last_known_get
) {
236 return GetErrorState();
239 uint32_t Graphics3D::InsertSyncPoint() {
244 gpu::CommandBuffer
* Graphics3D::GetCommandBuffer() {
245 return locking_command_buffer_
.get();
248 int32
Graphics3D::DoSwapBuffers() {
249 // gles2_impl()->SwapBuffers() results in CommandBuffer calls, and we already
250 // have the proxy lock.
251 ScopedNoLocking
already_locked(this);
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
);
259 return PP_OK_COMPLETIONPENDING
;
262 void Graphics3D::PushAlreadyLocked() {
263 ppapi::ProxyLock::AssertAcquired();
264 if (!locking_command_buffer_
) {
268 if (num_already_locked_calls_
== 0)
269 locking_command_buffer_
->set_need_to_lock(false);
270 ++num_already_locked_calls_
;
273 void 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_
) {
282 --num_already_locked_calls_
;
283 if (num_already_locked_calls_
== 0)
284 locking_command_buffer_
->set_need_to_lock(true);
287 PPB_Graphics3D_Proxy::PPB_Graphics3D_Proxy(Dispatcher
* dispatcher
)
288 : InterfaceProxy(dispatcher
),
289 callback_factory_(this) {
292 PPB_Graphics3D_Proxy::~PPB_Graphics3D_Proxy() {
296 PP_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
);
302 return PP_ERROR_BADARGUMENT
;
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);
309 return PP_ERROR_BADARGUMENT
;
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();
317 std::vector
<int32_t> attribs
;
319 for (const int32_t* attr
= attrib_list
;
320 attr
[0] != PP_GRAPHICS3DATTRIB_NONE
;
322 attribs
.push_back(attr
[0]);
323 attribs
.push_back(attr
[1]);
326 attribs
.push_back(PP_GRAPHICS3DATTRIB_NONE
);
329 dispatcher
->Send(new PpapiHostMsg_PPBGraphics3D_Create(
330 API_ID_PPB_GRAPHICS_3D
, instance
, share_host
, attribs
, &result
));
331 if (result
.is_null())
334 scoped_refptr
<Graphics3D
> graphics_3d(new Graphics3D(result
));
335 if (!graphics_3d
->Init(share_gles2
))
337 return graphics_3d
->GetReference();
340 bool PPB_Graphics3D_Proxy::OnMessageReceived(const IPC::Message
& msg
) {
342 IPC_BEGIN_MESSAGE_MAP(PPB_Graphics3D_Proxy
, msg
)
343 #if !defined(OS_NACL)
344 IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBGraphics3D_Create
,
346 IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBGraphics3D_SetGetBuffer
,
348 IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBGraphics3D_GetState
,
350 IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBGraphics3D_Flush
,
352 IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBGraphics3D_AsyncFlush
,
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
,
362 IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBGraphics3D_InsertSyncPoint
,
363 OnMsgInsertSyncPoint
)
364 #endif // !defined(OS_NACL)
366 IPC_MESSAGE_HANDLER(PpapiMsg_PPBGraphics3D_SwapBuffersACK
,
368 IPC_MESSAGE_UNHANDLED(handled
= false)
370 IPC_END_MESSAGE_MAP()
371 // FIXME(brettw) handle bad messages!
375 #if !defined(OS_NACL)
376 void 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.
385 thunk::EnterResourceCreation
enter(instance
);
387 if (enter
.succeeded()) {
388 result
->SetHostResource(
390 enter
.functions()->CreateGraphics3DRaw(instance
,
391 share_context
.host_resource(),
396 void 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
);
404 void PPB_Graphics3D_Proxy::OnMsgGetState(const HostResource
& context
,
405 gpu::CommandBuffer::State
* state
,
407 EnterHostFromHostResource
<PPB_Graphics3D_API
> enter(context
);
408 if (enter
.failed()) {
412 *state
= enter
.object()->GetState();
416 void PPB_Graphics3D_Proxy::OnMsgFlush(const HostResource
& context
,
418 int32 last_known_get
,
419 gpu::CommandBuffer::State
* state
,
421 EnterHostFromHostResource
<PPB_Graphics3D_API
> enter(context
);
422 if (enter
.failed()) {
426 *state
= enter
.object()->FlushSyncFast(put_offset
, last_known_get
);
430 void PPB_Graphics3D_Proxy::OnMsgAsyncFlush(const HostResource
& context
,
432 EnterHostFromHostResource
<PPB_Graphics3D_API
> enter(context
);
433 if (enter
.succeeded())
434 enter
.object()->Flush(put_offset
);
437 void PPB_Graphics3D_Proxy::OnMsgCreateTransferBuffer(
438 const HostResource
& context
,
441 EnterHostFromHostResource
<PPB_Graphics3D_API
> enter(context
);
442 if (enter
.succeeded())
443 *id
= enter
.object()->CreateTransferBuffer(size
);
448 void PPB_Graphics3D_Proxy::OnMsgDestroyTransferBuffer(
449 const HostResource
& context
,
451 EnterHostFromHostResource
<PPB_Graphics3D_API
> enter(context
);
452 if (enter
.succeeded())
453 enter
.object()->DestroyTransferBuffer(id
);
456 void PPB_Graphics3D_Proxy::OnMsgGetTransferBuffer(
457 const HostResource
& context
,
459 ppapi::proxy::SerializedHandle
* transfer_buffer
) {
460 transfer_buffer
->set_null_shmem();
462 EnterHostFromHostResource
<PPB_Graphics3D_API
> enter(context
);
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
),
473 void 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()));
481 void PPB_Graphics3D_Proxy::OnMsgInsertSyncPoint(const HostResource
& context
,
482 uint32
* sync_point
) {
484 EnterHostFromHostResource
<PPB_Graphics3D_API
> enter(context
);
485 if (enter
.succeeded())
486 *sync_point
= enter
.object()->InsertSyncPoint();
488 #endif // !defined(OS_NACL)
490 void PPB_Graphics3D_Proxy::OnMsgSwapBuffersACK(const HostResource
& resource
,
492 EnterPluginFromHostResource
<PPB_Graphics3D_API
> enter(resource
);
493 if (enter
.succeeded())
494 static_cast<Graphics3D
*>(enter
.object())->SwapBuffersACK(pp_error
);
497 #if !defined(OS_NACL)
498 void PPB_Graphics3D_Proxy::SendSwapBuffersACKToPlugin(
500 const HostResource
& context
) {
501 dispatcher()->Send(new PpapiMsg_PPBGraphics3D_SwapBuffersACK(
502 API_ID_PPB_GRAPHICS_3D
, context
, result
));
504 #endif // !defined(OS_NACL)