1 // Copyright 2013 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 "gpu/command_buffer/service/async_pixel_transfer_manager_share_group.h" 6 7 #include <list> 8 9 #include "base/bind.h" 10 #include "base/debug/trace_event.h" 11 #include "base/lazy_instance.h" 12 #include "base/logging.h" 13 #include "base/memory/ref_counted.h" 14 #include "base/memory/weak_ptr.h" 15 #include "base/synchronization/cancellation_flag.h" 16 #include "base/synchronization/lock.h" 17 #include "base/synchronization/waitable_event.h" 18 #include "base/threading/thread.h" 19 #include "base/threading/thread_checker.h" 20 #include "gpu/command_buffer/service/async_pixel_transfer_delegate.h" 21 #include "gpu/command_buffer/service/safe_shared_memory_pool.h" 22 #include "ui/gl/gl_bindings.h" 23 #include "ui/gl/gl_context.h" 24 #include "ui/gl/gl_surface.h" 25 #include "ui/gl/gpu_preference.h" 26 #include "ui/gl/scoped_binders.h" 27 28 namespace gpu { 29 30 namespace { 31 32 const char kAsyncTransferThreadName[] = "AsyncTransferThread"; 33 34 void PerformNotifyCompletion( 35 AsyncMemoryParams mem_params, 36 ScopedSafeSharedMemory* safe_shared_memory, 37 scoped_refptr<AsyncPixelTransferCompletionObserver> observer) { 38 TRACE_EVENT0("gpu", "PerformNotifyCompletion"); 39 AsyncMemoryParams safe_mem_params = mem_params; 40 safe_mem_params.shared_memory = safe_shared_memory->shared_memory(); 41 observer->DidComplete(safe_mem_params); 42 } 43 44 // TODO(backer): Factor out common thread scheduling logic from the EGL and 45 // ShareGroup implementations. http://crbug.com/239889 46 class TransferThread : public base::Thread { 47 public: 48 TransferThread() 49 : base::Thread(kAsyncTransferThreadName), 50 initialized_(false) { 51 Start(); 52 #if defined(OS_ANDROID) || defined(OS_LINUX) 53 SetPriority(base::kThreadPriority_Background); 54 #endif 55 } 56 57 virtual ~TransferThread() { 58 // The only instance of this class was declared leaky. 59 NOTREACHED(); 60 } 61 62 void InitializeOnMainThread(gfx::GLContext* parent_context) { 63 TRACE_EVENT0("gpu", "TransferThread::InitializeOnMainThread"); 64 if (initialized_) 65 return; 66 67 base::WaitableEvent wait_for_init(true, false); 68 message_loop_proxy()->PostTask( 69 FROM_HERE, 70 base::Bind(&TransferThread::InitializeOnTransferThread, 71 base::Unretained(this), 72 base::Unretained(parent_context), 73 &wait_for_init)); 74 wait_for_init.Wait(); 75 } 76 77 virtual void CleanUp() OVERRIDE { 78 surface_ = NULL; 79 context_ = NULL; 80 } 81 82 SafeSharedMemoryPool* safe_shared_memory_pool() { 83 return &safe_shared_memory_pool_; 84 } 85 86 private: 87 bool initialized_; 88 89 scoped_refptr<gfx::GLSurface> surface_; 90 scoped_refptr<gfx::GLContext> context_; 91 SafeSharedMemoryPool safe_shared_memory_pool_; 92 93 void InitializeOnTransferThread(gfx::GLContext* parent_context, 94 base::WaitableEvent* caller_wait) { 95 TRACE_EVENT0("gpu", "InitializeOnTransferThread"); 96 97 if (!parent_context) { 98 LOG(ERROR) << "No parent context provided."; 99 caller_wait->Signal(); 100 return; 101 } 102 103 surface_ = gfx::GLSurface::CreateOffscreenGLSurface(gfx::Size(1, 1)); 104 if (!surface_.get()) { 105 LOG(ERROR) << "Unable to create GLSurface"; 106 caller_wait->Signal(); 107 return; 108 } 109 110 // TODO(backer): This is coded for integrated GPUs. For discrete GPUs 111 // we would probably want to use a PBO texture upload for a true async 112 // upload (that would hopefully be optimized as a DMA transfer by the 113 // driver). 114 context_ = gfx::GLContext::CreateGLContext(parent_context->share_group(), 115 surface_.get(), 116 gfx::PreferIntegratedGpu); 117 if (!context_.get()) { 118 LOG(ERROR) << "Unable to create GLContext."; 119 caller_wait->Signal(); 120 return; 121 } 122 123 context_->MakeCurrent(surface_.get()); 124 initialized_ = true; 125 caller_wait->Signal(); 126 } 127 128 DISALLOW_COPY_AND_ASSIGN(TransferThread); 129 }; 130 131 base::LazyInstance<TransferThread>::Leaky 132 g_transfer_thread = LAZY_INSTANCE_INITIALIZER; 133 134 base::MessageLoopProxy* transfer_message_loop_proxy() { 135 return g_transfer_thread.Pointer()->message_loop_proxy().get(); 136 } 137 138 SafeSharedMemoryPool* safe_shared_memory_pool() { 139 return g_transfer_thread.Pointer()->safe_shared_memory_pool(); 140 } 141 142 class PendingTask : public base::RefCountedThreadSafe<PendingTask> { 143 public: 144 explicit PendingTask(const base::Closure& task) 145 : task_(task), task_pending_(true, false) {} 146 147 bool TryRun() { 148 // This is meant to be called on the main thread where the texture 149 // is already bound. 150 DCHECK(checker_.CalledOnValidThread()); 151 if (task_lock_.Try()) { 152 // Only run once. 153 if (!task_.is_null()) 154 task_.Run(); 155 task_.Reset(); 156 157 task_lock_.Release(); 158 task_pending_.Signal(); 159 return true; 160 } 161 return false; 162 } 163 164 void BindAndRun(GLuint texture_id) { 165 // This is meant to be called on the upload thread where we don't have to 166 // restore the previous texture binding. 167 DCHECK(!checker_.CalledOnValidThread()); 168 base::AutoLock locked(task_lock_); 169 if (!task_.is_null()) { 170 glBindTexture(GL_TEXTURE_2D, texture_id); 171 task_.Run(); 172 task_.Reset(); 173 glBindTexture(GL_TEXTURE_2D, 0); 174 // Flush for synchronization between threads. 175 glFlush(); 176 task_pending_.Signal(); 177 } 178 } 179 180 void Cancel() { 181 base::AutoLock locked(task_lock_); 182 task_.Reset(); 183 task_pending_.Signal(); 184 } 185 186 bool TaskIsInProgress() { 187 return !task_pending_.IsSignaled(); 188 } 189 190 void WaitForTask() { 191 task_pending_.Wait(); 192 } 193 194 private: 195 friend class base::RefCountedThreadSafe<PendingTask>; 196 197 virtual ~PendingTask() {} 198 199 base::ThreadChecker checker_; 200 201 base::Lock task_lock_; 202 base::Closure task_; 203 base::WaitableEvent task_pending_; 204 205 DISALLOW_COPY_AND_ASSIGN(PendingTask); 206 }; 207 208 // Class which holds async pixel transfers state. 209 // The texture_id is accessed by either thread, but everything 210 // else accessed only on the main thread. 211 class TransferStateInternal 212 : public base::RefCountedThreadSafe<TransferStateInternal> { 213 public: 214 TransferStateInternal(GLuint texture_id, 215 const AsyncTexImage2DParams& define_params) 216 : texture_id_(texture_id), define_params_(define_params) {} 217 218 bool TransferIsInProgress() { 219 return pending_upload_task_.get() && 220 pending_upload_task_->TaskIsInProgress(); 221 } 222 223 void BindTransfer() { 224 TRACE_EVENT2("gpu", "BindAsyncTransfer", 225 "width", define_params_.width, 226 "height", define_params_.height); 227 DCHECK(texture_id_); 228 229 glBindTexture(GL_TEXTURE_2D, texture_id_); 230 bind_callback_.Run(); 231 } 232 233 void WaitForTransferCompletion() { 234 TRACE_EVENT0("gpu", "WaitForTransferCompletion"); 235 DCHECK(pending_upload_task_.get()); 236 if (!pending_upload_task_->TryRun()) { 237 pending_upload_task_->WaitForTask(); 238 } 239 pending_upload_task_ = NULL; 240 } 241 242 void CancelUpload() { 243 TRACE_EVENT0("gpu", "CancelUpload"); 244 if (pending_upload_task_.get()) 245 pending_upload_task_->Cancel(); 246 pending_upload_task_ = NULL; 247 } 248 249 void ScheduleAsyncTexImage2D( 250 const AsyncTexImage2DParams tex_params, 251 const AsyncMemoryParams mem_params, 252 scoped_refptr<AsyncPixelTransferUploadStats> texture_upload_stats, 253 const base::Closure& bind_callback) { 254 pending_upload_task_ = new PendingTask(base::Bind( 255 &TransferStateInternal::PerformAsyncTexImage2D, 256 this, 257 tex_params, 258 mem_params, 259 // Duplicate the shared memory so there is no way we can get 260 // a use-after-free of the raw pixels. 261 base::Owned(new ScopedSafeSharedMemory(safe_shared_memory_pool(), 262 mem_params.shared_memory, 263 mem_params.shm_size)), 264 texture_upload_stats)); 265 transfer_message_loop_proxy()->PostTask( 266 FROM_HERE, 267 base::Bind( 268 &PendingTask::BindAndRun, pending_upload_task_, texture_id_)); 269 270 // Save the late bind callback, so we can notify the client when it is 271 // bound. 272 bind_callback_ = bind_callback; 273 } 274 275 void ScheduleAsyncTexSubImage2D( 276 AsyncTexSubImage2DParams tex_params, 277 AsyncMemoryParams mem_params, 278 scoped_refptr<AsyncPixelTransferUploadStats> texture_upload_stats) { 279 pending_upload_task_ = new PendingTask(base::Bind( 280 &TransferStateInternal::PerformAsyncTexSubImage2D, 281 this, 282 tex_params, 283 mem_params, 284 base::Owned(new ScopedSafeSharedMemory(safe_shared_memory_pool(), 285 mem_params.shared_memory, 286 mem_params.shm_size)), 287 texture_upload_stats)); 288 transfer_message_loop_proxy()->PostTask( 289 FROM_HERE, 290 base::Bind( 291 &PendingTask::BindAndRun, pending_upload_task_, texture_id_)); 292 } 293 294 private: 295 friend class base::RefCountedThreadSafe<TransferStateInternal>; 296 297 virtual ~TransferStateInternal() { 298 } 299 300 void PerformAsyncTexImage2D( 301 AsyncTexImage2DParams tex_params, 302 AsyncMemoryParams mem_params, 303 ScopedSafeSharedMemory* safe_shared_memory, 304 scoped_refptr<AsyncPixelTransferUploadStats> texture_upload_stats) { 305 TRACE_EVENT2("gpu", 306 "PerformAsyncTexImage", 307 "width", 308 tex_params.width, 309 "height", 310 tex_params.height); 311 DCHECK_EQ(0, tex_params.level); 312 313 base::TimeTicks begin_time; 314 if (texture_upload_stats.get()) 315 begin_time = base::TimeTicks::HighResNow(); 316 317 void* data = 318 AsyncPixelTransferDelegate::GetAddress(safe_shared_memory, mem_params); 319 320 { 321 TRACE_EVENT0("gpu", "glTexImage2D"); 322 glTexImage2D(GL_TEXTURE_2D, 323 tex_params.level, 324 tex_params.internal_format, 325 tex_params.width, 326 tex_params.height, 327 tex_params.border, 328 tex_params.format, 329 tex_params.type, 330 data); 331 } 332 333 if (texture_upload_stats.get()) { 334 texture_upload_stats->AddUpload(base::TimeTicks::HighResNow() - 335 begin_time); 336 } 337 } 338 339 void PerformAsyncTexSubImage2D( 340 AsyncTexSubImage2DParams tex_params, 341 AsyncMemoryParams mem_params, 342 ScopedSafeSharedMemory* safe_shared_memory, 343 scoped_refptr<AsyncPixelTransferUploadStats> texture_upload_stats) { 344 TRACE_EVENT2("gpu", 345 "PerformAsyncTexSubImage2D", 346 "width", 347 tex_params.width, 348 "height", 349 tex_params.height); 350 DCHECK_EQ(0, tex_params.level); 351 352 base::TimeTicks begin_time; 353 if (texture_upload_stats.get()) 354 begin_time = base::TimeTicks::HighResNow(); 355 356 void* data = 357 AsyncPixelTransferDelegate::GetAddress(safe_shared_memory, mem_params); 358 359 { 360 TRACE_EVENT0("gpu", "glTexSubImage2D"); 361 glTexSubImage2D(GL_TEXTURE_2D, 362 tex_params.level, 363 tex_params.xoffset, 364 tex_params.yoffset, 365 tex_params.width, 366 tex_params.height, 367 tex_params.format, 368 tex_params.type, 369 data); 370 } 371 372 if (texture_upload_stats.get()) { 373 texture_upload_stats->AddUpload(base::TimeTicks::HighResNow() - 374 begin_time); 375 } 376 } 377 378 scoped_refptr<PendingTask> pending_upload_task_; 379 380 GLuint texture_id_; 381 382 // Definition params for texture that needs binding. 383 AsyncTexImage2DParams define_params_; 384 385 // Callback to invoke when AsyncTexImage2D is complete 386 // and the client can safely use the texture. This occurs 387 // during BindCompletedAsyncTransfers(). 388 base::Closure bind_callback_; 389 }; 390 391 } // namespace 392 393 class AsyncPixelTransferDelegateShareGroup 394 : public AsyncPixelTransferDelegate, 395 public base::SupportsWeakPtr<AsyncPixelTransferDelegateShareGroup> { 396 public: 397 AsyncPixelTransferDelegateShareGroup( 398 AsyncPixelTransferManagerShareGroup::SharedState* shared_state, 399 GLuint texture_id, 400 const AsyncTexImage2DParams& define_params); 401 virtual ~AsyncPixelTransferDelegateShareGroup(); 402 403 void BindTransfer() { state_->BindTransfer(); } 404 405 // Implement AsyncPixelTransferDelegate: 406 virtual void AsyncTexImage2D( 407 const AsyncTexImage2DParams& tex_params, 408 const AsyncMemoryParams& mem_params, 409 const base::Closure& bind_callback) OVERRIDE; 410 virtual void AsyncTexSubImage2D( 411 const AsyncTexSubImage2DParams& tex_params, 412 const AsyncMemoryParams& mem_params) OVERRIDE; 413 virtual bool TransferIsInProgress() OVERRIDE; 414 virtual void WaitForTransferCompletion() OVERRIDE; 415 416 private: 417 // A raw pointer is safe because the SharedState is owned by the Manager, 418 // which owns this Delegate. 419 AsyncPixelTransferManagerShareGroup::SharedState* shared_state_; 420 scoped_refptr<TransferStateInternal> state_; 421 422 DISALLOW_COPY_AND_ASSIGN(AsyncPixelTransferDelegateShareGroup); 423 }; 424 425 AsyncPixelTransferDelegateShareGroup::AsyncPixelTransferDelegateShareGroup( 426 AsyncPixelTransferManagerShareGroup::SharedState* shared_state, 427 GLuint texture_id, 428 const AsyncTexImage2DParams& define_params) 429 : shared_state_(shared_state), 430 state_(new TransferStateInternal(texture_id, define_params)) {} 431 432 AsyncPixelTransferDelegateShareGroup::~AsyncPixelTransferDelegateShareGroup() { 433 TRACE_EVENT0("gpu", " ~AsyncPixelTransferDelegateShareGroup"); 434 state_->CancelUpload(); 435 } 436 437 bool AsyncPixelTransferDelegateShareGroup::TransferIsInProgress() { 438 return state_->TransferIsInProgress(); 439 } 440 441 void AsyncPixelTransferDelegateShareGroup::WaitForTransferCompletion() { 442 if (state_->TransferIsInProgress()) { 443 state_->WaitForTransferCompletion(); 444 DCHECK(!state_->TransferIsInProgress()); 445 } 446 447 // Fast track the BindTransfer, if applicable. 448 for (AsyncPixelTransferManagerShareGroup::SharedState::TransferQueue::iterator 449 iter = shared_state_->pending_allocations.begin(); 450 iter != shared_state_->pending_allocations.end(); 451 ++iter) { 452 if (iter->get() != this) 453 continue; 454 455 shared_state_->pending_allocations.erase(iter); 456 BindTransfer(); 457 break; 458 } 459 } 460 461 void AsyncPixelTransferDelegateShareGroup::AsyncTexImage2D( 462 const AsyncTexImage2DParams& tex_params, 463 const AsyncMemoryParams& mem_params, 464 const base::Closure& bind_callback) { 465 DCHECK(mem_params.shared_memory); 466 DCHECK_LE(mem_params.shm_data_offset + mem_params.shm_data_size, 467 mem_params.shm_size); 468 DCHECK(!state_->TransferIsInProgress()); 469 DCHECK_EQ(static_cast<GLenum>(GL_TEXTURE_2D), tex_params.target); 470 DCHECK_EQ(tex_params.level, 0); 471 472 shared_state_->pending_allocations.push_back(AsWeakPtr()); 473 state_->ScheduleAsyncTexImage2D(tex_params, 474 mem_params, 475 shared_state_->texture_upload_stats, 476 bind_callback); 477 } 478 479 void AsyncPixelTransferDelegateShareGroup::AsyncTexSubImage2D( 480 const AsyncTexSubImage2DParams& tex_params, 481 const AsyncMemoryParams& mem_params) { 482 TRACE_EVENT2("gpu", "AsyncTexSubImage2D", 483 "width", tex_params.width, 484 "height", tex_params.height); 485 DCHECK(!state_->TransferIsInProgress()); 486 DCHECK(mem_params.shared_memory); 487 DCHECK_LE(mem_params.shm_data_offset + mem_params.shm_data_size, 488 mem_params.shm_size); 489 DCHECK_EQ(static_cast<GLenum>(GL_TEXTURE_2D), tex_params.target); 490 DCHECK_EQ(tex_params.level, 0); 491 492 state_->ScheduleAsyncTexSubImage2D( 493 tex_params, mem_params, shared_state_->texture_upload_stats); 494 } 495 496 AsyncPixelTransferManagerShareGroup::SharedState::SharedState() 497 // TODO(reveman): Skip this if --enable-gpu-benchmarking is not present. 498 : texture_upload_stats(new AsyncPixelTransferUploadStats) {} 499 500 AsyncPixelTransferManagerShareGroup::SharedState::~SharedState() {} 501 502 AsyncPixelTransferManagerShareGroup::AsyncPixelTransferManagerShareGroup( 503 gfx::GLContext* context) { 504 g_transfer_thread.Pointer()->InitializeOnMainThread(context); 505 } 506 507 AsyncPixelTransferManagerShareGroup::~AsyncPixelTransferManagerShareGroup() {} 508 509 void AsyncPixelTransferManagerShareGroup::BindCompletedAsyncTransfers() { 510 scoped_ptr<gfx::ScopedTextureBinder> texture_binder; 511 512 while (!shared_state_.pending_allocations.empty()) { 513 if (!shared_state_.pending_allocations.front().get()) { 514 shared_state_.pending_allocations.pop_front(); 515 continue; 516 } 517 AsyncPixelTransferDelegateShareGroup* delegate = 518 shared_state_.pending_allocations.front().get(); 519 // Terminate early, as all transfers finish in order, currently. 520 if (delegate->TransferIsInProgress()) 521 break; 522 523 if (!texture_binder) 524 texture_binder.reset(new gfx::ScopedTextureBinder(GL_TEXTURE_2D, 0)); 525 526 // Used to set tex info from the gles2 cmd decoder once upload has 527 // finished (it'll bind the texture and call a callback). 528 delegate->BindTransfer(); 529 530 shared_state_.pending_allocations.pop_front(); 531 } 532 } 533 534 void AsyncPixelTransferManagerShareGroup::AsyncNotifyCompletion( 535 const AsyncMemoryParams& mem_params, 536 AsyncPixelTransferCompletionObserver* observer) { 537 DCHECK(mem_params.shared_memory); 538 DCHECK_LE(mem_params.shm_data_offset + mem_params.shm_data_size, 539 mem_params.shm_size); 540 // Post a PerformNotifyCompletion task to the upload thread. This task 541 // will run after all async transfers are complete. 542 transfer_message_loop_proxy()->PostTask( 543 FROM_HERE, 544 base::Bind(&PerformNotifyCompletion, 545 mem_params, 546 base::Owned( 547 new ScopedSafeSharedMemory(safe_shared_memory_pool(), 548 mem_params.shared_memory, 549 mem_params.shm_size)), 550 make_scoped_refptr(observer))); 551 } 552 553 uint32 AsyncPixelTransferManagerShareGroup::GetTextureUploadCount() { 554 return shared_state_.texture_upload_stats->GetStats(NULL); 555 } 556 557 base::TimeDelta 558 AsyncPixelTransferManagerShareGroup::GetTotalTextureUploadTime() { 559 base::TimeDelta total_texture_upload_time; 560 shared_state_.texture_upload_stats->GetStats(&total_texture_upload_time); 561 return total_texture_upload_time; 562 } 563 564 void AsyncPixelTransferManagerShareGroup::ProcessMorePendingTransfers() { 565 } 566 567 bool AsyncPixelTransferManagerShareGroup::NeedsProcessMorePendingTransfers() { 568 return false; 569 } 570 571 AsyncPixelTransferDelegate* 572 AsyncPixelTransferManagerShareGroup::CreatePixelTransferDelegateImpl( 573 gles2::TextureRef* ref, 574 const AsyncTexImage2DParams& define_params) { 575 return new AsyncPixelTransferDelegateShareGroup( 576 &shared_state_, ref->service_id(), define_params); 577 } 578 579 } // namespace gpu 580