ppb_graphics_3d_proxy.cc revision 424c4d7b64af9d0d8fd9624f381f469654d5e3d2
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 48gpu::CommandBuffer::State GetErrorState() { 49 gpu::CommandBuffer::State error_state; 50 error_state.error = gpu::error::kGenericError; 51 return error_state; 52} 53 54} // namespace 55 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. 59// 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 67// was created. 68// 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. 72class Graphics3D::LockingCommandBuffer : public gpu::CommandBuffer { 73 public: 74 explicit LockingCommandBuffer(gpu::CommandBuffer* gpu_command_buffer) 75 : gpu_command_buffer_(gpu_command_buffer), need_to_lock_(true) { 76 } 77 virtual ~LockingCommandBuffer() { 78 } 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_; } 81 82 private: 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. 86 struct MaybeLock { 87 explicit MaybeLock(bool need_to_lock) : locked_(need_to_lock) { 88 if (need_to_lock) 89 ppapi::ProxyLock::Acquire(); 90 else 91 ppapi::ProxyLock::AssertAcquired(); 92 } 93 ~MaybeLock() { 94 if (locked_) 95 ppapi::ProxyLock::Release(); 96 } 97 private: 98 bool locked_; 99 }; 100 101 // gpu::CommandBuffer implementation: 102 virtual bool Initialize() OVERRIDE { 103 MaybeLock lock(need_to_lock_); 104 return gpu_command_buffer_->Initialize(); 105 } 106 virtual State GetState() OVERRIDE { 107 MaybeLock lock(need_to_lock_); 108 return gpu_command_buffer_->GetState(); 109 } 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. 119 // 120 // MaybeLock lock(need_to_lock_); 121 return gpu_command_buffer_->GetLastState(); 122 } 123 virtual int32 GetLastToken() OVERRIDE { 124 return GetLastState().token; 125 } 126 virtual void Flush(int32 put_offset) OVERRIDE { 127 MaybeLock lock(need_to_lock_); 128 gpu_command_buffer_->Flush(put_offset); 129 } 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); 133 } 134 virtual void SetGetBuffer(int32 transfer_buffer_id) OVERRIDE { 135 MaybeLock lock(need_to_lock_); 136 gpu_command_buffer_->SetGetBuffer(transfer_buffer_id); 137 } 138 virtual void SetGetOffset(int32 get_offset) OVERRIDE { 139 MaybeLock lock(need_to_lock_); 140 gpu_command_buffer_->SetGetOffset(get_offset); 141 } 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); 146 } 147 virtual void DestroyTransferBuffer(int32 id) OVERRIDE { 148 MaybeLock lock(need_to_lock_); 149 gpu_command_buffer_->DestroyTransferBuffer(id); 150 } 151 virtual gpu::Buffer GetTransferBuffer(int32 id) OVERRIDE { 152 MaybeLock lock(need_to_lock_); 153 return gpu_command_buffer_->GetTransferBuffer(id); 154 } 155 virtual void SetToken(int32 token) OVERRIDE { 156 MaybeLock lock(need_to_lock_); 157 gpu_command_buffer_->SetToken(token); 158 } 159 virtual void SetParseError(gpu::error::Error error) OVERRIDE { 160 MaybeLock lock(need_to_lock_); 161 gpu_command_buffer_->SetParseError(error); 162 } 163 virtual void SetContextLostReason( 164 gpu::error::ContextLostReason reason) OVERRIDE { 165 MaybeLock lock(need_to_lock_); 166 gpu_command_buffer_->SetContextLostReason(reason); 167 } 168 virtual uint32 InsertSyncPoint() OVERRIDE { 169 MaybeLock lock(need_to_lock_); 170 return gpu_command_buffer_->InsertSyncPoint(); 171 } 172 173 // Weak pointer - see class Graphics3D for the scopted_ptr. 174 gpu::CommandBuffer* gpu_command_buffer_; 175 176 bool need_to_lock_; 177}; 178 179Graphics3D::Graphics3D(const HostResource& resource) 180 : PPB_Graphics3D_Shared(resource), 181 num_already_locked_calls_(0) { 182} 183 184Graphics3D::~Graphics3D() { 185 if (gles2_impl()) 186 DestroyGLES2Impl(); 187} 188 189bool Graphics3D::Init(gpu::gles2::GLES2Implementation* share_gles2) { 190 PluginDispatcher* dispatcher = PluginDispatcher::GetForResource(this); 191 if (!dispatcher) 192 return false; 193 194 command_buffer_.reset( 195 new PpapiCommandBufferProxy(host_resource(), dispatcher)); 196 locking_command_buffer_.reset( 197 new LockingCommandBuffer(command_buffer_.get())); 198 199 ScopedNoLocking already_locked(this); 200 return CreateGLES2Impl(kCommandBufferSize, kTransferBufferSize, 201 share_gles2); 202} 203 204PP_Bool Graphics3D::SetGetBuffer(int32_t /* transfer_buffer_id */) { 205 return PP_FALSE; 206} 207 208gpu::CommandBuffer::State Graphics3D::GetState() { 209 return GetErrorState(); 210} 211 212PP_Bool Graphics3D::Flush(int32_t put_offset) { 213 return PP_FALSE; 214} 215 216gpu::CommandBuffer::State Graphics3D::FlushSync(int32_t put_offset) { 217 return GetErrorState(); 218} 219 220int32_t Graphics3D::CreateTransferBuffer(uint32_t size) { 221 return PP_FALSE; 222} 223 224PP_Bool Graphics3D::DestroyTransferBuffer(int32_t id) { 225 return PP_FALSE; 226} 227 228PP_Bool Graphics3D::GetTransferBuffer(int32_t id, 229 int* shm_handle, 230 uint32_t* shm_size) { 231 return PP_FALSE; 232} 233 234gpu::CommandBuffer::State Graphics3D::FlushSyncFast(int32_t put_offset, 235 int32_t last_known_get) { 236 return GetErrorState(); 237} 238 239uint32_t Graphics3D::InsertSyncPoint() { 240 NOTREACHED(); 241 return 0; 242} 243 244gpu::CommandBuffer* Graphics3D::GetCommandBuffer() { 245 return locking_command_buffer_.get(); 246} 247 248int32 Graphics3D::DoSwapBuffers() { 249 // gles2_impl()->SwapBuffers() results in CommandBuffer calls, and we already 250 // have the proxy lock. 251 ScopedNoLocking already_locked(this); 252 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); 258 259 return PP_OK_COMPLETIONPENDING; 260} 261 262void Graphics3D::PushAlreadyLocked() { 263 ppapi::ProxyLock::AssertAcquired(); 264 if (!locking_command_buffer_) { 265 NOTREACHED(); 266 return; 267 } 268 if (num_already_locked_calls_ == 0) 269 locking_command_buffer_->set_need_to_lock(false); 270 ++num_already_locked_calls_; 271} 272 273void 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_) { 279 NOTREACHED(); 280 return; 281 } 282 --num_already_locked_calls_; 283 if (num_already_locked_calls_ == 0) 284 locking_command_buffer_->set_need_to_lock(true); 285} 286 287PPB_Graphics3D_Proxy::PPB_Graphics3D_Proxy(Dispatcher* dispatcher) 288 : InterfaceProxy(dispatcher), 289 callback_factory_(this) { 290} 291 292PPB_Graphics3D_Proxy::~PPB_Graphics3D_Proxy() { 293} 294 295// static 296PP_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); 301 if (!dispatcher) 302 return PP_ERROR_BADARGUMENT; 303 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); 308 if (enter.failed()) 309 return PP_ERROR_BADARGUMENT; 310 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(); 315 } 316 317 std::vector<int32_t> attribs; 318 if (attrib_list) { 319 for (const int32_t* attr = attrib_list; 320 attr[0] != PP_GRAPHICS3DATTRIB_NONE; 321 attr += 2) { 322 attribs.push_back(attr[0]); 323 attribs.push_back(attr[1]); 324 } 325 } 326 attribs.push_back(PP_GRAPHICS3DATTRIB_NONE); 327 328 HostResource result; 329 dispatcher->Send(new PpapiHostMsg_PPBGraphics3D_Create( 330 API_ID_PPB_GRAPHICS_3D, instance, share_host, attribs, &result)); 331 if (result.is_null()) 332 return 0; 333 334 scoped_refptr<Graphics3D> graphics_3d(new Graphics3D(result)); 335 if (!graphics_3d->Init(share_gles2)) 336 return 0; 337 return graphics_3d->GetReference(); 338} 339 340bool PPB_Graphics3D_Proxy::OnMessageReceived(const IPC::Message& msg) { 341 bool handled = true; 342 IPC_BEGIN_MESSAGE_MAP(PPB_Graphics3D_Proxy, msg) 343#if !defined(OS_NACL) 344 IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBGraphics3D_Create, 345 OnMsgCreate) 346 IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBGraphics3D_SetGetBuffer, 347 OnMsgSetGetBuffer) 348 IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBGraphics3D_GetState, 349 OnMsgGetState) 350 IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBGraphics3D_Flush, 351 OnMsgFlush) 352 IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBGraphics3D_AsyncFlush, 353 OnMsgAsyncFlush) 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, 361 OnMsgSwapBuffers) 362 IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBGraphics3D_InsertSyncPoint, 363 OnMsgInsertSyncPoint) 364#endif // !defined(OS_NACL) 365 366 IPC_MESSAGE_HANDLER(PpapiMsg_PPBGraphics3D_SwapBuffersACK, 367 OnMsgSwapBuffersACK) 368 IPC_MESSAGE_UNHANDLED(handled = false) 369 370 IPC_END_MESSAGE_MAP() 371 // FIXME(brettw) handle bad messages! 372 return handled; 373} 374 375#if !defined(OS_NACL) 376void 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. 384 385 thunk::EnterResourceCreation enter(instance); 386 387 if (enter.succeeded()) { 388 result->SetHostResource( 389 instance, 390 enter.functions()->CreateGraphics3DRaw(instance, 391 share_context.host_resource(), 392 &attribs.front())); 393 } 394} 395 396void 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); 402} 403 404void PPB_Graphics3D_Proxy::OnMsgGetState(const HostResource& context, 405 gpu::CommandBuffer::State* state, 406 bool* success) { 407 EnterHostFromHostResource<PPB_Graphics3D_API> enter(context); 408 if (enter.failed()) { 409 *success = false; 410 return; 411 } 412 *state = enter.object()->GetState(); 413 *success = true; 414} 415 416void PPB_Graphics3D_Proxy::OnMsgFlush(const HostResource& context, 417 int32 put_offset, 418 int32 last_known_get, 419 gpu::CommandBuffer::State* state, 420 bool* success) { 421 EnterHostFromHostResource<PPB_Graphics3D_API> enter(context); 422 if (enter.failed()) { 423 *success = false; 424 return; 425 } 426 *state = enter.object()->FlushSyncFast(put_offset, last_known_get); 427 *success = true; 428} 429 430void PPB_Graphics3D_Proxy::OnMsgAsyncFlush(const HostResource& context, 431 int32 put_offset) { 432 EnterHostFromHostResource<PPB_Graphics3D_API> enter(context); 433 if (enter.succeeded()) 434 enter.object()->Flush(put_offset); 435} 436 437void PPB_Graphics3D_Proxy::OnMsgCreateTransferBuffer( 438 const HostResource& context, 439 uint32 size, 440 int32* id) { 441 EnterHostFromHostResource<PPB_Graphics3D_API> enter(context); 442 if (enter.succeeded()) 443 *id = enter.object()->CreateTransferBuffer(size); 444 else 445 *id = -1; 446} 447 448void PPB_Graphics3D_Proxy::OnMsgDestroyTransferBuffer( 449 const HostResource& context, 450 int32 id) { 451 EnterHostFromHostResource<PPB_Graphics3D_API> enter(context); 452 if (enter.succeeded()) 453 enter.object()->DestroyTransferBuffer(id); 454} 455 456void PPB_Graphics3D_Proxy::OnMsgGetTransferBuffer( 457 const HostResource& context, 458 int32 id, 459 ppapi::proxy::SerializedHandle* transfer_buffer) { 460 transfer_buffer->set_null_shmem(); 461 462 EnterHostFromHostResource<PPB_Graphics3D_API> enter(context); 463 int shm_handle = 0; 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), 469 shm_size); 470 } 471} 472 473void 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())); 479} 480 481void PPB_Graphics3D_Proxy::OnMsgInsertSyncPoint(const HostResource& context, 482 uint32* sync_point) { 483 *sync_point = 0; 484 EnterHostFromHostResource<PPB_Graphics3D_API> enter(context); 485 if (enter.succeeded()) 486 *sync_point = enter.object()->InsertSyncPoint(); 487} 488#endif // !defined(OS_NACL) 489 490void PPB_Graphics3D_Proxy::OnMsgSwapBuffersACK(const HostResource& resource, 491 int32_t pp_error) { 492 EnterPluginFromHostResource<PPB_Graphics3D_API> enter(resource); 493 if (enter.succeeded()) 494 static_cast<Graphics3D*>(enter.object())->SwapBuffersACK(pp_error); 495} 496 497#if !defined(OS_NACL) 498void PPB_Graphics3D_Proxy::SendSwapBuffersACKToPlugin( 499 int32_t result, 500 const HostResource& context) { 501 dispatcher()->Send(new PpapiMsg_PPBGraphics3D_SwapBuffersACK( 502 API_ID_PPB_GRAPHICS_3D, context, result)); 503} 504#endif // !defined(OS_NACL) 505 506} // namespace proxy 507} // namespace ppapi 508 509