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