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
67 // TODO(nfullagar): At some point, allow multiple threads to concurrently render
68 // each to its own context. First step is to allow a single thread (either main
69 // thread or background thread) to render to a single Graphics3D context.
70 class Graphics3D::LockingCommandBuffer
: public gpu::CommandBuffer
{
72 explicit LockingCommandBuffer(gpu::CommandBuffer
* gpu_command_buffer
)
73 : gpu_command_buffer_(gpu_command_buffer
), need_to_lock_(true) {
75 virtual ~LockingCommandBuffer() {
77 void set_need_to_lock(bool need_to_lock
) { need_to_lock_
= need_to_lock
; }
78 bool need_to_lock() const { return need_to_lock_
; }
81 // MaybeLock acquires the proxy lock on construction if and only if
82 // need_to_lock is true. If it acquired the lock, it releases it on
83 // destruction. If need_to_lock is false, then the lock must already be held.
85 explicit MaybeLock(bool need_to_lock
) : locked_(need_to_lock
) {
87 ppapi::ProxyLock::Acquire();
89 ppapi::ProxyLock::AssertAcquired();
93 ppapi::ProxyLock::Release();
99 // gpu::CommandBuffer implementation:
100 virtual bool Initialize() OVERRIDE
{
101 MaybeLock
lock(need_to_lock_
);
102 return gpu_command_buffer_
->Initialize();
104 virtual State
GetState() OVERRIDE
{
105 MaybeLock
lock(need_to_lock_
);
106 return gpu_command_buffer_
->GetState();
108 virtual State
GetLastState() OVERRIDE
{
109 // During a normal scene, the vast majority of calls are to GetLastState().
110 // We don't allow multi-threaded rendering on the same contex, so for
111 // performance reasons, avoid the global lock for this entry point. We can
112 // get away with this here because the underlying implementation of
113 // GetLastState() is trivial and does not involve global or shared state
114 // between other contexts.
115 // TODO(nfullagar): We can probably skip MaybeLock for other methods, but
116 // the performance gain may not be worth it.
118 // MaybeLock lock(need_to_lock_);
119 return gpu_command_buffer_
->GetLastState();
121 virtual int32
GetLastToken() OVERRIDE
{
122 return GetLastState().token
;
124 virtual void Flush(int32 put_offset
) OVERRIDE
{
125 MaybeLock
lock(need_to_lock_
);
126 gpu_command_buffer_
->Flush(put_offset
);
128 virtual State
FlushSync(int32 put_offset
, int32 last_known_get
) OVERRIDE
{
129 MaybeLock
lock(need_to_lock_
);
130 return gpu_command_buffer_
->FlushSync(put_offset
, last_known_get
);
132 virtual void SetGetBuffer(int32 transfer_buffer_id
) OVERRIDE
{
133 MaybeLock
lock(need_to_lock_
);
134 gpu_command_buffer_
->SetGetBuffer(transfer_buffer_id
);
136 virtual void SetGetOffset(int32 get_offset
) OVERRIDE
{
137 MaybeLock
lock(need_to_lock_
);
138 gpu_command_buffer_
->SetGetOffset(get_offset
);
140 virtual gpu::Buffer
CreateTransferBuffer(size_t size
,
141 int32
* id
) OVERRIDE
{
142 MaybeLock
lock(need_to_lock_
);
143 return gpu_command_buffer_
->CreateTransferBuffer(size
, id
);
145 virtual void DestroyTransferBuffer(int32 id
) OVERRIDE
{
146 MaybeLock
lock(need_to_lock_
);
147 gpu_command_buffer_
->DestroyTransferBuffer(id
);
149 virtual gpu::Buffer
GetTransferBuffer(int32 id
) OVERRIDE
{
150 MaybeLock
lock(need_to_lock_
);
151 return gpu_command_buffer_
->GetTransferBuffer(id
);
153 virtual void SetToken(int32 token
) OVERRIDE
{
154 MaybeLock
lock(need_to_lock_
);
155 gpu_command_buffer_
->SetToken(token
);
157 virtual void SetParseError(gpu::error::Error error
) OVERRIDE
{
158 MaybeLock
lock(need_to_lock_
);
159 gpu_command_buffer_
->SetParseError(error
);
161 virtual void SetContextLostReason(
162 gpu::error::ContextLostReason reason
) OVERRIDE
{
163 MaybeLock
lock(need_to_lock_
);
164 gpu_command_buffer_
->SetContextLostReason(reason
);
166 virtual uint32
InsertSyncPoint() OVERRIDE
{
167 MaybeLock
lock(need_to_lock_
);
168 return gpu_command_buffer_
->InsertSyncPoint();
171 // Weak pointer - see class Graphics3D for the scopted_ptr.
172 gpu::CommandBuffer
* gpu_command_buffer_
;
177 Graphics3D::Graphics3D(const HostResource
& resource
)
178 : PPB_Graphics3D_Shared(resource
),
179 num_already_locked_calls_(0) {
182 Graphics3D::~Graphics3D() {
186 bool Graphics3D::Init(gpu::gles2::GLES2Implementation
* share_gles2
) {
187 PluginDispatcher
* dispatcher
= PluginDispatcher::GetForResource(this);
191 command_buffer_
.reset(
192 new PpapiCommandBufferProxy(host_resource(), dispatcher
));
193 locking_command_buffer_
.reset(
194 new LockingCommandBuffer(command_buffer_
.get()));
196 ScopedNoLocking
already_locked(this);
197 return CreateGLES2Impl(kCommandBufferSize
, kTransferBufferSize
,
201 PP_Bool
Graphics3D::SetGetBuffer(int32_t /* transfer_buffer_id */) {
205 gpu::CommandBuffer::State
Graphics3D::GetState() {
206 return GetErrorState();
209 PP_Bool
Graphics3D::Flush(int32_t put_offset
) {
213 gpu::CommandBuffer::State
Graphics3D::FlushSync(int32_t put_offset
) {
214 return GetErrorState();
217 int32_t Graphics3D::CreateTransferBuffer(uint32_t size
) {
221 PP_Bool
Graphics3D::DestroyTransferBuffer(int32_t id
) {
225 PP_Bool
Graphics3D::GetTransferBuffer(int32_t id
,
227 uint32_t* shm_size
) {
231 gpu::CommandBuffer::State
Graphics3D::FlushSyncFast(int32_t put_offset
,
232 int32_t last_known_get
) {
233 return GetErrorState();
236 uint32_t Graphics3D::InsertSyncPoint() {
241 gpu::CommandBuffer
* Graphics3D::GetCommandBuffer() {
242 return locking_command_buffer_
.get();
245 int32
Graphics3D::DoSwapBuffers() {
246 // gles2_impl()->SwapBuffers() results in CommandBuffer calls, and we already
247 // have the proxy lock.
248 ScopedNoLocking
already_locked(this);
250 gles2_impl()->SwapBuffers();
251 IPC::Message
* msg
= new PpapiHostMsg_PPBGraphics3D_SwapBuffers(
252 API_ID_PPB_GRAPHICS_3D
, host_resource());
253 msg
->set_unblock(true);
254 PluginDispatcher::GetForResource(this)->Send(msg
);
256 return PP_OK_COMPLETIONPENDING
;
259 void Graphics3D::PushAlreadyLocked() {
260 ppapi::ProxyLock::AssertAcquired();
261 if (num_already_locked_calls_
== 0)
262 locking_command_buffer_
->set_need_to_lock(false);
263 ++num_already_locked_calls_
;
266 void Graphics3D::PopAlreadyLocked() {
267 // We must have Pushed before we can Pop.
268 DCHECK(!locking_command_buffer_
->need_to_lock());
269 DCHECK_GT(num_already_locked_calls_
, 0);
270 ppapi::ProxyLock::AssertAcquired();
271 --num_already_locked_calls_
;
272 if (num_already_locked_calls_
== 0)
273 locking_command_buffer_
->set_need_to_lock(true);
276 PPB_Graphics3D_Proxy::PPB_Graphics3D_Proxy(Dispatcher
* dispatcher
)
277 : InterfaceProxy(dispatcher
),
278 callback_factory_(this) {
281 PPB_Graphics3D_Proxy::~PPB_Graphics3D_Proxy() {
285 PP_Resource
PPB_Graphics3D_Proxy::CreateProxyResource(
286 PP_Instance instance
,
287 PP_Resource share_context
,
288 const int32_t* attrib_list
) {
289 PluginDispatcher
* dispatcher
= PluginDispatcher::GetForInstance(instance
);
291 return PP_ERROR_BADARGUMENT
;
293 HostResource share_host
;
294 gpu::gles2::GLES2Implementation
* share_gles2
= NULL
;
295 if (share_context
!= 0) {
296 EnterResourceNoLock
<PPB_Graphics3D_API
> enter(share_context
, true);
298 return PP_ERROR_BADARGUMENT
;
300 PPB_Graphics3D_Shared
* share_graphics
=
301 static_cast<PPB_Graphics3D_Shared
*>(enter
.object());
302 share_host
= share_graphics
->host_resource();
303 share_gles2
= share_graphics
->gles2_impl();
306 std::vector
<int32_t> attribs
;
308 for (const int32_t* attr
= attrib_list
;
309 attr
[0] != PP_GRAPHICS3DATTRIB_NONE
;
311 attribs
.push_back(attr
[0]);
312 attribs
.push_back(attr
[1]);
315 attribs
.push_back(PP_GRAPHICS3DATTRIB_NONE
);
318 dispatcher
->Send(new PpapiHostMsg_PPBGraphics3D_Create(
319 API_ID_PPB_GRAPHICS_3D
, instance
, share_host
, attribs
, &result
));
320 if (result
.is_null())
323 scoped_refptr
<Graphics3D
> graphics_3d(new Graphics3D(result
));
324 if (!graphics_3d
->Init(share_gles2
))
326 return graphics_3d
->GetReference();
329 bool PPB_Graphics3D_Proxy::OnMessageReceived(const IPC::Message
& msg
) {
331 IPC_BEGIN_MESSAGE_MAP(PPB_Graphics3D_Proxy
, msg
)
332 #if !defined(OS_NACL)
333 IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBGraphics3D_Create
,
335 IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBGraphics3D_SetGetBuffer
,
337 IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBGraphics3D_GetState
,
339 IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBGraphics3D_Flush
,
341 IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBGraphics3D_AsyncFlush
,
343 IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBGraphics3D_CreateTransferBuffer
,
344 OnMsgCreateTransferBuffer
)
345 IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBGraphics3D_DestroyTransferBuffer
,
346 OnMsgDestroyTransferBuffer
)
347 IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBGraphics3D_GetTransferBuffer
,
348 OnMsgGetTransferBuffer
)
349 IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBGraphics3D_SwapBuffers
,
351 IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBGraphics3D_InsertSyncPoint
,
352 OnMsgInsertSyncPoint
)
353 #endif // !defined(OS_NACL)
355 IPC_MESSAGE_HANDLER(PpapiMsg_PPBGraphics3D_SwapBuffersACK
,
357 IPC_MESSAGE_UNHANDLED(handled
= false)
359 IPC_END_MESSAGE_MAP()
360 // FIXME(brettw) handle bad messages!
364 #if !defined(OS_NACL)
365 void PPB_Graphics3D_Proxy::OnMsgCreate(PP_Instance instance
,
366 HostResource share_context
,
367 const std::vector
<int32_t>& attribs
,
368 HostResource
* result
) {
369 if (attribs
.empty() ||
370 attribs
.back() != PP_GRAPHICS3DATTRIB_NONE
||
371 !(attribs
.size() & 1))
372 return; // Bad message.
374 thunk::EnterResourceCreation
enter(instance
);
376 if (enter
.succeeded()) {
377 result
->SetHostResource(
379 enter
.functions()->CreateGraphics3DRaw(instance
,
380 share_context
.host_resource(),
385 void PPB_Graphics3D_Proxy::OnMsgSetGetBuffer(
386 const HostResource
& context
,
387 int32 transfer_buffer_id
) {
388 EnterHostFromHostResource
<PPB_Graphics3D_API
> enter(context
);
389 if (enter
.succeeded())
390 enter
.object()->SetGetBuffer(transfer_buffer_id
);
393 void PPB_Graphics3D_Proxy::OnMsgGetState(const HostResource
& context
,
394 gpu::CommandBuffer::State
* state
,
396 EnterHostFromHostResource
<PPB_Graphics3D_API
> enter(context
);
397 if (enter
.failed()) {
401 *state
= enter
.object()->GetState();
405 void PPB_Graphics3D_Proxy::OnMsgFlush(const HostResource
& context
,
407 int32 last_known_get
,
408 gpu::CommandBuffer::State
* state
,
410 EnterHostFromHostResource
<PPB_Graphics3D_API
> enter(context
);
411 if (enter
.failed()) {
415 *state
= enter
.object()->FlushSyncFast(put_offset
, last_known_get
);
419 void PPB_Graphics3D_Proxy::OnMsgAsyncFlush(const HostResource
& context
,
421 EnterHostFromHostResource
<PPB_Graphics3D_API
> enter(context
);
422 if (enter
.succeeded())
423 enter
.object()->Flush(put_offset
);
426 void PPB_Graphics3D_Proxy::OnMsgCreateTransferBuffer(
427 const HostResource
& context
,
430 EnterHostFromHostResource
<PPB_Graphics3D_API
> enter(context
);
431 if (enter
.succeeded())
432 *id
= enter
.object()->CreateTransferBuffer(size
);
437 void PPB_Graphics3D_Proxy::OnMsgDestroyTransferBuffer(
438 const HostResource
& context
,
440 EnterHostFromHostResource
<PPB_Graphics3D_API
> enter(context
);
441 if (enter
.succeeded())
442 enter
.object()->DestroyTransferBuffer(id
);
445 void PPB_Graphics3D_Proxy::OnMsgGetTransferBuffer(
446 const HostResource
& context
,
448 ppapi::proxy::SerializedHandle
* transfer_buffer
) {
449 transfer_buffer
->set_null_shmem();
451 EnterHostFromHostResource
<PPB_Graphics3D_API
> enter(context
);
453 uint32_t shm_size
= 0;
454 if (enter
.succeeded() &&
455 enter
.object()->GetTransferBuffer(id
, &shm_handle
, &shm_size
)) {
456 transfer_buffer
->set_shmem(
457 TransportSHMHandleFromInt(dispatcher(), shm_handle
),
462 void PPB_Graphics3D_Proxy::OnMsgSwapBuffers(const HostResource
& context
) {
463 EnterHostFromHostResourceForceCallback
<PPB_Graphics3D_API
> enter(
464 context
, callback_factory_
,
465 &PPB_Graphics3D_Proxy::SendSwapBuffersACKToPlugin
, context
);
466 if (enter
.succeeded())
467 enter
.SetResult(enter
.object()->SwapBuffers(enter
.callback()));
470 void PPB_Graphics3D_Proxy::OnMsgInsertSyncPoint(const HostResource
& context
,
471 uint32
* sync_point
) {
473 EnterHostFromHostResource
<PPB_Graphics3D_API
> enter(context
);
474 if (enter
.succeeded())
475 *sync_point
= enter
.object()->InsertSyncPoint();
477 #endif // !defined(OS_NACL)
479 void PPB_Graphics3D_Proxy::OnMsgSwapBuffersACK(const HostResource
& resource
,
481 EnterPluginFromHostResource
<PPB_Graphics3D_API
> enter(resource
);
482 if (enter
.succeeded())
483 static_cast<Graphics3D
*>(enter
.object())->SwapBuffersACK(pp_error
);
486 #if !defined(OS_NACL)
487 void PPB_Graphics3D_Proxy::SendSwapBuffersACKToPlugin(
489 const HostResource
& context
) {
490 dispatcher()->Send(new PpapiMsg_PPBGraphics3D_SwapBuffersACK(
491 API_ID_PPB_GRAPHICS_3D
, context
, result
));
493 #endif // !defined(OS_NACL)