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 PP_Graphics3DTrustedState
GetErrorState() {
49 PP_Graphics3DTrustedState error_state
= { 0 };
50 error_state
.error
= PPB_GRAPHICS3D_TRUSTED_ERROR_GENERICERROR
;
54 gpu::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
;
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.
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
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.
82 class Graphics3D::LockingCommandBuffer
: public gpu::CommandBuffer
{
84 explicit LockingCommandBuffer(gpu::CommandBuffer
* gpu_command_buffer
)
85 : gpu_command_buffer_(gpu_command_buffer
), need_to_lock_(true) {
87 virtual ~LockingCommandBuffer() {
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_
; }
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.
97 explicit MaybeLock(bool need_to_lock
) : locked_(need_to_lock
) {
99 ppapi::ProxyLock::Acquire();
101 ppapi::ProxyLock::AssertAcquired();
105 ppapi::ProxyLock::Release();
111 // gpu::CommandBuffer implementation:
112 virtual bool Initialize() OVERRIDE
{
113 MaybeLock
lock(need_to_lock_
);
114 return gpu_command_buffer_
->Initialize();
116 virtual State
GetState() OVERRIDE
{
117 MaybeLock
lock(need_to_lock_
);
118 return gpu_command_buffer_
->GetState();
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.
130 // MaybeLock lock(need_to_lock_);
131 return gpu_command_buffer_
->GetLastState();
133 virtual void Flush(int32 put_offset
) OVERRIDE
{
134 MaybeLock
lock(need_to_lock_
);
135 gpu_command_buffer_
->Flush(put_offset
);
137 virtual State
FlushSync(int32 put_offset
, int32 last_known_get
) OVERRIDE
{
138 MaybeLock
lock(need_to_lock_
);
139 return gpu_command_buffer_
->FlushSync(put_offset
, last_known_get
);
141 virtual void SetGetBuffer(int32 transfer_buffer_id
) OVERRIDE
{
142 MaybeLock
lock(need_to_lock_
);
143 gpu_command_buffer_
->SetGetBuffer(transfer_buffer_id
);
145 virtual void SetGetOffset(int32 get_offset
) OVERRIDE
{
146 MaybeLock
lock(need_to_lock_
);
147 gpu_command_buffer_
->SetGetOffset(get_offset
);
149 virtual int32
CreateTransferBuffer(size_t size
, int32 id_request
) OVERRIDE
{
150 MaybeLock
lock(need_to_lock_
);
151 return gpu_command_buffer_
->CreateTransferBuffer(size
, id_request
);
153 virtual int32
RegisterTransferBuffer(base::SharedMemory
* shared_memory
,
155 int32 id_request
) OVERRIDE
{
156 MaybeLock
lock(need_to_lock_
);
157 return gpu_command_buffer_
->RegisterTransferBuffer(shared_memory
, size
,
160 virtual void DestroyTransferBuffer(int32 id
) OVERRIDE
{
161 MaybeLock
lock(need_to_lock_
);
162 gpu_command_buffer_
->DestroyTransferBuffer(id
);
164 virtual gpu::Buffer
GetTransferBuffer(int32 handle
) OVERRIDE
{
165 MaybeLock
lock(need_to_lock_
);
166 return gpu_command_buffer_
->GetTransferBuffer(handle
);
168 virtual void SetToken(int32 token
) OVERRIDE
{
169 MaybeLock
lock(need_to_lock_
);
170 gpu_command_buffer_
->SetToken(token
);
172 virtual void SetParseError(gpu::error::Error error
) OVERRIDE
{
173 MaybeLock
lock(need_to_lock_
);
174 gpu_command_buffer_
->SetParseError(error
);
176 virtual void SetContextLostReason(
177 gpu::error::ContextLostReason reason
) OVERRIDE
{
178 MaybeLock
lock(need_to_lock_
);
179 gpu_command_buffer_
->SetContextLostReason(reason
);
182 // Weak pointer - see class Graphics3D for the scopted_ptr.
183 gpu::CommandBuffer
* gpu_command_buffer_
;
188 Graphics3D::Graphics3D(const HostResource
& resource
)
189 : PPB_Graphics3D_Shared(resource
),
190 num_already_locked_calls_(0) {
193 Graphics3D::~Graphics3D() {
197 bool Graphics3D::Init(gpu::gles2::GLES2Implementation
* share_gles2
) {
198 PluginDispatcher
* dispatcher
= PluginDispatcher::GetForResource(this);
202 command_buffer_
.reset(
203 new PpapiCommandBufferProxy(host_resource(), dispatcher
));
204 locking_command_buffer_
.reset(
205 new LockingCommandBuffer(command_buffer_
.get()));
207 ScopedNoLocking
already_locked(this);
208 if (!command_buffer_
->Initialize())
211 return CreateGLES2Impl(kCommandBufferSize
, kTransferBufferSize
,
215 PP_Bool
Graphics3D::InitCommandBuffer() {
219 PP_Bool
Graphics3D::SetGetBuffer(int32_t /* transfer_buffer_id */) {
223 PP_Graphics3DTrustedState
Graphics3D::GetState() {
224 return GetErrorState();
227 PP_Bool
Graphics3D::Flush(int32_t put_offset
) {
231 PP_Graphics3DTrustedState
Graphics3D::FlushSync(int32_t put_offset
) {
232 return GetErrorState();
235 int32_t Graphics3D::CreateTransferBuffer(uint32_t size
) {
239 PP_Bool
Graphics3D::DestroyTransferBuffer(int32_t id
) {
243 PP_Bool
Graphics3D::GetTransferBuffer(int32_t id
,
245 uint32_t* shm_size
) {
249 PP_Graphics3DTrustedState
Graphics3D::FlushSyncFast(int32_t put_offset
,
250 int32_t last_known_get
) {
251 return GetErrorState();
254 gpu::CommandBuffer
* Graphics3D::GetCommandBuffer() {
255 return locking_command_buffer_
.get();
258 int32
Graphics3D::DoSwapBuffers() {
259 // gles2_impl()->SwapBuffers() results in CommandBuffer calls, and we already
260 // have the proxy lock.
261 ScopedNoLocking
already_locked(this);
263 gles2_impl()->SwapBuffers();
264 IPC::Message
* msg
= new PpapiHostMsg_PPBGraphics3D_SwapBuffers(
265 API_ID_PPB_GRAPHICS_3D
, host_resource());
266 msg
->set_unblock(true);
267 PluginDispatcher::GetForResource(this)->Send(msg
);
269 return PP_OK_COMPLETIONPENDING
;
272 void Graphics3D::PushAlreadyLocked() {
273 ppapi::ProxyLock::AssertAcquired();
274 if (num_already_locked_calls_
== 0)
275 locking_command_buffer_
->set_need_to_lock(false);
276 ++num_already_locked_calls_
;
279 void Graphics3D::PopAlreadyLocked() {
280 // We must have Pushed before we can Pop.
281 DCHECK(!locking_command_buffer_
->need_to_lock());
282 DCHECK_GT(num_already_locked_calls_
, 0);
283 ppapi::ProxyLock::AssertAcquired();
284 --num_already_locked_calls_
;
285 if (num_already_locked_calls_
== 0)
286 locking_command_buffer_
->set_need_to_lock(true);
289 PPB_Graphics3D_Proxy::PPB_Graphics3D_Proxy(Dispatcher
* dispatcher
)
290 : InterfaceProxy(dispatcher
),
291 callback_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) {
294 PPB_Graphics3D_Proxy::~PPB_Graphics3D_Proxy() {
298 PP_Resource
PPB_Graphics3D_Proxy::CreateProxyResource(
299 PP_Instance instance
,
300 PP_Resource share_context
,
301 const int32_t* attrib_list
) {
302 PluginDispatcher
* dispatcher
= PluginDispatcher::GetForInstance(instance
);
304 return PP_ERROR_BADARGUMENT
;
306 HostResource share_host
;
307 gpu::gles2::GLES2Implementation
* share_gles2
= NULL
;
308 if (share_context
!= 0) {
309 EnterResourceNoLock
<PPB_Graphics3D_API
> enter(share_context
, true);
311 return PP_ERROR_BADARGUMENT
;
313 PPB_Graphics3D_Shared
* share_graphics
=
314 static_cast<PPB_Graphics3D_Shared
*>(enter
.object());
315 share_host
= share_graphics
->host_resource();
316 share_gles2
= share_graphics
->gles2_impl();
319 std::vector
<int32_t> attribs
;
321 for (const int32_t* attr
= attrib_list
;
322 attr
[0] != PP_GRAPHICS3DATTRIB_NONE
;
324 attribs
.push_back(attr
[0]);
325 attribs
.push_back(attr
[1]);
328 attribs
.push_back(PP_GRAPHICS3DATTRIB_NONE
);
331 dispatcher
->Send(new PpapiHostMsg_PPBGraphics3D_Create(
332 API_ID_PPB_GRAPHICS_3D
, instance
, share_host
, attribs
, &result
));
333 if (result
.is_null())
336 scoped_refptr
<Graphics3D
> graphics_3d(new Graphics3D(result
));
337 if (!graphics_3d
->Init(share_gles2
))
339 return graphics_3d
->GetReference();
342 bool PPB_Graphics3D_Proxy::OnMessageReceived(const IPC::Message
& msg
) {
344 IPC_BEGIN_MESSAGE_MAP(PPB_Graphics3D_Proxy
, msg
)
345 #if !defined(OS_NACL)
346 IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBGraphics3D_Create
,
348 IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBGraphics3D_InitCommandBuffer
,
349 OnMsgInitCommandBuffer
)
350 IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBGraphics3D_SetGetBuffer
,
352 IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBGraphics3D_GetState
,
354 IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBGraphics3D_Flush
,
356 IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBGraphics3D_AsyncFlush
,
358 IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBGraphics3D_CreateTransferBuffer
,
359 OnMsgCreateTransferBuffer
)
360 IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBGraphics3D_DestroyTransferBuffer
,
361 OnMsgDestroyTransferBuffer
)
362 IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBGraphics3D_GetTransferBuffer
,
363 OnMsgGetTransferBuffer
)
364 IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBGraphics3D_SwapBuffers
,
366 #endif // !defined(OS_NACL)
368 IPC_MESSAGE_HANDLER(PpapiMsg_PPBGraphics3D_SwapBuffersACK
,
370 IPC_MESSAGE_UNHANDLED(handled
= false)
372 IPC_END_MESSAGE_MAP()
373 // FIXME(brettw) handle bad messages!
377 #if !defined(OS_NACL)
378 void PPB_Graphics3D_Proxy::OnMsgCreate(PP_Instance instance
,
379 HostResource share_context
,
380 const std::vector
<int32_t>& attribs
,
381 HostResource
* result
) {
382 if (attribs
.empty() ||
383 attribs
.back() != PP_GRAPHICS3DATTRIB_NONE
||
384 !(attribs
.size() & 1))
385 return; // Bad message.
387 thunk::EnterResourceCreation
enter(instance
);
389 if (enter
.succeeded()) {
390 result
->SetHostResource(
392 enter
.functions()->CreateGraphics3DRaw(instance
,
393 share_context
.host_resource(),
398 void PPB_Graphics3D_Proxy::OnMsgInitCommandBuffer(
399 const HostResource
& context
) {
400 EnterHostFromHostResource
<PPB_Graphics3D_API
> enter(context
);
404 if (!enter
.object()->InitCommandBuffer())
408 void PPB_Graphics3D_Proxy::OnMsgSetGetBuffer(
409 const HostResource
& context
,
410 int32 transfer_buffer_id
) {
411 EnterHostFromHostResource
<PPB_Graphics3D_API
> enter(context
);
412 if (enter
.succeeded())
413 enter
.object()->SetGetBuffer(transfer_buffer_id
);
416 void PPB_Graphics3D_Proxy::OnMsgGetState(const HostResource
& context
,
417 gpu::CommandBuffer::State
* state
,
419 EnterHostFromHostResource
<PPB_Graphics3D_API
> enter(context
);
420 if (enter
.failed()) {
424 PP_Graphics3DTrustedState pp_state
= enter
.object()->GetState();
425 *state
= GPUStateFromPPState(pp_state
);
429 void PPB_Graphics3D_Proxy::OnMsgFlush(const HostResource
& context
,
431 int32 last_known_get
,
432 gpu::CommandBuffer::State
* state
,
434 EnterHostFromHostResource
<PPB_Graphics3D_API
> enter(context
);
435 if (enter
.failed()) {
439 PP_Graphics3DTrustedState pp_state
= enter
.object()->FlushSyncFast(
440 put_offset
, last_known_get
);
441 *state
= GPUStateFromPPState(pp_state
);
445 void PPB_Graphics3D_Proxy::OnMsgAsyncFlush(const HostResource
& context
,
447 EnterHostFromHostResource
<PPB_Graphics3D_API
> enter(context
);
448 if (enter
.succeeded())
449 enter
.object()->Flush(put_offset
);
452 void PPB_Graphics3D_Proxy::OnMsgCreateTransferBuffer(
453 const HostResource
& context
,
456 EnterHostFromHostResource
<PPB_Graphics3D_API
> enter(context
);
457 if (enter
.succeeded())
458 *id
= enter
.object()->CreateTransferBuffer(size
);
463 void PPB_Graphics3D_Proxy::OnMsgDestroyTransferBuffer(
464 const HostResource
& context
,
466 EnterHostFromHostResource
<PPB_Graphics3D_API
> enter(context
);
467 if (enter
.succeeded())
468 enter
.object()->DestroyTransferBuffer(id
);
471 void PPB_Graphics3D_Proxy::OnMsgGetTransferBuffer(
472 const HostResource
& context
,
474 ppapi::proxy::SerializedHandle
* transfer_buffer
) {
475 transfer_buffer
->set_null_shmem();
477 EnterHostFromHostResource
<PPB_Graphics3D_API
> enter(context
);
479 uint32_t shm_size
= 0;
480 if (enter
.succeeded() &&
481 enter
.object()->GetTransferBuffer(id
, &shm_handle
, &shm_size
)) {
482 transfer_buffer
->set_shmem(
483 TransportSHMHandleFromInt(dispatcher(), shm_handle
),
488 void PPB_Graphics3D_Proxy::OnMsgSwapBuffers(const HostResource
& context
) {
489 EnterHostFromHostResourceForceCallback
<PPB_Graphics3D_API
> enter(
490 context
, callback_factory_
,
491 &PPB_Graphics3D_Proxy::SendSwapBuffersACKToPlugin
, context
);
492 if (enter
.succeeded())
493 enter
.SetResult(enter
.object()->SwapBuffers(enter
.callback()));
495 #endif // !defined(OS_NACL)
497 void PPB_Graphics3D_Proxy::OnMsgSwapBuffersACK(const HostResource
& resource
,
499 EnterPluginFromHostResource
<PPB_Graphics3D_API
> enter(resource
);
500 if (enter
.succeeded())
501 static_cast<Graphics3D
*>(enter
.object())->SwapBuffersACK(pp_error
);
504 #if !defined(OS_NACL)
505 void PPB_Graphics3D_Proxy::SendSwapBuffersACKToPlugin(
507 const HostResource
& context
) {
508 dispatcher()->Send(new PpapiMsg_PPBGraphics3D_SwapBuffersACK(
509 API_ID_PPB_GRAPHICS_3D
, context
, result
));
511 #endif // !defined(OS_NACL)